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:
Max Schmitt 2023-06-26 22:21:44 +02:00 коммит произвёл GitHub
Родитель 9a580af1e6
Коммит dcdf38f119
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 30 добавлений и 34 удалений

Просмотреть файл

@ -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'>