chore: allow running failing (#21569)

This commit is contained in:
Pavel Feldman 2023-03-10 12:41:00 -08:00 committed by GitHub
parent 8abe9fcf22
commit 1073e42686
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 103 additions and 15 deletions

2
package-lock.json generated
View file

@ -9888,7 +9888,7 @@
"requires": { "requires": {
"codemirror": "^5.65.9", "codemirror": "^5.65.9",
"xterm": "^5.1.0", "xterm": "^5.1.0",
"xterm-addon-fit": "*" "xterm-addon-fit": "^0.7.0"
} }
}, },
"which": { "which": {

View file

@ -166,9 +166,7 @@ export class TeleReporterReceiver {
private _onTestBegin(testId: string, payload: JsonTestResultStart) { private _onTestBegin(testId: string, payload: JsonTestResultStart) {
const test = this._tests.get(testId)!; const test = this._tests.get(testId)!;
test.results = []; const testResult = test._createTestResult(payload.id);
test.resultsMap.clear();
const testResult = test._appendTestResult(payload.id);
testResult.retry = payload.retry; testResult.retry = payload.retry;
testResult.workerIndex = payload.workerIndex; testResult.workerIndex = payload.workerIndex;
testResult.parallelIndex = payload.parallelIndex; testResult.parallelIndex = payload.parallelIndex;
@ -398,7 +396,9 @@ export class TeleTestCase implements reporterTypes.TestCase {
return status === 'expected' || status === 'flaky' || status === 'skipped'; return status === 'expected' || status === 'flaky' || status === 'skipped';
} }
_appendTestResult(id: string): reporterTypes.TestResult { _createTestResult(id: string): reporterTypes.TestResult {
this.results = [];
this.resultsMap.clear();
const result: TeleTestResult = { const result: TeleTestResult = {
retry: this.results.length, retry: this.results.length,
parallelIndex: -1, parallelIndex: -1,

View file

@ -68,6 +68,43 @@
margin: 0 5px; margin: 0 5px;
} }
.list-view {
margin-top: 5px;
}
.list-view-entry:not(.selected):not(.highlighted) .toolbar-button { .list-view-entry:not(.selected):not(.highlighted) .toolbar-button {
display: none; display: none;
} }
.filters {
flex: none;
margin-left: 5px;
line-height: 24px;
}
.filters > span {
color: var(--vscode-panelTitle-inactiveForeground);
padding-left: 3px;
}
.filters > div {
display: inline-block;
margin: 0 5px;
user-select: none;
cursor: pointer;
}
.filters > div:hover,
.filters > div.filters-toggled {
color: var(--vscode-notificationLink-foreground);
}
.watch-mode-sidebar input[type=search] {
padding: 0 5px;
line-height: 24px;
outline: none;
margin: 0 4px;
border: none;
color: var(--vscode-input-foreground);
background-color: var(--vscode-input-background);
}

View file

@ -21,6 +21,7 @@ import React from 'react';
import { TreeView } from '@web/components/treeView'; import { TreeView } from '@web/components/treeView';
import type { TreeState } from '@web/components/treeView'; import type { TreeState } from '@web/components/treeView';
import { TeleReporterReceiver } from '../../../playwright-test/src/isomorphic/teleReceiver'; import { TeleReporterReceiver } from '../../../playwright-test/src/isomorphic/teleReceiver';
import type { TeleTestCase } from '../../../playwright-test/src/isomorphic/teleReceiver';
import type { FullConfig, Suite, TestCase, TestResult, TestStep, Location } from '../../../playwright-test/types/testReporter'; import type { FullConfig, Suite, TestCase, TestResult, TestStep, Location } from '../../../playwright-test/types/testReporter';
import { SplitView } from '@web/components/splitView'; import { SplitView } from '@web/components/splitView';
import { MultiTraceModel } from './modelUtil'; import { MultiTraceModel } from './modelUtil';
@ -32,6 +33,7 @@ import type { ContextEntry } from '../entries';
import type * as trace from '@trace/trace'; import type * as trace from '@trace/trace';
import type { XtermDataSource } from '@web/components/xtermWrapper'; import type { XtermDataSource } from '@web/components/xtermWrapper';
import { XtermWrapper } from '@web/components/xtermWrapper'; import { XtermWrapper } from '@web/components/xtermWrapper';
import { Expandable } from '@web/components/expandable';
let updateRootSuite: (rootSuite: Suite, progress: Progress) => void = () => {}; let updateRootSuite: (rootSuite: Suite, progress: Progress) => void = () => {};
let updateStepsProgress: () => void = () => {}; let updateStepsProgress: () => void = () => {};
@ -59,6 +61,7 @@ export const WatchModeView: React.FC<{}> = ({
const [isWatchingFiles, setIsWatchingFiles] = React.useState<boolean>(true); const [isWatchingFiles, setIsWatchingFiles] = React.useState<boolean>(true);
const [visibleTestIds, setVisibleTestIds] = React.useState<string[]>([]); const [visibleTestIds, setVisibleTestIds] = React.useState<string[]>([]);
const [filterText, setFilterText] = React.useState<string>(''); const [filterText, setFilterText] = React.useState<string>('');
const [filterExpanded, setFilterExpanded] = React.useState<boolean>(false);
const inputRef = React.useRef<HTMLInputElement>(null); const inputRef = React.useRef<HTMLInputElement>(null);
React.useEffect(() => { React.useEffect(() => {
@ -89,7 +92,7 @@ export const WatchModeView: React.FC<{}> = ({
const testIdSet = new Set(testIds); const testIdSet = new Set(testIds);
for (const test of rootSuite.value?.allTests() || []) { for (const test of rootSuite.value?.allTests() || []) {
if (testIdSet.has(test.id)) if (testIdSet.has(test.id))
test.results = []; (test as TeleTestCase)._createTestResult('pending');
} }
setRootSuite({ ...rootSuite }); setRootSuite({ ...rootSuite });
} }
@ -103,6 +106,24 @@ export const WatchModeView: React.FC<{}> = ({
}); });
}; };
const updateFilter = (name: string, value: string) => {
const result: string[] = [];
const prefix = name + ':';
for (const t of filterText.split(' ')) {
if (t.startsWith(prefix)) {
if (value) {
result.push(prefix + value);
value = '';
}
} else {
result.push(t);
}
}
if (value)
result.unshift(prefix + value);
setFilterText(result.join(' '));
};
const result = selectedTest?.results[0]; const result = selectedTest?.results[0];
return <div className='vbox'> return <div className='vbox'>
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}> <SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}>
@ -117,16 +138,29 @@ export const WatchModeView: React.FC<{}> = ({
<div className='spacer'></div> <div className='spacer'></div>
<ToolbarButton icon='gear' title='Toggle color mode' toggled={settingsVisible} onClick={() => { setSettingsVisible(!settingsVisible); }}></ToolbarButton> <ToolbarButton icon='gear' title='Toggle color mode' toggled={settingsVisible} onClick={() => { setSettingsVisible(!settingsVisible); }}></ToolbarButton>
</Toolbar> </Toolbar>
<Toolbar> {!settingsVisible && <Expandable
<input ref={inputRef} type='search' placeholder='Filter (e.g. text, @tag)' spellCheck={false} value={filterText} title={<input ref={inputRef} type='search' placeholder='Filter (e.g. text, @tag)' spellCheck={false} value={filterText}
onChange={e => { onChange={e => {
setFilterText(e.target.value); setFilterText(e.target.value);
}} }}
onKeyDown={e => { onKeyDown={e => {
if (e.key === 'Enter') if (e.key === 'Enter')
runTests(visibleTestIds); runTests(visibleTestIds);
}}></input> }}></input>}
</Toolbar> style={{ flex: 'none', marginTop: 8 }}
expanded={filterExpanded}
setExpanded={setFilterExpanded}>
<div className='filters'>
<span>Status:</span>
<div onClick={() => updateFilter('s', '')}>all</div>
{['failed', 'passed', 'skipped'].map(s => <div className={filterText.includes('s:' + s) ? 'filters-toggled' : ''} onClick={() => updateFilter('s', s)}>{s}</div>)}
</div>
{[...projects.values()].filter(v => v).length > 1 && <div className='filters'>
<span>Project:</span>
<div onClick={() => updateFilter('p', '')}>all</div>
{[...projects].filter(([k, v]) => v).map(([k, v]) => k).map(p => <div className={filterText.includes('p:' + p) ? 'filters-toggled' : ''} onClick={() => updateFilter('p', p)}>{p}</div>)}
</div>}
</Expandable>}
<TestList <TestList
projects={projects} projects={projects}
filterText={filterText} filterText={filterText}
@ -459,11 +493,13 @@ type GroupItem = TreeItemBase & {
type TestCaseItem = TreeItemBase & { type TestCaseItem = TreeItemBase & {
kind: 'case', kind: 'case',
tests: TestCase[]; tests: TestCase[];
children: TestItem[];
}; };
type TestItem = TreeItemBase & { type TestItem = TreeItemBase & {
kind: 'test', kind: 'test',
test: TestCase; test: TestCase;
project: string;
}; };
type TreeItem = GroupItem | TestCaseItem | TestItem; type TreeItem = GroupItem | TestCaseItem | TestItem;
@ -531,6 +567,7 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
test, test,
children: [], children: [],
status, status,
project: projectName
}); });
} }
}; };
@ -573,13 +610,27 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
function filterTree(rootItem: GroupItem, 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 textTokens = filterTokens.filter(token => !token.match(/^[sp]:/));
const statuses = new Set(filterTokens.filter(t => t.startsWith('s:')).map(t => t.substring(2)));
if (statuses.size)
statuses.add('running');
const projects = new Set(filterTokens.filter(t => t.startsWith('p:')).map(t => t.substring(2)));
const filter = (testCase: TestCaseItem) => {
const title = testCase.tests[0].titlePath().join(' ').toLowerCase();
if (!textTokens.every(token => title.includes(token)))
return false;
testCase.children = (testCase.children as TestItem[]).filter(test => !statuses.size || statuses.has(test.status));
testCase.children = (testCase.children as TestItem[]).filter(test => !projects.size || projects.has(test.project));
testCase.tests = (testCase.children as TestItem[]).map(c => c.test);
return !!testCase.children.length;
};
const visit = (treeItem: GroupItem) => { const visit = (treeItem: GroupItem) => {
const newChildren: (GroupItem | TestCaseItem)[] = []; const newChildren: (GroupItem | TestCaseItem)[] = [];
for (const child of treeItem.children) { for (const child of treeItem.children) {
if (child.kind === 'case') { if (child.kind === 'case') {
const title = child.tests[0].titlePath().join(' ').toLowerCase(); if (filter(child))
if (filterTokens.every(token => title.includes(token)))
newChildren.push(child); newChildren.push(child);
} else { } else {
visit(child); visit(child);

View file

@ -26,10 +26,10 @@ export const Expandable: React.FunctionComponent<React.PropsWithChildren<{
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', whiteSpace: 'nowrap' }}> <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', whiteSpace: 'nowrap' }}>
<div <div
className={'codicon codicon-' + (expanded ? 'chevron-down' : 'chevron-right')} className={'codicon codicon-' + (expanded ? 'chevron-down' : 'chevron-right')}
style={{ cursor: 'pointer', color: 'var(--vscode-foreground)', marginRight: '4px' }} style={{ cursor: 'pointer', color: 'var(--vscode-foreground)', marginLeft: '5px' }}
onClick={() => setExpanded(!expanded)} /> onClick={() => setExpanded(!expanded)} />
{title} {title}
</div> </div>
{ expanded && <div style={{ display: 'flex', flex: 'auto', margin: '5px 0 5px 20px' }}>{children}</div> } { expanded && <div style={{ margin: '5px 0 5px 20px' }}>{children}</div> }
</div>; </div>;
}; };

View file

@ -41,5 +41,5 @@
} }
.toolbar-button.toggled { .toolbar-button.toggled {
color: var(--vscode-inputOption-activeBorder); color: var(--vscode-notificationLink-foreground);
} }