chore: keep UI Mode running when used with browser mode (#23876)
This will keep UI Mode running in browser mode. When launched in normal persistent context mode, we know when the persistent context closes, so we can run the project teardown code. Fixes https://github.com/microsoft/playwright/issues/23801
This commit is contained in:
Родитель
9a580af1e6
Коммит
dcdf38f119
|
@ -22,7 +22,8 @@ import path from 'path';
|
||||||
import type { Command } from '../utilsBundle';
|
import type { Command } from '../utilsBundle';
|
||||||
import { program } from '../utilsBundle';
|
import { program } from '../utilsBundle';
|
||||||
import { runDriver, runServer, printApiJson, launchBrowserServer } from './driver';
|
import { runDriver, runServer, printApiJson, launchBrowserServer } from './driver';
|
||||||
import { showTraceViewer } from '../server/trace/viewer/traceViewer';
|
import type { OpenTraceViewerOptions } from '../server/trace/viewer/traceViewer';
|
||||||
|
import { openTraceInBrowser, openTraceViewerApp } from '../server/trace/viewer/traceViewer';
|
||||||
import * as playwright from '../..';
|
import * as playwright from '../..';
|
||||||
import type { BrowserContext } from '../client/browserContext';
|
import type { BrowserContext } from '../client/browserContext';
|
||||||
import type { Browser } from '../client/browser';
|
import type { Browser } from '../client/browser';
|
||||||
|
@ -297,14 +298,19 @@ program
|
||||||
if (options.browser === 'wk')
|
if (options.browser === 'wk')
|
||||||
options.browser = 'webkit';
|
options.browser = 'webkit';
|
||||||
|
|
||||||
const openOptions = {
|
const openOptions: OpenTraceViewerOptions = {
|
||||||
headless: false,
|
headless: false,
|
||||||
host: options.host,
|
host: options.host,
|
||||||
port: +options.port,
|
port: +options.port,
|
||||||
isServer: !!options.stdin,
|
isServer: !!options.stdin,
|
||||||
openInBrowser: options.port !== undefined || options.host !== undefined
|
|
||||||
};
|
};
|
||||||
showTraceViewer(traces, options.browser, openOptions).catch(logErrorAndExit);
|
if (options.port !== undefined || options.host !== undefined) {
|
||||||
|
openTraceInBrowser(traces, openOptions).catch(logErrorAndExit);
|
||||||
|
} else {
|
||||||
|
openTraceViewerApp(traces, options.browser, openOptions).then(page => {
|
||||||
|
page.on('close', () => process.exit(0));
|
||||||
|
}).catch(logErrorAndExit);
|
||||||
|
}
|
||||||
}).addHelpText('afterAll', `
|
}).addHelpText('afterAll', `
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
|
|
@ -29,5 +29,5 @@ export { createPlaywright } from './playwright';
|
||||||
|
|
||||||
export type { DispatcherScope } from './dispatchers/dispatcher';
|
export type { DispatcherScope } from './dispatchers/dispatcher';
|
||||||
export type { Playwright } from './playwright';
|
export type { Playwright } from './playwright';
|
||||||
export { showTraceViewer, openTraceViewerApp } from './trace/viewer/traceViewer';
|
export { openTraceInBrowser, openTraceViewerApp } from './trace/viewer/traceViewer';
|
||||||
export { serverSideCallMetadata } from './instrumentation';
|
export { serverSideCallMetadata } from './instrumentation';
|
|
@ -33,25 +33,16 @@ export type Transport = {
|
||||||
onclose: () => void;
|
onclose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Options = {
|
export type OpenTraceViewerOptions = {
|
||||||
app?: string;
|
app?: string;
|
||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
host?: string;
|
host?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
isServer?: boolean;
|
isServer?: boolean;
|
||||||
openInBrowser?: boolean;
|
|
||||||
transport?: Transport;
|
transport?: Transport;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function showTraceViewer(traceUrls: string[], browserName: string, options?: Options): Promise<void> {
|
async function startTraceViewerServer(traceUrls: string[], options?: OpenTraceViewerOptions): Promise<{ server: HttpServer, url: string }> {
|
||||||
if (options?.openInBrowser) {
|
|
||||||
await openTraceInBrowser(traceUrls, options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await openTraceViewerApp(traceUrls, browserName, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startTraceViewerServer(traceUrls: string[], options?: Options): Promise<{ server: HttpServer, url: string }> {
|
|
||||||
for (const traceUrl of traceUrls) {
|
for (const traceUrl of traceUrls) {
|
||||||
let traceFile = traceUrl;
|
let traceFile = traceUrl;
|
||||||
// If .json is requested, we'll synthesize it.
|
// If .json is requested, we'll synthesize it.
|
||||||
|
@ -134,7 +125,7 @@ async function startTraceViewerServer(traceUrls: string[], options?: Options): P
|
||||||
return { server, url };
|
return { server, url };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openTraceViewerApp(traceUrls: string[], browserName: string, options?: Options): Promise<Page> {
|
export async function openTraceViewerApp(traceUrls: string[], browserName: string, options?: OpenTraceViewerOptions): Promise<Page> {
|
||||||
const { url } = await startTraceViewerServer(traceUrls, options);
|
const { url } = await startTraceViewerServer(traceUrls, options);
|
||||||
const traceViewerPlaywright = createPlaywright({ sdkLanguage: 'javascript', isInternalPlaywright: true });
|
const traceViewerPlaywright = createPlaywright({ sdkLanguage: 'javascript', isInternalPlaywright: true });
|
||||||
const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName;
|
const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName;
|
||||||
|
@ -171,24 +162,21 @@ export async function openTraceViewerApp(traceUrls: string[], browserName: strin
|
||||||
|
|
||||||
if (isUnderTest())
|
if (isUnderTest())
|
||||||
page.on('close', () => context.close(serverSideCallMetadata()).catch(() => {}));
|
page.on('close', () => context.close(serverSideCallMetadata()).catch(() => {}));
|
||||||
else
|
|
||||||
page.on('close', () => process.exit());
|
|
||||||
|
|
||||||
await page.mainFrame().goto(serverSideCallMetadata(), url);
|
await page.mainFrame().goto(serverSideCallMetadata(), url);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openTraceInBrowser(traceUrls: string[], options?: Options) {
|
export async function openTraceInBrowser(traceUrls: string[], options?: OpenTraceViewerOptions) {
|
||||||
const { url } = await startTraceViewerServer(traceUrls, options);
|
const { url } = await startTraceViewerServer(traceUrls, options);
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('\nListening on ' + url);
|
console.log('\nListening on ' + url);
|
||||||
await open(url, { wait: true }).catch(() => {});
|
await open(url).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
class StdinServer implements Transport {
|
class StdinServer implements Transport {
|
||||||
private _pollTimer: NodeJS.Timeout | undefined;
|
private _pollTimer: NodeJS.Timeout | undefined;
|
||||||
private _traceUrl: string | undefined;
|
private _traceUrl: string | undefined;
|
||||||
private _page: Page | undefined;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
process.stdin.on('data', data => {
|
process.stdin.on('data', data => {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { showTraceViewer } from 'playwright-core/lib/server';
|
import { openTraceViewerApp, openTraceInBrowser } from 'playwright-core/lib/server';
|
||||||
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';
|
||||||
|
@ -27,7 +27,7 @@ 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';
|
import type { OpenTraceViewerOptions, Transport } from 'playwright-core/lib/server/trace/viewer/traceViewer';
|
||||||
|
|
||||||
class UIMode {
|
class UIMode {
|
||||||
private _config: FullConfigInternal;
|
private _config: FullConfigInternal;
|
||||||
|
@ -82,7 +82,6 @@ class UIMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
async showUI(options: { host?: string, port?: number }) {
|
async showUI(options: { host?: string, port?: number }) {
|
||||||
const exitPromise = new ManualPromise();
|
|
||||||
let queue = Promise.resolve();
|
let queue = Promise.resolve();
|
||||||
|
|
||||||
this._transport = {
|
this._transport = {
|
||||||
|
@ -90,10 +89,6 @@ class UIMode {
|
||||||
if (method === 'ping')
|
if (method === 'ping')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (method === 'exit') {
|
|
||||||
exitPromise.resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (method === 'watch') {
|
if (method === 'watch') {
|
||||||
this._watchFiles(params.fileNames);
|
this._watchFiles(params.fileNames);
|
||||||
return;
|
return;
|
||||||
|
@ -117,16 +112,22 @@ class UIMode {
|
||||||
await queue;
|
await queue;
|
||||||
},
|
},
|
||||||
|
|
||||||
onclose: () => exitPromise.resolve(),
|
onclose: () => { },
|
||||||
};
|
};
|
||||||
await showTraceViewer([], 'chromium', {
|
const openOptions: OpenTraceViewerOptions = {
|
||||||
app: 'uiMode.html',
|
app: 'uiMode.html',
|
||||||
headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
||||||
transport: this._transport,
|
transport: this._transport,
|
||||||
host: options.host,
|
host: options.host,
|
||||||
port: options.port,
|
port: options.port,
|
||||||
openInBrowser: options.host !== undefined || options.port !== undefined,
|
};
|
||||||
});
|
const exitPromise = new ManualPromise<void>();
|
||||||
|
if (options.host !== undefined || options.port !== undefined) {
|
||||||
|
await openTraceInBrowser([], openOptions);
|
||||||
|
} else {
|
||||||
|
const page = await openTraceViewerApp([], 'chromium', openOptions);
|
||||||
|
page.on('close', () => exitPromise.resolve());
|
||||||
|
}
|
||||||
|
|
||||||
if (!process.env.PWTEST_DEBUG) {
|
if (!process.env.PWTEST_DEBUG) {
|
||||||
process.stdout.write = (chunk: string | Buffer) => {
|
process.stdout.write = (chunk: string | Buffer) => {
|
||||||
|
|
|
@ -168,7 +168,8 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
|
|
||||||
return <div className='vbox ui-mode'>
|
return <div className='vbox ui-mode'>
|
||||||
{isDisconnected && <div className='drop-target'>
|
{isDisconnected && <div className='drop-target'>
|
||||||
<div className='title'>Process disconnected</div>
|
<div className='title'>UI Mode disconnected</div>
|
||||||
|
<div><a href='#' onClick={() => window.location.reload()}>Reload the page</a> to reconnect</div>
|
||||||
</div>}
|
</div>}
|
||||||
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}>
|
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}>
|
||||||
<div className='vbox'>
|
<div className='vbox'>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче