chore: remove ui mode update globals (#30031)
This commit is contained in:
parent
3e73a6ce69
commit
7ad0a12c23
|
|
@ -14,20 +14,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TeleReporterReceiver } from '@testIsomorphic/teleReceiver';
|
import { TeleReporterReceiver, TeleSuite } from '@testIsomorphic/teleReceiver';
|
||||||
import { statusEx } from '@testIsomorphic/testTree';
|
import { statusEx } from '@testIsomorphic/testTree';
|
||||||
import type { ReporterV2 } from 'playwright/src/reporters/reporterV2';
|
import type { ReporterV2 } from 'playwright/src/reporters/reporterV2';
|
||||||
import type * as reporterTypes from 'playwright/types/testReporter';
|
import type * as reporterTypes from 'playwright/types/testReporter';
|
||||||
|
import type { Progress, TestModel } from './uiModeModel';
|
||||||
export type Progress = {
|
|
||||||
total: number;
|
|
||||||
passed: number;
|
|
||||||
failed: number;
|
|
||||||
skipped: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TeleSuiteUpdaterOptions = {
|
export type TeleSuiteUpdaterOptions = {
|
||||||
onUpdate: (source: TeleSuiteUpdater, force?: boolean) => void,
|
onUpdate: (force?: boolean) => void,
|
||||||
onError?: (error: reporterTypes.TestError) => void;
|
onError?: (error: reporterTypes.TestError) => void;
|
||||||
pathSeparator: string;
|
pathSeparator: string;
|
||||||
};
|
};
|
||||||
|
|
@ -87,16 +81,16 @@ export class TeleSuiteUpdater {
|
||||||
this.progress.passed = 0;
|
this.progress.passed = 0;
|
||||||
this.progress.failed = 0;
|
this.progress.failed = 0;
|
||||||
this.progress.skipped = 0;
|
this.progress.skipped = 0;
|
||||||
this._options.onUpdate(this, true);
|
this._options.onUpdate(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
onEnd: () => {
|
onEnd: () => {
|
||||||
this._options.onUpdate(this, true);
|
this._options.onUpdate(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
onTestBegin: (test: reporterTypes.TestCase, testResult: reporterTypes.TestResult) => {
|
onTestBegin: (test: reporterTypes.TestCase, testResult: reporterTypes.TestResult) => {
|
||||||
(testResult as any)[statusEx] = 'running';
|
(testResult as any)[statusEx] = 'running';
|
||||||
this._options.onUpdate(this);
|
this._options.onUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
onTestEnd: (test: reporterTypes.TestCase, testResult: reporterTypes.TestResult) => {
|
onTestEnd: (test: reporterTypes.TestCase, testResult: reporterTypes.TestResult) => {
|
||||||
|
|
@ -107,13 +101,13 @@ export class TeleSuiteUpdater {
|
||||||
else
|
else
|
||||||
++this.progress.passed;
|
++this.progress.passed;
|
||||||
(testResult as any)[statusEx] = testResult.status;
|
(testResult as any)[statusEx] = testResult.status;
|
||||||
this._options.onUpdate(this);
|
this._options.onUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: (error: reporterTypes.TestError) => {
|
onError: (error: reporterTypes.TestError) => {
|
||||||
this.loadErrors.push(error);
|
this.loadErrors.push(error);
|
||||||
this._options.onError?.(error);
|
this._options.onError?.(error);
|
||||||
this._options.onUpdate(this);
|
this._options.onUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
printsToStdio: () => {
|
printsToStdio: () => {
|
||||||
|
|
@ -134,10 +128,19 @@ export class TeleSuiteUpdater {
|
||||||
this._receiver.dispatch(message);
|
this._receiver.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
processTestReport(message: any) {
|
processTestReportEvent(message: any) {
|
||||||
// The order of receiver dispatches matters here, we want to assign `lastRunTestCount`
|
// The order of receiver dispatches matters here, we want to assign `lastRunTestCount`
|
||||||
// before we use it.
|
// before we use it.
|
||||||
this._lastRunReceiver?.dispatch(message)?.catch(() => {});
|
this._lastRunReceiver?.dispatch(message)?.catch(() => {});
|
||||||
this._receiver.dispatch(message)?.catch(() => {});
|
this._receiver.dispatch(message)?.catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asModel(): TestModel {
|
||||||
|
return {
|
||||||
|
rootSuite: this.rootSuite || new TeleSuite('', 'root'),
|
||||||
|
config: this.config!,
|
||||||
|
loadErrors: this.loadErrors,
|
||||||
|
progress: this.progress,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,18 @@
|
||||||
|
|
||||||
import type * as reporterTypes from 'playwright/types/testReporter';
|
import type * as reporterTypes from 'playwright/types/testReporter';
|
||||||
|
|
||||||
|
export type Progress = {
|
||||||
|
total: number;
|
||||||
|
passed: number;
|
||||||
|
failed: number;
|
||||||
|
skipped: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type TestModel = {
|
export type TestModel = {
|
||||||
config: reporterTypes.FullConfig | undefined;
|
config: reporterTypes.FullConfig;
|
||||||
rootSuite: reporterTypes.Suite | undefined;
|
rootSuite: reporterTypes.Suite;
|
||||||
loadErrors: reporterTypes.TestError[];
|
loadErrors: reporterTypes.TestError[];
|
||||||
|
progress: Progress;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pathSeparator = navigator.userAgent.toLowerCase().includes('windows') ? '\\' : '/';
|
export const pathSeparator = navigator.userAgent.toLowerCase().includes('windows') ? '\\' : '/';
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ export const TestListView: React.FC<{
|
||||||
filterText: string,
|
filterText: string,
|
||||||
testTree: TestTree,
|
testTree: TestTree,
|
||||||
testServerConnection: TestServerConnection | undefined,
|
testServerConnection: TestServerConnection | undefined,
|
||||||
testModel: TestModel,
|
testModel?: TestModel,
|
||||||
runTests: (mode: 'bounce-if-busy' | 'queue-if-busy', testIds: Set<string>) => void,
|
runTests: (mode: 'bounce-if-busy' | 'queue-if-busy', testIds: Set<string>) => void,
|
||||||
runningState?: { testIds: Set<string>, itemSelectedByUser?: boolean },
|
runningState?: { testIds: Set<string>, itemSelectedByUser?: boolean },
|
||||||
watchAll: boolean,
|
watchAll: boolean,
|
||||||
|
|
@ -86,6 +86,8 @@ export const TestListView: React.FC<{
|
||||||
|
|
||||||
// Compute selected item.
|
// Compute selected item.
|
||||||
const { selectedTreeItem } = React.useMemo(() => {
|
const { selectedTreeItem } = React.useMemo(() => {
|
||||||
|
if (!testModel)
|
||||||
|
return { selectedTreeItem: undefined };
|
||||||
const selectedTreeItem = selectedTreeItemId ? testTree.treeItemById(selectedTreeItemId) : undefined;
|
const selectedTreeItem = selectedTreeItemId ? testTree.treeItemById(selectedTreeItemId) : undefined;
|
||||||
let testFile: SourceLocation | undefined;
|
let testFile: SourceLocation | undefined;
|
||||||
if (selectedTreeItem) {
|
if (selectedTreeItem) {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
import '@web/third_party/vscode/codicon.css';
|
import '@web/third_party/vscode/codicon.css';
|
||||||
import '@web/common.css';
|
import '@web/common.css';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { baseFullConfig, TeleSuite } from '@testIsomorphic/teleReceiver';
|
import { TeleSuite } from '@testIsomorphic/teleReceiver';
|
||||||
import { TeleSuiteUpdater } from './teleSuiteUpdater';
|
import { TeleSuiteUpdater } from './teleSuiteUpdater';
|
||||||
import type { Progress } from './teleSuiteUpdater';
|
import type { Progress } from './uiModeModel';
|
||||||
import type { TeleTestCase } from '@testIsomorphic/teleReceiver';
|
import type { TeleTestCase } from '@testIsomorphic/teleReceiver';
|
||||||
import type * as reporterTypes from 'playwright/types/testReporter';
|
import type * as reporterTypes from 'playwright/types/testReporter';
|
||||||
import { SplitView } from '@web/components/splitView';
|
import { SplitView } from '@web/components/splitView';
|
||||||
|
|
@ -33,7 +33,6 @@ import { toggleTheme } from '@web/theme';
|
||||||
import { settings, useSetting } from '@web/uiUtils';
|
import { settings, useSetting } from '@web/uiUtils';
|
||||||
import { statusEx, TestTree } from '@testIsomorphic/testTree';
|
import { statusEx, TestTree } from '@testIsomorphic/testTree';
|
||||||
import type { TreeItem } from '@testIsomorphic/testTree';
|
import type { TreeItem } from '@testIsomorphic/testTree';
|
||||||
import type { Disposable } from '@testIsomorphic/events';
|
|
||||||
import { TestServerConnection } from '@testIsomorphic/testServerConnection';
|
import { TestServerConnection } from '@testIsomorphic/testServerConnection';
|
||||||
import { pathSeparator } from './uiModeModel';
|
import { pathSeparator } from './uiModeModel';
|
||||||
import type { TestModel } from './uiModeModel';
|
import type { TestModel } from './uiModeModel';
|
||||||
|
|
@ -41,10 +40,7 @@ import { FiltersView } from './uiModeFiltersView';
|
||||||
import { TestListView } from './uiModeTestListView';
|
import { TestListView } from './uiModeTestListView';
|
||||||
import { TraceView } from './uiModeTraceView';
|
import { TraceView } from './uiModeTraceView';
|
||||||
|
|
||||||
let updateRootSuite: (config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], progress: Progress | undefined) => void = () => {};
|
|
||||||
// let runWatchedTests = (fileNames: string[]) => {};
|
|
||||||
let xtermSize = { cols: 80, rows: 24 };
|
let xtermSize = { cols: 80, rows: 24 };
|
||||||
|
|
||||||
const xtermDataSource: XtermDataSource = {
|
const xtermDataSource: XtermDataSource = {
|
||||||
pending: [],
|
pending: [],
|
||||||
clear: () => {},
|
clear: () => {},
|
||||||
|
|
@ -62,7 +58,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
['skipped', false],
|
['skipped', false],
|
||||||
]));
|
]));
|
||||||
const [projectFilters, setProjectFilters] = React.useState<Map<string, boolean>>(new Map());
|
const [projectFilters, setProjectFilters] = React.useState<Map<string, boolean>>(new Map());
|
||||||
const [testModel, setTestModel] = React.useState<TestModel>({ config: undefined, rootSuite: undefined, loadErrors: [] });
|
const [testModel, setTestModel] = React.useState<TestModel>();
|
||||||
const [progress, setProgress] = React.useState<Progress & { total: number } | undefined>();
|
const [progress, setProgress] = React.useState<Progress & { total: number } | undefined>();
|
||||||
const [selectedItem, setSelectedItem] = React.useState<{ treeItem?: TreeItem, testFile?: SourceLocation, testCase?: reporterTypes.TestCase }>({});
|
const [selectedItem, setSelectedItem] = React.useState<{ treeItem?: TreeItem, testFile?: SourceLocation, testCase?: reporterTypes.TestCase }>({});
|
||||||
const [visibleTestIds, setVisibleTestIds] = React.useState<Set<string>>(new Set());
|
const [visibleTestIds, setVisibleTestIds] = React.useState<Set<string>>(new Set());
|
||||||
|
|
@ -83,62 +79,132 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
const guid = new URLSearchParams(window.location.search).get('ws');
|
const guid = new URLSearchParams(window.location.search).get('ws');
|
||||||
const wsURL = new URL(`../${guid}`, window.location.toString());
|
const wsURL = new URL(`../${guid}`, window.location.toString());
|
||||||
wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
|
wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
|
||||||
const connection = new TestServerConnection(wsURL.toString());
|
setTestServerConnection(new TestServerConnection(wsURL.toString()));
|
||||||
setTestServerConnection(connection);
|
|
||||||
setIsLoading(true);
|
|
||||||
setWatchedTreeIds({ value: new Set() });
|
|
||||||
updateRootSuite(baseFullConfig, new TeleSuite('', 'root'), [], undefined);
|
|
||||||
(async () => {
|
|
||||||
const status = await connection.runGlobalSetup();
|
|
||||||
if (status === 'passed')
|
|
||||||
await refreshRootSuite(connection);
|
|
||||||
setIsLoading(false);
|
|
||||||
const { hasBrowsers } = await connection.checkBrowsers();
|
|
||||||
setHasBrowsers(hasBrowsers);
|
|
||||||
})();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
// Load tests on startup.
|
||||||
if (!testServerConnection)
|
|
||||||
return;
|
|
||||||
const disposables = [
|
|
||||||
...wireConnectionListeners(testServerConnection),
|
|
||||||
testServerConnection.onClose(() => setIsDisconnected(true))
|
|
||||||
];
|
|
||||||
return () => {
|
|
||||||
for (const disposable of disposables)
|
|
||||||
disposable.dispose();
|
|
||||||
};
|
|
||||||
}, [testServerConnection]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
reloadTests();
|
reloadTests();
|
||||||
}, [reloadTests]);
|
}, [reloadTests]);
|
||||||
|
|
||||||
updateRootSuite = React.useCallback((config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], newProgress: Progress | undefined) => {
|
// Wire server connection to the auxiliary UI features.
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!testServerConnection)
|
||||||
|
return;
|
||||||
|
const disposables = [
|
||||||
|
testServerConnection.onStdio(params => {
|
||||||
|
if (params.buffer) {
|
||||||
|
const data = atob(params.buffer);
|
||||||
|
xtermDataSource.write(data);
|
||||||
|
} else {
|
||||||
|
xtermDataSource.write(params.text!);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
testServerConnection.onClose(() => setIsDisconnected(true))
|
||||||
|
];
|
||||||
|
xtermDataSource.resize = (cols, rows) => {
|
||||||
|
xtermSize = { cols, rows };
|
||||||
|
testServerConnection.resizeTerminalNoReply({ cols, rows });
|
||||||
|
};
|
||||||
|
return () => {
|
||||||
|
for (const disposable of disposables)
|
||||||
|
disposable.dispose();
|
||||||
|
};
|
||||||
|
}, [testServerConnection]);
|
||||||
|
|
||||||
|
|
||||||
|
// This is the main routine, every time connection updates it starts the
|
||||||
|
// whole workflow.
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!testServerConnection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let throttleTimer: NodeJS.Timeout | undefined;
|
||||||
|
const teleSuiteUpdater = new TeleSuiteUpdater({
|
||||||
|
onUpdate: immediate => {
|
||||||
|
clearTimeout(throttleTimer);
|
||||||
|
throttleTimer = undefined;
|
||||||
|
if (immediate) {
|
||||||
|
setTestModel(teleSuiteUpdater.asModel());
|
||||||
|
} else if (!throttleTimer) {
|
||||||
|
throttleTimer = setTimeout(() => {
|
||||||
|
setTestModel(teleSuiteUpdater.asModel());
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: error => {
|
||||||
|
xtermDataSource.write((error.stack || error.value || '') + '\n');
|
||||||
|
},
|
||||||
|
pathSeparator,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateList = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const result = await testServerConnection.listTests({});
|
||||||
|
teleSuiteUpdater.processListReport(result.report);
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
setTestModel(undefined);
|
||||||
|
setIsLoading(true);
|
||||||
|
setWatchedTreeIds({ value: new Set() });
|
||||||
|
(async () => {
|
||||||
|
const status = await testServerConnection.runGlobalSetup();
|
||||||
|
if (status !== 'passed')
|
||||||
|
return;
|
||||||
|
const result = await testServerConnection.listTests({});
|
||||||
|
teleSuiteUpdater.processListReport(result.report);
|
||||||
|
|
||||||
|
testServerConnection.onListChanged(updateList);
|
||||||
|
testServerConnection.onReport(params => {
|
||||||
|
teleSuiteUpdater.processTestReportEvent(params);
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
|
||||||
|
const { hasBrowsers } = await testServerConnection.checkBrowsers();
|
||||||
|
setHasBrowsers(hasBrowsers);
|
||||||
|
})();
|
||||||
|
return () => {
|
||||||
|
clearTimeout(throttleTimer);
|
||||||
|
};
|
||||||
|
}, [testServerConnection]);
|
||||||
|
|
||||||
|
// Update project filter default values.
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!testModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const { config, rootSuite } = testModel;
|
||||||
const selectedProjects = config.configFile ? settings.getObject<string[] | undefined>(config.configFile + ':projects', undefined) : undefined;
|
const selectedProjects = config.configFile ? settings.getObject<string[] | undefined>(config.configFile + ':projects', undefined) : undefined;
|
||||||
for (const projectName of projectFilters.keys()) {
|
const newFilter = new Map(projectFilters);
|
||||||
|
for (const projectName of newFilter.keys()) {
|
||||||
if (!rootSuite.suites.find(s => s.title === projectName))
|
if (!rootSuite.suites.find(s => s.title === projectName))
|
||||||
projectFilters.delete(projectName);
|
newFilter.delete(projectName);
|
||||||
}
|
}
|
||||||
for (const projectSuite of rootSuite.suites) {
|
for (const projectSuite of rootSuite.suites) {
|
||||||
if (!projectFilters.has(projectSuite.title))
|
if (!newFilter.has(projectSuite.title))
|
||||||
projectFilters.set(projectSuite.title, !!selectedProjects?.includes(projectSuite.title));
|
newFilter.set(projectSuite.title, !!selectedProjects?.includes(projectSuite.title));
|
||||||
}
|
}
|
||||||
if (!selectedProjects && projectFilters.size && ![...projectFilters.values()].includes(true))
|
if (!selectedProjects && newFilter.size && ![...newFilter.values()].includes(true))
|
||||||
projectFilters.set(projectFilters.entries().next().value[0], true);
|
newFilter.set(newFilter.entries().next().value[0], true);
|
||||||
|
if (projectFilters.size !== newFilter.size || [...projectFilters].some(([k, v]) => newFilter.get(k) !== v))
|
||||||
|
setProjectFilters(newFilter);
|
||||||
|
}, [projectFilters, testModel]);
|
||||||
|
|
||||||
setTestModel({ config, rootSuite, loadErrors });
|
// Update progress.
|
||||||
setProjectFilters(new Map(projectFilters));
|
React.useEffect(() => {
|
||||||
if (runningState && newProgress)
|
if (runningState && testModel?.progress)
|
||||||
setProgress(newProgress);
|
setProgress(testModel.progress);
|
||||||
else if (!newProgress)
|
else if (!testModel)
|
||||||
setProgress(undefined);
|
setProgress(undefined);
|
||||||
}, [projectFilters, runningState]);
|
}, [testModel, runningState]);
|
||||||
|
|
||||||
|
// Test tree is built from the model and filters.
|
||||||
const { testTree } = React.useMemo(() => {
|
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);
|
const testTree = new TestTree('', testModel.rootSuite, testModel.loadErrors, projectFilters, pathSeparator);
|
||||||
testTree.filterTree(filterText, statusFilters, runningState?.testIds);
|
testTree.filterTree(filterText, statusFilters, runningState?.testIds);
|
||||||
testTree.sortAndPropagateStatus();
|
testTree.sortAndPropagateStatus();
|
||||||
|
|
@ -149,7 +215,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
}, [filterText, testModel, statusFilters, projectFilters, setVisibleTestIds, runningState]);
|
}, [filterText, testModel, statusFilters, projectFilters, setVisibleTestIds, runningState]);
|
||||||
|
|
||||||
const runTests = React.useCallback((mode: 'queue-if-busy' | 'bounce-if-busy', testIds: Set<string>) => {
|
const runTests = React.useCallback((mode: 'queue-if-busy' | 'bounce-if-busy', testIds: Set<string>) => {
|
||||||
if (!testServerConnection)
|
if (!testServerConnection || !testModel)
|
||||||
return;
|
return;
|
||||||
if (mode === 'bounce-if-busy' && runningState)
|
if (mode === 'bounce-if-busy' && runningState)
|
||||||
return;
|
return;
|
||||||
|
|
@ -189,6 +255,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
});
|
});
|
||||||
}, [projectFilters, runningState, testModel, testServerConnection]);
|
}, [projectFilters, runningState, testModel, testServerConnection]);
|
||||||
|
|
||||||
|
// Watch implementation.
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!testServerConnection)
|
if (!testServerConnection)
|
||||||
return;
|
return;
|
||||||
|
|
@ -217,6 +284,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
return () => disposable.dispose();
|
return () => disposable.dispose();
|
||||||
}, [runTests, testServerConnection, testTree, watchAll, watchedTreeIds]);
|
}, [runTests, testServerConnection, testTree, watchAll, watchedTreeIds]);
|
||||||
|
|
||||||
|
// Shortcuts.
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!testServerConnection)
|
if (!testServerConnection)
|
||||||
return;
|
return;
|
||||||
|
|
@ -229,9 +297,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
reloadTests();
|
reloadTests();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
addEventListener('keydown', onShortcutEvent);
|
addEventListener('keydown', onShortcutEvent);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
removeEventListener('keydown', onShortcutEvent);
|
removeEventListener('keydown', onShortcutEvent);
|
||||||
};
|
};
|
||||||
|
|
@ -287,7 +353,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
<XtermWrapper source={xtermDataSource}></XtermWrapper>
|
<XtermWrapper source={xtermDataSource}></XtermWrapper>
|
||||||
</div>
|
</div>
|
||||||
<div className={'vbox' + (isShowingOutput ? ' hidden' : '')}>
|
<div className={'vbox' + (isShowingOutput ? ' hidden' : '')}>
|
||||||
<TraceView item={selectedItem} rootDir={testModel.config?.rootDir} />
|
<TraceView item={selectedItem} rootDir={testModel?.config?.rootDir} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='vbox ui-mode-sidebar'>
|
<div className='vbox ui-mode-sidebar'>
|
||||||
|
|
@ -343,60 +409,3 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
</SplitView>
|
</SplitView>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
let teleSuiteUpdater: TeleSuiteUpdater | undefined;
|
|
||||||
|
|
||||||
let throttleTimer: NodeJS.Timeout | undefined;
|
|
||||||
let throttleData: { config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], progress: Progress } | undefined;
|
|
||||||
const throttledAction = () => {
|
|
||||||
clearTimeout(throttleTimer);
|
|
||||||
throttleTimer = undefined;
|
|
||||||
updateRootSuite(throttleData!.config, throttleData!.rootSuite, throttleData!.loadErrors, throttleData!.progress);
|
|
||||||
};
|
|
||||||
|
|
||||||
const throttleUpdateRootSuite = (config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], progress: Progress, immediate = false) => {
|
|
||||||
throttleData = { config, rootSuite, loadErrors, progress };
|
|
||||||
if (immediate)
|
|
||||||
throttledAction();
|
|
||||||
else if (!throttleTimer)
|
|
||||||
throttleTimer = setTimeout(throttledAction, 250);
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshRootSuite = async (testServerConnection: TestServerConnection): Promise<void> => {
|
|
||||||
teleSuiteUpdater = new TeleSuiteUpdater({
|
|
||||||
onUpdate: (source, immediate) => {
|
|
||||||
throttleUpdateRootSuite(source.config!, source.rootSuite || new TeleSuite('', 'root'), source.loadErrors, source.progress, immediate);
|
|
||||||
},
|
|
||||||
onError: error => {
|
|
||||||
xtermDataSource.write((error.stack || error.value || '') + '\n');
|
|
||||||
},
|
|
||||||
pathSeparator,
|
|
||||||
});
|
|
||||||
const { report } = await testServerConnection.listTests({});
|
|
||||||
teleSuiteUpdater?.processListReport(report);
|
|
||||||
};
|
|
||||||
|
|
||||||
const wireConnectionListeners = (testServerConnection: TestServerConnection): Disposable[] => {
|
|
||||||
const disposables: Disposable[] = [
|
|
||||||
testServerConnection.onListChanged(async () => {
|
|
||||||
const { report } = await testServerConnection.listTests({});
|
|
||||||
teleSuiteUpdater?.processListReport(report);
|
|
||||||
}),
|
|
||||||
testServerConnection.onStdio(params => {
|
|
||||||
if (params.buffer) {
|
|
||||||
const data = atob(params.buffer);
|
|
||||||
xtermDataSource.write(data);
|
|
||||||
} else {
|
|
||||||
xtermDataSource.write(params.text!);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
testServerConnection.onReport(params => {
|
|
||||||
teleSuiteUpdater?.processTestReport(params);
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
xtermDataSource.resize = (cols, rows) => {
|
|
||||||
xtermSize = { cols, rows };
|
|
||||||
testServerConnection.resizeTerminalNoReply({ cols, rows });
|
|
||||||
};
|
|
||||||
return disposables;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue