chore: support suites in the tree (#21508)

This commit is contained in:
Pavel Feldman 2023-03-08 19:50:32 -08:00 committed by GitHub
parent 72c41db668
commit 5ef2edb9f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 103 deletions

View File

@ -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 {

View File

@ -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.location.file + ':' + treeItem.location.line;
return treeItem.test.location.file + ':' + treeItem.test.location.line;
if (treeItem.kind === 'case')
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,77 +430,77 @@ 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()) {
const title = test.titlePath().slice(3).join(' ');
let testCaseItem = fileItem.children.find(t => t.title === title) as TestCaseItem;
if (!testCaseItem) {
testCaseItem = {
kind: 'case',
id: fileItem.id + ' / ' + title,
title,
parent: fileItem,
children: [],
tests: [],
location: test.location,
status: 'none',
};
fileItem.children.push(testCaseItem);
}
let status: 'none' | 'running' | 'passed' | 'failed' = 'none';
if (test.results.some(r => r.duration === -1))
status = 'running';
else if (test.results.length && test.outcome() !== 'expected')
status = 'failed';
else if (test.outcome() === 'expected')
status = 'passed';
testCaseItem.tests.push(test);
testCaseItem.children.push({
kind: 'test',
id: test.id,
title: projectSuite.title,
parent: testCaseItem,
test,
children: [],
status,
});
}
(fileItem.children as TestCaseItem[]).sort((a, b) => a.location.line - b.location.line);
} }
for (const test of parentSuite.tests) {
const title = test.title;
let testCaseItem = parentGroup.children.find(t => t.title === title) as TestCaseItem;
if (!testCaseItem) {
testCaseItem = {
kind: 'case',
id: parentGroup.id + '\x1e' + title,
title,
children: [],
tests: [],
location: test.location,
status: 'none',
};
parentGroup.children.push(testCaseItem);
}
let status: 'none' | 'running' | 'passed' | 'failed' = 'none';
if (test.results.some(r => r.duration === -1))
status = 'running';
else if (test.results.length && test.outcome() !== 'expected')
status = 'failed';
else if (test.outcome() === 'expected')
status = 'passed';
testCaseItem.tests.push(test);
testCaseItem.children.push({
kind: 'test',
id: test.id,
title: projectName,
location: test.location!,
test,
children: [],
status,
});
}
};
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 = [];

View File

@ -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',
}; };