chore: allow opening ui mode over http (#23536)
This commit is contained in:
parent
dfd1518327
commit
699ac3a0f2
|
|
@ -18,15 +18,30 @@ import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { HttpServer } from '../../../utils/httpServer';
|
import { HttpServer } from '../../../utils/httpServer';
|
||||||
import { findChromiumChannel } from '../../registry';
|
import { findChromiumChannel } from '../../registry';
|
||||||
import { gracefullyCloseAll, isUnderTest } from '../../../utils';
|
import { createGuid, gracefullyCloseAll, isUnderTest } from '../../../utils';
|
||||||
import { installAppIcon, syncLocalStorageWithSettings } from '../../chromium/crApp';
|
import { installAppIcon, syncLocalStorageWithSettings } from '../../chromium/crApp';
|
||||||
import { serverSideCallMetadata } from '../../instrumentation';
|
import { serverSideCallMetadata } from '../../instrumentation';
|
||||||
import { createPlaywright } from '../../playwright';
|
import { createPlaywright } from '../../playwright';
|
||||||
import { ProgressController } from '../../progress';
|
import { ProgressController } from '../../progress';
|
||||||
import { open } from 'playwright-core/lib/utilsBundle';
|
import { open, wsServer } from 'playwright-core/lib/utilsBundle';
|
||||||
import type { Page } from '../../page';
|
import type { Page } from '../../page';
|
||||||
|
|
||||||
type Options = { app?: string, headless?: boolean, host?: string, port?: number, isServer?: boolean, openInBrowser?: boolean };
|
export type Transport = {
|
||||||
|
sendEvent?: (method: string, params: any) => void;
|
||||||
|
dispatch: (method: string, params: any) => Promise<void>;
|
||||||
|
close?: () => void;
|
||||||
|
onclose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
app?: string;
|
||||||
|
headless?: boolean;
|
||||||
|
host?: string;
|
||||||
|
port?: number;
|
||||||
|
isServer?: boolean;
|
||||||
|
openInBrowser?: boolean;
|
||||||
|
transport?: Transport;
|
||||||
|
};
|
||||||
|
|
||||||
export async function showTraceViewer(traceUrls: string[], browserName: string, options?: Options): Promise<void> {
|
export async function showTraceViewer(traceUrls: string[], browserName: string, options?: Options): Promise<void> {
|
||||||
if (options?.openInBrowser) {
|
if (options?.openInBrowser) {
|
||||||
|
|
@ -79,6 +94,25 @@ async function startTraceViewerServer(traceUrls: string[], options?: Options): P
|
||||||
});
|
});
|
||||||
|
|
||||||
const params = traceUrls.map(t => `trace=${t}`);
|
const params = traceUrls.map(t => `trace=${t}`);
|
||||||
|
|
||||||
|
if (options?.transport) {
|
||||||
|
const transport = options?.transport;
|
||||||
|
const guid = createGuid();
|
||||||
|
params.push('ws=' + guid);
|
||||||
|
const wss = new wsServer({ server: server.server(), path: '/' + guid });
|
||||||
|
wss.on('connection', ws => {
|
||||||
|
transport.sendEvent = (method, params) => ws.send(JSON.stringify({ method, params }));
|
||||||
|
transport.close = () => ws.close();
|
||||||
|
ws.on('message', async (message: string) => {
|
||||||
|
const { id, method, params } = JSON.parse(message);
|
||||||
|
const result = await transport.dispatch(method, params);
|
||||||
|
ws.send(JSON.stringify({ id, result }));
|
||||||
|
});
|
||||||
|
ws.on('close', () => transport.onclose());
|
||||||
|
ws.on('error', () => transport.onclose());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (options?.isServer)
|
if (options?.isServer)
|
||||||
params.push('isServer');
|
params.push('isServer');
|
||||||
if (isUnderTest())
|
if (isUnderTest())
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,8 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||||
|
|
||||||
const runner = new Runner(config);
|
const runner = new Runner(config);
|
||||||
let status: FullResult['status'];
|
let status: FullResult['status'];
|
||||||
if (opts.ui)
|
if (opts.ui || opts.uiWeb)
|
||||||
status = await runner.uiAllTests();
|
status = await runner.uiAllTests(!!opts.uiWeb);
|
||||||
else if (process.env.PWTEST_WATCH)
|
else if (process.env.PWTEST_WATCH)
|
||||||
status = await runner.watchAllTests();
|
status = await runner.watchAllTests();
|
||||||
else
|
else
|
||||||
|
|
@ -328,6 +328,7 @@ const testOptions: [string, string][] = [
|
||||||
['--timeout <timeout>', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${defaultTimeout})`],
|
['--timeout <timeout>', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${defaultTimeout})`],
|
||||||
['--trace <mode>', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`],
|
['--trace <mode>', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`],
|
||||||
['--ui', `Run tests in interactive UI mode`],
|
['--ui', `Run tests in interactive UI mode`],
|
||||||
|
['--ui-web', `Open interactive UI mode in a browser tab`],
|
||||||
['-u, --update-snapshots', `Update snapshots with actual results (default: only create missing snapshots)`],
|
['-u, --update-snapshots', `Update snapshots with actual results (default: only create missing snapshots)`],
|
||||||
['-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`],
|
['-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`],
|
||||||
['-x', `Stop after the first failure`],
|
['-x', `Stop after the first failure`],
|
||||||
|
|
|
||||||
|
|
@ -107,9 +107,9 @@ export class Runner {
|
||||||
return await runWatchModeLoop(config);
|
return await runWatchModeLoop(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
async uiAllTests(): Promise<FullResult['status']> {
|
async uiAllTests(openInBrowser: boolean): Promise<FullResult['status']> {
|
||||||
const config = this._config;
|
const config = this._config;
|
||||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||||
return await runUIMode(config);
|
return await runUIMode(config, openInBrowser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { openTraceViewerApp } from 'playwright-core/lib/server';
|
import { showTraceViewer } from 'playwright-core/lib/server';
|
||||||
import type { Page } from 'playwright-core/lib/server/page';
|
|
||||||
import { isUnderTest, ManualPromise } from 'playwright-core/lib/utils';
|
import { isUnderTest, ManualPromise } from 'playwright-core/lib/utils';
|
||||||
import type { FullResult } from '../../reporter';
|
import type { FullResult } from '../../reporter';
|
||||||
import { clearCompilationCache, collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache';
|
import { clearCompilationCache, collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache';
|
||||||
|
|
@ -28,10 +27,11 @@ import { chokidar } from '../utilsBundle';
|
||||||
import type { FSWatcher } from 'chokidar';
|
import type { FSWatcher } from 'chokidar';
|
||||||
import { open } from 'playwright-core/lib/utilsBundle';
|
import { open } from 'playwright-core/lib/utilsBundle';
|
||||||
import ListReporter from '../reporters/list';
|
import ListReporter from '../reporters/list';
|
||||||
|
import type { Transport } from 'playwright-core/lib/server/trace/viewer/traceViewer';
|
||||||
|
|
||||||
class UIMode {
|
class UIMode {
|
||||||
private _config: FullConfigInternal;
|
private _config: FullConfigInternal;
|
||||||
private _page!: Page;
|
private _transport!: Transport;
|
||||||
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 _globalWatcher: Watcher;
|
private _globalWatcher: Watcher;
|
||||||
|
|
@ -58,11 +58,11 @@ class UIMode {
|
||||||
|
|
||||||
this._originalStdoutWrite = process.stdout.write;
|
this._originalStdoutWrite = process.stdout.write;
|
||||||
this._originalStderrWrite = process.stderr.write;
|
this._originalStderrWrite = process.stderr.write;
|
||||||
this._globalWatcher = new Watcher('deep', () => this._dispatchEvent({ method: 'listChanged' }));
|
this._globalWatcher = new Watcher('deep', () => this._dispatchEvent('listChanged', {}));
|
||||||
this._testWatcher = new Watcher('flat', events => {
|
this._testWatcher = new Watcher('flat', events => {
|
||||||
const collector = new Set<string>();
|
const collector = new Set<string>();
|
||||||
events.forEach(f => collectAffectedTestFiles(f.file, collector));
|
events.forEach(f => collectAffectedTestFiles(f.file, collector));
|
||||||
this._dispatchEvent({ method: 'testFilesChanged', params: { testFileNames: [...collector] } });
|
this._dispatchEvent('testFilesChanged', { testFileNames: [...collector] });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,23 +81,12 @@ class UIMode {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
async showUI() {
|
async showUI(openInBrowser: boolean) {
|
||||||
this._page = await openTraceViewerApp([], 'chromium', { app: 'uiMode.html', headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1' });
|
|
||||||
if (!process.env.PWTEST_DEBUG) {
|
|
||||||
process.stdout.write = (chunk: string | Buffer) => {
|
|
||||||
this._dispatchEvent({ method: 'stdio', params: chunkToPayload('stdout', chunk) });
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
process.stderr.write = (chunk: string | Buffer) => {
|
|
||||||
this._dispatchEvent({ method: 'stdio', params: chunkToPayload('stderr', chunk) });
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const exitPromise = new ManualPromise();
|
const exitPromise = new ManualPromise();
|
||||||
this._page.on('close', () => exitPromise.resolve());
|
|
||||||
let queue = Promise.resolve();
|
let queue = Promise.resolve();
|
||||||
await this._page.exposeBinding('sendMessage', false, async (source, data) => {
|
|
||||||
const { method, params }: { method: string, params: any } = data;
|
this._transport = {
|
||||||
|
dispatch: async (method, params) => {
|
||||||
if (method === 'exit') {
|
if (method === 'exit') {
|
||||||
exitPromise.resolve();
|
exitPromise.resolve();
|
||||||
return;
|
return;
|
||||||
|
|
@ -123,7 +112,27 @@ class UIMode {
|
||||||
}
|
}
|
||||||
queue = queue.then(() => this._queueListOrRun(method, params));
|
queue = queue.then(() => this._queueListOrRun(method, params));
|
||||||
await queue;
|
await queue;
|
||||||
|
},
|
||||||
|
|
||||||
|
onclose: () => exitPromise.resolve(),
|
||||||
|
};
|
||||||
|
await showTraceViewer([], 'chromium', {
|
||||||
|
app: 'uiMode.html',
|
||||||
|
headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
||||||
|
transport: this._transport,
|
||||||
|
openInBrowser,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!process.env.PWTEST_DEBUG) {
|
||||||
|
process.stdout.write = (chunk: string | Buffer) => {
|
||||||
|
this._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
process.stderr.write = (chunk: string | Buffer) => {
|
||||||
|
this._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
await exitPromise;
|
await exitPromise;
|
||||||
|
|
||||||
if (!process.env.PWTEST_DEBUG) {
|
if (!process.env.PWTEST_DEBUG) {
|
||||||
|
|
@ -139,13 +148,12 @@ class UIMode {
|
||||||
await this._runTests(params.testIds);
|
await this._runTests(params.testIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dispatchEvent(message: any) {
|
private _dispatchEvent(method: string, params?: any) {
|
||||||
// eslint-disable-next-line no-console
|
this._transport.sendEvent?.(method, params);
|
||||||
this._page.mainFrame().evaluateExpression(dispatchFuncSource, { isFunction: true }, message).catch(e => this._originalStderrWrite.call(process.stderr, String(e)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _listTests() {
|
private async _listTests() {
|
||||||
const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e));
|
const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params));
|
||||||
const reporter = new InternalReporter([listReporter]);
|
const reporter = new InternalReporter([listReporter]);
|
||||||
this._config.cliListOnly = true;
|
this._config.cliListOnly = true;
|
||||||
this._config.testIdMatcher = undefined;
|
this._config.testIdMatcher = undefined;
|
||||||
|
|
@ -170,7 +178,7 @@ class UIMode {
|
||||||
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
||||||
|
|
||||||
const reporters = await createReporters(this._config, 'ui');
|
const reporters = await createReporters(this._config, 'ui');
|
||||||
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e)));
|
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params)));
|
||||||
const reporter = new InternalReporter(reporters);
|
const reporter = new InternalReporter(reporters);
|
||||||
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
||||||
const testRun = new TestRun(this._config, reporter);
|
const testRun = new TestRun(this._config, reporter);
|
||||||
|
|
@ -202,16 +210,12 @@ class UIMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatchFuncSource = String((message: any) => {
|
export async function runUIMode(config: FullConfigInternal, openInBrowser: boolean): Promise<FullResult['status']> {
|
||||||
(window as any).dispatch(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function runUIMode(config: FullConfigInternal): Promise<FullResult['status']> {
|
|
||||||
const uiMode = new UIMode(config);
|
const uiMode = new UIMode(config);
|
||||||
const status = await uiMode.runGlobalSetup();
|
const status = await uiMode.runGlobalSetup();
|
||||||
if (status !== 'passed')
|
if (status !== 'passed')
|
||||||
return status;
|
return status;
|
||||||
await uiMode.showUI();
|
await uiMode.showUI(openInBrowser);
|
||||||
return await uiMode.globalCleanup?.() || 'passed';
|
return await uiMode.globalCleanup?.() || 'passed';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,16 @@
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-mode .disconnected {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
.status-line {
|
.status-line {
|
||||||
flex: auto;
|
flex: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
const runTestPromiseChain = React.useRef(Promise.resolve());
|
const runTestPromiseChain = React.useRef(Promise.resolve());
|
||||||
const runTestBacklog = React.useRef<Set<string>>(new Set());
|
const runTestBacklog = React.useRef<Set<string>>(new Set());
|
||||||
const [collapseAllCount, setCollapseAllCount] = React.useState(0);
|
const [collapseAllCount, setCollapseAllCount] = React.useState(0);
|
||||||
|
const [isDisconnected, setIsDisconnected] = React.useState(false);
|
||||||
|
|
||||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
|
@ -94,7 +95,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
reloadTests();
|
initWebSocket(() => setIsDisconnected(true)).then(() => reloadTests());
|
||||||
}, [reloadTests]);
|
}, [reloadTests]);
|
||||||
|
|
||||||
updateRootSuite = React.useCallback((config: FullConfig, rootSuite: Suite, loadErrors: TestError[], newProgress: Progress | undefined) => {
|
updateRootSuite = React.useCallback((config: FullConfig, rootSuite: Suite, loadErrors: TestError[], newProgress: Progress | undefined) => {
|
||||||
|
|
@ -159,6 +160,9 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
const isRunningTest = !!runningState;
|
const isRunningTest = !!runningState;
|
||||||
|
|
||||||
return <div className='vbox ui-mode'>
|
return <div className='vbox ui-mode'>
|
||||||
|
{isDisconnected && <div className='drop-target'>
|
||||||
|
<div className='title'>Process disconnected</div>
|
||||||
|
</div>}
|
||||||
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}>
|
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}>
|
||||||
<div className='vbox'>
|
<div className='vbox'>
|
||||||
<div className={'vbox' + (isShowingOutput ? '' : ' hidden')}>
|
<div className={'vbox' + (isShowingOutput ? '' : ' hidden')}>
|
||||||
|
|
@ -399,6 +403,8 @@ const TestList: React.FC<{
|
||||||
|
|
||||||
// Update watch all.
|
// Update watch all.
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
if (!testModel.rootSuite)
|
||||||
|
return;
|
||||||
if (watchAll) {
|
if (watchAll) {
|
||||||
sendMessageNoReply('watch', { fileNames: [...fileNames] });
|
sendMessageNoReply('watch', { fileNames: [...fileNames] });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -411,7 +417,7 @@ const TestList: React.FC<{
|
||||||
}
|
}
|
||||||
sendMessageNoReply('watch', { fileNames: [...fileNames] });
|
sendMessageNoReply('watch', { fileNames: [...fileNames] });
|
||||||
}
|
}
|
||||||
}, [rootItem, fileNames, watchAll, watchedTreeIds, treeItemMap]);
|
}, [testModel, rootItem, fileNames, watchAll, watchedTreeIds, treeItemMap]);
|
||||||
|
|
||||||
const runTreeItem = (treeItem: TreeItem) => {
|
const runTreeItem = (treeItem: TreeItem) => {
|
||||||
setSelectedTreeItemId(treeItem.id);
|
setSelectedTreeItemId(treeItem.id);
|
||||||
|
|
@ -561,12 +567,6 @@ const TraceView: React.FC<{
|
||||||
drawer='bottom' />;
|
drawer='bottom' />;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
binding(data: any): Promise<void>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let receiver: TeleReporterReceiver | undefined;
|
let receiver: TeleReporterReceiver | undefined;
|
||||||
|
|
||||||
let throttleTimer: NodeJS.Timeout | undefined;
|
let throttleTimer: NodeJS.Timeout | undefined;
|
||||||
|
|
@ -638,32 +638,41 @@ const refreshRootSuite = (eraseResults: boolean): Promise<void> => {
|
||||||
return sendMessage('list', {});
|
return sendMessage('list', {});
|
||||||
};
|
};
|
||||||
|
|
||||||
(window as any).dispatch = (message: any) => {
|
let lastId = 0;
|
||||||
if (message.method === 'listChanged') {
|
let _ws: WebSocket;
|
||||||
refreshRootSuite(false).catch(() => {});
|
const callbacks = new Map<number, { resolve: (arg: any) => void, reject: (arg: Error) => void }>();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.method === 'testFilesChanged') {
|
const initWebSocket = async (onClose: () => void) => {
|
||||||
runWatchedTests(message.params.testFileNames);
|
const guid = new URLSearchParams(window.location.search).get('ws');
|
||||||
|
const ws = new WebSocket(`ws://${window.location.hostname}:${window.location.port}/${guid}`);
|
||||||
|
await new Promise(f => ws.addEventListener('open', f));
|
||||||
|
ws.addEventListener('close', onClose);
|
||||||
|
ws.addEventListener('message', event => {
|
||||||
|
const message = JSON.parse(event.data);
|
||||||
|
const { id, result, error, method, params } = message;
|
||||||
|
if (id) {
|
||||||
|
const callback = callbacks.get(id);
|
||||||
|
if (!callback)
|
||||||
return;
|
return;
|
||||||
}
|
callbacks.delete(id);
|
||||||
|
if (error)
|
||||||
if (message.method === 'stdio') {
|
callback.reject(new Error(error));
|
||||||
if (message.params.buffer) {
|
else
|
||||||
const data = atob(message.params.buffer);
|
callback.resolve(result);
|
||||||
xtermDataSource.write(data);
|
|
||||||
} else {
|
} else {
|
||||||
xtermDataSource.write(message.params.text);
|
dispatchMessage(method, params);
|
||||||
}
|
}
|
||||||
return;
|
});
|
||||||
}
|
_ws = ws;
|
||||||
|
|
||||||
receiver?.dispatch(message)?.catch(() => {});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendMessage = async (method: string, params: any) => {
|
const sendMessage = async (method: string, params: any): Promise<any> => {
|
||||||
await (window as any).sendMessage({ method, params });
|
const id = ++lastId;
|
||||||
|
const message = { id, method, params };
|
||||||
|
_ws.send(JSON.stringify(message));
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
callbacks.set(id, { resolve, reject });
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendMessageNoReply = (method: string, params?: any) => {
|
const sendMessageNoReply = (method: string, params?: any) => {
|
||||||
|
|
@ -677,6 +686,30 @@ const sendMessageNoReply = (method: string, params?: any) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dispatchMessage = (method: string, params?: any) => {
|
||||||
|
if (method === 'listChanged') {
|
||||||
|
refreshRootSuite(false).catch(() => {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === 'testFilesChanged') {
|
||||||
|
runWatchedTests(params.testFileNames);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === 'stdio') {
|
||||||
|
if (params.buffer) {
|
||||||
|
const data = atob(params.buffer);
|
||||||
|
xtermDataSource.write(data);
|
||||||
|
} else {
|
||||||
|
xtermDataSource.write(params.text);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
receiver?.dispatch({ method, params })?.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
const outputDirForTestCase = (testCase: TestCase): string | undefined => {
|
const outputDirForTestCase = (testCase: TestCase): string | undefined => {
|
||||||
for (let suite: Suite | undefined = testCase.parent; suite; suite = suite.parent) {
|
for (let suite: Suite | undefined = testCase.parent; suite; suite = suite.parent) {
|
||||||
if (suite.project())
|
if (suite.project())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue