chore: incremental bidi expectation update, ff expectations (#32570)
* Do not override expectations for the tests that didn't run or already had expectations * Sort expectations alphabetically * Add firefox expectations
This commit is contained in:
parent
29a0f49e9b
commit
a8103abee6
|
|
@ -18,7 +18,8 @@ import type {
|
||||||
FullConfig, FullResult, Reporter, Suite, TestCase
|
FullConfig, FullResult, Reporter, Suite, TestCase
|
||||||
} from '@playwright/test/reporter';
|
} from '@playwright/test/reporter';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { projectExpectationPath } from './expectationUtil';
|
import { parseBidiExpectations as parseExpectations, projectExpectationPath } from './expectationUtil';
|
||||||
|
import type { TestExpectation } from './expectationUtil';
|
||||||
|
|
||||||
type ReporterOptions = {
|
type ReporterOptions = {
|
||||||
rebase?: boolean;
|
rebase?: boolean;
|
||||||
|
|
@ -27,6 +28,7 @@ type ReporterOptions = {
|
||||||
class ExpectationReporter implements Reporter {
|
class ExpectationReporter implements Reporter {
|
||||||
private _suite: Suite;
|
private _suite: Suite;
|
||||||
private _options: ReporterOptions;
|
private _options: ReporterOptions;
|
||||||
|
private _pendingUpdates: Promise<void>[] = [];
|
||||||
|
|
||||||
constructor(options: ReporterOptions) {
|
constructor(options: ReporterOptions) {
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
|
@ -40,18 +42,27 @@ class ExpectationReporter implements Reporter {
|
||||||
if (!this._options.rebase)
|
if (!this._options.rebase)
|
||||||
return;
|
return;
|
||||||
for (const project of this._suite.suites)
|
for (const project of this._suite.suites)
|
||||||
this._updateProjectExpectations(project);
|
this._pendingUpdates.push(this._updateProjectExpectations(project));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateProjectExpectations(project: Suite) {
|
async onExit() {
|
||||||
const results = project.allTests().map(test => {
|
await Promise.all(this._pendingUpdates);
|
||||||
const outcome = getOutcome(test);
|
}
|
||||||
const line = `${test.titlePath().slice(1).join(' › ')} [${outcome}]`;
|
|
||||||
return line;
|
private async _updateProjectExpectations(project: Suite) {
|
||||||
});
|
|
||||||
const outputFile = projectExpectationPath(project.title);
|
const outputFile = projectExpectationPath(project.title);
|
||||||
|
const expectations = await parseExpectations(project.title);
|
||||||
|
for (const test of project.allTests()) {
|
||||||
|
const outcome = getOutcome(test);
|
||||||
|
const key = test.titlePath().slice(1).join(' › ');
|
||||||
|
if (!expectations.has(key) || expectations.get(key) === 'unknown')
|
||||||
|
expectations.set(key, outcome);
|
||||||
|
}
|
||||||
|
const keys = Array.from(expectations.keys());
|
||||||
|
keys.sort();
|
||||||
|
const results = keys.map(key => `${key} [${expectations.get(key)}]`);
|
||||||
console.log('Writing new expectations to', outputFile);
|
console.log('Writing new expectations to', outputFile);
|
||||||
fs.writeFileSync(outputFile, results.join('\n'));
|
await fs.promises.writeFile(outputFile, results.join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
printsToStdio(): boolean {
|
printsToStdio(): boolean {
|
||||||
|
|
@ -59,7 +70,7 @@ class ExpectationReporter implements Reporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOutcome(test: TestCase): 'unknown' | 'flaky' | 'pass' | 'fail' | 'timeout' {
|
function getOutcome(test: TestCase): TestExpectation {
|
||||||
if (test.results.length === 0)
|
if (test.results.length === 0)
|
||||||
return 'unknown';
|
return 'unknown';
|
||||||
if (test.results.every(r => r.status === 'timedOut'))
|
if (test.results.every(r => r.status === 'timedOut'))
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,25 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { TestInfo } from 'playwright/test';
|
import type { TestInfo } from 'playwright/test';
|
||||||
|
|
||||||
|
export type TestExpectation = 'unknown' | 'flaky' | 'pass' | 'fail' | 'timeout';
|
||||||
|
|
||||||
type ShouldSkipPredicate = (info: TestInfo) => boolean;
|
type ShouldSkipPredicate = (info: TestInfo) => boolean;
|
||||||
|
|
||||||
export async function parseBidiExpectations(projectName: string): Promise<ShouldSkipPredicate> {
|
export async function createSkipTestPredicate(projectName: string): Promise<ShouldSkipPredicate> {
|
||||||
|
const expectationsMap = await parseBidiExpectations(projectName);
|
||||||
|
return (info: TestInfo) => {
|
||||||
|
const key = [info.project.name, ...info.titlePath].join(' › ');
|
||||||
|
const expectation = expectationsMap.get(key);
|
||||||
|
return expectation === 'fail' || expectation === 'timeout';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parseBidiExpectations(projectName: string): Promise<Map<string, TestExpectation>> {
|
||||||
const filePath = projectExpectationPath(projectName);
|
const filePath = projectExpectationPath(projectName);
|
||||||
try {
|
try {
|
||||||
await fs.promises.access(filePath);
|
await fs.promises.access(filePath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return () => false;
|
return new Map();
|
||||||
}
|
}
|
||||||
const content = await fs.promises.readFile(filePath);
|
const content = await fs.promises.readFile(filePath);
|
||||||
const pairs = content.toString().split('\n').map(line => {
|
const pairs = content.toString().split('\n').map(line => {
|
||||||
|
|
@ -35,14 +46,8 @@ export async function parseBidiExpectations(projectName: string): Promise<Should
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return [match.groups!.titlePath, match.groups!.expectation];
|
return [match.groups!.titlePath, match.groups!.expectation];
|
||||||
}).filter(Boolean) as [string, string][];
|
}).filter(Boolean) as [string, TestExpectation][];
|
||||||
const expectationsMap = new Map(pairs);
|
return new Map(pairs);
|
||||||
|
|
||||||
return (info: TestInfo) => {
|
|
||||||
const key = [info.project.name, ...info.titlePath].join(' › ');
|
|
||||||
const expectation = expectationsMap.get(key);
|
|
||||||
return expectation === 'fail' || expectation === 'timeout';
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function projectExpectationPath(project: string): string {
|
export function projectExpectationPath(project: string): string {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
1911
tests/bidi/expectations/bidi-firefox-beta-library.txt
Normal file
1911
tests/bidi/expectations/bidi-firefox-beta-library.txt
Normal file
File diff suppressed because it is too large
Load diff
1971
tests/bidi/expectations/bidi-firefox-beta-page.txt
Normal file
1971
tests/bidi/expectations/bidi-firefox-beta-page.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -24,7 +24,7 @@ import { baseTest } from './baseTest';
|
||||||
import { type RemoteServerOptions, type PlaywrightServer, RunServer, RemoteServer } from './remoteServer';
|
import { type RemoteServerOptions, type PlaywrightServer, RunServer, RemoteServer } from './remoteServer';
|
||||||
import type { Log } from '../../packages/trace/src/har';
|
import type { Log } from '../../packages/trace/src/har';
|
||||||
import { parseHar } from '../config/utils';
|
import { parseHar } from '../config/utils';
|
||||||
import { parseBidiExpectations as parseBidiProjectExpectations } from '../bidi/expectationUtil';
|
import { createSkipTestPredicate } from '../bidi/expectationUtil';
|
||||||
import type { TestInfo } from '@playwright/test';
|
import type { TestInfo } from '@playwright/test';
|
||||||
|
|
||||||
export type BrowserTestWorkerFixtures = PageWorkerFixtures & {
|
export type BrowserTestWorkerFixtures = PageWorkerFixtures & {
|
||||||
|
|
@ -172,7 +172,7 @@ const test = baseTest.extend<BrowserTestTestFixtures, BrowserTestWorkerFixtures>
|
||||||
},
|
},
|
||||||
|
|
||||||
bidiTestSkipPredicate: [async ({ }, run) => {
|
bidiTestSkipPredicate: [async ({ }, run) => {
|
||||||
const filter = await parseBidiProjectExpectations(test.info().project.name);
|
const filter = await createSkipTestPredicate(test.info().project.name);
|
||||||
await run(filter);
|
await run(filter);
|
||||||
}, { scope: 'worker' }],
|
}, { scope: 'worker' }],
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue