chore: allow running failing (#21569)
This commit is contained in:
parent
8abe9fcf22
commit
1073e42686
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -41,5 +41,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-button.toggled {
|
.toolbar-button.toggled {
|
||||||
color: var(--vscode-inputOption-activeBorder);
|
color: var(--vscode-notificationLink-foreground);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue