chore: filter skipped tests (#21938)

Fixes https://github.com/microsoft/playwright/issues/21918
This commit is contained in:
Pavel Feldman 2023-03-23 13:29:52 -07:00 committed by GitHub
parent 6b83631f24
commit 31e70c17be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 53 deletions

View File

@ -175,6 +175,7 @@ export class TeleReporterReceiver {
testResult.workerIndex = payload.workerIndex;
testResult.parallelIndex = payload.parallelIndex;
testResult.startTime = new Date(payload.startTime);
testResult.statusEx = 'running';
this._reporter.onTestBegin?.(test, testResult);
}
@ -183,6 +184,7 @@ export class TeleReporterReceiver {
const result = test.resultsMap.get(payload.id)!;
result.duration = payload.duration;
result.status = payload.status;
result.statusEx = payload.status;
result.errors = payload.errors;
result.attachments = payload.attachments;
this._reporter.onTestEnd?.(test, result);
@ -380,7 +382,7 @@ export class TeleSuite implements SuitePrivate {
export class TeleTestCase implements reporterTypes.TestCase {
title: string;
fn = () => {};
results: reporterTypes.TestResult[] = [];
results: TeleTestResult[] = [];
location: Location;
parent!: TeleSuite;
@ -426,7 +428,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
this.resultsMap.clear();
}
_createTestResult(id: string): reporterTypes.TestResult {
_createTestResult(id: string): TeleTestResult {
this._clearResults();
const result: TeleTestResult = {
retry: this.results.length,
@ -438,6 +440,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
stderr: [],
attachments: [],
status: 'skipped',
statusEx: 'scheduled',
steps: [],
errors: [],
stepMap: new Map(),
@ -453,6 +456,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
export type TeleTestResult = reporterTypes.TestResult & {
stepMap: Map<string, reporterTypes.TestStep>;
stepStack: (reporterTypes.TestStep | reporterTypes.TestResult)[];
statusEx: reporterTypes.TestResult['status'] | 'scheduled' | 'running';
};
export type TeleFullProject = FullProject & { id: string };

View File

@ -307,8 +307,11 @@ const TestList: React.FC<{
// Build the test tree.
const { rootItem, treeItemMap, fileNames } = React.useMemo(() => {
const rootItem = createTree(testModel.rootSuite, projectFilters);
let rootItem = createTree(testModel.rootSuite, projectFilters);
filterTree(rootItem, filterText, statusFilters, runningState?.testIds);
sortAndPropagateStatus(rootItem);
rootItem = shortenRoot(rootItem);
hideOnlyTests(rootItem);
const treeItemMap = new Map<string, TreeItem>();
const visibleTestIds = new Set<string>();
@ -769,18 +772,19 @@ function createTree(rootSuite: Suite | undefined, projectFilters: Map<string, bo
parentGroup.children.push(testCaseItem);
}
const result = (test as TeleTestCase).results[0];
let status: 'none' | 'running' | 'scheduled' | 'passed' | 'failed' | 'skipped' = 'none';
if (test.results.some(r => r.workerIndex === -1))
if (result?.statusEx === 'scheduled')
status = 'scheduled';
else if (test.results.some(r => r.duration === -1))
else if (result?.statusEx === 'running')
status = 'running';
else if (test.results.length && test.results[0].status === 'skipped')
else if (result?.status === 'skipped')
status = 'skipped';
else if (test.results.length && test.results[0].status === 'interrupted')
else if (result?.status === 'interrupted')
status = 'none';
else if (test.results.length && test.outcome() !== 'expected')
else if (result && test.outcome() !== 'expected')
status = 'failed';
else if (test.results.length && test.outcome() === 'expected')
else if (result && test.outcome() === 'expected')
status = 'passed';
testCaseItem.tests.push(test);
@ -809,50 +813,7 @@ function createTree(rootSuite: Suite | undefined, projectFilters: Map<string, bo
visitSuite(projectSuite.title, fileSuite, fileItem);
}
}
const sortAndPropagateStatus = (treeItem: TreeItem) => {
for (const child of treeItem.children)
sortAndPropagateStatus(child);
if (treeItem.kind === 'group') {
treeItem.children.sort((a, b) => {
const fc = a.location.file.localeCompare(b.location.file);
return fc || a.location.line - b.location.line;
});
}
let allPassed = treeItem.children.length > 0;
let allSkipped = treeItem.children.length > 0;
let hasFailed = false;
let hasRunning = false;
let hasScheduled = false;
for (const child of treeItem.children) {
allSkipped = allSkipped && child.status === 'skipped';
allPassed = allPassed && (child.status === 'passed' || child.status === 'skipped');
hasFailed = hasFailed || child.status === 'failed';
hasRunning = hasRunning || child.status === 'running';
hasScheduled = hasScheduled || child.status === 'scheduled';
}
if (hasRunning)
treeItem.status = 'running';
else if (hasScheduled)
treeItem.status = 'scheduled';
else if (hasFailed)
treeItem.status = 'failed';
else if (allSkipped)
treeItem.status = 'skipped';
else if (allPassed)
treeItem.status = 'passed';
};
sortAndPropagateStatus(rootItem);
let shortRoot = rootItem;
while (shortRoot.children.length === 1 && shortRoot.children[0].kind === 'group' && shortRoot.children[0].subKind === 'folder')
shortRoot = shortRoot.children[0];
shortRoot.location = rootItem.location;
return shortRoot;
return rootItem;
}
function filterTree(rootItem: GroupItem, filterText: string, statusFilters: Map<string, boolean>, runningTestIds: Set<string> | undefined) {
@ -887,6 +848,51 @@ function filterTree(rootItem: GroupItem, filterText: string, statusFilters: Map<
visit(rootItem);
}
function sortAndPropagateStatus(treeItem: TreeItem) {
for (const child of treeItem.children)
sortAndPropagateStatus(child);
if (treeItem.kind === 'group') {
treeItem.children.sort((a, b) => {
const fc = a.location.file.localeCompare(b.location.file);
return fc || a.location.line - b.location.line;
});
}
let allPassed = treeItem.children.length > 0;
let allSkipped = treeItem.children.length > 0;
let hasFailed = false;
let hasRunning = false;
let hasScheduled = false;
for (const child of treeItem.children) {
allSkipped = allSkipped && child.status === 'skipped';
allPassed = allPassed && (child.status === 'passed' || child.status === 'skipped');
hasFailed = hasFailed || child.status === 'failed';
hasRunning = hasRunning || child.status === 'running';
hasScheduled = hasScheduled || child.status === 'scheduled';
}
if (hasRunning)
treeItem.status = 'running';
else if (hasScheduled)
treeItem.status = 'scheduled';
else if (hasFailed)
treeItem.status = 'failed';
else if (allSkipped)
treeItem.status = 'skipped';
else if (allPassed)
treeItem.status = 'passed';
}
function shortenRoot(rootItem: GroupItem): GroupItem {
let shortRoot = rootItem;
while (shortRoot.children.length === 1 && shortRoot.children[0].kind === 'group' && shortRoot.children[0].subKind === 'folder')
shortRoot = shortRoot.children[0];
shortRoot.location = rootItem.location;
return shortRoot;
}
function hideOnlyTests(rootItem: GroupItem) {
const visit = (treeItem: TreeItem) => {
if (treeItem.kind === 'case' && treeItem.children.length === 1)

View File

@ -178,3 +178,28 @@ test('should not hide filtered while running', async ({ runUITest, createLatch }
fails <=
`);
});
test('should filter skipped', async ({ runUITest, createLatch }) => {
const page = await runUITest({
'a.test.ts': `
import { test, expect } from '@playwright/test';
test('passes', () => {});
test.skip('fails', async () => {
expect(1).toBe(2);
});
`,
});
await page.getByTitle('Run all').click();
await expect.poll(dumpTestTree(page), { timeout: 15000 }).toBe(`
a.test.ts
passes
fails
`);
await page.getByText('Status:').click();
await page.getByLabel('skipped').setChecked(true);
await expect.poll(dumpTestTree(page), { timeout: 15000 }).toBe(`
a.test.ts
fails
`);
});