From a89847acd8a4115cfb54329b27c7941ac403a792 Mon Sep 17 00:00:00 2001 From: Alex Neo Date: Mon, 31 Oct 2022 14:05:31 +0200 Subject: [PATCH] feat: define multiple test cases for a single test --- README.md | 27 +++++++- src/playwright-azure-reporter.ts | 115 +++++++++++++++++-------------- tests/example.spec.ts | 38 ++++++---- tsconfig.json | 2 +- 4 files changed, 116 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 8846303..6e6c7f5 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,13 @@ You must register an ID already existing test cases from Azure DevOps before run > **You need write testCaseId wraped in square brackets at the test name.** +You can define multiple test cases for a single test with next format: + +- `[1] Test name` - single test case +- `[1,2,3] Test name` - multiple test cases +- `[16, 17, 18] Test name` - multiple test cases with spaces +- `[1, 2, 3] Test name [4] Test name [5][6] Test name` - with combined format + For example: ```typescript @@ -20,17 +27,33 @@ describe('Test suite', () => { expect(true).toBe(true); }); - test('[3] Correct test', () => { + test('Correct test [3]', () => { expect(true).toBe(true); }); - test.skip('[4] Skipped test', () => { + test.skip('Skipped test [4]', () => { expect(true).toBe(true); }); test('[6] Failed test', () => { expect(true).toBe(false); }); + + test('[7] Test seven [8] Test eight [9] Test nine', () => { + expect(true).toBe(true); + }); + + test('[10,11,12] Test ten, eleven, twelve', () => { + expect(true).toBe(true); + }); + + test('[13, 14, 15] Test thirteen, fourteen, fifteen', () => { + expect(true).toBe(true); + }); + + test('[16, 17, 18] Test sixteen, seventeen, eighteen [19] Test nineteen', () => { + expect(true).toBe(true); + }); }); ``` diff --git a/src/playwright-azure-reporter.ts b/src/playwright-azure-reporter.ts index 7615c55..1c73ba4 100644 --- a/src/playwright-azure-reporter.ts +++ b/src/playwright-azure-reporter.ts @@ -18,6 +18,10 @@ export function createGuid(): string { return crypto.randomBytes(16).toString('hex'); } +export function shortID(): string { + return crypto.randomBytes(8).toString('hex'); +} + enum AzureTestStatuses { passed = 'Passed', failed = 'Failed', @@ -266,9 +270,16 @@ class AzureDevOpsReporter implements Reporter { console.log(`${chalk.magenta('azure:')} ${chalk.yellow(message)}`); } - private _getCaseIds(test: TestCase): string | undefined { - const results = /\[([\d,]+)\]/.exec(test.title); - if (results && results.length === 2) return results[1]; + private _getCaseIds(test: TestCase): string[] { + const result: string[] = []; + const regex = new RegExp(/\[([\d,\s]+)\]/, 'gm'); + const matchesAll = test.title.matchAll(regex); + const matches = [...matchesAll].map((match) => match[1]); + matches.forEach((match) => { + const ids = match.split(',').map((id) => id.trim()); + result.push(...ids); + }); + return result; } private _logTestItem(test: TestCase, testResult: TestResult) { @@ -454,55 +465,59 @@ class AzureDevOpsReporter implements Reporter { } private async _publishCaseResult(test: TestCase, testResult: TestResult): Promise { - const caseId = this._getCaseIds(test); - if (!caseId) return; - - const testAlias = `${caseId} - ${test.title}`; - this.resultsToBePublished.push(testAlias); - try { - const runId = await this.runIdPromise; - this._log(chalk.gray(`Start publishing: ${test.title}`)); + const caseIds = this._getCaseIds(test); + if (!caseIds || !caseIds.length) return; - const points = await this._getTestPointIdsByTCIds(this.planId as number, [parseInt(caseId, 10)]); - if (!points.point) { - this._removePublished(testAlias); - throw new Error(`No test points found for test case [${caseId}]`); - } - const results: TestInterfaces.TestCaseResult[] = [ - { - testCase: { id: caseId }, - testPoint: { id: String(points.point) }, - testCaseTitle: test.title, - outcome: AzureTestStatuses[testResult.status], - state: 'Completed', - durationInMs: testResult.duration, - errorMessage: testResult.error - ? `${test.title}: ${testResult.error?.message?.replace(/\u001b\[.*?m/g, '') as string}` - : undefined, - stackTrace: testResult.error?.stack?.replace(/\u001b\[.*?m/g, ''), - ...(points.configurationId && { - configuration: { id: points.configurationId, name: points.configurationName }, - }), - }, - ]; + await Promise.all( + caseIds.map(async (caseId) => { + const testAlias = `${shortID()} - ${test.title}`; + this.resultsToBePublished.push(testAlias); + try { + const runId = await this.runIdPromise; + this._log(chalk.gray(`Start publishing: TC:${caseId} - ${test.title}`)); - if (!this.testApi) this.testApi = await this.connection.getTestApi(); - const testCaseResult: TestResultsToTestRun = (await this._addReportingOverride( - this.testApi - ).addTestResultsToTestRun(results, this.projectName, runId!)) as unknown as TestResultsToTestRun; - if (!testCaseResult?.result) throw new Error(`Failed to publish test result for test case [${caseId}]`); - - if (this.uploadAttachments && testResult.attachments.length > 0) - await this._uploadAttachmentsFunc(testResult, testCaseResult.result.value![0].id, caseId); - - this._removePublished(testAlias); - this.publishedResultsCount++; - this._log(chalk.gray(`Result published: ${test.title}`)); - return testCaseResult; - } catch (error: any) { - this._removePublished(testAlias); - this._warning(chalk.red(error.message)); - } + const points = await this._getTestPointIdsByTCIds(this.planId as number, [parseInt(caseId, 10)]); + if (!points.point) { + this._removePublished(testAlias); + throw new Error(`No test points found for test case [${caseIds}]`); + } + const results: TestInterfaces.TestCaseResult[] = [ + { + testCase: { id: caseId }, + testPoint: { id: String(points.point) }, + testCaseTitle: test.title, + outcome: AzureTestStatuses[testResult.status], + state: 'Completed', + durationInMs: testResult.duration, + errorMessage: testResult.error + ? `${test.title}: ${testResult.error?.message?.replace(/\u001b\[.*?m/g, '') as string}` + : undefined, + stackTrace: testResult.error?.stack?.replace(/\u001b\[.*?m/g, ''), + ...(points.configurationId && { + configuration: { id: points.configurationId, name: points.configurationName }, + }), + }, + ]; + + if (!this.testApi) this.testApi = await this.connection.getTestApi(); + const testCaseResult: TestResultsToTestRun = (await this._addReportingOverride( + this.testApi + ).addTestResultsToTestRun(results, this.projectName, runId!)) as unknown as TestResultsToTestRun; + if (!testCaseResult?.result) throw new Error(`Failed to publish test result for test case [${caseId}]`); + + if (this.uploadAttachments && testResult.attachments.length > 0) + await this._uploadAttachmentsFunc(testResult, testCaseResult.result.value![0].id, caseId); + + this._removePublished(testAlias); + this.publishedResultsCount++; + this._log(chalk.gray(`Result published: TC:${caseId} - ${test.title}`)); + return testCaseResult; + } catch (error: any) { + this._removePublished(testAlias); + this._warning(chalk.red(error.message)); + } + }) + ); } } diff --git a/tests/example.spec.ts b/tests/example.spec.ts index 470885a..fcda3d4 100644 --- a/tests/example.spec.ts +++ b/tests/example.spec.ts @@ -1,20 +1,32 @@ -import { expect, test } from '@playwright/test' +import { expect, test } from '@playwright/test'; test.beforeEach(async ({ page }) => { - await page.goto('https://playwright.dev/') -}) + await page.goto('https://playwright.dev/'); +}); test.describe('Describe', () => { test('[3] Should page opened', async ({ page }) => { - await page.locator('text=Get started').click() - await expect(page).toHaveTitle(/Getting started/) - }) + await page.locator('text=Get started').click(); + await expect(page).toHaveTitle(/Getting started/); + }); test('[7] Should page closed', async ({ page }) => { - await page.locator('text=Get started').click() - await expect(page).toHaveTitle(/Getting started/) - }) + await page.locator('text=Get started').click(); + await expect(page).toHaveTitle(/Getting started/); + }); test('[8] Awaiting for user input', async ({ page }) => { - await page.locator('text=Get started').click() - await expect(page).toHaveTitle(/Getting started/) - }) -}) + await page.locator('text=Get started').click(); + await expect(page).toHaveTitle(/Getting started/); + }); + test('[3] [7] [8] Awaiting for user input', async ({ page }) => { + await page.locator('text=Get started').click(); + await expect(page).toHaveTitle(/Getting started/); + }); + test('[3,7,8] Awaiting for user input', async ({ page }) => { + await page.locator('text=Get started').click(); + await expect(page).toHaveTitle(/Getting started/); + }); + test('[3, 7, 8] Awaiting for user input', async ({ page }) => { + await page.locator('text=Get started').click(); + await expect(page).toHaveTitle(/Getting started/); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 835f574..3003b78 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { /* Basic Options */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["es2020", "dom"], /* Specify library files to be included in the compilation. */ "declaration": true, /* Generates corresponding '.d.ts' file. */