}) => void,
@@ -154,7 +154,7 @@ export const TestListView: React.FC<{
{!!treeItem.duration && treeItem.status !== 'skipped' && {msToString(treeItem.duration)}
}
- runTreeItem(treeItem)} disabled={!!runningState}>
+ runTreeItem(treeItem)} disabled={!!runningState && !runningState.completed}>
{!watchAll && {
if (watchedTreeIds.value.has(treeItem.id))
diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx
index 244a25ca5e..b12617c300 100644
--- a/packages/trace-viewer/src/ui/uiModeView.tsx
+++ b/packages/trace-viewer/src/ui/uiModeView.tsx
@@ -85,7 +85,9 @@ export const UIModeView: React.FC<{}> = ({
const [selectedItem, setSelectedItem] = React.useState<{ treeItem?: TreeItem, testFile?: SourceLocation, testCase?: reporterTypes.TestCase }>({});
const [visibleTestIds, setVisibleTestIds] = React.useState>(new Set());
const [isLoading, setIsLoading] = React.useState(false);
- const [runningState, setRunningState] = React.useState<{ testIds: Set, itemSelectedByUser?: boolean } | undefined>();
+ const [runningState, setRunningState] = React.useState<{ testIds: Set, itemSelectedByUser?: boolean, completed?: boolean } | undefined>();
+ const isRunningTest = runningState && !runningState.completed;
+
const [watchAll, setWatchAll] = useSetting('watch-all', false);
const [watchedTreeIds, setWatchedTreeIds] = React.useState<{ value: Set }>({ value: new Set() });
const commandQueue = React.useRef(Promise.resolve());
@@ -251,29 +253,29 @@ export const UIModeView: React.FC<{}> = ({
// Update progress.
React.useEffect(() => {
- if (runningState && testModel?.progress)
+ if (isRunningTest && testModel?.progress)
setProgress(testModel.progress);
else if (!testModel)
setProgress(undefined);
- }, [testModel, runningState]);
+ }, [testModel, isRunningTest]);
// Test tree is built from the model and filters.
const { testTree } = React.useMemo(() => {
if (!testModel)
return { testTree: new TestTree('', new TeleSuite('', 'root'), [], projectFilters, pathSeparator) };
const testTree = new TestTree('', testModel.rootSuite, testModel.loadErrors, projectFilters, pathSeparator);
- testTree.filterTree(filterText, statusFilters, runningState?.testIds);
+ testTree.filterTree(filterText, statusFilters, isRunningTest ? runningState?.testIds : undefined);
testTree.sortAndPropagateStatus();
testTree.shortenRoot();
testTree.flattenForSingleProject();
setVisibleTestIds(testTree.testIds());
return { testTree };
- }, [filterText, testModel, statusFilters, projectFilters, setVisibleTestIds, runningState]);
+ }, [filterText, testModel, statusFilters, projectFilters, setVisibleTestIds, runningState, isRunningTest]);
const runTests = React.useCallback((mode: 'queue-if-busy' | 'bounce-if-busy', testIds: Set) => {
if (!testServerConnection || !testModel)
return;
- if (mode === 'bounce-if-busy' && runningState)
+ if (mode === 'bounce-if-busy' && isRunningTest)
return;
runTestBacklog.current = new Set([...runTestBacklog.current, ...testIds]);
@@ -320,9 +322,9 @@ export const UIModeView: React.FC<{}> = ({
test.results = [];
}
setTestModel({ ...testModel });
- setRunningState(undefined);
+ setRunningState(oldState => oldState ? ({ ...oldState, completed: true }) : undefined);
});
- }, [projectFilters, runningState, testModel, testServerConnection, runWorkers, runHeaded, runUpdateSnapshots]);
+ }, [projectFilters, isRunningTest, testModel, testServerConnection, runWorkers, runHeaded, runUpdateSnapshots]);
React.useEffect(() => {
if (!testServerConnection || !teleSuiteUpdater)
@@ -396,7 +398,6 @@ export const UIModeView: React.FC<{}> = ({
};
}, [runTests, reloadTests, testServerConnection, visibleTestIds, isShowingOutput]);
- const isRunningTest = !!runningState;
const dialogRef = React.useRef(null);
const openInstallDialog = React.useCallback((e: React.MouseEvent) => {
e.preventDefault();
diff --git a/packages/trace-viewer/src/uiMode.tsx b/packages/trace-viewer/src/uiMode.tsx
index f2f846a112..5dac2082e8 100644
--- a/packages/trace-viewer/src/uiMode.tsx
+++ b/packages/trace-viewer/src/uiMode.tsx
@@ -17,7 +17,7 @@
import '@web/common.css';
import { applyTheme } from '@web/theme';
import '@web/third_party/vscode/codicon.css';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { UIModeView } from './ui/uiModeView';
(async () => {
@@ -38,5 +38,5 @@ import { UIModeView } from './ui/uiModeView';
setInterval(function() { fetch('ping'); }, 10000);
}
- ReactDOM.render(, document.querySelector('#root'));
+ ReactDOM.createRoot(document.querySelector('#root')!).render();
})();
diff --git a/tests/playwright-test/ui-mode-test-run.spec.ts b/tests/playwright-test/ui-mode-test-run.spec.ts
index 2d41aa18d4..9c1120ea04 100644
--- a/tests/playwright-test/ui-mode-test-run.spec.ts
+++ b/tests/playwright-test/ui-mode-test-run.spec.ts
@@ -174,7 +174,9 @@ test('should run by project', async ({ runUITest }) => {
await expect.poll(dumpTestTree(page)).toBe(`
▼ ❌ a.test.ts
► ◯ passes
- ► ❌ fails <=
+ ▼ ❌ fails
+ ❌ foo <=
+ ◯ bar
► ❌ suite
▼ ❌ b.test.ts
► ◯ passes