diff --git a/packages/trace-viewer/src/ui/watchMode.css b/packages/trace-viewer/src/ui/watchMode.css index d665369214..7f0a16098c 100644 --- a/packages/trace-viewer/src/ui/watchMode.css +++ b/packages/trace-viewer/src/ui/watchMode.css @@ -57,6 +57,15 @@ padding: 0 10px; color: var(--vscode-statusBar-foreground); background-color: var(--vscode-statusBar-background); + display: flex; + flex-direction: row; + align-items: center; +} + +.status-line > div { + display: flex; + align-items: center; + margin: 0 5px; } .list-view-entry:not(.selected):not(.highlighted) .toolbar-button { diff --git a/packages/trace-viewer/src/ui/watchMode.tsx b/packages/trace-viewer/src/ui/watchMode.tsx index 590e66da75..d006c34c4f 100644 --- a/packages/trace-viewer/src/ui/watchMode.tsx +++ b/packages/trace-viewer/src/ui/watchMode.tsx @@ -36,7 +36,6 @@ import { XtermWrapper } from '@web/components/xtermWrapper'; let updateRootSuite: (rootSuite: Suite, progress: Progress) => void = () => {}; let updateStepsProgress: () => void = () => {}; let runWatchedTests = () => {}; -let runVisibleTests = () => {}; let xtermSize = { cols: 80, rows: 24 }; const xtermDataSource: XtermDataSource = { @@ -54,12 +53,20 @@ export const WatchModeView: React.FC<{}> = ({ const [projects, setProjects] = React.useState>(new Map()); const [rootSuite, setRootSuite] = React.useState<{ value: Suite | undefined }>({ value: undefined }); const [isRunningTest, setIsRunningTest] = React.useState(false); - const [progress, setProgress] = React.useState({ total: 0, passed: 0, failed: 0 }); + const [progress, setProgress] = React.useState({ total: 0, passed: 0, failed: 0, skipped: 0 }); const [selectedTest, setSelectedTest] = React.useState(undefined); const [settingsVisible, setSettingsVisible] = React.useState(false); const [isWatchingFiles, setIsWatchingFiles] = React.useState(true); + const [visibleTestIds, setVisibleTestIds] = React.useState([]); + const [filterText, setFilterText] = React.useState(''); + const inputRef = React.useRef(null); - updateRootSuite = (rootSuite: Suite, { passed, failed }: Progress) => { + React.useEffect(() => { + inputRef.current?.focus(); + refreshRootSuite(true); + }, []); + + updateRootSuite = (rootSuite: Suite, newProgress: Progress) => { for (const projectName of projects.keys()) { if (!rootSuite.suites.find(s => s.title === projectName)) projects.delete(projectName); @@ -71,12 +78,9 @@ export const WatchModeView: React.FC<{}> = ({ if (![...projects.values()].includes(true)) projects.set(projects.entries().next().value[0], true); - progress.passed = passed; - progress.failed = failed; - setRootSuite({ value: rootSuite }); setProjects(new Map(projects)); - setProgress({ ...progress }); + setProgress(newProgress); }; const runTests = (testIds: string[]) => { @@ -92,7 +96,7 @@ export const WatchModeView: React.FC<{}> = ({ 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, skipped: 0 }); setIsRunningTest(true); sendMessage('run', { testIds }).then(() => { setIsRunningTest(false); @@ -106,26 +110,43 @@ export const WatchModeView: React.FC<{}> = ({
setSettingsVisible(false)}>Tests
- runVisibleTests()} disabled={isRunningTest}> + runTests(visibleTestIds)} disabled={isRunningTest}> sendMessageNoReply('stop')} disabled={!isRunningTest}> refreshRootSuite(true)} disabled={isRunningTest}> setIsWatchingFiles(!isWatchingFiles)}>
{ setSettingsVisible(!settingsVisible); }}>
+ + { + setFilterText(e.target.value); + }} + onKeyDown={e => { + if (e.key === 'Enter') + runTests(visibleTestIds); + }}> + + isVisible={!settingsVisible} + setVisibleTestIds={setVisibleTestIds} /> {settingsVisible && setSettingsVisible(false)}>}
- Running: {progress.total} tests | {progress.passed} passed | {progress.failed} failed +
Total: {progress.total}
+ {isRunningTest &&
Running {visibleTestIds.length}
} + {!isRunningTest &&
Showing: {visibleTestIds.length}
} +
{progress.passed} passed
+
{progress.failed} failed
+
{progress.skipped} skipped
; }; @@ -134,20 +155,19 @@ const TreeListView = TreeView; export const TestList: React.FC<{ projects: Map, + filterText: string, rootSuite: { value: Suite | undefined }, runTests: (testIds: string[]) => void, isRunningTest: boolean, isWatchingFiles: boolean, - isVisible: boolean + isVisible: boolean, + setVisibleTestIds: (testIds: string[]) => void, onTestSelected: (test: TestCase | undefined) => void, -}> = ({ projects, rootSuite, runTests, isRunningTest, isWatchingFiles, isVisible, onTestSelected }) => { +}> = ({ projects, filterText, rootSuite, runTests, isRunningTest, isWatchingFiles, isVisible, onTestSelected, setVisibleTestIds }) => { const [treeState, setTreeState] = React.useState({ expandedItems: new Map() }); - const [filterText, setFilterText] = React.useState(''); const [selectedTreeItemId, setSelectedTreeItemId] = React.useState(); - const inputRef = React.useRef(null); React.useEffect(() => { - inputRef.current?.focus(); refreshRootSuite(true); }, []); @@ -164,9 +184,9 @@ export const TestList: React.FC<{ treeItemMap.set(treeItem.id, treeItem); }; visit(rootItem); - runVisibleTests = () => runTests([...visibleTestIds]); + setVisibleTestIds([...visibleTestIds]); return { rootItem, treeItemMap }; - }, [filterText, rootSuite, projects, runTests]); + }, [filterText, rootSuite, projects, setVisibleTestIds]); const { selectedTreeItem } = React.useMemo(() => { const selectedTreeItem = selectedTreeItemId ? treeItemMap.get(selectedTreeItemId) : undefined; @@ -195,46 +215,34 @@ export const TestList: React.FC<{ if (!isVisible) return <>; - return
- - { - setFilterText(e.target.value); - }} - onKeyDown={e => { - if (e.key === 'Enter') - runVisibleTests(); - }}> - - { - return
-
{treeItem.title}
- runTreeItem(treeItem)} disabled={isRunningTest}> - sendMessageNoReply('open', { location: locationToOpen(treeItem) })}> -
; - }} - icon={treeItem => { - if (treeItem.status === 'running') - return 'codicon-loading'; - if (treeItem.status === 'failed') - return 'codicon-error'; - if (treeItem.status === 'passed') - return 'codicon-check'; - if (treeItem.status === 'skipped') - return 'codicon-circle-slash'; - return 'codicon-circle-outline'; - }} - selectedItem={selectedTreeItem} - onAccepted={runTreeItem} - onSelected={treeItem => { - setSelectedTreeItemId(treeItem.id); - }} - noItemsMessage='No tests' /> -
; + return { + return
+
{treeItem.title}
+ runTreeItem(treeItem)} disabled={isRunningTest}> + sendMessageNoReply('open', { location: locationToOpen(treeItem) })}> +
; + }} + icon={treeItem => { + if (treeItem.status === 'running') + return 'codicon-loading'; + if (treeItem.status === 'failed') + return 'codicon-error'; + if (treeItem.status === 'passed') + return 'codicon-check'; + if (treeItem.status === 'skipped') + return 'codicon-circle-slash'; + return 'codicon-circle-outline'; + }} + selectedItem={selectedTreeItem} + onAccepted={runTreeItem} + onSelected={treeItem => { + setSelectedTreeItemId(treeItem.id); + }} + noItemsMessage='No tests' />; }; export const SettingsView: React.FC<{ @@ -326,13 +334,16 @@ const refreshRootSuite = (eraseResults: boolean) => { total: 0, passed: 0, failed: 0, + skipped: 0, }; receiver = new TeleReporterReceiver({ onBegin: (config: FullConfig, suite: Suite) => { if (!rootSuite) rootSuite = suite; + progress.total = suite.allTests().length; progress.passed = 0; progress.failed = 0; + progress.skipped = 0; updateRootSuite(rootSuite, progress); }, @@ -341,7 +352,9 @@ const refreshRootSuite = (eraseResults: boolean) => { }, onTestEnd: (test: TestCase) => { - if (test.outcome() === 'unexpected') + if (test.outcome() === 'skipped') + ++progress.skipped; + else if (test.outcome() === 'unexpected') ++progress.failed; else ++progress.passed; @@ -426,6 +439,7 @@ type Progress = { total: number; passed: number; failed: number; + skipped: number; }; type TreeItemBase = { diff --git a/packages/web/src/common.css b/packages/web/src/common.css index 3fd325fc84..e1a640f2a4 100644 --- a/packages/web/src/common.css +++ b/packages/web/src/common.css @@ -139,3 +139,13 @@ body.dark-mode ::-webkit-scrollbar-thumb:hover { body.dark-mode ::-webkit-scrollbar-track:hover { background-color: #444; } + +.codicon-loading { + animation: spin 1s infinite linear; +} + +@keyframes spin { + 100% { + transform: rotate(360deg); + } +}