mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: support suites in the tree (#21508)
This commit is contained in:
parent
72c41db668
commit
5ef2edb9f7
@ -38,7 +38,6 @@ class UIMode {
|
|||||||
private _testWatcher: FSWatcher | undefined;
|
private _testWatcher: FSWatcher | undefined;
|
||||||
private _watchTestFile: string | undefined;
|
private _watchTestFile: string | undefined;
|
||||||
private _originalStderr: (buffer: string | Uint8Array) => void;
|
private _originalStderr: (buffer: string | Uint8Array) => void;
|
||||||
private _globalWatcher: FSWatcher;
|
|
||||||
|
|
||||||
constructor(config: FullConfigInternal) {
|
constructor(config: FullConfigInternal) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
@ -58,7 +57,7 @@ class UIMode {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
this._globalWatcher = this._installGlobalWatcher();
|
this._installGlobalWatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _installGlobalWatcher(): FSWatcher {
|
private _installGlobalWatcher(): FSWatcher {
|
||||||
|
|||||||
@ -324,6 +324,8 @@ const refreshRootSuite = (eraseResults: boolean) => {
|
|||||||
else
|
else
|
||||||
++progress.passed;
|
++progress.passed;
|
||||||
updateRootSuite(rootSuite, progress);
|
updateRootSuite(rootSuite, progress);
|
||||||
|
// This will update selected trace viewer.
|
||||||
|
updateStepsProgress();
|
||||||
},
|
},
|
||||||
|
|
||||||
onStepBegin: () => {
|
onStepBegin: () => {
|
||||||
@ -373,22 +375,13 @@ const sendMessageNoReply = (method: string, params?: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fileName = (treeItem?: TreeItem): string | undefined => {
|
const fileName = (treeItem?: TreeItem): string | undefined => {
|
||||||
if (!treeItem)
|
return treeItem?.location.file;
|
||||||
return;
|
|
||||||
if (treeItem.kind === 'file')
|
|
||||||
return treeItem.file;
|
|
||||||
return fileName(treeItem.parent || undefined);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const locationToOpen = (treeItem?: TreeItem) => {
|
const locationToOpen = (treeItem?: TreeItem) => {
|
||||||
if (!treeItem)
|
if (!treeItem)
|
||||||
return;
|
return;
|
||||||
if (treeItem.kind === 'test')
|
|
||||||
return treeItem.test.location.file + ':' + treeItem.test.location.line;
|
|
||||||
if (treeItem.kind === 'case')
|
|
||||||
return treeItem.location.file + ':' + treeItem.location.line;
|
return treeItem.location.file + ':' + treeItem.location.line;
|
||||||
if (treeItem.kind === 'file')
|
|
||||||
return treeItem.file;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const collectTestIds = (treeItem?: TreeItem): string[] => {
|
const collectTestIds = (treeItem?: TreeItem): string[] => {
|
||||||
@ -414,29 +407,22 @@ type Progress = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type TreeItemBase = {
|
type TreeItemBase = {
|
||||||
kind: 'root' | 'file' | 'case' | 'test',
|
kind: 'root' | 'group' | 'case' | 'test',
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
parent: TreeItem | null;
|
location: Location,
|
||||||
children: TreeItem[];
|
children: TreeItem[];
|
||||||
status: 'none' | 'running' | 'passed' | 'failed';
|
status: 'none' | 'running' | 'passed' | 'failed';
|
||||||
};
|
};
|
||||||
|
|
||||||
type RootItem = TreeItemBase & {
|
type GroupItem = TreeItemBase & {
|
||||||
kind: 'root',
|
kind: 'group',
|
||||||
children: FileItem[];
|
children: (TestCaseItem | GroupItem)[];
|
||||||
};
|
|
||||||
|
|
||||||
type FileItem = TreeItemBase & {
|
|
||||||
kind: 'file',
|
|
||||||
file: string;
|
|
||||||
children: TestCaseItem[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type TestCaseItem = TreeItemBase & {
|
type TestCaseItem = TreeItemBase & {
|
||||||
kind: 'case',
|
kind: 'case',
|
||||||
tests: TestCase[];
|
tests: TestCase[];
|
||||||
location: Location,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type TestItem = TreeItemBase & {
|
type TestItem = TreeItemBase & {
|
||||||
@ -444,54 +430,50 @@ type TestItem = TreeItemBase & {
|
|||||||
test: TestCase;
|
test: TestCase;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TreeItem = RootItem | FileItem | TestCaseItem | TestItem;
|
type TreeItem = GroupItem | TestCaseItem | TestItem;
|
||||||
|
|
||||||
function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>): RootItem {
|
function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>): GroupItem {
|
||||||
const rootItem: RootItem = {
|
const rootItem: GroupItem = {
|
||||||
kind: 'root',
|
kind: 'group',
|
||||||
id: 'root',
|
id: 'root',
|
||||||
title: '',
|
title: '',
|
||||||
parent: null,
|
location: { file: '', line: 0, column: 0 },
|
||||||
children: [],
|
children: [],
|
||||||
status: 'none',
|
status: 'none',
|
||||||
};
|
};
|
||||||
const fileItems = new Map<string, FileItem>();
|
|
||||||
for (const projectSuite of rootSuite?.suites || []) {
|
|
||||||
if (!projects.get(projectSuite.title))
|
|
||||||
continue;
|
|
||||||
for (const fileSuite of projectSuite.suites) {
|
|
||||||
const file = fileSuite.location!.file;
|
|
||||||
|
|
||||||
let fileItem = fileItems.get(file);
|
const visitSuite = (projectName: string, parentSuite: Suite, parentGroup: GroupItem) => {
|
||||||
if (!fileItem) {
|
for (const suite of parentSuite.suites) {
|
||||||
fileItem = {
|
const title = suite.title;
|
||||||
kind: 'file',
|
let group = parentGroup.children.find(item => item.title === title) as GroupItem | undefined;
|
||||||
id: fileSuite.title,
|
if (!group) {
|
||||||
title: fileSuite.title,
|
group = {
|
||||||
file,
|
kind: 'group',
|
||||||
parent: null,
|
id: parentGroup.id + '\x1e' + title,
|
||||||
|
title,
|
||||||
|
location: suite.location!,
|
||||||
children: [],
|
children: [],
|
||||||
status: 'none',
|
status: 'none',
|
||||||
};
|
};
|
||||||
fileItems.set(fileSuite.location!.file, fileItem);
|
parentGroup.children.push(group);
|
||||||
rootItem.children.push(fileItem);
|
}
|
||||||
|
visitSuite(projectName, suite, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const test of fileSuite.allTests()) {
|
for (const test of parentSuite.tests) {
|
||||||
const title = test.titlePath().slice(3).join(' › ');
|
const title = test.title;
|
||||||
let testCaseItem = fileItem.children.find(t => t.title === title) as TestCaseItem;
|
let testCaseItem = parentGroup.children.find(t => t.title === title) as TestCaseItem;
|
||||||
if (!testCaseItem) {
|
if (!testCaseItem) {
|
||||||
testCaseItem = {
|
testCaseItem = {
|
||||||
kind: 'case',
|
kind: 'case',
|
||||||
id: fileItem.id + ' / ' + title,
|
id: parentGroup.id + '\x1e' + title,
|
||||||
title,
|
title,
|
||||||
parent: fileItem,
|
|
||||||
children: [],
|
children: [],
|
||||||
tests: [],
|
tests: [],
|
||||||
location: test.location,
|
location: test.location,
|
||||||
status: 'none',
|
status: 'none',
|
||||||
};
|
};
|
||||||
fileItem.children.push(testCaseItem);
|
parentGroup.children.push(testCaseItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
let status: 'none' | 'running' | 'passed' | 'failed' = 'none';
|
let status: 'none' | 'running' | 'passed' | 'failed' = 'none';
|
||||||
@ -506,15 +488,19 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
|
|||||||
testCaseItem.children.push({
|
testCaseItem.children.push({
|
||||||
kind: 'test',
|
kind: 'test',
|
||||||
id: test.id,
|
id: test.id,
|
||||||
title: projectSuite.title,
|
title: projectName,
|
||||||
parent: testCaseItem,
|
location: test.location!,
|
||||||
test,
|
test,
|
||||||
children: [],
|
children: [],
|
||||||
status,
|
status,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
(fileItem.children as TestCaseItem[]).sort((a, b) => a.location.line - b.location.line);
|
};
|
||||||
}
|
|
||||||
|
for (const projectSuite of rootSuite?.suites || []) {
|
||||||
|
if (!projects.get(projectSuite.title))
|
||||||
|
continue;
|
||||||
|
visitSuite(projectSuite.title, projectSuite, rootItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
const propagateStatus = (treeItem: TreeItem) => {
|
const propagateStatus = (treeItem: TreeItem) => {
|
||||||
@ -542,27 +528,29 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
|
|||||||
return rootItem;
|
return rootItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterTree(rootItem: RootItem, filterText: string) {
|
function filterTree(rootItem: GroupItem, filterText: string) {
|
||||||
const trimmedFilterText = filterText.trim();
|
const trimmedFilterText = filterText.trim();
|
||||||
const filterTokens = trimmedFilterText.toLowerCase().split(' ');
|
const filterTokens = trimmedFilterText.toLowerCase().split(' ');
|
||||||
const result: FileItem[] = [];
|
|
||||||
for (const fileItem of rootItem.children) {
|
const visit = (treeItem: GroupItem) => {
|
||||||
if (trimmedFilterText) {
|
const newChildren: (GroupItem | TestCaseItem)[] = [];
|
||||||
const filteredCases: TestCaseItem[] = [];
|
for (const child of treeItem.children) {
|
||||||
for (const testCaseItem of fileItem.children) {
|
if (child.kind === 'case') {
|
||||||
const fullTitle = (fileItem.title + ' ' + testCaseItem.title).toLowerCase();
|
const title = child.tests[0].titlePath().join(' ').toLowerCase();
|
||||||
if (filterTokens.every(token => fullTitle.includes(token)))
|
if (filterTokens.every(token => title.includes(token)))
|
||||||
filteredCases.push(testCaseItem);
|
newChildren.push(child);
|
||||||
|
} else {
|
||||||
|
visit(child);
|
||||||
|
if (child.children.length)
|
||||||
|
newChildren.push(child);
|
||||||
}
|
}
|
||||||
fileItem.children = filteredCases;
|
|
||||||
}
|
}
|
||||||
if (fileItem.children.length)
|
treeItem.children = newChildren;
|
||||||
result.push(fileItem);
|
};
|
||||||
}
|
visit(rootItem);
|
||||||
rootItem.children = result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideOnlyTests(rootItem: RootItem) {
|
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)
|
||||||
treeItem.children = [];
|
treeItem.children = [];
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './xtermWrapper.css';
|
import './xtermWrapper.css';
|
||||||
import type { Terminal } from 'xterm';
|
import type { ITheme, Terminal } from 'xterm';
|
||||||
import type { XtermModule } from './xtermModule';
|
import type { XtermModule } from './xtermModule';
|
||||||
import { isDarkTheme } from '@web/theme';
|
import { isDarkTheme } from '@web/theme';
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ export const XtermWrapper: React.FC<{ source: XtermDataSource }> = ({
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const lightTheme = {
|
const lightTheme: ITheme = {
|
||||||
foreground: '#383a42',
|
foreground: '#383a42',
|
||||||
background: '#fafafa',
|
background: '#fafafa',
|
||||||
cursor: '#383a42',
|
cursor: '#383a42',
|
||||||
@ -96,10 +96,12 @@ const lightTheme = {
|
|||||||
brightBlue: '#4078f2',
|
brightBlue: '#4078f2',
|
||||||
brightMagenta: '#a626a4',
|
brightMagenta: '#a626a4',
|
||||||
brightCyan: '#0184bc',
|
brightCyan: '#0184bc',
|
||||||
brightWhite: '#383a42'
|
brightWhite: '#383a42',
|
||||||
|
selectionBackground: '#d7d7d7',
|
||||||
|
selectionForeground: '#383a42',
|
||||||
};
|
};
|
||||||
|
|
||||||
const darkTheme = {
|
const darkTheme: ITheme = {
|
||||||
foreground: '#f8f8f2',
|
foreground: '#f8f8f2',
|
||||||
background: '#1e1e1e',
|
background: '#1e1e1e',
|
||||||
cursor: '#f8f8f0',
|
cursor: '#f8f8f0',
|
||||||
@ -119,4 +121,6 @@ const darkTheme = {
|
|||||||
brightMagenta: '#ff92df',
|
brightMagenta: '#ff92df',
|
||||||
brightCyan: '#a4ffff',
|
brightCyan: '#a4ffff',
|
||||||
brightWhite: '#e6e6e6',
|
brightWhite: '#e6e6e6',
|
||||||
|
selectionBackground: '#44475a',
|
||||||
|
selectionForeground: '#f8f8f2',
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user