chore: prepare to reuse test server from ui mode (4) (#29995)
This commit is contained in:
parent
3ee13cbf2b
commit
54aca430b0
|
|
@ -120,12 +120,12 @@ export async function installRootRedirect(server: HttpServer, traceUrls: string[
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runTraceViewerApp(traceUrls: string[], browserName: string, options: TraceViewerServerOptions, exitOnClose?: boolean) {
|
export async function runTraceViewerApp(traceUrls: string[], browserName: string, options: TraceViewerServerOptions & { headless?: boolean }, exitOnClose?: boolean) {
|
||||||
if (!validateTraceUrls(traceUrls))
|
if (!validateTraceUrls(traceUrls))
|
||||||
return;
|
return;
|
||||||
const server = await startTraceViewerServer(options);
|
const server = await startTraceViewerServer(options);
|
||||||
await installRootRedirect(server, traceUrls, options);
|
await installRootRedirect(server, traceUrls, options);
|
||||||
const page = await openTraceViewerApp(server.urlPrefix(), browserName);
|
const page = await openTraceViewerApp(server.urlPrefix(), browserName, options);
|
||||||
if (exitOnClose)
|
if (exitOnClose)
|
||||||
page.on('close', () => gracefullyProcessExitDoNotHang(0));
|
page.on('close', () => gracefullyProcessExitDoNotHang(0));
|
||||||
return page;
|
return page;
|
||||||
|
|
@ -150,7 +150,7 @@ export async function openTraceViewerApp(url: string, browserName: string, optio
|
||||||
persistentContextOptions: {
|
persistentContextOptions: {
|
||||||
...options?.persistentContextOptions,
|
...options?.persistentContextOptions,
|
||||||
useWebSocket: isUnderTest(),
|
useWebSocket: isUnderTest(),
|
||||||
headless: options?.headless,
|
headless: !!options?.headless,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ export class HttpServer {
|
||||||
wss.on('connection', ws => {
|
wss.on('connection', ws => {
|
||||||
transport.sendEvent = (method, params) => ws.send(JSON.stringify({ method, params }));
|
transport.sendEvent = (method, params) => ws.send(JSON.stringify({ method, params }));
|
||||||
transport.close = () => ws.close();
|
transport.close = () => ws.close();
|
||||||
ws.on('message', async (message: string) => {
|
ws.on('message', async message => {
|
||||||
const { id, method, params } = JSON.parse(message);
|
const { id, method, params } = JSON.parse(String(message));
|
||||||
try {
|
try {
|
||||||
const result = await transport.dispatch(method, params);
|
const result = await transport.dispatch(method, params);
|
||||||
ws.send(JSON.stringify({ id, result }));
|
ws.send(JSON.stringify({ id, result }));
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type * as reporterTypes from '../../types/testReporter';
|
import type * as reporterTypes from '../../types/testReporter';
|
||||||
|
import type { Event } from './events';
|
||||||
|
|
||||||
export interface TestServerInterface {
|
export interface TestServerInterface {
|
||||||
ping(): Promise<void>;
|
ping(): Promise<void>;
|
||||||
|
|
@ -31,6 +32,10 @@ export interface TestServerInterface {
|
||||||
|
|
||||||
installBrowsers(): Promise<void>;
|
installBrowsers(): Promise<void>;
|
||||||
|
|
||||||
|
runGlobalSetup(): Promise<reporterTypes.FullResult['status']>;
|
||||||
|
|
||||||
|
runGlobalTeardown(): Promise<reporterTypes.FullResult['status']>;
|
||||||
|
|
||||||
listFiles(): Promise<{
|
listFiles(): Promise<{
|
||||||
projects: {
|
projects: {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -69,10 +74,11 @@ export interface TestServerInterface {
|
||||||
closeGracefully(): Promise<void>;
|
closeGracefully(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TestServerEvents {
|
export interface TestServerInterfaceEvents {
|
||||||
on(event: 'listReport', listener: (params: any) => void): void;
|
onClose: Event<void>;
|
||||||
on(event: 'testReport', listener: (params: any) => void): void;
|
onListReport: Event<any>;
|
||||||
on(event: 'stdio', listener: (params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }) => void): void;
|
onTestReport: Event<any>;
|
||||||
on(event: 'listChanged', listener: () => void): void;
|
onStdio: Event<{ type: 'stdout' | 'stderr', text?: string, buffer?: string }>;
|
||||||
on(event: 'testFilesChanged', listener: (testFileNames: string[]) => void): void;
|
onListChanged: Event<void>;
|
||||||
|
onTestFilesChanged: Event<string[]>;
|
||||||
}
|
}
|
||||||
|
|
@ -308,12 +308,6 @@ export class TestTree {
|
||||||
visit(treeItem);
|
visit(treeItem);
|
||||||
return testIds;
|
return testIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
locationToOpen(treeItem?: TreeItem) {
|
|
||||||
if (!treeItem)
|
|
||||||
return;
|
|
||||||
return treeItem.location.file + ':' + treeItem.location.line;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sortAndPropagateStatus(treeItem: TreeItem) {
|
export function sortAndPropagateStatus(treeItem: TreeItem) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { registry, startTraceViewerServer } from 'playwright-core/lib/server';
|
import { registry, startTraceViewerServer } from 'playwright-core/lib/server';
|
||||||
import { ManualPromise, gracefullyProcessExitDoNotHang } from 'playwright-core/lib/utils';
|
import { ManualPromise, gracefullyProcessExitDoNotHang, isUnderTest } from 'playwright-core/lib/utils';
|
||||||
import type { Transport, HttpServer } from 'playwright-core/lib/utils';
|
import type { Transport, HttpServer } from 'playwright-core/lib/utils';
|
||||||
import type { FullResult, Location, TestError } from '../../types/testReporter';
|
import type { FullResult, Location, TestError } from '../../types/testReporter';
|
||||||
import { collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache';
|
import { collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache';
|
||||||
|
|
@ -28,7 +28,7 @@ import ListReporter from '../reporters/list';
|
||||||
import { Multiplexer } from '../reporters/multiplexer';
|
import { Multiplexer } from '../reporters/multiplexer';
|
||||||
import { SigIntWatcher } from './sigIntWatcher';
|
import { SigIntWatcher } from './sigIntWatcher';
|
||||||
import { Watcher } from '../fsWatcher';
|
import { Watcher } from '../fsWatcher';
|
||||||
import type { TestServerInterface } from './testServerInterface';
|
import type { TestServerInterface } from '../isomorphic/testServerInterface';
|
||||||
import { Runner } from './runner';
|
import { Runner } from './runner';
|
||||||
import { serializeError } from '../util';
|
import { serializeError } from '../util';
|
||||||
import { prepareErrorStack } from '../reporters/base';
|
import { prepareErrorStack } from '../reporters/base';
|
||||||
|
|
@ -36,7 +36,6 @@ import { prepareErrorStack } from '../reporters/base';
|
||||||
class TestServer {
|
class TestServer {
|
||||||
private _config: FullConfigInternal;
|
private _config: FullConfigInternal;
|
||||||
private _dispatcher: TestServerDispatcher | undefined;
|
private _dispatcher: TestServerDispatcher | undefined;
|
||||||
globalCleanup: (() => Promise<FullResult['status']>) | undefined;
|
|
||||||
private _originalStdoutWrite: NodeJS.WriteStream['write'];
|
private _originalStdoutWrite: NodeJS.WriteStream['write'];
|
||||||
private _originalStderrWrite: NodeJS.WriteStream['write'];
|
private _originalStderrWrite: NodeJS.WriteStream['write'];
|
||||||
|
|
||||||
|
|
@ -58,27 +57,15 @@ class TestServer {
|
||||||
this._originalStderrWrite = process.stderr.write;
|
this._originalStderrWrite = process.stderr.write;
|
||||||
}
|
}
|
||||||
|
|
||||||
async runGlobalSetup(): Promise<FullResult['status']> {
|
|
||||||
const reporter = new InternalReporter(new ListReporter());
|
|
||||||
const taskRunner = createTaskRunnerForWatchSetup(this._config, reporter);
|
|
||||||
reporter.onConfigure(this._config.config);
|
|
||||||
const testRun = new TestRun(this._config, reporter);
|
|
||||||
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
|
|
||||||
await reporter.onEnd({ status });
|
|
||||||
await reporter.onExit();
|
|
||||||
if (status !== 'passed') {
|
|
||||||
await globalCleanup();
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
this.globalCleanup = globalCleanup;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
async start(options: { host?: string, port?: number }): Promise<HttpServer> {
|
async start(options: { host?: string, port?: number }): Promise<HttpServer> {
|
||||||
this._dispatcher = new TestServerDispatcher(this._config);
|
this._dispatcher = new TestServerDispatcher(this._config);
|
||||||
return await startTraceViewerServer({ ...options, transport: this._dispatcher.transport });
|
return await startTraceViewerServer({ ...options, transport: this._dispatcher.transport });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
await this._dispatcher?.runGlobalTeardown();
|
||||||
|
}
|
||||||
|
|
||||||
wireStdIO() {
|
wireStdIO() {
|
||||||
if (!process.env.PWTEST_DEBUG) {
|
if (!process.env.PWTEST_DEBUG) {
|
||||||
process.stdout.write = (chunk: string | Buffer) => {
|
process.stdout.write = (chunk: string | Buffer) => {
|
||||||
|
|
@ -107,6 +94,7 @@ class TestServerDispatcher implements TestServerInterface {
|
||||||
private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
||||||
readonly transport: Transport;
|
readonly transport: Transport;
|
||||||
private _queue = Promise.resolve();
|
private _queue = Promise.resolve();
|
||||||
|
private _globalCleanup: (() => Promise<FullResult['status']>) | undefined;
|
||||||
|
|
||||||
constructor(config: FullConfigInternal) {
|
constructor(config: FullConfigInternal) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
|
@ -125,8 +113,10 @@ class TestServerDispatcher implements TestServerInterface {
|
||||||
async ping() {}
|
async ping() {}
|
||||||
|
|
||||||
async open(params: { location: Location }) {
|
async open(params: { location: Location }) {
|
||||||
|
if (isUnderTest())
|
||||||
|
return;
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
open('vscode://file/' + params.location.file + ':' + params.location.column).catch(e => console.error(e));
|
open('vscode://file/' + params.location.file + ':' + params.location.line).catch(e => console.error(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
async resizeTerminal(params: { cols: number; rows: number; }) {
|
async resizeTerminal(params: { cols: number; rows: number; }) {
|
||||||
|
|
@ -144,6 +134,30 @@ class TestServerDispatcher implements TestServerInterface {
|
||||||
await installBrowsers();
|
await installBrowsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async runGlobalSetup(): Promise<FullResult['status']> {
|
||||||
|
await this.runGlobalTeardown();
|
||||||
|
|
||||||
|
const reporter = new InternalReporter(new ListReporter());
|
||||||
|
const taskRunner = createTaskRunnerForWatchSetup(this._config, reporter);
|
||||||
|
reporter.onConfigure(this._config.config);
|
||||||
|
const testRun = new TestRun(this._config, reporter);
|
||||||
|
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
|
||||||
|
await reporter.onEnd({ status });
|
||||||
|
await reporter.onExit();
|
||||||
|
if (status !== 'passed') {
|
||||||
|
await globalCleanup();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
this._globalCleanup = globalCleanup;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
async runGlobalTeardown() {
|
||||||
|
const result = (await this._globalCleanup?.()) || 'passed';
|
||||||
|
this._globalCleanup = undefined;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async listFiles() {
|
async listFiles() {
|
||||||
try {
|
try {
|
||||||
const runner = new Runner(this._config);
|
const runner = new Runner(this._config);
|
||||||
|
|
@ -246,9 +260,6 @@ class TestServerDispatcher implements TestServerInterface {
|
||||||
|
|
||||||
export async function runTestServer(config: FullConfigInternal, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>) => Promise<void>): Promise<FullResult['status']> {
|
export async function runTestServer(config: FullConfigInternal, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>) => Promise<void>): Promise<FullResult['status']> {
|
||||||
const testServer = new TestServer(config);
|
const testServer = new TestServer(config);
|
||||||
const globalSetupStatus = await testServer.runGlobalSetup();
|
|
||||||
if (globalSetupStatus !== 'passed')
|
|
||||||
return globalSetupStatus;
|
|
||||||
const cancelPromise = new ManualPromise<void>();
|
const cancelPromise = new ManualPromise<void>();
|
||||||
const sigintWatcher = new SigIntWatcher();
|
const sigintWatcher = new SigIntWatcher();
|
||||||
void sigintWatcher.promise().then(() => cancelPromise.resolve());
|
void sigintWatcher.promise().then(() => cancelPromise.resolve());
|
||||||
|
|
@ -259,9 +270,10 @@ export async function runTestServer(config: FullConfigInternal, options: { host?
|
||||||
await cancelPromise;
|
await cancelPromise;
|
||||||
} finally {
|
} finally {
|
||||||
testServer.unwireStdIO();
|
testServer.unwireStdIO();
|
||||||
|
await testServer.stop();
|
||||||
sigintWatcher.disarm();
|
sigintWatcher.disarm();
|
||||||
}
|
}
|
||||||
return await testServer.globalCleanup?.() || (sigintWatcher.hadSignal() ? 'interrupted' : 'passed');
|
return sigintWatcher.hadSignal() ? 'interrupted' : 'passed';
|
||||||
}
|
}
|
||||||
|
|
||||||
type StdioPayload = {
|
type StdioPayload = {
|
||||||
|
|
|
||||||
136
packages/trace-viewer/src/ui/testServerConnection.ts
Normal file
136
packages/trace-viewer/src/ui/testServerConnection.ts
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { TestServerInterface, TestServerInterfaceEvents } from '@testIsomorphic/testServerInterface';
|
||||||
|
import type { Location, TestError } from 'playwright/types/testReporter';
|
||||||
|
import { connect } from './wsPort';
|
||||||
|
import type { Event } from '@testIsomorphic/events';
|
||||||
|
import { EventEmitter } from '@testIsomorphic/events';
|
||||||
|
|
||||||
|
export class TestServerConnection implements TestServerInterface, TestServerInterfaceEvents {
|
||||||
|
readonly onClose: Event<void>;
|
||||||
|
readonly onListReport: Event<any>;
|
||||||
|
readonly onTestReport: Event<any>;
|
||||||
|
readonly onStdio: Event<{ type: 'stderr' | 'stdout'; text?: string | undefined; buffer?: string | undefined; }>;
|
||||||
|
readonly onListChanged: Event<void>;
|
||||||
|
readonly onTestFilesChanged: Event<string[]>;
|
||||||
|
|
||||||
|
private _onCloseEmitter = new EventEmitter<void>();
|
||||||
|
private _onListReportEmitter = new EventEmitter<any>();
|
||||||
|
private _onTestReportEmitter = new EventEmitter<any>();
|
||||||
|
private _onStdioEmitter = new EventEmitter<{ type: 'stderr' | 'stdout'; text?: string | undefined; buffer?: string | undefined; }>();
|
||||||
|
private _onListChangedEmitter = new EventEmitter<void>();
|
||||||
|
private _onTestFilesChangedEmitter = new EventEmitter<string[]>();
|
||||||
|
|
||||||
|
private _send: Promise<(method: string, params?: any) => Promise<any>>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.onClose = this._onCloseEmitter.event;
|
||||||
|
this.onListReport = this._onListReportEmitter.event;
|
||||||
|
this.onTestReport = this._onTestReportEmitter.event;
|
||||||
|
this.onStdio = this._onStdioEmitter.event;
|
||||||
|
this.onListChanged = this._onListChangedEmitter.event;
|
||||||
|
this.onTestFilesChanged = this._onTestFilesChangedEmitter.event;
|
||||||
|
|
||||||
|
this._send = connect({
|
||||||
|
onEvent: (method, params) => this._dispatchEvent(method, params),
|
||||||
|
onClose: () => this._onCloseEmitter.fire(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _sendMessage(method: string, params?: any): Promise<any> {
|
||||||
|
if ((window as any)._sniffProtocolForTest)
|
||||||
|
(window as any)._sniffProtocolForTest({ method, params }).catch(() => {});
|
||||||
|
|
||||||
|
const send = await this._send;
|
||||||
|
const logForTest = (window as any).__logForTest;
|
||||||
|
logForTest?.({ method, params });
|
||||||
|
return send(method, params).catch((e: Error) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dispatchEvent(method: string, params?: any) {
|
||||||
|
if (method === 'close')
|
||||||
|
this._onCloseEmitter.fire(undefined);
|
||||||
|
else if (method === 'listReport')
|
||||||
|
this._onListReportEmitter.fire(params);
|
||||||
|
else if (method === 'testReport')
|
||||||
|
this._onTestReportEmitter.fire(params);
|
||||||
|
else if (method === 'stdio')
|
||||||
|
this._onStdioEmitter.fire(params);
|
||||||
|
else if (method === 'listChanged')
|
||||||
|
this._onListChangedEmitter.fire(undefined);
|
||||||
|
else if (method === 'testFilesChanged')
|
||||||
|
this._onTestFilesChangedEmitter.fire(params.testFileNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ping(): Promise<void> {
|
||||||
|
await this._sendMessage('ping');
|
||||||
|
}
|
||||||
|
|
||||||
|
async watch(params: { fileNames: string[]; }): Promise<void> {
|
||||||
|
await this._sendMessage('watch', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(params: { location: Location; }): Promise<void> {
|
||||||
|
await this._sendMessage('open', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async resizeTerminal(params: { cols: number; rows: number; }): Promise<void> {
|
||||||
|
await this._sendMessage('resizeTerminal', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkBrowsers(): Promise<{ hasBrowsers: boolean; }> {
|
||||||
|
return await this._sendMessage('checkBrowsers');
|
||||||
|
}
|
||||||
|
|
||||||
|
async installBrowsers(): Promise<void> {
|
||||||
|
await this._sendMessage('installBrowsers');
|
||||||
|
}
|
||||||
|
|
||||||
|
async runGlobalSetup(): Promise<'passed' | 'failed' | 'timedout' | 'interrupted'> {
|
||||||
|
return await this._sendMessage('runGlobalSetup');
|
||||||
|
}
|
||||||
|
|
||||||
|
async runGlobalTeardown(): Promise<'passed' | 'failed' | 'timedout' | 'interrupted'> {
|
||||||
|
return await this._sendMessage('runGlobalTeardown');
|
||||||
|
}
|
||||||
|
|
||||||
|
async listFiles(): Promise<{ projects: { name: string; testDir: string; use: { testIdAttribute?: string | undefined; }; files: string[]; }[]; cliEntryPoint?: string | undefined; error?: TestError | undefined; }> {
|
||||||
|
return await this._sendMessage('listFiles');
|
||||||
|
}
|
||||||
|
|
||||||
|
async listTests(params: { reporter?: string | undefined; fileNames?: string[] | undefined; }): Promise<void> {
|
||||||
|
await this._sendMessage('listTests', params);
|
||||||
|
}
|
||||||
|
async runTests(params: { reporter?: string | undefined; locations?: string[] | undefined; grep?: string | undefined; testIds?: string[] | undefined; headed?: boolean | undefined; oneWorker?: boolean | undefined; trace?: 'off' | 'on' | undefined; projects?: string[] | undefined; reuseContext?: boolean | undefined; connectWsEndpoint?: string | undefined; }): Promise<void> {
|
||||||
|
await this._sendMessage('runTests', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findRelatedTestFiles(params: { files: string[]; }): Promise<{ testFiles: string[]; errors?: TestError[] | undefined; }> {
|
||||||
|
return await this._sendMessage('findRelatedTestFiles', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop(): Promise<void> {
|
||||||
|
await this._sendMessage('stop');
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeGracefully(): Promise<void> {
|
||||||
|
await this._sendMessage('closeGracefully');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,25 +39,20 @@ import { toggleTheme } from '@web/theme';
|
||||||
import { artifactsFolderName } from '@testIsomorphic/folders';
|
import { artifactsFolderName } from '@testIsomorphic/folders';
|
||||||
import { msToString, settings, useSetting } from '@web/uiUtils';
|
import { msToString, settings, useSetting } from '@web/uiUtils';
|
||||||
import type { ActionTraceEvent } from '@trace/trace';
|
import type { ActionTraceEvent } from '@trace/trace';
|
||||||
import { connect } from './wsPort';
|
|
||||||
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 { testStatusIcon } from './testUtils';
|
import { testStatusIcon } from './testUtils';
|
||||||
|
import { TestServerConnection } from './testServerConnection';
|
||||||
|
|
||||||
let updateRootSuite: (config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], progress: Progress | undefined) => void = () => {};
|
let updateRootSuite: (config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], progress: Progress | undefined) => void = () => {};
|
||||||
let runWatchedTests = (fileNames: string[]) => {};
|
let runWatchedTests = (fileNames: string[]) => {};
|
||||||
let xtermSize = { cols: 80, rows: 24 };
|
let xtermSize = { cols: 80, rows: 24 };
|
||||||
|
|
||||||
let sendMessage: (method: string, params?: any) => Promise<any> = async () => {};
|
|
||||||
|
|
||||||
const xtermDataSource: XtermDataSource = {
|
const xtermDataSource: XtermDataSource = {
|
||||||
pending: [],
|
pending: [],
|
||||||
clear: () => {},
|
clear: () => {},
|
||||||
write: data => xtermDataSource.pending.push(data),
|
write: data => xtermDataSource.pending.push(data),
|
||||||
resize: (cols: number, rows: number) => {
|
resize: () => {},
|
||||||
xtermSize = { cols, rows };
|
|
||||||
sendMessageNoReply('resizeTerminal', { cols, rows });
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type TestModel = {
|
type TestModel = {
|
||||||
|
|
@ -90,31 +85,32 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
const [collapseAllCount, setCollapseAllCount] = React.useState(0);
|
const [collapseAllCount, setCollapseAllCount] = React.useState(0);
|
||||||
const [isDisconnected, setIsDisconnected] = React.useState(false);
|
const [isDisconnected, setIsDisconnected] = React.useState(false);
|
||||||
const [hasBrowsers, setHasBrowsers] = React.useState(true);
|
const [hasBrowsers, setHasBrowsers] = React.useState(true);
|
||||||
|
const [testServerConnection, setTestServerConnection] = React.useState<TestServerConnection>();
|
||||||
|
|
||||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const reloadTests = React.useCallback(() => {
|
const reloadTests = React.useCallback(() => {
|
||||||
|
const connection = new TestServerConnection();
|
||||||
|
wireConnectionListeners(connection);
|
||||||
|
connection.onClose(() => setIsDisconnected(true));
|
||||||
|
setTestServerConnection(connection);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setWatchedTreeIds({ value: new Set() });
|
setWatchedTreeIds({ value: new Set() });
|
||||||
updateRootSuite(baseFullConfig, new TeleSuite('', 'root'), [], undefined);
|
updateRootSuite(baseFullConfig, new TeleSuite('', 'root'), [], undefined);
|
||||||
refreshRootSuite().then(async () => {
|
(async () => {
|
||||||
|
const status = await connection.runGlobalSetup();
|
||||||
|
if (status === 'passed')
|
||||||
|
await refreshRootSuite(connection);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
const { hasBrowsers } = await sendMessage('checkBrowsers');
|
const { hasBrowsers } = await connection.checkBrowsers();
|
||||||
setHasBrowsers(hasBrowsers);
|
setHasBrowsers(hasBrowsers);
|
||||||
});
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
connect({ onEvent: dispatchEvent, onClose: () => setIsDisconnected(true) }).then(send => {
|
reloadTests();
|
||||||
sendMessage = async (method, params) => {
|
|
||||||
const logForTest = (window as any).__logForTest;
|
|
||||||
logForTest?.({ method, params });
|
|
||||||
await send(method, params);
|
|
||||||
};
|
|
||||||
reloadTests();
|
|
||||||
});
|
|
||||||
}, [reloadTests]);
|
}, [reloadTests]);
|
||||||
|
|
||||||
updateRootSuite = React.useCallback((config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], newProgress: Progress | undefined) => {
|
updateRootSuite = React.useCallback((config: reporterTypes.FullConfig, rootSuite: reporterTypes.Suite, loadErrors: reporterTypes.TestError[], newProgress: Progress | undefined) => {
|
||||||
|
|
@ -139,6 +135,8 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
}, [projectFilters, runningState]);
|
}, [projectFilters, 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)
|
||||||
|
return;
|
||||||
if (mode === 'bounce-if-busy' && runningState)
|
if (mode === 'bounce-if-busy' && runningState)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -166,7 +164,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
setProgress({ total: 0, passed: 0, failed: 0, skipped: 0 });
|
setProgress({ total: 0, passed: 0, failed: 0, skipped: 0 });
|
||||||
setRunningState({ testIds });
|
setRunningState({ testIds });
|
||||||
|
|
||||||
await sendMessage('runTests', { testIds: [...testIds], projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p) });
|
await testServerConnection.runTests({ testIds: [...testIds], projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p) });
|
||||||
// Clear pending tests in case of interrupt.
|
// Clear pending tests in case of interrupt.
|
||||||
for (const test of testModel.rootSuite?.allTests() || []) {
|
for (const test of testModel.rootSuite?.allTests() || []) {
|
||||||
if (test.results[0]?.duration === -1)
|
if (test.results[0]?.duration === -1)
|
||||||
|
|
@ -175,13 +173,15 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
setTestModel({ ...testModel });
|
setTestModel({ ...testModel });
|
||||||
setRunningState(undefined);
|
setRunningState(undefined);
|
||||||
});
|
});
|
||||||
}, [projectFilters, runningState, testModel]);
|
}, [projectFilters, runningState, testModel, testServerConnection]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
if (!testServerConnection)
|
||||||
|
return;
|
||||||
const onShortcutEvent = (e: KeyboardEvent) => {
|
const onShortcutEvent = (e: KeyboardEvent) => {
|
||||||
if (e.code === 'F6') {
|
if (e.code === 'F6') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
sendMessageNoReply('stop');
|
testServerConnection?.stop().catch(() => {});
|
||||||
} else if (e.code === 'F5') {
|
} else if (e.code === 'F5') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
reloadTests();
|
reloadTests();
|
||||||
|
|
@ -193,7 +193,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
return () => {
|
return () => {
|
||||||
removeEventListener('keydown', onShortcutEvent);
|
removeEventListener('keydown', onShortcutEvent);
|
||||||
};
|
};
|
||||||
}, [runTests, reloadTests]);
|
}, [runTests, reloadTests, testServerConnection]);
|
||||||
|
|
||||||
const isRunningTest = !!runningState;
|
const isRunningTest = !!runningState;
|
||||||
const dialogRef = React.useRef<HTMLDialogElement>(null);
|
const dialogRef = React.useRef<HTMLDialogElement>(null);
|
||||||
|
|
@ -210,12 +210,12 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
const installBrowsers = React.useCallback((e: React.MouseEvent) => {
|
const installBrowsers = React.useCallback((e: React.MouseEvent) => {
|
||||||
closeInstallDialog(e);
|
closeInstallDialog(e);
|
||||||
setIsShowingOutput(true);
|
setIsShowingOutput(true);
|
||||||
sendMessage('installBrowsers').then(async () => {
|
testServerConnection?.installBrowsers().then(async () => {
|
||||||
setIsShowingOutput(false);
|
setIsShowingOutput(false);
|
||||||
const { hasBrowsers } = await sendMessage('checkBrowsers');
|
const { hasBrowsers } = await testServerConnection?.checkBrowsers();
|
||||||
setHasBrowsers(hasBrowsers);
|
setHasBrowsers(hasBrowsers);
|
||||||
});
|
});
|
||||||
}, [closeInstallDialog]);
|
}, [closeInstallDialog, testServerConnection]);
|
||||||
|
|
||||||
return <div className='vbox ui-mode'>
|
return <div className='vbox ui-mode'>
|
||||||
{!hasBrowsers && <dialog ref={dialogRef}>
|
{!hasBrowsers && <dialog ref={dialogRef}>
|
||||||
|
|
@ -275,7 +275,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
<div>Running {progress.passed}/{runningState.testIds.size} passed ({(progress.passed / runningState.testIds.size) * 100 | 0}%)</div>
|
<div>Running {progress.passed}/{runningState.testIds.size} passed ({(progress.passed / runningState.testIds.size) * 100 | 0}%)</div>
|
||||||
</div>}
|
</div>}
|
||||||
<ToolbarButton icon='play' title='Run all' onClick={() => runTests('bounce-if-busy', visibleTestIds)} disabled={isRunningTest || isLoading}></ToolbarButton>
|
<ToolbarButton icon='play' title='Run all' onClick={() => runTests('bounce-if-busy', visibleTestIds)} disabled={isRunningTest || isLoading}></ToolbarButton>
|
||||||
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => sendMessageNoReply('stop')} disabled={!isRunningTest || isLoading}></ToolbarButton>
|
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => testServerConnection?.stop()} disabled={!isRunningTest || isLoading}></ToolbarButton>
|
||||||
<ToolbarButton icon='eye' title='Watch all' toggled={watchAll} onClick={() => {
|
<ToolbarButton icon='eye' title='Watch all' toggled={watchAll} onClick={() => {
|
||||||
setWatchedTreeIds({ value: new Set() });
|
setWatchedTreeIds({ value: new Set() });
|
||||||
setWatchAll(!watchAll);
|
setWatchAll(!watchAll);
|
||||||
|
|
@ -297,7 +297,8 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
watchedTreeIds={watchedTreeIds}
|
watchedTreeIds={watchedTreeIds}
|
||||||
setWatchedTreeIds={setWatchedTreeIds}
|
setWatchedTreeIds={setWatchedTreeIds}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
requestedCollapseAllCount={collapseAllCount} />
|
requestedCollapseAllCount={collapseAllCount}
|
||||||
|
testServerConnection={testServerConnection} />
|
||||||
</div>
|
</div>
|
||||||
</SplitView>
|
</SplitView>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
@ -390,7 +391,8 @@ const TestList: React.FC<{
|
||||||
setVisibleTestIds: (testIds: Set<string>) => void,
|
setVisibleTestIds: (testIds: Set<string>) => void,
|
||||||
onItemSelected: (item: { treeItem?: TreeItem, testCase?: reporterTypes.TestCase, testFile?: SourceLocation }) => void,
|
onItemSelected: (item: { treeItem?: TreeItem, testCase?: reporterTypes.TestCase, testFile?: SourceLocation }) => void,
|
||||||
requestedCollapseAllCount: number,
|
requestedCollapseAllCount: number,
|
||||||
}> = ({ statusFilters, projectFilters, filterText, testModel, runTests, runningState, watchAll, watchedTreeIds, setWatchedTreeIds, isLoading, onItemSelected, setVisibleTestIds, requestedCollapseAllCount }) => {
|
testServerConnection: TestServerConnection | undefined,
|
||||||
|
}> = ({ statusFilters, projectFilters, filterText, testModel, runTests, runningState, watchAll, watchedTreeIds, setWatchedTreeIds, isLoading, onItemSelected, setVisibleTestIds, requestedCollapseAllCount, testServerConnection }) => {
|
||||||
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
|
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
|
||||||
const [selectedTreeItemId, setSelectedTreeItemId] = React.useState<string | undefined>();
|
const [selectedTreeItemId, setSelectedTreeItemId] = React.useState<string | undefined>();
|
||||||
const [collapseAllCount, setCollapseAllCount] = React.useState(requestedCollapseAllCount);
|
const [collapseAllCount, setCollapseAllCount] = React.useState(requestedCollapseAllCount);
|
||||||
|
|
@ -464,10 +466,10 @@ const TestList: React.FC<{
|
||||||
|
|
||||||
// Update watch all.
|
// Update watch all.
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isLoading)
|
if (isLoading || !testServerConnection)
|
||||||
return;
|
return;
|
||||||
if (watchAll) {
|
if (watchAll) {
|
||||||
sendMessageNoReply('watch', { fileNames: testTree.fileNames() });
|
testServerConnection.watch({ fileNames: testTree.fileNames() }).catch(() => {});
|
||||||
} else {
|
} else {
|
||||||
const fileNames = new Set<string>();
|
const fileNames = new Set<string>();
|
||||||
for (const itemId of watchedTreeIds.value) {
|
for (const itemId of watchedTreeIds.value) {
|
||||||
|
|
@ -476,9 +478,9 @@ const TestList: React.FC<{
|
||||||
if (fileName)
|
if (fileName)
|
||||||
fileNames.add(fileName);
|
fileNames.add(fileName);
|
||||||
}
|
}
|
||||||
sendMessageNoReply('watch', { fileNames: [...fileNames] });
|
testServerConnection.watch({ fileNames: [...fileNames] }).catch(() => {});
|
||||||
}
|
}
|
||||||
}, [isLoading, testTree, watchAll, watchedTreeIds]);
|
}, [isLoading, testTree, watchAll, watchedTreeIds, testServerConnection]);
|
||||||
|
|
||||||
const runTreeItem = (treeItem: TreeItem) => {
|
const runTreeItem = (treeItem: TreeItem) => {
|
||||||
setSelectedTreeItemId(treeItem.id);
|
setSelectedTreeItemId(treeItem.id);
|
||||||
|
|
@ -520,7 +522,7 @@ const TestList: React.FC<{
|
||||||
{!!treeItem.duration && treeItem.status !== 'skipped' && <div className='ui-mode-list-item-time'>{msToString(treeItem.duration)}</div>}
|
{!!treeItem.duration && treeItem.status !== 'skipped' && <div className='ui-mode-list-item-time'>{msToString(treeItem.duration)}</div>}
|
||||||
<Toolbar noMinHeight={true} noShadow={true}>
|
<Toolbar noMinHeight={true} noShadow={true}>
|
||||||
<ToolbarButton icon='play' title='Run' onClick={() => runTreeItem(treeItem)} disabled={!!runningState}></ToolbarButton>
|
<ToolbarButton icon='play' title='Run' onClick={() => runTreeItem(treeItem)} disabled={!!runningState}></ToolbarButton>
|
||||||
<ToolbarButton icon='go-to-file' title='Open in VS Code' onClick={() => sendMessageNoReply('open', { location: testTree.locationToOpen(treeItem) })} style={(treeItem.kind === 'group' && treeItem.subKind === 'folder') ? { visibility: 'hidden' } : {}}></ToolbarButton>
|
<ToolbarButton icon='go-to-file' title='Open in VS Code' onClick={() => testServerConnection?.open({ location: treeItem.location }).catch(() => {})} style={(treeItem.kind === 'group' && treeItem.subKind === 'folder') ? { visibility: 'hidden' } : {}}></ToolbarButton>
|
||||||
{!watchAll && <ToolbarButton icon='eye' title='Watch' onClick={() => {
|
{!watchAll && <ToolbarButton icon='eye' title='Watch' onClick={() => {
|
||||||
if (watchedTreeIds.value.has(treeItem.id))
|
if (watchedTreeIds.value.has(treeItem.id))
|
||||||
watchedTreeIds.value.delete(treeItem.id);
|
watchedTreeIds.value.delete(treeItem.id);
|
||||||
|
|
@ -633,7 +635,7 @@ const throttleUpdateRootSuite = (config: reporterTypes.FullConfig, rootSuite: re
|
||||||
throttleTimer = setTimeout(throttledAction, 250);
|
throttleTimer = setTimeout(throttledAction, 250);
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshRootSuite = (): Promise<void> => {
|
const refreshRootSuite = async (testServerConnection: TestServerConnection): Promise<void> => {
|
||||||
teleSuiteUpdater = new TeleSuiteUpdater({
|
teleSuiteUpdater = new TeleSuiteUpdater({
|
||||||
onUpdate: (source, immediate) => {
|
onUpdate: (source, immediate) => {
|
||||||
throttleUpdateRootSuite(source.config!, source.rootSuite || new TeleSuite('', 'root'), source.loadErrors, source.progress, immediate);
|
throttleUpdateRootSuite(source.config!, source.rootSuite || new TeleSuite('', 'root'), source.loadErrors, source.progress, immediate);
|
||||||
|
|
@ -643,45 +645,39 @@ const refreshRootSuite = (): Promise<void> => {
|
||||||
},
|
},
|
||||||
pathSeparator,
|
pathSeparator,
|
||||||
});
|
});
|
||||||
return sendMessage('listTests', {});
|
return testServerConnection.listTests({});
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendMessageNoReply = (method: string, params?: any) => {
|
const wireConnectionListeners = (testServerConnection: TestServerConnection) => {
|
||||||
if ((window as any)._overrideProtocolForTest) {
|
testServerConnection.onListChanged(() => {
|
||||||
(window as any)._overrideProtocolForTest({ method, params }).catch(() => {});
|
testServerConnection.listTests({}).catch(() => {});
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendMessage(method, params).catch((e: Error) => {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(e);
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const dispatchEvent = (method: string, params?: any) => {
|
testServerConnection.onTestFilesChanged(testFiles => {
|
||||||
if (method === 'listChanged') {
|
runWatchedTests(testFiles);
|
||||||
sendMessage('listTests', {}).catch(() => {});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method === 'testFilesChanged') {
|
testServerConnection.onStdio(params => {
|
||||||
runWatchedTests(params.testFileNames);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method === 'stdio') {
|
|
||||||
if (params.buffer) {
|
if (params.buffer) {
|
||||||
const data = atob(params.buffer);
|
const data = atob(params.buffer);
|
||||||
xtermDataSource.write(data);
|
xtermDataSource.write(data);
|
||||||
} else {
|
} else {
|
||||||
xtermDataSource.write(params.text);
|
xtermDataSource.write(params.text!);
|
||||||
}
|
}
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (method === 'listReport')
|
testServerConnection.onListReport(params => {
|
||||||
teleSuiteUpdater?.dispatch('list', params);
|
teleSuiteUpdater?.dispatch('list', params);
|
||||||
if (method === 'testReport')
|
});
|
||||||
|
|
||||||
|
testServerConnection.onTestReport(params => {
|
||||||
teleSuiteUpdater?.dispatch('test', params);
|
teleSuiteUpdater?.dispatch('test', params);
|
||||||
|
});
|
||||||
|
|
||||||
|
xtermDataSource.resize = (cols, rows) => {
|
||||||
|
xtermSize = { cols, rows };
|
||||||
|
testServerConnection.resizeTerminal({ cols, rows }).catch(() => {});
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const outputDirForTestCase = (testCase: reporterTypes.TestCase): string | undefined => {
|
const outputDirForTestCase = (testCase: reporterTypes.TestCase): string | undefined => {
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,12 @@ test('should run global setup and teardown', async ({ runUITest }) => {
|
||||||
});
|
});
|
||||||
await page.getByTitle('Run all').click();
|
await page.getByTitle('Run all').click();
|
||||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||||
|
|
||||||
|
await page.getByTitle('Toggle output').click();
|
||||||
|
await expect(page.getByTestId('output')).toContainText('from-global-setup');
|
||||||
await page.close();
|
await page.close();
|
||||||
|
|
||||||
await expect.poll(() => testProcess.outputLines()).toEqual([
|
await expect.poll(() => testProcess.outputLines()).toEqual([
|
||||||
'from-global-setup',
|
|
||||||
'from-global-teardown',
|
'from-global-teardown',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
@ -70,9 +73,11 @@ test('should teardown on sigint', async ({ runUITest }) => {
|
||||||
});
|
});
|
||||||
await page.getByTitle('Run all').click();
|
await page.getByTitle('Run all').click();
|
||||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||||
|
await page.getByTitle('Toggle output').click();
|
||||||
|
await expect(page.getByTestId('output')).toContainText('from-global-setup');
|
||||||
|
|
||||||
testProcess.process.kill('SIGINT');
|
testProcess.process.kill('SIGINT');
|
||||||
await expect.poll(() => testProcess.outputLines()).toEqual([
|
await expect.poll(() => testProcess.outputLines()).toEqual([
|
||||||
'from-global-setup',
|
|
||||||
'from-global-teardown',
|
'from-global-teardown',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ test('should update test locations', async ({ runUITest, writeFiles, deleteFile
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const messages: any = [];
|
const messages: any = [];
|
||||||
await page.exposeBinding('_overrideProtocolForTest', (_, data) => messages.push(data));
|
await page.exposeBinding('_sniffProtocolForTest', (_, data) => messages.push(data));
|
||||||
|
|
||||||
const passesItemLocator = page.getByRole('listitem').filter({ hasText: 'passes' });
|
const passesItemLocator = page.getByRole('listitem').filter({ hasText: 'passes' });
|
||||||
await passesItemLocator.hover();
|
await passesItemLocator.hover();
|
||||||
|
|
@ -192,7 +192,11 @@ test('should update test locations', async ({ runUITest, writeFiles, deleteFile
|
||||||
expect(messages).toEqual([{
|
expect(messages).toEqual([{
|
||||||
method: 'open',
|
method: 'open',
|
||||||
params: {
|
params: {
|
||||||
location: expect.stringContaining('a.test.ts:3'),
|
location: {
|
||||||
|
file: expect.stringContaining('a.test.ts'),
|
||||||
|
line: 3,
|
||||||
|
column: 11,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
@ -218,7 +222,11 @@ test('should update test locations', async ({ runUITest, writeFiles, deleteFile
|
||||||
expect(messages).toEqual([{
|
expect(messages).toEqual([{
|
||||||
method: 'open',
|
method: 'open',
|
||||||
params: {
|
params: {
|
||||||
location: expect.stringContaining('a.test.ts:5'),
|
location: {
|
||||||
|
file: expect.stringContaining('a.test.ts'),
|
||||||
|
line: 5,
|
||||||
|
column: 11,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue