From d9e480f6e2a780bc1333684e761cad88d0f7ca84 Mon Sep 17 00:00:00 2001 From: reportportal-agents-ai Date: Wed, 27 May 2026 14:44:18 +0400 Subject: [PATCH 1/2] [EPMRPP-113709] [AGENT][Perf] Introduce the retry_of property for JS agents (ai) --- CHANGELOG.md | 5 ++ __tests__/report-portal-client.spec.js | 96 ++++++++++++++++++++++++++ index.d.ts | 12 ++++ lib/report-portal-client.js | 5 +- 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc0a5e8..1c6d94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### Added +- `retry_of` property is now automatically included in the `startTestItem` + request payload when `retry: true` and a previous attempt exists in the + retry chain. This allows the ReportPortal backend to link retry chains + efficiently, improving query performance for large test runs. ## [5.5.12] - 2026-07-06 ### Changed diff --git a/__tests__/report-portal-client.spec.js b/__tests__/report-portal-client.spec.js index 4754279..afb5479 100644 --- a/__tests__/report-portal-client.spec.js +++ b/__tests__/report-portal-client.spec.js @@ -879,6 +879,102 @@ describe('ReportPortal javascript client', () => { expect(client.itemRetriesChainMap.get).toHaveBeenCalledWith('id1__name__'); }); + + it('should include retry_of with the previous item UUID when retry is true', async () => { + const client = new RPClient({ + apiKey: 'test', + endpoint: 'https://rp.us/api/v1', + project: 'tst', + }); + const prevRealId = 'prev-item-uuid-1234'; + const prevPromise = Promise.resolve({ id: prevRealId }); + + client.map = { + launchId: { + children: [], + finishSend: false, + promiseStart: Promise.resolve(), + }, + }; + + const itemKey = client.calculateItemRetriesChainMapKey( + 'launchId', undefined, 'My test', undefined, + ); + client.itemRetriesChainMap.set(itemKey, prevPromise); + + jest.spyOn(client.restClient, 'create').mockResolvedValue({ id: 'new-item-uuid' }); + jest.spyOn(client, 'getUniqId').mockReturnValue('newTempId'); + + await client.startTestItem( + { name: 'My test', type: 'STEP', retry: true }, + 'launchId', + ).promise; + + expect(client.restClient.create).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ retry_of: prevRealId }), + ); + }); + + it('should not include retry_of when retry is true but no previous entry exists', async () => { + const client = new RPClient({ + apiKey: 'test', + endpoint: 'https://rp.us/api/v1', + project: 'tst', + }); + client.map = { + launchId: { + children: [], + finishSend: false, + promiseStart: Promise.resolve(), + }, + }; + jest.spyOn(client.restClient, 'create').mockResolvedValue({ id: 'new-item-uuid' }); + jest.spyOn(client, 'getUniqId').mockReturnValue('newTempId'); + + await client.startTestItem( + { name: 'My test', type: 'STEP', retry: true }, + 'launchId', + ).promise; + + expect(client.restClient.create).toHaveBeenCalledWith( + expect.any(String), + expect.not.objectContaining({ retry_of: expect.anything() }), + ); + }); + + it('should not include retry_of when retry is false', async () => { + const client = new RPClient({ + apiKey: 'test', + endpoint: 'https://rp.us/api/v1', + project: 'tst', + }); + const prevPromise = Promise.resolve({ id: 'prev-item-uuid-1234' }); + client.map = { + launchId: { + children: [], + finishSend: false, + promiseStart: Promise.resolve(), + }, + }; + const itemKey = client.calculateItemRetriesChainMapKey( + 'launchId', undefined, 'My test', undefined, + ); + client.itemRetriesChainMap.set(itemKey, prevPromise); + + jest.spyOn(client.restClient, 'create').mockResolvedValue({ id: 'new-item-uuid' }); + jest.spyOn(client, 'getUniqId').mockReturnValue('newTempId'); + + await client.startTestItem( + { name: 'My test', type: 'STEP', retry: false }, + 'launchId', + ).promise; + + expect(client.restClient.create).toHaveBeenCalledWith( + expect.any(String), + expect.not.objectContaining({ retry_of: expect.anything() }), + ); + }); }); describe('finishTestItem', () => { diff --git a/index.d.ts b/index.d.ts index fedd35d..46c684e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -200,6 +200,18 @@ declare module '@reportportal/client-javascript' { startTime?: string | number; attributes?: Array<{ key?: string; value?: string } | string>; hasStats?: boolean; + /** + * Set to true when this item is a retry of a previous attempt. + * The client will automatically populate `retry_of` with the UUID of the + * previous attempt. + */ + retry?: boolean; + /** + * UUID of the immediately-preceding retry attempt. + * Populated automatically by the client when `retry: true` and a previous + * attempt exists. Do not set manually. + */ + retry_of?: string; } /** diff --git a/lib/report-portal-client.js b/lib/report-portal-client.js index c835772..562d5a4 100644 --- a/lib/report-portal-client.js +++ b/lib/report-portal-client.js @@ -562,13 +562,16 @@ class RPClient { const tempId = this.getUniqId(); this.map[tempId] = this.getNewItemObj((resolve, reject) => { (executionItemPromise || parentPromise).then( - () => { + (prevResponse) => { const realLaunchId = this.map[launchTempId].realId; let url = 'item/'; if (parentTempId) { const realParentId = this.map[parentTempId].realId; url += `${realParentId}`; } + if (executionItemPromise && prevResponse?.id) { + testItemData.retry_of = prevResponse.id; + } testItemData.launchUuid = realLaunchId; this.logDebug(`Start test item with tempId ${tempId}`, testItemData); this.restClient.create(url, testItemData).then( From 5fc6c5ec82794c9e05dcadd5dc74578bd9fbf8cb Mon Sep 17 00:00:00 2001 From: Ilya_Hancharyk Date: Mon, 13 Jul 2026 12:58:20 +0200 Subject: [PATCH 2/2] TS. Enhance start test items types --- index.d.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 46c684e..75dd7e1 100644 --- a/index.d.ts +++ b/index.d.ts @@ -212,6 +212,9 @@ declare module '@reportportal/client-javascript' { * attempt exists. Do not set manually. */ retry_of?: string; + codeRef?: string; + parameters?: Array<{ key: string; value: string }>; + testCaseId?: string; } /** @@ -280,7 +283,10 @@ declare module '@reportportal/client-javascript' { /** * Initializes a new Report Portal client. */ - constructor(config: ReportPortalConfig, agentInfo?: { name?: string; version?: string; framework_version?: string }); + constructor( + config: ReportPortalConfig, + agentInfo?: { name?: string; version?: string; framework_version?: string }, + ); /** * Starts a new launch.