chore(watch): allow toggling browser (#20738)
This commit is contained in:
parent
d962f3b70a
commit
0678b6575f
|
|
@ -174,6 +174,11 @@ export class PlaywrightServer {
|
|||
await new Promise(f => server.options.server!.close(f));
|
||||
this._wsServer = undefined;
|
||||
debugLog('closed server');
|
||||
|
||||
debugLog('closing browsers');
|
||||
if (this._preLaunchedPlaywright)
|
||||
await Promise.all(this._preLaunchedPlaywright.allBrowsers().map(browser => browser.close()));
|
||||
debugLog('closed browsers');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,28 +15,33 @@
|
|||
*/
|
||||
|
||||
import type * as channels from '@protocol/channels';
|
||||
import { eventsHelper } from 'playwright-core/lib/utils';
|
||||
import type { RegisteredListener } from 'playwright-core/lib/utils/eventsHelper';
|
||||
import { DebugController } from '../debugController';
|
||||
import type { DispatcherConnection, RootDispatcher } from './dispatcher';
|
||||
import { Dispatcher } from './dispatcher';
|
||||
|
||||
export class DebugControllerDispatcher extends Dispatcher<DebugController, channels.DebugControllerChannel, RootDispatcher> implements channels.DebugControllerChannel {
|
||||
_type_DebugController;
|
||||
private _listeners: RegisteredListener[];
|
||||
|
||||
constructor(connection: DispatcherConnection, debugController: DebugController) {
|
||||
super(connection, debugController, 'DebugController', {});
|
||||
this._type_DebugController = true;
|
||||
this._object.on(DebugController.Events.StateChanged, params => {
|
||||
this._dispatchEvent('stateChanged', params);
|
||||
});
|
||||
this._object.on(DebugController.Events.InspectRequested, ({ selector, locator }) => {
|
||||
this._dispatchEvent('inspectRequested', { selector, locator });
|
||||
});
|
||||
this._object.on(DebugController.Events.SourceChanged, ({ text, header, footer, actions }) => {
|
||||
this._dispatchEvent('sourceChanged', ({ text, header, footer, actions }));
|
||||
});
|
||||
this._object.on(DebugController.Events.Paused, ({ paused }) => {
|
||||
this._dispatchEvent('paused', ({ paused }));
|
||||
});
|
||||
this._listeners = [
|
||||
eventsHelper.addEventListener(this._object, DebugController.Events.StateChanged, params => {
|
||||
this._dispatchEvent('stateChanged', params);
|
||||
}),
|
||||
eventsHelper.addEventListener(this._object, DebugController.Events.InspectRequested, ({ selector, locator }) => {
|
||||
this._dispatchEvent('inspectRequested', { selector, locator });
|
||||
}),
|
||||
eventsHelper.addEventListener(this._object, DebugController.Events.SourceChanged, ({ text, header, footer, actions }) => {
|
||||
this._dispatchEvent('sourceChanged', ({ text, header, footer, actions }));
|
||||
}),
|
||||
eventsHelper.addEventListener(this._object, DebugController.Events.Paused, ({ paused }) => {
|
||||
this._dispatchEvent('paused', ({ paused }));
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
async initialize(params: channels.DebugControllerInitializeParams) {
|
||||
|
|
@ -80,6 +85,7 @@ export class DebugControllerDispatcher extends Dispatcher<DebugController, chann
|
|||
}
|
||||
|
||||
override _onDispose() {
|
||||
eventsHelper.removeEventListeners(this._listeners);
|
||||
this._object.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export * from './comparators';
|
|||
export * from './crypto';
|
||||
export * from './debug';
|
||||
export * from './env';
|
||||
export * from './eventsHelper';
|
||||
export * from './fileUtils';
|
||||
export * from './glob';
|
||||
export * from './headers';
|
||||
|
|
|
|||
|
|
@ -108,6 +108,14 @@ export class ListModeReporter implements Reporter {
|
|||
let seq = 0;
|
||||
|
||||
export class WatchModeReporter extends ListReporter {
|
||||
private _options: { isShowBrowser?: () => boolean; } | undefined;
|
||||
constructor(options?: {
|
||||
isShowBrowser?: () => boolean,
|
||||
}) {
|
||||
super();
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
override generateStartingMessage(): string {
|
||||
const tokens: string[] = [];
|
||||
tokens.push('npx playwright test');
|
||||
|
|
@ -121,6 +129,7 @@ export class WatchModeReporter extends ListReporter {
|
|||
const sep = separator();
|
||||
lines.push('\x1Bc' + sep);
|
||||
lines.push(`${tokens.join(' ')}` + super.generateStartingMessage());
|
||||
lines.push(`${colors.dim('Show & reuse browser:')} ${colors.bold(this._options?.isShowBrowser?.() ? 'on' : 'off')}${colors.dim(', press')} ${colors.bold('s')} ${colors.dim('to toggle.')}`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import readline from 'readline';
|
||||
import { ManualPromise } from 'playwright-core/lib/utils';
|
||||
import { createGuid, ManualPromise } from 'playwright-core/lib/utils';
|
||||
import type { FullConfigInternal, FullProjectInternal } from '../common/types';
|
||||
import { Multiplexer } from '../reporters/multiplexer';
|
||||
import { createFileMatcherFromArguments } from '../util';
|
||||
|
|
@ -30,6 +30,7 @@ import { WatchModeReporter } from './reporters';
|
|||
import { colors } from 'playwright-core/lib/utilsBundle';
|
||||
import { enquirer } from '../utilsBundle';
|
||||
import { separator } from '../reporters/base';
|
||||
import { PlaywrightServer } from 'playwright-core/lib/remote/playwrightServer';
|
||||
|
||||
class FSWatcher {
|
||||
private _dirtyFiles = new Set<string>();
|
||||
|
|
@ -70,6 +71,8 @@ export async function runWatchModeLoop(config: FullConfigInternal, failedTests:
|
|||
|
||||
const originalCliArgs = config._internal.cliArgs;
|
||||
const originalCliGrep = config._internal.cliGrep;
|
||||
const originalWorkers = config.workers;
|
||||
|
||||
let lastRun: { type: 'changed' | 'regular' | 'failed', failedTestIds?: Set<string>, dirtyFiles?: Set<string> } = { type: 'regular' };
|
||||
|
||||
const fsWatcher = new FSWatcher(projectClosure.map(p => p.testDir));
|
||||
|
|
@ -110,7 +113,6 @@ Waiting for file changes. Press ${colors.bold('h')} for help or ${colors.bold('q
|
|||
type: 'text',
|
||||
name: 'filePattern',
|
||||
message: 'Input filename pattern (regex)',
|
||||
initial: config._internal.cliArgs.join(' '),
|
||||
});
|
||||
if (filePattern.trim())
|
||||
config._internal.cliArgs = [filePattern];
|
||||
|
|
@ -126,7 +128,6 @@ Waiting for file changes. Press ${colors.bold('h')} for help or ${colors.bold('q
|
|||
type: 'text',
|
||||
name: 'testPattern',
|
||||
message: 'Input test name pattern (regex)',
|
||||
initial: config._internal.cliGrep,
|
||||
});
|
||||
if (testPattern.trim())
|
||||
config._internal.cliGrep = testPattern;
|
||||
|
|
@ -160,6 +161,11 @@ Waiting for file changes. Press ${colors.bold('h')} for help or ${colors.bold('q
|
|||
continue;
|
||||
}
|
||||
|
||||
if (command === 'toggle-show-browser') {
|
||||
await toggleShowBrowser(config, originalWorkers);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (command === 'exit')
|
||||
return 'passed';
|
||||
|
||||
|
|
@ -203,7 +209,7 @@ async function runChangedTests(config: FullConfigInternal, failedTestIdCollector
|
|||
}
|
||||
|
||||
async function runTests(config: FullConfigInternal, failedTestIdCollector: Set<string>, projectsToIgnore?: Set<FullProjectInternal>, additionalFileMatcher?: Matcher) {
|
||||
const reporter = new Multiplexer([new WatchModeReporter()]);
|
||||
const reporter = new Multiplexer([new WatchModeReporter({ isShowBrowser: () => !!showBrowserServer })]);
|
||||
const taskRunner = createTaskRunnerForWatch(config, reporter, projectsToIgnore, additionalFileMatcher);
|
||||
const context: TaskRunnerState = {
|
||||
config,
|
||||
|
|
@ -281,6 +287,7 @@ ${commands.map(i => ' ' + colors.bold(i[0]) + `: ${i[1]}`).join('\n')}
|
|||
case 't': result.resolve('grep'); break;
|
||||
case 'f': result.resolve('failed'); break;
|
||||
case 'r': result.resolve('repeat'); break;
|
||||
case 's': result.resolve('toggle-show-browser'); break;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -294,7 +301,27 @@ ${commands.map(i => ' ' + colors.bold(i[0]) + `: ${i[1]}`).join('\n')}
|
|||
return result;
|
||||
}
|
||||
|
||||
type Command = 'all' | 'failed' | 'repeat' | 'changed' | 'file' | 'grep' | 'exit' | 'interrupted';
|
||||
let showBrowserServer: PlaywrightServer | undefined;
|
||||
|
||||
async function toggleShowBrowser(config: FullConfigInternal, originalWorkers: number) {
|
||||
if (!showBrowserServer) {
|
||||
config.workers = 1;
|
||||
showBrowserServer = new PlaywrightServer({ path: '/' + createGuid(), maxConnections: 1 });
|
||||
const wsEndpoint = await showBrowserServer.listen();
|
||||
process.env.PW_TEST_REUSE_CONTEXT = '1';
|
||||
process.env.PW_TEST_CONNECT_WS_ENDPOINT = wsEndpoint;
|
||||
process.stdout.write(`${colors.dim('Show & reuse browser:')} ${colors.bold('on')}\n`);
|
||||
} else {
|
||||
config.workers = originalWorkers;
|
||||
await showBrowserServer?.close();
|
||||
showBrowserServer = undefined;
|
||||
delete process.env.PW_TEST_REUSE_CONTEXT;
|
||||
delete process.env.PW_TEST_CONNECT_WS_ENDPOINT;
|
||||
process.stdout.write(`${colors.dim('Show & reuse browser:')} ${colors.bold('off')}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
type Command = 'all' | 'failed' | 'repeat' | 'changed' | 'file' | 'grep' | 'exit' | 'interrupted' | 'toggle-show-browser';
|
||||
|
||||
const commands = [
|
||||
['a', 'rerun all tests'],
|
||||
|
|
@ -302,5 +329,6 @@ const commands = [
|
|||
['r', 'repeat last run'],
|
||||
['p', 'filter by a filename'],
|
||||
['t', 'filter by a test name regex pattern'],
|
||||
['s', 'toggle show & reuse the browser'],
|
||||
['q', 'quit'],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -95,4 +95,4 @@ it('should scroll into view span element', async ({ page }) => {
|
|||
`);
|
||||
await page.locator('#small').scrollIntoViewIfNeeded();
|
||||
expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(9000);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue