chore: split ui mode toolbar into lines, show progress (#21403)

This commit is contained in:
Pavel Feldman 2023-03-05 13:46:21 -08:00 committed by GitHub
parent d8d410db6e
commit bae9173208
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 31 deletions

View File

@ -114,6 +114,7 @@ class UIMode {
const run = taskRunner.run(context, 0, stop).then(async status => { const run = taskRunner.run(context, 0, stop).then(async status => {
await reporter.onExit({ status }); await reporter.onExit({ status });
this._testRun = undefined; this._testRun = undefined;
this._config._internal.testIdMatcher = undefined;
return status; return status;
}); });
this._testRun = { run, stop }; this._testRun = { run, stop };

View File

@ -43,3 +43,26 @@
.watch-mode-sidebar .toolbar-button { .watch-mode-sidebar .toolbar-button {
margin: 0; margin: 0;
} }
.watch-mode-sidebar .toolbar h3.title {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 11px;
min-width: 3ch;
margin: 0 10px;
-webkit-margin-before: 0;
-webkit-margin-after: 0;
text-transform: uppercase;
}
.watch-mode-sidebar .spacer {
flex: auto;
}
.watch-mode-sidebar .status-line {
flex: none;
border-top: 1px solid var(--vscode-panel-border);
line-height: 22px;
padding: 0 10px;
}

View File

@ -26,15 +26,22 @@ import type { MultiTraceModel } from './modelUtil';
import './watchMode.css'; import './watchMode.css';
import { ToolbarButton } from '@web/components/toolbarButton'; import { ToolbarButton } from '@web/components/toolbarButton';
import { Toolbar } from '@web/components/toolbar'; import { Toolbar } from '@web/components/toolbar';
import { toggleTheme } from '@web/theme';
let updateRootSuite: (rootSuite: Suite) => void = () => {}; let updateRootSuite: (rootSuite: Suite, progress: Progress) => void = () => {};
let updateProgress: () => void = () => {}; let updateStepsProgress: () => void = () => {};
let runWatchedTests = () => {}; let runWatchedTests = () => {};
export const WatchModeView: React.FC<{}> = ({ export const WatchModeView: React.FC<{}> = ({
}) => { }) => {
const [rootSuite, setRootSuite] = React.useState<{ value: Suite | undefined }>({ value: undefined }); const [rootSuite, setRootSuite] = React.useState<{ value: Suite | undefined }>({ value: undefined });
updateRootSuite = (rootSuite: Suite) => setRootSuite({ value: rootSuite }); const [progress, setProgress] = React.useState<Progress>({ total: 0, passed: 0, failed: 0 });
updateRootSuite = (rootSuite: Suite, { passed, failed }: Progress) => {
setRootSuite({ value: rootSuite });
progress.passed = passed;
progress.failed = failed;
setProgress({ ...progress });
};
const [selectedTreeItemId, setSelectedTreeItemId] = React.useState<string | undefined>(); const [selectedTreeItemId, setSelectedTreeItemId] = React.useState<string | undefined>();
const [isRunningTest, setIsRunningTest] = React.useState<boolean>(false); const [isRunningTest, setIsRunningTest] = React.useState<boolean>(false);
const [filterText, setFilterText] = React.useState<string>(''); const [filterText, setFilterText] = React.useState<string>('');
@ -45,7 +52,7 @@ export const WatchModeView: React.FC<{}> = ({
React.useEffect(() => { React.useEffect(() => {
inputRef.current?.focus(); inputRef.current?.focus();
sendMessageNoReply('list'); resetCollectingRootSuite();
}, []); }, []);
React.useEffect(() => { React.useEffect(() => {
@ -91,18 +98,33 @@ export const WatchModeView: React.FC<{}> = ({
runTests(collectTestIds(selectedTreeItem)); runTests(collectTestIds(selectedTreeItem));
}; };
const runTests = (testIds: string[] | undefined) => { const runTests = (testIds: string[]) => {
setProgress({ total: testIds.length, passed: 0, failed: 0 });
setIsRunningTest(true); setIsRunningTest(true);
sendMessage('run', { testIds }).then(() => { sendMessage('run', { testIds }).then(() => {
setIsRunningTest(false); setIsRunningTest(false);
}); });
}; };
let selectedTestItem: TestItem | undefined;
if (selectedTreeItem?.kind === 'test')
selectedTestItem = selectedTreeItem;
else if (selectedTreeItem?.kind === 'case' && selectedTreeItem.children?.length === 1)
selectedTestItem = selectedTreeItem.children[0]! as TestItem;
return <SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}> return <SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
<TraceView testItem={selectedTreeItem?.kind === 'test' ? selectedTreeItem : undefined} isRunningTest={isRunningTest}></TraceView> <TraceView testItem={selectedTestItem} isRunningTest={isRunningTest}></TraceView>
<div className='vbox watch-mode-sidebar'> <div className='vbox watch-mode-sidebar'>
<Toolbar> <Toolbar>
<input ref={inputRef} type='search' placeholder='Filter tests' spellCheck={false} value={filterText} <h3 className='title'>Test explorer</h3>
<ToolbarButton icon='play' title='Run' onClick={() => runTests([...visibleTestIds])} disabled={isRunningTest}></ToolbarButton>
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => sendMessageNoReply('stop')} disabled={!isRunningTest}></ToolbarButton>
<ToolbarButton icon='refresh' title='Reload' onClick={resetCollectingRootSuite} disabled={isRunningTest}></ToolbarButton>
<div className='spacer'></div>
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
</Toolbar>
<Toolbar>
<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);
}} }}
@ -110,8 +132,6 @@ export const WatchModeView: React.FC<{}> = ({
if (e.key === 'Enter') if (e.key === 'Enter')
runTests([...visibleTestIds]); runTests([...visibleTestIds]);
}}></input> }}></input>
<ToolbarButton icon='play' title='Run' onClick={() => runTests([...visibleTestIds])} disabled={isRunningTest}></ToolbarButton>
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => sendMessageNoReply('stop')} disabled={!isRunningTest}></ToolbarButton>
</Toolbar> </Toolbar>
<ListView <ListView
items={listItems} items={listItems}
@ -171,11 +191,17 @@ export const WatchModeView: React.FC<{}> = ({
}} }}
showNoItemsMessage={true}></ListView> showNoItemsMessage={true}></ListView>
{(rootSuite.value?.suites.length || 0) > 1 && <div style={{ flex: 'none', borderTop: '1px solid var(--vscode-panel-border)' }}> {(rootSuite.value?.suites.length || 0) > 1 && <div style={{ flex: 'none', borderTop: '1px solid var(--vscode-panel-border)' }}>
<Toolbar>
<h3 className='title'>Projects</h3>
</Toolbar>
<ListView <ListView
items={rootSuite.value!.suites} items={rootSuite.value!.suites}
onSelected={(suite: Suite) => { onSelected={(suite: Suite) => {
const copy = [...projectNames]; const copy = [...projectNames];
copy.includes(suite.title) ? copy.splice(copy.indexOf(suite.title), 1) : copy.push(suite.title); if (copy.includes(suite.title))
copy.splice(copy.indexOf(suite.title), 1);
else
copy.push(suite.title);
setProjectNames(copy); setProjectNames(copy);
}} }}
itemRender={(suite: Suite) => { itemRender={(suite: Suite) => {
@ -186,17 +212,23 @@ export const WatchModeView: React.FC<{}> = ({
}} }}
/> />
</div>} </div>}
{isRunningTest && <div className='status-line'>
Running: {progress.total} tests | {progress.passed} passed | {progress.failed} failed
</div>}
{!isRunningTest && <div className='status-line'>
Total: {visibleTestIds.size} tests
</div>}
</div> </div>
</SplitView>; </SplitView>;
}; };
export const ProgressView: React.FC<{ export const StepsView: React.FC<{
testItem: TestItem | undefined, testItem: TestItem | undefined,
}> = ({ }> = ({
testItem, testItem,
}) => { }) => {
const [updateCounter, setUpdateCounter] = React.useState(0); const [updateCounter, setUpdateCounter] = React.useState(0);
updateProgress = () => setUpdateCounter(updateCounter + 1); updateStepsProgress = () => setUpdateCounter(updateCounter + 1);
const steps: (TestCase | TestStep)[] = []; const steps: (TestCase | TestStep)[] = [];
for (const result of testItem?.test.results || []) for (const result of testItem?.test.results || [])
@ -232,7 +264,7 @@ export const TraceView: React.FC<{
}, [testItem, isRunningTest]); }, [testItem, isRunningTest]);
if (isRunningTest) if (isRunningTest)
return <ProgressView testItem={testItem}></ProgressView>; return <StepsView testItem={testItem}></StepsView>;
if (!model) { if (!model) {
return <div className='vbox'> return <div className='vbox'>
@ -255,40 +287,53 @@ declare global {
} }
} }
{ let receiver: TeleReporterReceiver | undefined;
const resetCollectingRootSuite = () => {
let rootSuite: Suite; let rootSuite: Suite;
const receiver = new TeleReporterReceiver({ const progress: Progress = {
total: 0,
passed: 0,
failed: 0,
};
receiver = new TeleReporterReceiver({
onBegin: (config: FullConfig, suite: Suite) => { onBegin: (config: FullConfig, suite: Suite) => {
if (!rootSuite) if (!rootSuite)
rootSuite = suite; rootSuite = suite;
updateRootSuite(rootSuite); progress.passed = 0;
progress.failed = 0;
updateRootSuite(rootSuite, progress);
}, },
onTestBegin: () => { onTestBegin: () => {
updateRootSuite(rootSuite); updateRootSuite(rootSuite, progress);
}, },
onTestEnd: () => { onTestEnd: (test: TestCase) => {
updateRootSuite(rootSuite); if (test.outcome() === 'unexpected')
++progress.failed;
else
++progress.passed;
updateRootSuite(rootSuite, progress);
}, },
onStepBegin: () => { onStepBegin: () => {
updateProgress(); updateStepsProgress();
}, },
onStepEnd: () => { onStepEnd: () => {
updateProgress(); updateStepsProgress();
}, },
}); });
sendMessageNoReply('list');
};
(window as any).dispatch = (message: any) => {
(window as any).dispatch = (message: any) => { if (message.method === 'fileChanged')
if (message.method === 'fileChanged') runWatchedTests();
runWatchedTests(); else
else receiver?.dispatch(message);
receiver.dispatch(message); };
};
}
const sendMessage = async (method: string, params: any) => { const sendMessage = async (method: string, params: any) => {
await (window as any).sendMessage({ method, params }); await (window as any).sendMessage({ method, params });
@ -322,6 +367,12 @@ const collectTestIds = (treeItem?: TreeItem): string[] => {
return testIds; return testIds;
}; };
type Progress = {
total: number;
passed: number;
failed: number;
};
type TreeItemBase = { type TreeItemBase = {
kind: 'file' | 'case' | 'test', kind: 'file' | 'case' | 'test',
id: string; id: string;

View File

@ -20,7 +20,7 @@
justify-content: center; justify-content: center;
flex: auto; flex: auto;
flex-direction: column; flex-direction: column;
background-color: white; background-color: var(--vscode-editor-background);
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;

View File

@ -34,7 +34,7 @@
padding: 0 10px; padding: 0 10px;
line-height: 24px; line-height: 24px;
outline: none; outline: none;
margin-left: 10px; margin: 0 4px;
} }
.toolbar select { .toolbar select {