diff --git a/packages/html-reporter/src/filter.ts b/packages/html-reporter/src/filter.ts
index 249c338c9a..9ec4ea7e59 100644
--- a/packages/html-reporter/src/filter.ts
+++ b/packages/html-reporter/src/filter.ts
@@ -15,12 +15,12 @@
*/
import type { TestCaseSummary } from './types';
-
export class Filter {
project: string[] = [];
status: string[] = [];
text: string[] = [];
labels: string[] = [];
+ annotations: string[] = [];
empty(): boolean {
return this.project.length + this.status.length + this.text.length === 0;
@@ -32,6 +32,7 @@ export class Filter {
const status = new Set();
const text: string[] = [];
const labels = new Set();
+ const annotations = new Set();
for (const token of tokens) {
if (token.startsWith('p:')) {
project.add(token.slice(2));
@@ -45,6 +46,10 @@ export class Filter {
labels.add(token);
continue;
}
+ if (token.startsWith('annot:')) {
+ annotations.add(token.slice('annot:'.length));
+ continue;
+ }
text.push(token.toLowerCase());
}
@@ -53,6 +58,7 @@ export class Filter {
filter.project = [...project];
filter.status = [...status];
filter.labels = [...labels];
+ filter.annotations = [...annotations];
return filter;
}
@@ -127,7 +133,12 @@ export class Filter {
if (!matches)
return false;
}
-
+ if (this.annotations.length) {
+ const matches = this.annotations.every(annotation =>
+ searchValues.annotations.some(a => a.includes(annotation)));
+ if (!matches)
+ return false;
+ }
return true;
}
}
@@ -140,6 +151,7 @@ type SearchValues = {
line: string;
column: string;
labels: string[];
+ annotations: string[];
};
const searchValuesSymbol = Symbol('searchValues');
@@ -164,6 +176,7 @@ function cacheSearchValues(test: TestCaseSummary): SearchValues {
line: String(test.location.line),
column: String(test.location.column),
labels: test.tags.map(tag => tag.toLowerCase()),
+ annotations: test.annotations.map(a => a.type.toLowerCase() + '=' + a.description?.toLocaleLowerCase())
};
(test as any)[searchValuesSymbol] = searchValues;
return searchValues;
diff --git a/packages/html-reporter/src/headerView.spec.tsx b/packages/html-reporter/src/headerView.spec.tsx
index 64ed858475..3131ae645d 100644
--- a/packages/html-reporter/src/headerView.spec.tsx
+++ b/packages/html-reporter/src/headerView.spec.tsx
@@ -27,7 +27,7 @@ test('should render counters', async ({ mount }) => {
flaky: 17,
skipped: 10,
ok: false,
- }} filterText='' setFilterText={() => {}}>);
+ }} filterText='' setFilterText={() => { }}>);
await expect(component.locator('a', { hasText: 'All' }).locator('.counter')).toHaveText('90');
await expect(component.locator('a', { hasText: 'Passed' }).locator('.counter')).toHaveText('42');
await expect(component.locator('a', { hasText: 'Failed' }).locator('.counter')).toHaveText('31');
@@ -59,5 +59,6 @@ test('should toggle filters', async ({ page, mount }) => {
await expect(page).toHaveURL(/#\?q=s:flaky/);
await component.locator('a', { hasText: 'Skipped' }).click();
await expect(page).toHaveURL(/#\?q=s:skipped/);
- expect(filters).toEqual(['', 's:passed', 's:failed', 's:flaky', 's:skipped']);
+ await component.getByRole('searchbox').fill('annot:annotation type=annotation description');
+ expect(filters).toEqual(['', 's:passed', 's:failed', 's:flaky', 's:skipped', 'annot:annotation type=annotation description']);
});
diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts
index 8283ecb5fe..83992c0bad 100644
--- a/tests/playwright-test/reporter-html.spec.ts
+++ b/tests/playwright-test/reporter-html.spec.ts
@@ -1455,7 +1455,7 @@ for (const useIntermediateMergeReport of [false] as const) {
await showReport();
const searchInput = page.locator('.subnav-search-input');
- const smokeLabelButton = page.locator('.test-file-test', { has: page.getByText('Error Pages › @smoke fails', { exact: true }) }).locator('.label', { hasText: 'smoke' });
+ const smokeLabelButton = page.locator('.test-file-test', { has: page.getByText('Error Pages › @smoke fails', { exact: true }) }).locator('.label', { hasText: 'smoke' });
await expect(smokeLabelButton).toBeVisible();
await smokeLabelButton.click();
@@ -1465,7 +1465,7 @@ for (const useIntermediateMergeReport of [false] as const) {
await expect(page.locator('.chip', { hasText: 'b.test.js' })).toHaveCount(1);
await expect(page.locator('.test-file-test .test-file-title')).toHaveText('Error Pages › @smoke fails');
- const regressionLabelButton = page.locator('.test-file-test', { has: page.getByText('Error Pages › @regression passes', { exact: true }) }).locator('.label', { hasText: 'regression' });
+ const regressionLabelButton = page.locator('.test-file-test', { has: page.getByText('Error Pages › @regression passes', { exact: true }) }).locator('.label', { hasText: 'regression' });
await expect(regressionLabelButton).not.toBeVisible();
@@ -1577,7 +1577,7 @@ for (const useIntermediateMergeReport of [false] as const) {
const searchInput = page.locator('.subnav-search-input');
- const smokeLabelButton = page.locator('.test-file-test', { has: page.getByText('@smoke fails', { exact: true }) }).locator('.label', { hasText: 'smoke' });
+ const smokeLabelButton = page.locator('.test-file-test', { has: page.getByText('@smoke fails', { exact: true }) }).locator('.label', { hasText: 'smoke' });
await smokeLabelButton.click();
await expect(page).toHaveURL(/@smoke/);
await searchInput.clear();
@@ -1585,7 +1585,7 @@ for (const useIntermediateMergeReport of [false] as const) {
await expect(searchInput).toHaveValue('');
await expect(page).not.toHaveURL(/@smoke/);
- const regressionLabelButton = page.locator('.test-file-test', { has: page.getByText('@regression passes', { exact: true }) }).locator('.label', { hasText: 'regression' });
+ const regressionLabelButton = page.locator('.test-file-test', { has: page.getByText('@regression passes', { exact: true }) }).locator('.label', { hasText: 'regression' });
await regressionLabelButton.click();
await expect(page).toHaveURL(/@regression/);
await searchInput.clear();
@@ -1863,7 +1863,7 @@ for (const useIntermediateMergeReport of [false] as const) {
await expect(page.locator('.test-file-test .test-file-title', { hasText: '@company_information_widget fails' })).toHaveCount(1);
});
- test('handling of meta or ctrl key', async ({ runInlineTest, showReport, page, }) => {
+ test('handling of meta or ctrl key', async ({ runInlineTest, showReport, page, }) => {
const result = await runInlineTest({
'a.test.js': `
const { expect, test } = require('@playwright/test');
@@ -2214,6 +2214,29 @@ for (const useIntermediateMergeReport of [false] as const) {
await expect(page.getByText('passes title')).toBeVisible();
});
+ test('tests should filter by annotation texts', async ({ runInlineTest, showReport, page }) => {
+ const result = await runInlineTest({
+ 'a.test.js': `
+ const { test, expect } = require('@playwright/test');
+ test('annotated test',{ annotation :[{type:'key',description:'value'}]}, async ({}) => {expect(1).toBe(1);});
+ test('non-annotated test', async ({}) => {expect(1).toBe(2);});
+ `,
+ }, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
+
+ expect(result.exitCode).toBe(1);
+ expect(result.passed).toBe(1);
+ expect(result.failed).toBe(1);
+
+ await showReport();
+
+ const searchInput = page.locator('.subnav-search-input');
+
+ await searchInput.fill('annot:key=value');
+ await expect(page.getByText('a.test.js', { exact: true })).toBeVisible();
+ await expect(page.getByText('non-annotated test')).not.toBeVisible();
+ await expect(page.getByText('annotated test')).toBeVisible();
+ });
+
test('tests should filter by fileName:line/column', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({
'a.test.js': `