chore(ui): maintain test order when updating (#21478)
This commit is contained in:
parent
f5894ed089
commit
a2490a8fc8
|
|
@ -26,6 +26,7 @@ export type JsonStackFrame = { file: string, line: number, column: number };
|
||||||
export type JsonConfig = {
|
export type JsonConfig = {
|
||||||
rootDir: string;
|
rootDir: string;
|
||||||
configFile: string | undefined;
|
configFile: string | undefined;
|
||||||
|
listOnly: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type JsonPattern = {
|
export type JsonPattern = {
|
||||||
|
|
@ -148,6 +149,7 @@ export class TeleReporterReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onBegin(config: JsonConfig, projects: JsonProject[]) {
|
private _onBegin(config: JsonConfig, projects: JsonProject[]) {
|
||||||
|
const removeMissing = config.listOnly;
|
||||||
for (const project of projects) {
|
for (const project of projects) {
|
||||||
let projectSuite = this._rootSuite.suites.find(suite => suite.project()!.id === project.id);
|
let projectSuite = this._rootSuite.suites.find(suite => suite.project()!.id === project.id);
|
||||||
if (!projectSuite) {
|
if (!projectSuite) {
|
||||||
|
|
@ -157,7 +159,7 @@ export class TeleReporterReceiver {
|
||||||
}
|
}
|
||||||
const p = this._parseProject(project);
|
const p = this._parseProject(project);
|
||||||
projectSuite.project = () => p;
|
projectSuite.project = () => p;
|
||||||
this._mergeSuitesInto(project.suites, projectSuite);
|
this._mergeSuitesInto(project.suites, projectSuite, removeMissing);
|
||||||
}
|
}
|
||||||
this._reporter.onBegin?.(this._parseConfig(config), this._rootSuite);
|
this._reporter.onBegin?.(this._parseConfig(config), this._rootSuite);
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +262,7 @@ export class TeleReporterReceiver {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _mergeSuitesInto(jsonSuites: JsonSuite[], parent: TeleSuite) {
|
private _mergeSuitesInto(jsonSuites: JsonSuite[], parent: TeleSuite, removeMissing: boolean) {
|
||||||
for (const jsonSuite of jsonSuites) {
|
for (const jsonSuite of jsonSuites) {
|
||||||
let targetSuite = parent.suites.find(s => s.title === jsonSuite.title);
|
let targetSuite = parent.suites.find(s => s.title === jsonSuite.title);
|
||||||
if (!targetSuite) {
|
if (!targetSuite) {
|
||||||
|
|
@ -271,12 +273,16 @@ export class TeleReporterReceiver {
|
||||||
targetSuite.location = jsonSuite.location;
|
targetSuite.location = jsonSuite.location;
|
||||||
targetSuite._fileId = jsonSuite.fileId;
|
targetSuite._fileId = jsonSuite.fileId;
|
||||||
targetSuite._parallelMode = jsonSuite.parallelMode;
|
targetSuite._parallelMode = jsonSuite.parallelMode;
|
||||||
this._mergeSuitesInto(jsonSuite.suites, targetSuite);
|
this._mergeSuitesInto(jsonSuite.suites, targetSuite, removeMissing);
|
||||||
this._mergeTestsInto(jsonSuite.tests, targetSuite);
|
this._mergeTestsInto(jsonSuite.tests, targetSuite, removeMissing);
|
||||||
|
}
|
||||||
|
if (removeMissing) {
|
||||||
|
const suiteMap = new Map(parent.suites.map(p => [p.title, p]));
|
||||||
|
parent.suites = jsonSuites.map(s => suiteMap.get(s.title)).filter(Boolean) as TeleSuite[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _mergeTestsInto(jsonTests: JsonTestCase[], parent: TeleSuite) {
|
private _mergeTestsInto(jsonTests: JsonTestCase[], parent: TeleSuite, removeMissing: boolean) {
|
||||||
for (const jsonTest of jsonTests) {
|
for (const jsonTest of jsonTests) {
|
||||||
let targetTest = parent.tests.find(s => s.title === jsonTest.title);
|
let targetTest = parent.tests.find(s => s.title === jsonTest.title);
|
||||||
if (!targetTest) {
|
if (!targetTest) {
|
||||||
|
|
@ -287,6 +293,10 @@ export class TeleReporterReceiver {
|
||||||
}
|
}
|
||||||
this._updateTest(jsonTest, targetTest);
|
this._updateTest(jsonTest, targetTest);
|
||||||
}
|
}
|
||||||
|
if (removeMissing) {
|
||||||
|
const testMap = new Map(parent.tests.map(p => [p.title, p]));
|
||||||
|
parent.tests = jsonTests.map(s => testMap.get(s.title)).filter(Boolean) as TeleTestCase[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateTest(payload: JsonTestCase, test: TeleTestCase): TeleTestCase {
|
private _updateTest(payload: JsonTestCase, test: TeleTestCase): TeleTestCase {
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,11 @@ import type { FullConfig, FullResult, Reporter, TestError, TestResult, TestStep
|
||||||
import type { Suite, TestCase } from '../common/test';
|
import type { Suite, TestCase } from '../common/test';
|
||||||
import type { JsonConfig, JsonProject, JsonSuite, JsonTestCase, JsonTestResultEnd, JsonTestResultStart, JsonTestStepEnd, JsonTestStepStart } from '../isomorphic/teleReceiver';
|
import type { JsonConfig, JsonProject, JsonSuite, JsonTestCase, JsonTestResultEnd, JsonTestResultStart, JsonTestStepEnd, JsonTestStepStart } from '../isomorphic/teleReceiver';
|
||||||
import type { SuitePrivate } from '../../types/reporterPrivate';
|
import type { SuitePrivate } from '../../types/reporterPrivate';
|
||||||
import type { FullProjectInternal } from '../common/types';
|
import type { FullConfigInternal, FullProjectInternal } from '../common/types';
|
||||||
import { createGuid } from 'playwright-core/lib/utils';
|
import { createGuid } from 'playwright-core/lib/utils';
|
||||||
import { serializeRegexPatterns } from '../isomorphic/teleReceiver';
|
import { serializeRegexPatterns } from '../isomorphic/teleReceiver';
|
||||||
|
|
||||||
export class TeleReporterEmitter implements Reporter {
|
export class TeleReporterEmitter implements Reporter {
|
||||||
private config!: FullConfig;
|
|
||||||
private _messageSink: (message: any) => void;
|
private _messageSink: (message: any) => void;
|
||||||
|
|
||||||
constructor(messageSink: (message: any) => void) {
|
constructor(messageSink: (message: any) => void) {
|
||||||
|
|
@ -31,7 +30,6 @@ export class TeleReporterEmitter implements Reporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBegin(config: FullConfig, suite: Suite) {
|
onBegin(config: FullConfig, suite: Suite) {
|
||||||
this.config = config;
|
|
||||||
const projects: any[] = [];
|
const projects: any[] = [];
|
||||||
for (const projectSuite of suite.suites) {
|
for (const projectSuite of suite.suites) {
|
||||||
const report = this._serializeProject(projectSuite);
|
const report = this._serializeProject(projectSuite);
|
||||||
|
|
@ -116,6 +114,7 @@ export class TeleReporterEmitter implements Reporter {
|
||||||
return {
|
return {
|
||||||
rootDir: config.rootDir,
|
rootDir: config.rootDir,
|
||||||
configFile: config.configFile,
|
configFile: config.configFile,
|
||||||
|
listOnly: (config as FullConfigInternal)._internal.listOnly,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export class Runner {
|
||||||
webServerPluginsForConfig(config).forEach(p => config._internal.plugins.push({ factory: p }));
|
webServerPluginsForConfig(config).forEach(p => config._internal.plugins.push({ factory: p }));
|
||||||
|
|
||||||
const reporter = await createReporter(config, listOnly ? 'list' : 'run');
|
const reporter = await createReporter(config, listOnly ? 'list' : 'run');
|
||||||
const taskRunner = listOnly ? createTaskRunnerForList(config, reporter)
|
const taskRunner = listOnly ? createTaskRunnerForList(config, reporter, 'in-process')
|
||||||
: createTaskRunner(config, reporter);
|
: createTaskRunner(config, reporter);
|
||||||
|
|
||||||
const context: TaskRunnerState = {
|
const context: TaskRunnerState = {
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,9 @@ function addRunTasks(taskRunner: TaskRunner<TaskRunnerState>, config: FullConfig
|
||||||
return taskRunner;
|
return taskRunner;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTaskRunnerForList(config: FullConfigInternal, reporter: Multiplexer): TaskRunner<TaskRunnerState> {
|
export function createTaskRunnerForList(config: FullConfigInternal, reporter: Multiplexer, mode: 'in-process' | 'out-of-process'): TaskRunner<TaskRunnerState> {
|
||||||
const taskRunner = new TaskRunner<TaskRunnerState>(reporter, config.globalTimeout);
|
const taskRunner = new TaskRunner<TaskRunnerState>(reporter, config.globalTimeout);
|
||||||
taskRunner.addTask('load tests', createLoadTask('in-process', false));
|
taskRunner.addTask('load tests', createLoadTask(mode, false));
|
||||||
taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => {
|
taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => {
|
||||||
reporter.onBegin?.(config, rootSuite!);
|
reporter.onBegin?.(config, rootSuite!);
|
||||||
return () => reporter.onEnd();
|
return () => reporter.onEnd();
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,10 @@ class UIMode {
|
||||||
private _page!: Page;
|
private _page!: Page;
|
||||||
private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
||||||
globalCleanup: (() => Promise<FullResult['status']>) | undefined;
|
globalCleanup: (() => Promise<FullResult['status']>) | undefined;
|
||||||
private _watcher: FSWatcher | undefined;
|
private _testWatcher: FSWatcher | undefined;
|
||||||
private _watchTestFile: string | undefined;
|
private _watchTestFile: string | undefined;
|
||||||
private _originalStderr: (buffer: string | Uint8Array) => void;
|
private _originalStderr: (buffer: string | Uint8Array) => void;
|
||||||
|
private _globalWatcher: FSWatcher;
|
||||||
|
|
||||||
constructor(config: FullConfigInternal) {
|
constructor(config: FullConfigInternal) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
|
@ -46,6 +47,7 @@ class UIMode {
|
||||||
p.retries = 0;
|
p.retries = 0;
|
||||||
config._internal.configCLIOverrides.use = config._internal.configCLIOverrides.use || {};
|
config._internal.configCLIOverrides.use = config._internal.configCLIOverrides.use || {};
|
||||||
config._internal.configCLIOverrides.use.trace = 'on';
|
config._internal.configCLIOverrides.use.trace = 'on';
|
||||||
|
|
||||||
this._originalStderr = process.stderr.write.bind(process.stderr);
|
this._originalStderr = process.stderr.write.bind(process.stderr);
|
||||||
process.stdout.write = (chunk: string | Buffer) => {
|
process.stdout.write = (chunk: string | Buffer) => {
|
||||||
this._dispatchEvent({ method: 'stdio', params: chunkToPayload('stdout', chunk) });
|
this._dispatchEvent({ method: 'stdio', params: chunkToPayload('stdout', chunk) });
|
||||||
|
|
@ -55,6 +57,25 @@ class UIMode {
|
||||||
this._dispatchEvent({ method: 'stdio', params: chunkToPayload('stderr', chunk) });
|
this._dispatchEvent({ method: 'stdio', params: chunkToPayload('stderr', chunk) });
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._globalWatcher = this._installGlobalWatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _installGlobalWatcher(): FSWatcher {
|
||||||
|
const projectDirs = new Set<string>();
|
||||||
|
for (const p of this._config.projects)
|
||||||
|
projectDirs.add(p.testDir);
|
||||||
|
let coalescingTimer: NodeJS.Timeout | undefined;
|
||||||
|
const watcher = chokidar.watch([...projectDirs], { ignoreInitial: true, persistent: true }).on('all', async event => {
|
||||||
|
if (event !== 'add' && event !== 'change')
|
||||||
|
return;
|
||||||
|
if (coalescingTimer)
|
||||||
|
clearTimeout(coalescingTimer);
|
||||||
|
coalescingTimer = setTimeout(() => {
|
||||||
|
this._dispatchEvent({ method: 'listChanged' });
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
return watcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
async runGlobalSetup(): Promise<FullResult['status']> {
|
async runGlobalSetup(): Promise<FullResult['status']> {
|
||||||
|
|
@ -79,30 +100,45 @@ class UIMode {
|
||||||
this._page = await showTraceViewer([], 'chromium', { app: 'watch.html' });
|
this._page = await showTraceViewer([], 'chromium', { app: 'watch.html' });
|
||||||
const exitPromise = new ManualPromise();
|
const exitPromise = new ManualPromise();
|
||||||
this._page.on('close', () => exitPromise.resolve());
|
this._page.on('close', () => exitPromise.resolve());
|
||||||
|
let queue = Promise.resolve();
|
||||||
this._page.exposeBinding('sendMessage', false, async (source, data) => {
|
this._page.exposeBinding('sendMessage', false, async (source, data) => {
|
||||||
const { method, params }: { method: string, params: any } = data;
|
const { method, params }: { method: string, params: any } = data;
|
||||||
if (method === 'list')
|
if (method === 'exit') {
|
||||||
await this._listTests();
|
exitPromise.resolve();
|
||||||
if (method === 'run')
|
return;
|
||||||
await this._runTests(params.testIds);
|
}
|
||||||
if (method === 'stop')
|
if (method === 'watch') {
|
||||||
this._stopTests();
|
|
||||||
if (method === 'watch')
|
|
||||||
this._watchFile(params.fileName);
|
this._watchFile(params.fileName);
|
||||||
if (method === 'open' && params.location)
|
return;
|
||||||
|
}
|
||||||
|
if (method === 'open' && params.location) {
|
||||||
open.openApp('code', { arguments: ['--goto', params.location] }).catch(() => {});
|
open.openApp('code', { arguments: ['--goto', params.location] }).catch(() => {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (method === 'resizeTerminal') {
|
if (method === 'resizeTerminal') {
|
||||||
process.stdout.columns = params.cols;
|
process.stdout.columns = params.cols;
|
||||||
process.stdout.rows = params.rows;
|
process.stdout.rows = params.rows;
|
||||||
process.stderr.columns = params.cols;
|
process.stderr.columns = params.cols;
|
||||||
process.stderr.columns = params.rows;
|
process.stderr.columns = params.rows;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (method === 'exit')
|
if (method === 'stop') {
|
||||||
exitPromise.resolve();
|
this._stopTests();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
queue = queue.then(() => this._queueListOrRun(method, params));
|
||||||
|
await queue;
|
||||||
});
|
});
|
||||||
await exitPromise;
|
await exitPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _queueListOrRun(method: string, params: any) {
|
||||||
|
if (method === 'list')
|
||||||
|
await this._listTests();
|
||||||
|
if (method === 'run')
|
||||||
|
await this._runTests(params.testIds);
|
||||||
|
}
|
||||||
|
|
||||||
private _dispatchEvent(message: any) {
|
private _dispatchEvent(message: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
this._page.mainFrame().evaluateExpression(dispatchFuncSource, true, message).catch(e => this._originalStderr(String(e)));
|
this._page.mainFrame().evaluateExpression(dispatchFuncSource, true, message).catch(e => this._originalStderr(String(e)));
|
||||||
|
|
@ -111,8 +147,11 @@ class UIMode {
|
||||||
private async _listTests() {
|
private async _listTests() {
|
||||||
const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e));
|
const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e));
|
||||||
const reporter = new Multiplexer([listReporter]);
|
const reporter = new Multiplexer([listReporter]);
|
||||||
const taskRunner = createTaskRunnerForList(this._config, reporter);
|
this._config._internal.listOnly = true;
|
||||||
|
this._config._internal.testIdMatcher = undefined;
|
||||||
|
const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process');
|
||||||
const context: TaskRunnerState = { config: this._config, reporter, phases: [] };
|
const context: TaskRunnerState = { config: this._config, reporter, phases: [] };
|
||||||
|
clearCompilationCache();
|
||||||
reporter.onConfigure(this._config);
|
reporter.onConfigure(this._config);
|
||||||
await taskRunner.run(context, 0);
|
await taskRunner.run(context, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -121,6 +160,7 @@ class UIMode {
|
||||||
await this._stopTests();
|
await this._stopTests();
|
||||||
|
|
||||||
const testIdSet = testIds ? new Set<string>(testIds) : null;
|
const testIdSet = testIds ? new Set<string>(testIds) : null;
|
||||||
|
this._config._internal.listOnly = false;
|
||||||
this._config._internal.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
this._config._internal.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
||||||
|
|
||||||
const runReporter = new TeleReporterEmitter(e => this._dispatchEvent(e));
|
const runReporter = new TeleReporterEmitter(e => this._dispatchEvent(e));
|
||||||
|
|
@ -143,14 +183,14 @@ class UIMode {
|
||||||
private async _watchFile(fileName: string) {
|
private async _watchFile(fileName: string) {
|
||||||
if (this._watchTestFile === fileName)
|
if (this._watchTestFile === fileName)
|
||||||
return;
|
return;
|
||||||
if (this._watcher)
|
if (this._testWatcher)
|
||||||
await this._watcher.close();
|
await this._testWatcher.close();
|
||||||
this._watchTestFile = fileName;
|
this._watchTestFile = fileName;
|
||||||
if (!fileName)
|
if (!fileName)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const files = [fileName, ...dependenciesForTestFile(fileName)];
|
const files = [fileName, ...dependenciesForTestFile(fileName)];
|
||||||
this._watcher = chokidar.watch(files, { ignoreInitial: true }).on('all', async (event, file) => {
|
this._testWatcher = chokidar.watch(files, { ignoreInitial: true }).on('all', async (event, file) => {
|
||||||
if (event !== 'add' && event !== 'change')
|
if (event !== 'add' && event !== 'change')
|
||||||
return;
|
return;
|
||||||
this._dispatchEvent({ method: 'fileChanged', params: { fileName: file } });
|
this._dispatchEvent({ method: 'fileChanged', params: { fileName: file } });
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ export const WatchModeView: React.FC<{}> = ({
|
||||||
<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={resetCollectingRootSuite} 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>
|
||||||
<div className='spacer'></div>
|
<div className='spacer'></div>
|
||||||
<ToolbarButton icon='gear' title='Toggle color mode' toggled={settingsVisible} onClick={() => { setSettingsVisible(!settingsVisible); }}></ToolbarButton>
|
<ToolbarButton icon='gear' title='Toggle color mode' toggled={settingsVisible} onClick={() => { setSettingsVisible(!settingsVisible); }}></ToolbarButton>
|
||||||
|
|
@ -128,7 +128,7 @@ export const TestList: React.FC<{
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
resetCollectingRootSuite();
|
refreshRootSuite(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { filteredItems, treeItemMap, visibleTestIds } = React.useMemo(() => {
|
const { filteredItems, treeItemMap, visibleTestIds } = React.useMemo(() => {
|
||||||
|
|
@ -165,8 +165,8 @@ export const TestList: React.FC<{
|
||||||
}, [selectedTreeItemId, treeItemMap]);
|
}, [selectedTreeItemId, treeItemMap]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
sendMessageNoReply('watch', { fileName: isWatchingFiles ? fileName(selectedTestItem) : undefined });
|
sendMessageNoReply('watch', { fileName: isWatchingFiles ? fileName(selectedTreeItem) : undefined });
|
||||||
}, [selectedTestItem, isWatchingFiles]);
|
}, [selectedTreeItem, isWatchingFiles]);
|
||||||
|
|
||||||
onTestItemSelected(selectedTestItem);
|
onTestItemSelected(selectedTestItem);
|
||||||
|
|
||||||
|
|
@ -327,7 +327,12 @@ declare global {
|
||||||
|
|
||||||
let receiver: TeleReporterReceiver | undefined;
|
let receiver: TeleReporterReceiver | undefined;
|
||||||
|
|
||||||
const resetCollectingRootSuite = () => {
|
const refreshRootSuite = (eraseResults: boolean) => {
|
||||||
|
if (!eraseResults) {
|
||||||
|
sendMessageNoReply('list');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let rootSuite: Suite;
|
let rootSuite: Suite;
|
||||||
const progress: Progress = {
|
const progress: Progress = {
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
@ -367,18 +372,27 @@ const resetCollectingRootSuite = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
(window as any).dispatch = (message: any) => {
|
(window as any).dispatch = (message: any) => {
|
||||||
|
if (message.method === 'listChanged') {
|
||||||
|
refreshRootSuite(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.method === 'fileChanged') {
|
if (message.method === 'fileChanged') {
|
||||||
runWatchedTests();
|
runWatchedTests();
|
||||||
} else if (message.method === 'stdio') {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.method === 'stdio') {
|
||||||
if (message.params.buffer) {
|
if (message.params.buffer) {
|
||||||
const data = atob(message.params.buffer);
|
const data = atob(message.params.buffer);
|
||||||
xtermDataSource.write(data);
|
xtermDataSource.write(data);
|
||||||
} else {
|
} else {
|
||||||
xtermDataSource.write(message.params.text);
|
xtermDataSource.write(message.params.text);
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
receiver?.dispatch(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
receiver?.dispatch(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendMessage = async (method: string, params: any) => {
|
const sendMessage = async (method: string, params: any) => {
|
||||||
|
|
@ -442,6 +456,7 @@ type TreeItemBase = {
|
||||||
type FileItem = TreeItemBase & {
|
type FileItem = TreeItemBase & {
|
||||||
kind: 'file',
|
kind: 'file',
|
||||||
file: string;
|
file: string;
|
||||||
|
children?: TestCaseItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type TestCaseItem = TreeItemBase & {
|
type TestCaseItem = TreeItemBase & {
|
||||||
|
|
@ -501,6 +516,7 @@ function createTree(rootSuite: Suite | undefined, projects: Map<string, boolean>
|
||||||
test,
|
test,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
(fileItem.children as TestCaseItem[]).sort((a, b) => a.location.line - b.location.line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [...fileItems.values()];
|
return [...fileItems.values()];
|
||||||
|
|
@ -512,7 +528,7 @@ function filterTree(fileItems: FileItem[], filterText: string): FileItem[] {
|
||||||
const result: FileItem[] = [];
|
const result: FileItem[] = [];
|
||||||
for (const fileItem of fileItems) {
|
for (const fileItem of fileItems) {
|
||||||
if (trimmedFilterText) {
|
if (trimmedFilterText) {
|
||||||
const filteredCases: TreeItem[] = [];
|
const filteredCases: TestCaseItem[] = [];
|
||||||
for (const testCaseItem of fileItem.children!) {
|
for (const testCaseItem of fileItem.children!) {
|
||||||
const fullTitle = (fileItem.title + ' ' + testCaseItem.title).toLowerCase();
|
const fullTitle = (fileItem.title + ' ' + testCaseItem.title).toLowerCase();
|
||||||
if (filterTokens.every(token => fullTitle.includes(token)))
|
if (filterTokens.every(token => fullTitle.includes(token)))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue