Skip to content

Commit

Permalink
Merge pull request #80 from alexneo2003/78-feature-expand-testcaseidm…
Browse files Browse the repository at this point in the history
…atcher-to-work-with-annotations

feat: match test case IDs from test annotations
  • Loading branch information
alexneo2003 authored Sep 26, 2024
2 parents 9d89fe9 + 7ab8fc3 commit 5ca5d51
Show file tree
Hide file tree
Showing 6 changed files with 602 additions and 148 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# [1.12.0](https://github.com/alexneo2003/playwright-azure-reporter/compare/v1.11.0...v1.12.0) (2024-09-26)


### Features

* match test case IDs from test annotations ([ffb0d54](https://github.com/alexneo2003/playwright-azure-reporter/commit/ffb0d5484a436e96837992d55925876621790a11))



# [1.12.0-beta.0](https://github.com/alexneo2003/playwright-azure-reporter/compare/v1.11.0...v1.12.0-beta.0) (2024-09-22)


### Features

* match test case IDs from test annotations ([ffb0d54](https://github.com/alexneo2003/playwright-azure-reporter/commit/ffb0d5484a436e96837992d55925876621790a11))



# [1.11.0](https://github.com/alexneo2003/playwright-azure-reporter/compare/v1.11.0-beta.0...v1.11.0) (2024-09-19)


Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,27 @@ Reporter options (\* - required):
// This will throw an error: "Invalid testCaseIdMatcher. Must be a string or RegExp. Actual: 1234"
```
- `testCaseIdZone` [string] - Specifies where to look for the test case IDs. It can be either `'title'` or `'annotation'`. When set to `'title'`, the reporter will extract test case IDs from the test title and tag test section also. When set to `'annotation'`, it will extract test case IDs only from the test annotations. Default: `'title'`.
**Pay attention that if you use `testCaseIdZone: 'annotation'` and `testCaseIdMatcher` is not defined, the reporter will not extract test case IDs from the test annotations. You should define `testCaseIdMatcher` to extract test case IDs from the test annotations. Matcher should match the annotation type not the annotation description!**
#### Example Usage
- Test title: `Test case [12345]`
- `testCaseIdZone: 'title'`
- Extracted tags: `['12345']`
- Test annotations:
```typescript
test('Test case', { annotations: [{ type: 'TestCase', description: '12345' }] }, () => {
expect(true).toBe(true);
});
```
- `testCaseIdZone: 'annotation'`
- `testCaseIdMatcher: /(TestCase)/`
- Extracted tags: `['12345']`]
## Usefulness
- **AZURE_PW_TEST_RUN_ID** - Id of current test run. It will be set in environment variables after test run created. Can be accessed by `process.env.AZURE_PW_TEST_RUN_ID`. Pay attention what `publishTestResultsMode` configuration you use. If you use `testResult` mode - this variable will be set when test run created, at the start of tests execution, if you use `testRun` mode - this variable will be set when test run completed, at the end of tests execution.
Expand All @@ -271,3 +292,16 @@ Reporter options (\* - required):
- script: echo $(playwright.AZURE_PW_TEST_RUN_ID)
displayName: 'Print test run id'
```
- **AZUREPWDEBUG** - Enable debug logging from reporter `0` - disabled, `1` - enabled. Default: `0`.
Example of usage in Azure DevOps pipeline:
```yaml
- script: npx playwright test
displayName: 'Run Playwright tests'
name: 'playwright'
env:
CI: 'true'
AZUREPWDEBUG: '1'
```
18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alex_neo/playwright-azure-reporter",
"version": "1.11.0",
"version": "1.12.0",
"description": "Playwright Azure Reporter",
"main": "./dist/playwright-azure-reporter.js",
"types": "./dist/playwright-azure-reporter.d.js",
Expand All @@ -14,11 +14,25 @@
"clean": "rm -rf dist || true",
"prepublishOnly": "tsc",
"dev": "yarn run lint && tsc",
"dev:watch": "nodemon --watch './src/*' -e ts --exec \"yarn run build\"",
"dev:watch": "nodemon",
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
"release": "conventional-github-releaser -p angular",
"prepare": "husky install"
},
"nodemonConfig": {
"watch": [
"src"
],
"ext": "ts",
"ignore": [
"**/test/**",
"**/docs/**",
"**/dist/**",
"**/node_modules/**"
],
"delay": 2500,
"exec": "yarn run build"
},
"keywords": [
"playwright",
"azure",
Expand Down
98 changes: 75 additions & 23 deletions src/playwright-azure-reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type TAttachmentType = Array<(typeof attachmentTypesArray)[number] | RegExp>;
type TTestRunConfig = Omit<TestInterfaces.RunCreateModel, 'name' | 'automated' | 'plan' | 'pointIds'> | undefined;
type TTestResultsToBePublished = { testCase: ITestCaseExtended; testResult: TestResult };
type TPublishTestResults = 'testResult' | 'testRun';
type TTestCaseIdZone = 'title' | 'annotation';

interface ITestCaseExtended extends TestCase {
testAlias: string;
Expand All @@ -57,6 +58,7 @@ export interface AzureReporterOptions {
isExistingTestRun?: boolean;
testRunId?: number;
testCaseIdMatcher?: string | RegExp | Array<string | RegExp>;
testCaseIdZone?: TTestCaseIdZone;
}

interface TestResultsToTestRun {
Expand Down Expand Up @@ -145,6 +147,7 @@ class AzureDevOpsReporter implements Reporter {
private _testRunId: number | undefined;
private _isExistingTestRun = false;
private _testCaseIdMatcher: string | RegExp | Array<string | RegExp> = new RegExp(/\[([\d,\s]+)\]/, 'g');
private _testCaseIdZone: TTestCaseIdZone = 'title';

public constructor(options: AzureReporterOptions) {
this._runIdPromise = new Promise<number | void>((resolve, reject) => {
Expand Down Expand Up @@ -263,6 +266,19 @@ class AzureDevOpsReporter implements Reporter {
}
this._isExistingTestRun = options.isExistingTestRun || false;
this._testCaseIdMatcher = options.testCaseIdMatcher || new RegExp(/\[([\d,\s]+)\]/, 'g');
const validZones: TTestCaseIdZone[] = ['title', 'annotation'];
if (options.testCaseIdZone && validZones.includes(options.testCaseIdZone as TTestCaseIdZone)) {
this._testCaseIdZone = options.testCaseIdZone as TTestCaseIdZone;
} else {
this._testCaseIdZone = 'title';
}

if (this._testCaseIdZone === 'annotation' && !options.testCaseIdMatcher) {
this._logger?.warn("'testCaseIdMatcher' is not set. The default matcher is set to '\\[([\\d,\\s]+)\\]'.");
this._logger?.warn(
'This means you need to define your own testCaseIdMatcher, specifically for the "annotation" area'
);
}
}

async onBegin(): Promise<void> {
Expand Down Expand Up @@ -414,17 +430,19 @@ class AzureDevOpsReporter implements Reporter {
return result;
}

private _extractMatches(text: string): string[] {
const reList = (Array.isArray(this._testCaseIdMatcher) ? this._testCaseIdMatcher : [this._testCaseIdMatcher]).map(
(re) => {
if (typeof re === 'string') {
return new RegExp(re, 'g');
} else if (!isRegExp(re)) {
throw new Error(`Invalid testCaseIdMatcher. Must be a string or RegExp. Actual: ${re}`);
}
return re;
private _prepareExtractMatches(testCaseIdMatcher: string | RegExp | (string | RegExp)[]): RegExp[] {
return (Array.isArray(testCaseIdMatcher) ? testCaseIdMatcher : [testCaseIdMatcher]).map((re) => {
if (typeof re === 'string') {
return new RegExp(re, 'g');
} else if (!isRegExp(re)) {
throw new Error(`Invalid testCaseIdMatcher. Must be a string or RegExp. Actual: ${re}`);
}
);
return re;
});
}

private _extractMatchesFromText(text: string): string[] {
const reList = this._prepareExtractMatches(this._testCaseIdMatcher);

this._logger?.debug(`Extracting matches from text: ${text}`);
this._logger?.debug(`Using matchers: ${reList}`);
Expand All @@ -444,22 +462,56 @@ class AzureDevOpsReporter implements Reporter {
return matchesResult;
}

private _extractMatchesFromObject(obj: Record<string, string>): string[] {
if (!obj) return [];
if (Object.keys(obj).length === 0) return [];
if (Array.isArray(obj)) throw new Error('Object must be a key-value pair');
this._logger?.debug(`Extracting matches from object: \n${JSON.stringify(obj, null, 2)}`);
const matchesResult: string[] = [];
for (const key in obj) {
if (key === 'type') {
this._logger?.debug(`[_extractMatches] Checking key ${key}`);
for (const re of this._prepareExtractMatches(this._testCaseIdMatcher)) {
const matches = obj[key].match(re);
if (matches && matches.length > 1) {
this._logger?.debug(`[_extractMatches] Matches found: ${key} - ${matches[1]}`);
matchesResult.push(obj['description']);
}
}
}
}
return matchesResult;
}

private _getCaseIds(test: TestCase): string[] {
const result: string[] = [];
const matches = this._extractMatches(test.title);
this._logger?.debug(`[_getCaseIds] Matches found: ${matches}`);
matches.forEach((match) => {
const ids = match.split(',').map((id) => id.trim());
result.push(...ids);
});
if (test.tags) {
test.tags.forEach((tag) => {
const ids = this._extractMatches(tag);
this._logger?.debug(`[_getCaseIds] Matches found in tag: ${ids}`);
ids.forEach((id) => {
result.push(id);
});
if (this._testCaseIdZone === 'title') {
const matches = this._extractMatchesFromText(test.title);
this._logger?.debug(`[_getCaseIds] Matches found: ${matches}`);
matches.forEach((match) => {
const ids = match.split(',').map((id) => id.trim());
result.push(...ids);
});
if (test.tags) {
test.tags.forEach((tag) => {
const ids = this._extractMatchesFromText(tag);
this._logger?.debug(`[_getCaseIds] Matches found in tag: ${ids}`);
ids.forEach((id) => {
result.push(id);
});
});
}
} else {
if (test.annotations) {
test.annotations.forEach((annotation) => {
const matches = this._extractMatchesFromObject(annotation);
this._logger?.debug(`[_getCaseIds] Matches found in annotation: ${matches}`);
matches.forEach((id) => {
const ids = id.split(',').map((id) => id.trim());
result.push(...ids);
});
});
}
}
return [...new Set(result)];
}
Expand Down
Loading

0 comments on commit 5ca5d51

Please sign in to comment.