From f24d255cfdfed3b393412d9eaebb88c174ebc93e Mon Sep 17 00:00:00 2001 From: reportportal-agents-ai Date: Tue, 9 Jun 2026 16:15:18 +0000 Subject: [PATCH] [EPMRPP-113709] Introduce the retry_of property for JS agents (ai) --- CHANGELOG.md | 4 ++ __tests__/report-portal-client.spec.js | 80 ++++++++++++++++++++++++++ lib/report-portal-client.js | 15 ++++- package-lock.json | 2 +- test-output.log | 5 ++ 5 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 test-output.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 7800f22..50c4c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ +## [Unreleased] +### Added +- `retry_of` property support for test item start request to link retried test items to their predecessors in the hierarchy, improving performance tracking and metrics accuracy. + ## [5.5.11] - 2026-05-22 ### Added - Google Analytics improvements. diff --git a/__tests__/report-portal-client.spec.js b/__tests__/report-portal-client.spec.js index 4754279..d01fc7b 100644 --- a/__tests__/report-portal-client.spec.js +++ b/__tests__/report-portal-client.spec.js @@ -879,6 +879,86 @@ describe('ReportPortal javascript client', () => { expect(client.itemRetriesChainMap.get).toHaveBeenCalledWith('id1__name__'); }); + + it('should add retry_of property when retrying an item', async () => { + const client = new RPClient({ + apiKey: 'startLaunchTest', + endpoint: 'https://rp.us/api/v1', + project: 'tst', + }); + client.map = { + launchId: { + children: ['tempId1', 'tempId2'], + promiseStart: Promise.resolve(), + realId: 'launchRealId', + }, + tempId1: { + children: [], + promiseStart: Promise.resolve(), + realId: 'item1RealId', + }, + tempId2: { + children: [], + promiseStart: Promise.resolve(), + }, + }; + const itemKey = 'launchId__name__'; + jest.spyOn(client, 'calculateItemRetriesChainMapKey').mockReturnValue(itemKey); + jest.spyOn(client, 'getUniqId').mockReturnValue('tempId2'); + jest.spyOn(client.restClient, 'create').mockResolvedValue({ id: 'item2RealId' }); + + // First item - store it in the retry chain map + client.itemRetriesChainMap.set(itemKey, client.map.tempId1); + + // Now retry with second item + const result = client.startTestItem({ name: 'test', type: 'TEST', retry: true }, 'launchId'); + await result.promise; + + // Verify the API was called with retry_of in the payload + expect(client.restClient.create).toHaveBeenCalledWith( + 'item/', + expect.objectContaining({ + retry_of: 'item1RealId', + }), + ); + }); + + it('should not add retry_of if previous item not in chain map', async () => { + const client = new RPClient({ + apiKey: 'startLaunchTest', + endpoint: 'https://rp.us/api/v1', + project: 'tst', + }); + client.map = { + launchId: { + children: ['tempId1'], + promiseStart: Promise.resolve(), + realId: 'launchRealId', + }, + tempId1: { + children: [], + promiseStart: Promise.resolve(), + }, + }; + const itemKey = 'launchId__name__'; + jest.spyOn(client, 'calculateItemRetriesChainMapKey').mockReturnValue(itemKey); + jest.spyOn(client, 'getUniqId').mockReturnValue('tempId1'); + jest.spyOn(client.restClient, 'create').mockResolvedValue({ id: 'item1RealId' }); + + // Manually clear the retry chain map + client.itemRetriesChainMap.clear(); + + const result = client.startTestItem({ name: 'test', type: 'TEST', retry: true }, 'launchId'); + await result.promise; + + // Verify the API was called WITHOUT retry_of + expect(client.restClient.create).toHaveBeenCalledWith( + 'item/', + expect.not.objectContaining({ + retry_of: expect.anything(), + }), + ); + }); }); describe('finishTestItem', () => { diff --git a/lib/report-portal-client.js b/lib/report-portal-client.js index 7adbb25..7b16cb3 100644 --- a/lib/report-portal-client.js +++ b/lib/report-portal-client.js @@ -557,7 +557,12 @@ class RPClient { testItemDataRQ.name, testItemDataRQ.uniqueId, ); - const executionItemPromise = testItemDataRQ.retry && this.itemRetriesChainMap.get(itemKey); + let previousItemObj = null; + if (testItemDataRQ.retry) { + previousItemObj = this.itemRetriesChainMap.get(itemKey); + // previousItemObj may not have realId yet if promise hasn't resolved + } + const executionItemPromise = previousItemObj && previousItemObj.promiseStart; const tempId = this.getUniqId(); this.map[tempId] = this.getNewItemObj((resolve, reject) => { @@ -570,6 +575,12 @@ class RPClient { url += `${realParentId}`; } testItemData.launchUuid = realLaunchId; + + // Add retry_of if this is a retry and previous item exists + if (previousItemObj && previousItemObj.realId) { + testItemData.retry_of = previousItemObj.realId; + } + this.logDebug(`Start test item with tempId ${tempId}`, testItemData); this.restClient.create(url, testItemData).then( (response) => { @@ -591,7 +602,7 @@ class RPClient { }); this.map[parentMapId].children.push(tempId); this.itemRetriesChainKeyMapByTempId.set(tempId, itemKey); - this.itemRetriesChainMap.set(itemKey, this.map[tempId].promiseStart); + this.itemRetriesChainMap.set(itemKey, this.map[tempId]); return { tempId, diff --git a/package-lock.json b/package-lock.json index 0312957..46b051f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@reportportal/client-javascript", - "version": "5.5.10", + "version": "5.5.11", "license": "Apache-2.0", "dependencies": { "axios": "^1.15.2", diff --git a/test-output.log b/test-output.log new file mode 100644 index 0000000..5aaf11e --- /dev/null +++ b/test-output.log @@ -0,0 +1,5 @@ + +> @reportportal/client-javascript@5.5.11 test +> jest + +sh: 1: jest: not found