diff --git a/src/api/schemas.ts b/src/api/schemas.ts index 903ff48..16e809c 100644 --- a/src/api/schemas.ts +++ b/src/api/schemas.ts @@ -23,6 +23,7 @@ export interface CreateResultsRequestItem { tcaseId: string status: ResultStatus comment?: string + timeTaken: number | null // In milliseconds } export interface CreateResultsRequest { diff --git a/src/tests/fixtures/playwright-json/comprehensive-test.json b/src/tests/fixtures/playwright-json/comprehensive-test.json index 37be6cd..ebda228 100644 --- a/src/tests/fixtures/playwright-json/comprehensive-test.json +++ b/src/tests/fixtures/playwright-json/comprehensive-test.json @@ -22,6 +22,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [] } ], @@ -48,6 +49,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 6200, "attachments": [] } ], @@ -74,6 +76,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 2000, "attachments": [] } ], @@ -106,6 +109,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1400, "attachments": [] } ], @@ -132,6 +136,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 500, "attachments": [] } ], @@ -158,6 +163,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 5050, "attachments": [] } ], @@ -186,6 +192,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 0, "attachments": [] } ], @@ -213,6 +220,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 0, "attachments": [] } ], @@ -239,6 +247,7 @@ ], "stderr": [], "retry": 0, + "duration": 0, "attachments": [] } ], @@ -270,6 +279,7 @@ ], "stderr": [], "retry": 0, + "duration": 0, "attachments": [] } ], @@ -296,6 +306,7 @@ ], "stderr": [], "retry": 0, + "duration": 10000, "attachments": [] } ], @@ -318,6 +329,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "screenshot", diff --git a/src/tests/fixtures/playwright-json/empty-tsuite.json b/src/tests/fixtures/playwright-json/empty-tsuite.json index a6ec7c7..cffc021 100644 --- a/src/tests/fixtures/playwright-json/empty-tsuite.json +++ b/src/tests/fixtures/playwright-json/empty-tsuite.json @@ -18,6 +18,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 0, "attachments": [] } ], diff --git a/src/tests/fixtures/playwright-json/matching-tcases.json b/src/tests/fixtures/playwright-json/matching-tcases.json index c1254f7..96728e2 100644 --- a/src/tests/fixtures/playwright-json/matching-tcases.json +++ b/src/tests/fixtures/playwright-json/matching-tcases.json @@ -18,6 +18,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -46,6 +47,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -80,6 +82,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -112,6 +115,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -140,6 +144,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", diff --git a/src/tests/fixtures/playwright-json/missing-attachments.json b/src/tests/fixtures/playwright-json/missing-attachments.json index 69ec500..48d85ca 100644 --- a/src/tests/fixtures/playwright-json/missing-attachments.json +++ b/src/tests/fixtures/playwright-json/missing-attachments.json @@ -18,6 +18,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -46,6 +47,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -80,6 +82,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -112,6 +115,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -140,6 +144,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", diff --git a/src/tests/fixtures/playwright-json/missing-tcases.json b/src/tests/fixtures/playwright-json/missing-tcases.json index ba5c462..8cd343b 100644 --- a/src/tests/fixtures/playwright-json/missing-tcases.json +++ b/src/tests/fixtures/playwright-json/missing-tcases.json @@ -18,6 +18,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -46,6 +47,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -80,6 +82,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -108,6 +111,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -140,6 +144,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", @@ -168,6 +173,7 @@ "stdout": [], "stderr": [], "retry": 0, + "duration": 1000, "attachments": [ { "name": "attachment", diff --git a/src/tests/junit-xml-parsing.spec.ts b/src/tests/junit-xml-parsing.spec.ts index fdf4739..4b16e35 100644 --- a/src/tests/junit-xml-parsing.spec.ts +++ b/src/tests/junit-xml-parsing.spec.ts @@ -1,6 +1,6 @@ import { expect, test, describe } from 'vitest' +import { readFile } from 'node:fs/promises' import { parseJUnitXml } from '../utils/result-upload/junitXmlParser' -import { readFile } from 'fs/promises' const xmlBasePath = './src/tests/fixtures/junit-xml' @@ -46,6 +46,7 @@ describe('Junit XML parsing', () => { expect(tc).toHaveProperty('status') expect(tc).toHaveProperty('message') expect(tc).toHaveProperty('attachments') + expect(tc).toHaveProperty('timeTaken') expect(Array.isArray(tc.attachments)).toBe(true) }) }) @@ -184,7 +185,7 @@ describe('Junit XML parsing', () => { const xml = ` - + stdout content stderr content @@ -198,6 +199,7 @@ describe('Junit XML parsing', () => { expect(testcases).toHaveLength(1) expect(testcases[0].status).toBe('passed') + expect(testcases[0].timeTaken).toBe(10500) // Should include stdout but not stderr for passed tests expect(testcases[0].message).toContain('stdout content') expect(testcases[0].message).not.toContain('stderr content') @@ -207,7 +209,7 @@ describe('Junit XML parsing', () => { const xml = ` - + Failure details stdout from failed test stderr from failed test @@ -222,6 +224,7 @@ describe('Junit XML parsing', () => { expect(testcases).toHaveLength(1) expect(testcases[0].status).toBe('failed') + expect(testcases[0].timeTaken).toBe(0) // Should include both stdout and stderr for failed tests expect(testcases[0].message).toContain('Failure details') expect(testcases[0].message).toContain('stdout from failed test') @@ -232,7 +235,7 @@ describe('Junit XML parsing', () => { const xml = ` - + stdout content stderr content @@ -246,6 +249,7 @@ describe('Junit XML parsing', () => { expect(testcases).toHaveLength(1) expect(testcases[0].status).toBe('passed') + expect(testcases[0].timeTaken).toBe(null) // Should not include stdout or stderr for passed tests expect(testcases[0].message).toBe('') }) diff --git a/src/tests/playwright-json-parsing.spec.ts b/src/tests/playwright-json-parsing.spec.ts index c6d51a5..89f2184 100644 --- a/src/tests/playwright-json-parsing.spec.ts +++ b/src/tests/playwright-json-parsing.spec.ts @@ -44,6 +44,7 @@ describe('Playwright JSON parsing', () => { expect(tc).toHaveProperty('status') expect(tc).toHaveProperty('message') expect(tc).toHaveProperty('attachments') + expect(tc).toHaveProperty('timeTaken') expect(Array.isArray(tc.attachments)).toBe(true) }) }) @@ -60,6 +61,7 @@ describe('Playwright JSON parsing', () => { // Should only have the one test from ui.cart.spec.ts, not the empty ui.contents.spec.ts expect(testcases).toHaveLength(1) expect(testcases[0].name).toContain('Test cart TEST-002') + expect(testcases[0].timeTaken).toBe(0) }) test('Should use last result when there are retries', async () => { @@ -83,6 +85,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1300, attachments: [], }, { @@ -91,6 +94,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 1, + duration: 1200, attachments: [], }, ], @@ -111,6 +115,7 @@ describe('Playwright JSON parsing', () => { expect(testcases).toHaveLength(1) // Should use the last result (passed on retry) + expect(testcases[0].timeTaken).toBe(1200) expect(testcases[0].status).toBe('passed') expect(testcases[0].message).toContain('Test passed in 2 attempts') }) @@ -136,6 +141,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1000, attachments: [], }, ], @@ -163,6 +169,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 5100, attachments: [], }, ], @@ -191,6 +198,10 @@ describe('Playwright JSON parsing', () => { // Verify nested test has suite title as prefix expect(testcases[1].name).toContain('Nested Suite') expect(testcases[1].name).toContain('Nested test') + + // Verify time taken is set to duration of the test + expect(testcases[0].timeTaken).toBe(1000) + expect(testcases[1].timeTaken).toBe(5100) }) test('Should strip ANSI escape codes from errors and output', async () => { @@ -227,6 +238,7 @@ describe('Playwright JSON parsing', () => { }, ], retry: 0, + duration: 1000, attachments: [], }, ], @@ -286,6 +298,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1000, attachments: [], }, ], @@ -308,6 +321,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1000, attachments: [], }, ], @@ -335,6 +349,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1000, attachments: [], }, ], @@ -385,6 +400,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1000, attachments: [], }, ], @@ -407,7 +423,14 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, - attachments: [], + duration: 1000, + attachments: [ + { + name: 'screenshot', + contentType: 'image/png', + path: '../test-results/ui.cart-Test-cart-chromium/test-finished-1.png', + }, + ], }, ], status: 'unexpected', @@ -429,6 +452,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1000, attachments: [], }, { @@ -437,6 +461,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 1, + duration: 1000, attachments: [], }, ], @@ -459,6 +484,7 @@ describe('Playwright JSON parsing', () => { stdout: [], stderr: [], retry: 0, + duration: 1000, attachments: [], }, ], @@ -505,6 +531,7 @@ describe('Playwright JSON parsing', () => { stdout: [{ text: 'stdout content' }], stderr: [{ text: 'stderr content' }], retry: 0, + duration: 1000, attachments: [], }, ], @@ -550,7 +577,14 @@ describe('Playwright JSON parsing', () => { stdout: [{ text: 'stdout content' }], stderr: [{ text: 'stderr content' }], retry: 0, - attachments: [], + duration: 1000, + attachments: [ + { + name: 'screenshot', + contentType: 'image/png', + path: '../test-results/ui.cart-Test-cart-chromium/test-finished-1.png', + }, + ], }, ], status: 'expected', @@ -595,6 +629,7 @@ describe('Playwright JSON parsing', () => { stdout: [{ text: 'stdout content' }], stderr: [{ text: 'stderr content' }], retry: 0, + duration: 1000, attachments: [], }, ], @@ -640,6 +675,7 @@ describe('Playwright JSON parsing', () => { stdout: [{ text: 'stdout from failed test' }], stderr: [{ text: 'stderr from failed test' }], retry: 0, + duration: 1000, attachments: [], }, ], @@ -686,6 +722,7 @@ describe('Playwright JSON parsing', () => { stdout: [{ text: 'stdout content' }], stderr: [{ text: 'stderr content' }], retry: 0, + duration: 1000, attachments: [], }, ], diff --git a/src/utils/result-upload/ResultUploader.ts b/src/utils/result-upload/ResultUploader.ts index 55f2485..dfcb16c 100644 --- a/src/utils/result-upload/ResultUploader.ts +++ b/src/utils/result-upload/ResultUploader.ts @@ -246,6 +246,7 @@ ${chalk.yellow('To fix this issue, choose one of the following options:')} tcaseId: tcase.id, status: result.status, comment: result.message, + timeTaken: result.timeTaken, })), }) diff --git a/src/utils/result-upload/junitXmlParser.ts b/src/utils/result-upload/junitXmlParser.ts index 6bac18c..21d0712 100644 --- a/src/utils/result-upload/junitXmlParser.ts +++ b/src/utils/result-upload/junitXmlParser.ts @@ -92,11 +92,16 @@ export const parseJUnitXml: Parser = async ( for (const suite of validated.testsuites.testsuite) { for (const tcase of suite.testcase ?? []) { const result = getResult(tcase, options) + const timeTakenSeconds = Number.parseFloat(tcase.$.time ?? '') const index = testcases.push({ + ...result, folder: suite.$.name ?? '', name: tcase.$.name ?? '', - ...result, + timeTaken: + Number.isFinite(timeTakenSeconds) && timeTakenSeconds >= 0 + ? timeTakenSeconds * 1000 + : null, attachments: [], }) - 1 diff --git a/src/utils/result-upload/playwrightJsonParser.ts b/src/utils/result-upload/playwrightJsonParser.ts index c322dfc..6ba15ff 100644 --- a/src/utils/result-upload/playwrightJsonParser.ts +++ b/src/utils/result-upload/playwrightJsonParser.ts @@ -39,6 +39,7 @@ const resultSchema = z.object({ stdout: stdioEntrySchema.array(), stderr: stdioEntrySchema.array(), retry: z.number(), + duration: z.number(), attachments: attachmentSchema.array().optional(), annotations: annotationSchema.array().optional(), }) @@ -112,6 +113,7 @@ export const parsePlaywrightJson: Parser = async ( folder: topLevelSuite, status, message: buildMessage(result, status, options), + timeTaken: result.duration, attachments: [], }) diff --git a/src/utils/result-upload/types.ts b/src/utils/result-upload/types.ts index 0a51eb6..d46e3fc 100644 --- a/src/utils/result-upload/types.ts +++ b/src/utils/result-upload/types.ts @@ -14,5 +14,6 @@ export interface TestCaseResult { folder: string status: ResultStatus message: string + timeTaken: number | null // In milliseconds attachments: Attachment[] }