mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: ui mode fixes (#21546)
For https://github.com/microsoft/playwright/issues/21541
This commit is contained in:
parent
e737ff83b4
commit
0106a54e6e
@ -94,12 +94,6 @@
|
|||||||
color: var(--blue);
|
color: var(--blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
.call-error-message {
|
.call-tab .error-message {
|
||||||
font-family: var(--vscode-editor-font-family);
|
|
||||||
font-weight: var(--vscode-editor-font-weight);
|
|
||||||
font-size: var(--vscode-editor-font-size);
|
|
||||||
background-color: var(--vscode-inputValidation-errorBackground);
|
|
||||||
white-space: pre;
|
|
||||||
overflow: auto;
|
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ansi2html from 'ansi-to-html';
|
|
||||||
import type { SerializedValue } from '@protocol/channels';
|
import type { SerializedValue } from '@protocol/channels';
|
||||||
import type { ActionTraceEvent } from '@trace/trace';
|
import type { ActionTraceEvent } from '@trace/trace';
|
||||||
import { msToString } from '@web/uiUtils';
|
import { msToString } from '@web/uiUtils';
|
||||||
@ -23,6 +22,7 @@ import './callTab.css';
|
|||||||
import { CopyToClipboard } from './copyToClipboard';
|
import { CopyToClipboard } from './copyToClipboard';
|
||||||
import { asLocator } from '@isomorphic/locatorGenerators';
|
import { asLocator } from '@isomorphic/locatorGenerators';
|
||||||
import type { Language } from '@isomorphic/locatorGenerators';
|
import type { Language } from '@isomorphic/locatorGenerators';
|
||||||
|
import { ErrorMessage } from './errorMessage';
|
||||||
|
|
||||||
export const CallTab: React.FunctionComponent<{
|
export const CallTab: React.FunctionComponent<{
|
||||||
action: ActionTraceEvent | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
@ -39,7 +39,7 @@ export const CallTab: React.FunctionComponent<{
|
|||||||
const wallTime = action.wallTime ? new Date(action.wallTime).toLocaleString() : null;
|
const wallTime = action.wallTime ? new Date(action.wallTime).toLocaleString() : null;
|
||||||
const duration = action.endTime ? msToString(action.endTime - action.startTime) : 'Timed Out';
|
const duration = action.endTime ? msToString(action.endTime - action.startTime) : 'Timed Out';
|
||||||
return <div className='call-tab'>
|
return <div className='call-tab'>
|
||||||
{!!error && <ErrorMessage error={error}></ErrorMessage>}
|
{!!error && <ErrorMessage error={error} />}
|
||||||
{!!error && <div className='call-section'>Call</div>}
|
{!!error && <div className='call-section'>Call</div>}
|
||||||
<div className='call-line'>{action.apiName}</div>
|
<div className='call-line'>{action.apiName}</div>
|
||||||
{<>
|
{<>
|
||||||
@ -144,40 +144,3 @@ function parseSerializedValue(value: SerializedValue, handles: any[] | undefined
|
|||||||
}
|
}
|
||||||
return '<object>';
|
return '<object>';
|
||||||
}
|
}
|
||||||
|
|
||||||
const ErrorMessage: React.FC<{
|
|
||||||
error: string;
|
|
||||||
}> = ({ error }) => {
|
|
||||||
const html = React.useMemo(() => {
|
|
||||||
const config: any = {
|
|
||||||
bg: 'var(--vscode-panel-background)',
|
|
||||||
fg: 'var(--vscode-foreground)',
|
|
||||||
};
|
|
||||||
config.colors = ansiColors;
|
|
||||||
return new ansi2html(config).toHtml(escapeHTML(error));
|
|
||||||
}, [error]);
|
|
||||||
return <div className='call-error-message' dangerouslySetInnerHTML={{ __html: html || '' }}></div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ansiColors = {
|
|
||||||
0: '#000',
|
|
||||||
1: '#C00',
|
|
||||||
2: '#0C0',
|
|
||||||
3: '#C50',
|
|
||||||
4: '#00C',
|
|
||||||
5: '#C0C',
|
|
||||||
6: '#0CC',
|
|
||||||
7: '#CCC',
|
|
||||||
8: '#555',
|
|
||||||
9: '#F55',
|
|
||||||
10: '#5F5',
|
|
||||||
11: '#FF5',
|
|
||||||
12: '#55F',
|
|
||||||
13: '#F5F',
|
|
||||||
14: '#5FF',
|
|
||||||
15: '#FFF'
|
|
||||||
};
|
|
||||||
|
|
||||||
function escapeHTML(text: string): string {
|
|
||||||
return text.replace(/[&"<>]/g, c => ({ '&': '&', '"': '"', '<': '<', '>': '>' }[c]!));
|
|
||||||
}
|
|
||||||
|
24
packages/trace-viewer/src/ui/errorMessage.css
Normal file
24
packages/trace-viewer/src/ui/errorMessage.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-family: var(--vscode-editor-font-family);
|
||||||
|
font-weight: var(--vscode-editor-font-weight);
|
||||||
|
font-size: var(--vscode-editor-font-size);
|
||||||
|
background-color: var(--vscode-inputValidation-errorBackground);
|
||||||
|
white-space: pre;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
56
packages/trace-viewer/src/ui/errorMessage.tsx
Normal file
56
packages/trace-viewer/src/ui/errorMessage.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ansi2html from 'ansi-to-html';
|
||||||
|
import * as React from 'react';
|
||||||
|
import './errorMessage.css';
|
||||||
|
|
||||||
|
export const ErrorMessage: React.FC<{
|
||||||
|
error: string;
|
||||||
|
}> = ({ error }) => {
|
||||||
|
const html = React.useMemo(() => {
|
||||||
|
const config: any = {
|
||||||
|
bg: 'var(--vscode-panel-background)',
|
||||||
|
fg: 'var(--vscode-foreground)',
|
||||||
|
};
|
||||||
|
config.colors = ansiColors;
|
||||||
|
return new ansi2html(config).toHtml(escapeHTML(error));
|
||||||
|
}, [error]);
|
||||||
|
return <div className='error-message' dangerouslySetInnerHTML={{ __html: html || '' }}></div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ansiColors = {
|
||||||
|
0: '#000',
|
||||||
|
1: '#C00',
|
||||||
|
2: '#0C0',
|
||||||
|
3: '#C50',
|
||||||
|
4: '#00C',
|
||||||
|
5: '#C0C',
|
||||||
|
6: '#0CC',
|
||||||
|
7: '#CCC',
|
||||||
|
8: '#555',
|
||||||
|
9: '#F55',
|
||||||
|
10: '#5F5',
|
||||||
|
11: '#FF5',
|
||||||
|
12: '#55F',
|
||||||
|
13: '#F5F',
|
||||||
|
14: '#5FF',
|
||||||
|
15: '#FFF'
|
||||||
|
};
|
||||||
|
|
||||||
|
function escapeHTML(text: string): string {
|
||||||
|
return text.replace(/[&"<>]/g, c => ({ '&': '&', '"': '"', '<': '<', '>': '>' }[c]!));
|
||||||
|
}
|
@ -37,12 +37,16 @@ let updateRootSuite: (rootSuite: Suite, progress: Progress) => void = () => {};
|
|||||||
let updateStepsProgress: () => void = () => {};
|
let updateStepsProgress: () => void = () => {};
|
||||||
let runWatchedTests = () => {};
|
let runWatchedTests = () => {};
|
||||||
let runVisibleTests = () => {};
|
let runVisibleTests = () => {};
|
||||||
|
let xtermSize = { cols: 80, rows: 24 };
|
||||||
|
|
||||||
const xtermDataSource: XtermDataSource = {
|
const xtermDataSource: XtermDataSource = {
|
||||||
pending: [],
|
pending: [],
|
||||||
clear: () => {},
|
clear: () => {},
|
||||||
write: data => xtermDataSource.pending.push(data),
|
write: data => xtermDataSource.pending.push(data),
|
||||||
resize: (cols: number, rows: number) => sendMessageNoReply('resizeTerminal', { cols, rows }),
|
resize: (cols: number, rows: number) => {
|
||||||
|
xtermSize = { cols, rows };
|
||||||
|
sendMessageNoReply('resizeTerminal', { cols, rows });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WatchModeView: React.FC<{}> = ({
|
export const WatchModeView: React.FC<{}> = ({
|
||||||
@ -76,6 +80,18 @@ export const WatchModeView: React.FC<{}> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const runTests = (testIds: string[]) => {
|
const runTests = (testIds: string[]) => {
|
||||||
|
// Clear test results.
|
||||||
|
{
|
||||||
|
const testIdSet = new Set(testIds);
|
||||||
|
for (const test of rootSuite.value?.allTests() || []) {
|
||||||
|
if (testIdSet.has(test.id))
|
||||||
|
test.results = [];
|
||||||
|
}
|
||||||
|
setRootSuite({ ...rootSuite });
|
||||||
|
}
|
||||||
|
|
||||||
|
const time = ' [' + new Date().toLocaleTimeString() + ']';
|
||||||
|
xtermDataSource.write('\x1B[2m—'.repeat(Math.max(0, xtermSize.cols - time.length)) + time + '\x1B[22m');
|
||||||
setProgress({ total: testIds.length, passed: 0, failed: 0 });
|
setProgress({ total: testIds.length, passed: 0, failed: 0 });
|
||||||
setIsRunningTest(true);
|
setIsRunningTest(true);
|
||||||
sendMessage('run', { testIds }).then(() => {
|
sendMessage('run', { testIds }).then(() => {
|
||||||
@ -83,13 +99,14 @@ export const WatchModeView: React.FC<{}> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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}>
|
||||||
<TraceView test={selectedTest}></TraceView>
|
{(result && result.duration >= 0) ? <FinishedTraceView testResult={result} /> : <InProgressTraceView testResult={result} />}
|
||||||
<div className='vbox watch-mode-sidebar'>
|
<div className='vbox watch-mode-sidebar'>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<div className='section-title' style={{ cursor: 'pointer' }} onClick={() => setSettingsVisible(false)}>Tests</div>
|
<div className='section-title' style={{ cursor: 'pointer' }} onClick={() => setSettingsVisible(false)}>Tests</div>
|
||||||
<ToolbarButton icon='play' title='Run' onClick={runVisibleTests} disabled={isRunningTest}></ToolbarButton>
|
<ToolbarButton icon='play' title='Run' onClick={() => runVisibleTests()} disabled={isRunningTest}></ToolbarButton>
|
||||||
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => sendMessageNoReply('stop')} disabled={!isRunningTest}></ToolbarButton>
|
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => sendMessageNoReply('stop')} disabled={!isRunningTest}></ToolbarButton>
|
||||||
<ToolbarButton icon='refresh' title='Reload' onClick={() => refreshRootSuite(true)} disabled={isRunningTest}></ToolbarButton>
|
<ToolbarButton icon='refresh' title='Reload' onClick={() => refreshRootSuite(true)} disabled={isRunningTest}></ToolbarButton>
|
||||||
<ToolbarButton icon='eye-watch' title='Watch' toggled={isWatchingFiles} onClick={() => setIsWatchingFiles(!isWatchingFiles)}></ToolbarButton>
|
<ToolbarButton icon='eye-watch' title='Watch' toggled={isWatchingFiles} onClick={() => setIsWatchingFiles(!isWatchingFiles)}></ToolbarButton>
|
||||||
@ -108,7 +125,7 @@ export const WatchModeView: React.FC<{}> = ({
|
|||||||
</div>
|
</div>
|
||||||
</SplitView>
|
</SplitView>
|
||||||
<div className='status-line'>
|
<div className='status-line'>
|
||||||
Running: {progress.total} tests | {progress.passed} passed | {progress.failed} failed
|
Running: {progress.total} tests | {progress.passed} passed | {progress.failed} failed
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
@ -134,23 +151,22 @@ export const TestList: React.FC<{
|
|||||||
refreshRootSuite(true);
|
refreshRootSuite(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { rootItem, treeItemMap, visibleTestIds } = React.useMemo(() => {
|
const { rootItem, treeItemMap } = React.useMemo(() => {
|
||||||
const rootItem = createTree(rootSuite.value, projects);
|
const rootItem = createTree(rootSuite.value, projects);
|
||||||
filterTree(rootItem, filterText);
|
filterTree(rootItem, filterText);
|
||||||
|
hideOnlyTests(rootItem);
|
||||||
const treeItemMap = new Map<string, TreeItem>();
|
const treeItemMap = new Map<string, TreeItem>();
|
||||||
const visibleTestIds = new Set<string>();
|
const visibleTestIds = new Set<string>();
|
||||||
const visit = (treeItem: TreeItem) => {
|
const visit = (treeItem: TreeItem) => {
|
||||||
if (treeItem.kind === 'test')
|
if (treeItem.kind === 'case')
|
||||||
visibleTestIds.add(treeItem.id);
|
treeItem.tests.forEach(t => visibleTestIds.add(t.id));
|
||||||
treeItem.children?.forEach(visit);
|
treeItem.children.forEach(visit);
|
||||||
treeItemMap.set(treeItem.id, treeItem);
|
treeItemMap.set(treeItem.id, treeItem);
|
||||||
};
|
};
|
||||||
visit(rootItem);
|
visit(rootItem);
|
||||||
hideOnlyTests(rootItem);
|
runVisibleTests = () => runTests([...visibleTestIds]);
|
||||||
return { rootItem, treeItemMap, visibleTestIds };
|
return { rootItem, treeItemMap };
|
||||||
}, [filterText, rootSuite, projects]);
|
}, [filterText, rootSuite, projects, runTests]);
|
||||||
|
|
||||||
runVisibleTests = () => runTests([...visibleTestIds]);
|
|
||||||
|
|
||||||
const { selectedTreeItem } = React.useMemo(() => {
|
const { selectedTreeItem } = React.useMemo(() => {
|
||||||
const selectedTreeItem = selectedTreeItemId ? treeItemMap.get(selectedTreeItemId) : undefined;
|
const selectedTreeItem = selectedTreeItemId ? treeItemMap.get(selectedTreeItemId) : undefined;
|
||||||
@ -168,7 +184,6 @@ export const TestList: React.FC<{
|
|||||||
}, [selectedTreeItem, isWatchingFiles]);
|
}, [selectedTreeItem, isWatchingFiles]);
|
||||||
|
|
||||||
const runTreeItem = (treeItem: TreeItem) => {
|
const runTreeItem = (treeItem: TreeItem) => {
|
||||||
// expandedItems.set(treeItem.id, true);
|
|
||||||
setSelectedTreeItemId(treeItem.id);
|
setSelectedTreeItemId(treeItem.id);
|
||||||
runTests(collectTestIds(treeItem));
|
runTests(collectTestIds(treeItem));
|
||||||
};
|
};
|
||||||
@ -209,6 +224,8 @@ export const TestList: React.FC<{
|
|||||||
return 'codicon-error';
|
return 'codicon-error';
|
||||||
if (treeItem.status === 'passed')
|
if (treeItem.status === 'passed')
|
||||||
return 'codicon-check';
|
return 'codicon-check';
|
||||||
|
if (treeItem.status === 'skipped')
|
||||||
|
return 'codicon-circle-slash';
|
||||||
return 'codicon-circle-outline';
|
return 'codicon-circle-outline';
|
||||||
}}
|
}}
|
||||||
selectedItem={selectedTreeItem}
|
selectedItem={selectedTreeItem}
|
||||||
@ -252,33 +269,38 @@ export const SettingsView: React.FC<{
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TraceView: React.FC<{
|
export const InProgressTraceView: React.FC<{
|
||||||
test: TestCase | undefined,
|
testResult: TestResult | undefined,
|
||||||
}> = ({ test }) => {
|
}> = ({ testResult }) => {
|
||||||
const [model, setModel] = React.useState<MultiTraceModel | undefined>();
|
const [model, setModel] = React.useState<MultiTraceModel | undefined>();
|
||||||
const [stepsProgress, setStepsProgress] = React.useState(0);
|
const [stepsProgress, setStepsProgress] = React.useState(0);
|
||||||
updateStepsProgress = () => setStepsProgress(stepsProgress + 1);
|
updateStepsProgress = () => setStepsProgress(stepsProgress + 1);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
(async () => {
|
setModel(testResult ? stepsToModel(testResult) : undefined);
|
||||||
if (!test) {
|
}, [stepsProgress, testResult]);
|
||||||
setModel(undefined);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = test.results?.[0];
|
return <TraceView model={model} />;
|
||||||
if (result) {
|
};
|
||||||
const attachment = result.attachments.find(a => a.name === 'trace');
|
|
||||||
if (attachment && attachment.path)
|
|
||||||
loadSingleTraceFile(attachment.path).then(setModel);
|
|
||||||
else
|
|
||||||
setModel(stepsToModel(result));
|
|
||||||
} else {
|
|
||||||
setModel(undefined);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [test, stepsProgress]);
|
|
||||||
|
|
||||||
|
export const FinishedTraceView: React.FC<{
|
||||||
|
testResult: TestResult,
|
||||||
|
}> = ({ testResult }) => {
|
||||||
|
const [model, setModel] = React.useState<MultiTraceModel | undefined>();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// Test finished.
|
||||||
|
const attachment = testResult.attachments.find(a => a.name === 'trace');
|
||||||
|
if (attachment && attachment.path)
|
||||||
|
loadSingleTraceFile(attachment.path).then(setModel);
|
||||||
|
}, [testResult]);
|
||||||
|
|
||||||
|
return <TraceView model={model} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TraceView: React.FC<{
|
||||||
|
model: MultiTraceModel | undefined,
|
||||||
|
}> = ({ model }) => {
|
||||||
const xterm = <XtermWrapper source={xtermDataSource}></XtermWrapper>;
|
const xterm = <XtermWrapper source={xtermDataSource}></XtermWrapper>;
|
||||||
return <Workbench model={model} output={xterm} rightToolbar={[
|
return <Workbench model={model} output={xterm} rightToolbar={[
|
||||||
<ToolbarButton icon='trash' title='Clear output' onClick={() => xtermDataSource.clear()}></ToolbarButton>,
|
<ToolbarButton icon='trash' title='Clear output' onClick={() => xtermDataSource.clear()}></ToolbarButton>,
|
||||||
@ -412,7 +434,7 @@ type TreeItemBase = {
|
|||||||
title: string;
|
title: string;
|
||||||
location: Location,
|
location: Location,
|
||||||
children: TreeItem[];
|
children: TreeItem[];
|
||||||
status: 'none' | 'running' | 'passed' | 'failed';
|
status: 'none' | 'running' | 'passed' | 'failed' | 'skipped';
|
||||||
};
|
};
|
||||||
|
|
||||||
type GroupItem = TreeItemBase & {
|
type GroupItem = TreeItemBase & {
|
||||||
@ -476,12 +498,14 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
|
|||||||
parentGroup.children.push(testCaseItem);
|
parentGroup.children.push(testCaseItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
let status: 'none' | 'running' | 'passed' | 'failed' = 'none';
|
let status: 'none' | 'running' | 'passed' | 'failed' | 'skipped' = 'none';
|
||||||
if (test.results.some(r => r.duration === -1))
|
if (test.results.some(r => r.duration === -1))
|
||||||
status = 'running';
|
status = 'running';
|
||||||
|
else if (test.results.length && test.outcome() === 'skipped')
|
||||||
|
status = 'skipped';
|
||||||
else if (test.results.length && test.outcome() !== 'expected')
|
else if (test.results.length && test.outcome() !== 'expected')
|
||||||
status = 'failed';
|
status = 'failed';
|
||||||
else if (test.outcome() === 'expected')
|
else if (test.results.length && test.outcome() === 'expected')
|
||||||
status = 'passed';
|
status = 'passed';
|
||||||
|
|
||||||
testCaseItem.tests.push(test);
|
testCaseItem.tests.push(test);
|
||||||
@ -508,11 +532,13 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
|
|||||||
propagateStatus(child);
|
propagateStatus(child);
|
||||||
|
|
||||||
let allPassed = treeItem.children.length > 0;
|
let allPassed = treeItem.children.length > 0;
|
||||||
|
let allSkipped = treeItem.children.length > 0;
|
||||||
let hasFailed = false;
|
let hasFailed = false;
|
||||||
let hasRunning = false;
|
let hasRunning = false;
|
||||||
|
|
||||||
for (const child of treeItem.children) {
|
for (const child of treeItem.children) {
|
||||||
allPassed = allPassed && child.status === 'passed';
|
allSkipped = allSkipped && child.status === 'skipped';
|
||||||
|
allPassed = allPassed && (child.status === 'passed' || child.status === 'skipped');
|
||||||
hasFailed = hasFailed || child.status === 'failed';
|
hasFailed = hasFailed || child.status === 'failed';
|
||||||
hasRunning = hasRunning || child.status === 'running';
|
hasRunning = hasRunning || child.status === 'running';
|
||||||
}
|
}
|
||||||
@ -521,6 +547,8 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
|
|||||||
treeItem.status = 'running';
|
treeItem.status = 'running';
|
||||||
else if (hasFailed)
|
else if (hasFailed)
|
||||||
treeItem.status = 'failed';
|
treeItem.status = 'failed';
|
||||||
|
else if (allSkipped)
|
||||||
|
treeItem.status = 'skipped';
|
||||||
else if (allPassed)
|
else if (allPassed)
|
||||||
treeItem.status = 'passed';
|
treeItem.status = 'passed';
|
||||||
};
|
};
|
||||||
|
@ -117,6 +117,10 @@ body.dark-mode .CodeMirror span.cm-type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror .CodeMirror-gutters {
|
.CodeMirror .CodeMirror-gutters {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror .CodeMirror-gutterwrapper {
|
||||||
background: var(--vscode-editor-background);
|
background: var(--vscode-editor-background);
|
||||||
border-right: 1px solid var(--vscode-editorGroup-border);
|
border-right: 1px solid var(--vscode-editorGroup-border);
|
||||||
color: var(--vscode-editorLineNumber-foreground);
|
color: var(--vscode-editorLineNumber-foreground);
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
|
|
||||||
.list-view-content:focus .list-view-entry.selected * {
|
.list-view-content:focus .list-view-entry.selected * {
|
||||||
color: var(--vscode-list-activeSelectionForeground) !important;
|
color: var(--vscode-list-activeSelectionForeground) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-view-content:focus .list-view-entry.error.selected {
|
.list-view-content:focus .list-view-entry.error.selected {
|
||||||
|
@ -121,11 +121,19 @@ export function ListView<T>({
|
|||||||
onMouseLeave={() => setHighlightedItem(undefined)}
|
onMouseLeave={() => setHighlightedItem(undefined)}
|
||||||
>
|
>
|
||||||
{indentation ? <div style={{ minWidth: indentation * 16 }}></div> : undefined}
|
{indentation ? <div style={{ minWidth: indentation * 16 }}></div> : undefined}
|
||||||
{icon && <div className={'codicon ' + (icon(item) || 'blank')} style={{ minWidth: 16, marginRight: 4 }} onClick={e => {
|
{icon && <div
|
||||||
e.stopPropagation();
|
className={'codicon ' + (icon(item) || 'blank')}
|
||||||
e.preventDefault();
|
style={{ minWidth: 16, marginRight: 4 }}
|
||||||
onIconClicked?.(item);
|
onDoubleClick={e => {
|
||||||
}}></div>}
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
onIconClicked?.(item);
|
||||||
|
}}
|
||||||
|
></div>}
|
||||||
{typeof rendered === 'string' ? <div style={{ textOverflow: 'ellipsis', overflow: 'hidden' }}>{rendered}</div> : rendered}
|
{typeof rendered === 'string' ? <div style={{ textOverflow: 'ellipsis', overflow: 'hidden' }}>{rendered}</div> : rendered}
|
||||||
</div>;
|
</div>;
|
||||||
})}
|
})}
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
@import '../third_party/vscode/colors.css';
|
@import '../third_party/vscode/colors.css';
|
||||||
|
|
||||||
|
.xterm-wrapper {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.xterm-wrapper .xterm-viewport {
|
.xterm-wrapper .xterm-viewport {
|
||||||
background-color: var(--vscode-panel-background) !important;
|
background-color: var(--vscode-panel-background) !important;
|
||||||
color: var(--vscode-foreground) !important;
|
color: var(--vscode-foreground) !important;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user