Skip to content

Commit

Permalink
feat: define multiple test cases for a single test
Browse files Browse the repository at this point in the history
  • Loading branch information
alexneo2003 committed Oct 31, 2022
1 parent da3dc5e commit a89847a
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 66 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
});
});
```

Expand Down
115 changes: 65 additions & 50 deletions src/playwright-azure-reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -454,55 +465,59 @@ class AzureDevOpsReporter implements Reporter {
}

private async _publishCaseResult(test: TestCase, testResult: TestResult): Promise<TestResultsToTestRun | void> {
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));
}
})
);
}
}

Expand Down
38 changes: 25 additions & 13 deletions tests/example.spec.ts
Original file line number Diff line number Diff line change
@@ -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/);
});
});
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -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. */
Expand Down

0 comments on commit a89847a

Please sign in to comment.