mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: filter skipped tests (#21938)
Fixes https://github.com/microsoft/playwright/issues/21918
This commit is contained in:
parent
6b83631f24
commit
31e70c17be
@ -175,6 +175,7 @@ export class TeleReporterReceiver {
|
|||||||
testResult.workerIndex = payload.workerIndex;
|
testResult.workerIndex = payload.workerIndex;
|
||||||
testResult.parallelIndex = payload.parallelIndex;
|
testResult.parallelIndex = payload.parallelIndex;
|
||||||
testResult.startTime = new Date(payload.startTime);
|
testResult.startTime = new Date(payload.startTime);
|
||||||
|
testResult.statusEx = 'running';
|
||||||
this._reporter.onTestBegin?.(test, testResult);
|
this._reporter.onTestBegin?.(test, testResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +184,7 @@ export class TeleReporterReceiver {
|
|||||||
const result = test.resultsMap.get(payload.id)!;
|
const result = test.resultsMap.get(payload.id)!;
|
||||||
result.duration = payload.duration;
|
result.duration = payload.duration;
|
||||||
result.status = payload.status;
|
result.status = payload.status;
|
||||||
|
result.statusEx = payload.status;
|
||||||
result.errors = payload.errors;
|
result.errors = payload.errors;
|
||||||
result.attachments = payload.attachments;
|
result.attachments = payload.attachments;
|
||||||
this._reporter.onTestEnd?.(test, result);
|
this._reporter.onTestEnd?.(test, result);
|
||||||
@ -380,7 +382,7 @@ export class TeleSuite implements SuitePrivate {
|
|||||||
export class TeleTestCase implements reporterTypes.TestCase {
|
export class TeleTestCase implements reporterTypes.TestCase {
|
||||||
title: string;
|
title: string;
|
||||||
fn = () => {};
|
fn = () => {};
|
||||||
results: reporterTypes.TestResult[] = [];
|
results: TeleTestResult[] = [];
|
||||||
location: Location;
|
location: Location;
|
||||||
parent!: TeleSuite;
|
parent!: TeleSuite;
|
||||||
|
|
||||||
@ -426,7 +428,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
|
|||||||
this.resultsMap.clear();
|
this.resultsMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
_createTestResult(id: string): reporterTypes.TestResult {
|
_createTestResult(id: string): TeleTestResult {
|
||||||
this._clearResults();
|
this._clearResults();
|
||||||
const result: TeleTestResult = {
|
const result: TeleTestResult = {
|
||||||
retry: this.results.length,
|
retry: this.results.length,
|
||||||
@ -438,6 +440,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
|
|||||||
stderr: [],
|
stderr: [],
|
||||||
attachments: [],
|
attachments: [],
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
|
statusEx: 'scheduled',
|
||||||
steps: [],
|
steps: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
stepMap: new Map(),
|
stepMap: new Map(),
|
||||||
@ -453,6 +456,7 @@ export class TeleTestCase implements reporterTypes.TestCase {
|
|||||||
export type TeleTestResult = reporterTypes.TestResult & {
|
export type TeleTestResult = reporterTypes.TestResult & {
|
||||||
stepMap: Map<string, reporterTypes.TestStep>;
|
stepMap: Map<string, reporterTypes.TestStep>;
|
||||||
stepStack: (reporterTypes.TestStep | reporterTypes.TestResult)[];
|
stepStack: (reporterTypes.TestStep | reporterTypes.TestResult)[];
|
||||||
|
statusEx: reporterTypes.TestResult['status'] | 'scheduled' | 'running';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeleFullProject = FullProject & { id: string };
|
export type TeleFullProject = FullProject & { id: string };
|
||||||
|
@ -307,8 +307,11 @@ const TestList: React.FC<{
|
|||||||
|
|
||||||
// Build the test tree.
|
// Build the test tree.
|
||||||
const { rootItem, treeItemMap, fileNames } = React.useMemo(() => {
|
const { rootItem, treeItemMap, fileNames } = React.useMemo(() => {
|
||||||
const rootItem = createTree(testModel.rootSuite, projectFilters);
|
let rootItem = createTree(testModel.rootSuite, projectFilters);
|
||||||
filterTree(rootItem, filterText, statusFilters, runningState?.testIds);
|
filterTree(rootItem, filterText, statusFilters, runningState?.testIds);
|
||||||
|
sortAndPropagateStatus(rootItem);
|
||||||
|
rootItem = shortenRoot(rootItem);
|
||||||
|
|
||||||
hideOnlyTests(rootItem);
|
hideOnlyTests(rootItem);
|
||||||
const treeItemMap = new Map<string, TreeItem>();
|
const treeItemMap = new Map<string, TreeItem>();
|
||||||
const visibleTestIds = new Set<string>();
|
const visibleTestIds = new Set<string>();
|
||||||
@ -769,18 +772,19 @@ function createTree(rootSuite: Suite | undefined, projectFilters: Map<string, bo
|
|||||||
parentGroup.children.push(testCaseItem);
|
parentGroup.children.push(testCaseItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const result = (test as TeleTestCase).results[0];
|
||||||
let status: 'none' | 'running' | 'scheduled' | 'passed' | 'failed' | 'skipped' = 'none';
|
let status: 'none' | 'running' | 'scheduled' | 'passed' | 'failed' | 'skipped' = 'none';
|
||||||
if (test.results.some(r => r.workerIndex === -1))
|
if (result?.statusEx === 'scheduled')
|
||||||
status = 'scheduled';
|
status = 'scheduled';
|
||||||
else if (test.results.some(r => r.duration === -1))
|
else if (result?.statusEx === 'running')
|
||||||
status = 'running';
|
status = 'running';
|
||||||
else if (test.results.length && test.results[0].status === 'skipped')
|
else if (result?.status === 'skipped')
|
||||||
status = 'skipped';
|
status = 'skipped';
|
||||||
else if (test.results.length && test.results[0].status === 'interrupted')
|
else if (result?.status === 'interrupted')
|
||||||
status = 'none';
|
status = 'none';
|
||||||
else if (test.results.length && test.outcome() !== 'expected')
|
else if (result && test.outcome() !== 'expected')
|
||||||
status = 'failed';
|
status = 'failed';
|
||||||
else if (test.results.length && test.outcome() === 'expected')
|
else if (result && test.outcome() === 'expected')
|
||||||
status = 'passed';
|
status = 'passed';
|
||||||
|
|
||||||
testCaseItem.tests.push(test);
|
testCaseItem.tests.push(test);
|
||||||
@ -809,50 +813,7 @@ function createTree(rootSuite: Suite | undefined, projectFilters: Map<string, bo
|
|||||||
visitSuite(projectSuite.title, fileSuite, fileItem);
|
visitSuite(projectSuite.title, fileSuite, fileItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return rootItem;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterTree(rootItem: GroupItem, filterText: string, statusFilters: Map<string, boolean>, runningTestIds: Set<string> | undefined) {
|
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);
|
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) {
|
function hideOnlyTests(rootItem: GroupItem) {
|
||||||
const visit = (treeItem: TreeItem) => {
|
const visit = (treeItem: TreeItem) => {
|
||||||
if (treeItem.kind === 'case' && treeItem.children.length === 1)
|
if (treeItem.kind === 'case' && treeItem.children.length === 1)
|
||||||
|
@ -178,3 +178,28 @@ test('should not hide filtered while running', async ({ runUITest, createLatch }
|
|||||||
↻ fails <=
|
↻ 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
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user