chore: report paused signal to the debug controller clients (#18701)
This commit is contained in:
parent
f52fa4ceba
commit
ca2e7ef199
|
|
@ -344,6 +344,9 @@ scheme.DebugControllerSourceChangedEvent = tObject({
|
||||||
footer: tOptional(tString),
|
footer: tOptional(tString),
|
||||||
actions: tOptional(tArray(tString)),
|
actions: tOptional(tArray(tString)),
|
||||||
});
|
});
|
||||||
|
scheme.DebugControllerPausedEvent = tObject({
|
||||||
|
paused: tBoolean,
|
||||||
|
});
|
||||||
scheme.DebugControllerBrowsersChangedEvent = tObject({
|
scheme.DebugControllerBrowsersChangedEvent = tObject({
|
||||||
browsers: tArray(tObject({
|
browsers: tArray(tObject({
|
||||||
contexts: tArray(tObject({
|
contexts: tArray(tObject({
|
||||||
|
|
@ -377,6 +380,8 @@ scheme.DebugControllerHighlightParams = tObject({
|
||||||
scheme.DebugControllerHighlightResult = tOptional(tObject({}));
|
scheme.DebugControllerHighlightResult = tOptional(tObject({}));
|
||||||
scheme.DebugControllerHideHighlightParams = tOptional(tObject({}));
|
scheme.DebugControllerHideHighlightParams = tOptional(tObject({}));
|
||||||
scheme.DebugControllerHideHighlightResult = tOptional(tObject({}));
|
scheme.DebugControllerHideHighlightResult = tOptional(tObject({}));
|
||||||
|
scheme.DebugControllerResumeParams = tOptional(tObject({}));
|
||||||
|
scheme.DebugControllerResumeResult = tOptional(tObject({}));
|
||||||
scheme.DebugControllerKillParams = tOptional(tObject({}));
|
scheme.DebugControllerKillParams = tOptional(tObject({}));
|
||||||
scheme.DebugControllerKillResult = tOptional(tObject({}));
|
scheme.DebugControllerKillResult = tOptional(tObject({}));
|
||||||
scheme.DebugControllerCloseAllBrowsersParams = tOptional(tObject({}));
|
scheme.DebugControllerCloseAllBrowsersParams = tOptional(tObject({}));
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export class DebugController extends SdkObject {
|
||||||
StateChanged: 'stateChanged',
|
StateChanged: 'stateChanged',
|
||||||
InspectRequested: 'inspectRequested',
|
InspectRequested: 'inspectRequested',
|
||||||
SourceChanged: 'sourceChanged',
|
SourceChanged: 'sourceChanged',
|
||||||
|
Paused: 'paused',
|
||||||
};
|
};
|
||||||
|
|
||||||
private _autoCloseTimer: NodeJS.Timeout | undefined;
|
private _autoCloseTimer: NodeJS.Timeout | undefined;
|
||||||
|
|
@ -52,6 +53,7 @@ export class DebugController extends SdkObject {
|
||||||
initialize(codegenId: string, sdkLanguage: Language) {
|
initialize(codegenId: string, sdkLanguage: Language) {
|
||||||
this._codegenId = codegenId;
|
this._codegenId = codegenId;
|
||||||
this._sdkLanguage = sdkLanguage;
|
this._sdkLanguage = sdkLanguage;
|
||||||
|
Recorder.setAppFactory(async () => new InspectingRecorderApp(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
setAutoCloseAllowed(allowed: boolean) {
|
setAutoCloseAllowed(allowed: boolean) {
|
||||||
|
|
@ -61,6 +63,7 @@ export class DebugController extends SdkObject {
|
||||||
dispose() {
|
dispose() {
|
||||||
this.setReportStateChanged(false);
|
this.setReportStateChanged(false);
|
||||||
this.setAutoCloseAllowed(false);
|
this.setAutoCloseAllowed(false);
|
||||||
|
Recorder.setAppFactory(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
setReportStateChanged(enabled: boolean) {
|
setReportStateChanged(enabled: boolean) {
|
||||||
|
|
@ -157,6 +160,11 @@ export class DebugController extends SdkObject {
|
||||||
return [...this._playwright.allBrowsers()];
|
return [...this._playwright.allBrowsers()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resume() {
|
||||||
|
for (const recorder of await this._allRecorders())
|
||||||
|
recorder.resume();
|
||||||
|
}
|
||||||
|
|
||||||
async kill() {
|
async kill() {
|
||||||
selfDestruct();
|
selfDestruct();
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +200,7 @@ export class DebugController extends SdkObject {
|
||||||
const contexts = new Set<BrowserContext>();
|
const contexts = new Set<BrowserContext>();
|
||||||
for (const page of this._playwright.allPages())
|
for (const page of this._playwright.allPages())
|
||||||
contexts.add(page.context());
|
contexts.add(page.context());
|
||||||
const result = await Promise.all([...contexts].map(c => Recorder.show(c, { omitCallTracking: true }, () => Promise.resolve(new InspectingRecorderApp(this)))));
|
const result = await Promise.all([...contexts].map(c => Recorder.show(c, { omitCallTracking: true })));
|
||||||
return result.filter(Boolean) as Recorder[];
|
return result.filter(Boolean) as Recorder[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,4 +243,8 @@ class InspectingRecorderApp extends EmptyRecorderApp {
|
||||||
const { text, header, footer, actions } = source || { text: '' };
|
const { text, header, footer, actions } = source || { text: '' };
|
||||||
this._debugController.emit(DebugController.Events.SourceChanged, { text, header, footer, actions });
|
this._debugController.emit(DebugController.Events.SourceChanged, { text, header, footer, actions });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override async setPaused(paused: boolean) {
|
||||||
|
this._debugController.emit(DebugController.Events.Paused, { paused });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ export class Debugger extends EventEmitter implements InstrumentationListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
resume(step: boolean) {
|
resume(step: boolean) {
|
||||||
|
if (!this.isPaused())
|
||||||
|
return;
|
||||||
|
|
||||||
this._pauseOnNextStatement = step;
|
this._pauseOnNextStatement = step;
|
||||||
const endTime = monotonicTime();
|
const endTime = monotonicTime();
|
||||||
for (const [metadata, { resolve }] of this._pausedCallsMetadata) {
|
for (const [metadata, { resolve }] of this._pausedCallsMetadata) {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ export class DebugControllerDispatcher extends Dispatcher<DebugController, chann
|
||||||
this._object.on(DebugController.Events.SourceChanged, ({ text, header, footer, actions }) => {
|
this._object.on(DebugController.Events.SourceChanged, ({ text, header, footer, actions }) => {
|
||||||
this._dispatchEvent('sourceChanged', ({ text, header, footer, actions }));
|
this._dispatchEvent('sourceChanged', ({ text, header, footer, actions }));
|
||||||
});
|
});
|
||||||
|
this._object.on(DebugController.Events.Paused, ({ paused }) => {
|
||||||
|
this._dispatchEvent('paused', ({ paused }));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(params: channels.DebugControllerInitializeParams) {
|
async initialize(params: channels.DebugControllerInitializeParams) {
|
||||||
|
|
@ -64,6 +67,10 @@ export class DebugControllerDispatcher extends Dispatcher<DebugController, chann
|
||||||
await this._object.hideHighlight();
|
await this._object.hideHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resume() {
|
||||||
|
await this._object.resume();
|
||||||
|
}
|
||||||
|
|
||||||
async kill() {
|
async kill() {
|
||||||
await this._object.kill();
|
await this._object.kill();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,27 +58,31 @@ export class Recorder implements InstrumentationListener {
|
||||||
private _debugger: Debugger;
|
private _debugger: Debugger;
|
||||||
private _contextRecorder: ContextRecorder;
|
private _contextRecorder: ContextRecorder;
|
||||||
private _handleSIGINT: boolean | undefined;
|
private _handleSIGINT: boolean | undefined;
|
||||||
private _recorderAppFactory: (recorder: Recorder) => Promise<IRecorderApp>;
|
|
||||||
private _omitCallTracking = false;
|
private _omitCallTracking = false;
|
||||||
private _currentLanguage: Language;
|
private _currentLanguage: Language;
|
||||||
|
|
||||||
|
private static recorderAppFactory: ((recorder: Recorder) => Promise<IRecorderApp>) | undefined;
|
||||||
|
|
||||||
|
static setAppFactory(recorderAppFactory: ((recorder: Recorder) => Promise<IRecorderApp>) | undefined) {
|
||||||
|
Recorder.recorderAppFactory = recorderAppFactory;
|
||||||
|
}
|
||||||
|
|
||||||
static showInspector(context: BrowserContext) {
|
static showInspector(context: BrowserContext) {
|
||||||
Recorder.show(context, {}).catch(() => {});
|
Recorder.show(context, {}).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
static show(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}, recorderAppFactory = Recorder.defaultRecorderAppFactory): Promise<Recorder> {
|
static show(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<Recorder> {
|
||||||
let recorderPromise = (context as any)[recorderSymbol] as Promise<Recorder>;
|
let recorderPromise = (context as any)[recorderSymbol] as Promise<Recorder>;
|
||||||
if (!recorderPromise) {
|
if (!recorderPromise) {
|
||||||
const recorder = new Recorder(context, params, recorderAppFactory);
|
const recorder = new Recorder(context, params);
|
||||||
recorderPromise = recorder.install().then(() => recorder);
|
recorderPromise = recorder.install().then(() => recorder);
|
||||||
(context as any)[recorderSymbol] = recorderPromise;
|
(context as any)[recorderSymbol] = recorderPromise;
|
||||||
}
|
}
|
||||||
return recorderPromise;
|
return recorderPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams, recorderAppFactory: (recorder: Recorder) => Promise<IRecorderApp>) {
|
constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams) {
|
||||||
this._mode = params.mode || 'none';
|
this._mode = params.mode || 'none';
|
||||||
this._recorderAppFactory = recorderAppFactory;
|
|
||||||
this._contextRecorder = new ContextRecorder(context, params);
|
this._contextRecorder = new ContextRecorder(context, params);
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this._omitCallTracking = !!params.omitCallTracking;
|
this._omitCallTracking = !!params.omitCallTracking;
|
||||||
|
|
@ -95,7 +99,7 @@ export class Recorder implements InstrumentationListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
async install() {
|
async install() {
|
||||||
const recorderApp = await this._recorderAppFactory(this);
|
const recorderApp = await (Recorder.recorderAppFactory || Recorder.defaultRecorderAppFactory)(this);
|
||||||
this._recorderApp = recorderApp;
|
this._recorderApp = recorderApp;
|
||||||
recorderApp.once('close', () => {
|
recorderApp.once('close', () => {
|
||||||
this._debugger.resume(false);
|
this._debugger.resume(false);
|
||||||
|
|
@ -215,6 +219,10 @@ export class Recorder implements InstrumentationListener {
|
||||||
this._refreshOverlay();
|
this._refreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resume() {
|
||||||
|
this._debugger.resume(false);
|
||||||
|
}
|
||||||
|
|
||||||
setHighlightedSelector(language: Language, selector: string) {
|
setHighlightedSelector(language: Language, selector: string) {
|
||||||
this._highlightedSelector = locatorOrSelectorAsSelector(language, selector, this._contextRecorder.testIdAttributeName());
|
this._highlightedSelector = locatorOrSelectorAsSelector(language, selector, this._contextRecorder.testIdAttributeName());
|
||||||
this._refreshOverlay();
|
this._refreshOverlay();
|
||||||
|
|
|
||||||
|
|
@ -592,6 +592,7 @@ export interface DebugControllerEventTarget {
|
||||||
on(event: 'inspectRequested', callback: (params: DebugControllerInspectRequestedEvent) => void): this;
|
on(event: 'inspectRequested', callback: (params: DebugControllerInspectRequestedEvent) => void): this;
|
||||||
on(event: 'stateChanged', callback: (params: DebugControllerStateChangedEvent) => void): this;
|
on(event: 'stateChanged', callback: (params: DebugControllerStateChangedEvent) => void): this;
|
||||||
on(event: 'sourceChanged', callback: (params: DebugControllerSourceChangedEvent) => void): this;
|
on(event: 'sourceChanged', callback: (params: DebugControllerSourceChangedEvent) => void): this;
|
||||||
|
on(event: 'paused', callback: (params: DebugControllerPausedEvent) => void): this;
|
||||||
on(event: 'browsersChanged', callback: (params: DebugControllerBrowsersChangedEvent) => void): this;
|
on(event: 'browsersChanged', callback: (params: DebugControllerBrowsersChangedEvent) => void): this;
|
||||||
}
|
}
|
||||||
export interface DebugControllerChannel extends DebugControllerEventTarget, Channel {
|
export interface DebugControllerChannel extends DebugControllerEventTarget, Channel {
|
||||||
|
|
@ -603,6 +604,7 @@ export interface DebugControllerChannel extends DebugControllerEventTarget, Chan
|
||||||
setRecorderMode(params: DebugControllerSetRecorderModeParams, metadata?: Metadata): Promise<DebugControllerSetRecorderModeResult>;
|
setRecorderMode(params: DebugControllerSetRecorderModeParams, metadata?: Metadata): Promise<DebugControllerSetRecorderModeResult>;
|
||||||
highlight(params: DebugControllerHighlightParams, metadata?: Metadata): Promise<DebugControllerHighlightResult>;
|
highlight(params: DebugControllerHighlightParams, metadata?: Metadata): Promise<DebugControllerHighlightResult>;
|
||||||
hideHighlight(params?: DebugControllerHideHighlightParams, metadata?: Metadata): Promise<DebugControllerHideHighlightResult>;
|
hideHighlight(params?: DebugControllerHideHighlightParams, metadata?: Metadata): Promise<DebugControllerHideHighlightResult>;
|
||||||
|
resume(params?: DebugControllerResumeParams, metadata?: Metadata): Promise<DebugControllerResumeResult>;
|
||||||
kill(params?: DebugControllerKillParams, metadata?: Metadata): Promise<DebugControllerKillResult>;
|
kill(params?: DebugControllerKillParams, metadata?: Metadata): Promise<DebugControllerKillResult>;
|
||||||
closeAllBrowsers(params?: DebugControllerCloseAllBrowsersParams, metadata?: Metadata): Promise<DebugControllerCloseAllBrowsersResult>;
|
closeAllBrowsers(params?: DebugControllerCloseAllBrowsersParams, metadata?: Metadata): Promise<DebugControllerCloseAllBrowsersResult>;
|
||||||
}
|
}
|
||||||
|
|
@ -619,6 +621,9 @@ export type DebugControllerSourceChangedEvent = {
|
||||||
footer?: string,
|
footer?: string,
|
||||||
actions?: string[],
|
actions?: string[],
|
||||||
};
|
};
|
||||||
|
export type DebugControllerPausedEvent = {
|
||||||
|
paused: boolean,
|
||||||
|
};
|
||||||
export type DebugControllerBrowsersChangedEvent = {
|
export type DebugControllerBrowsersChangedEvent = {
|
||||||
browsers: {
|
browsers: {
|
||||||
contexts: {
|
contexts: {
|
||||||
|
|
@ -669,6 +674,9 @@ export type DebugControllerHighlightResult = void;
|
||||||
export type DebugControllerHideHighlightParams = {};
|
export type DebugControllerHideHighlightParams = {};
|
||||||
export type DebugControllerHideHighlightOptions = {};
|
export type DebugControllerHideHighlightOptions = {};
|
||||||
export type DebugControllerHideHighlightResult = void;
|
export type DebugControllerHideHighlightResult = void;
|
||||||
|
export type DebugControllerResumeParams = {};
|
||||||
|
export type DebugControllerResumeOptions = {};
|
||||||
|
export type DebugControllerResumeResult = void;
|
||||||
export type DebugControllerKillParams = {};
|
export type DebugControllerKillParams = {};
|
||||||
export type DebugControllerKillOptions = {};
|
export type DebugControllerKillOptions = {};
|
||||||
export type DebugControllerKillResult = void;
|
export type DebugControllerKillResult = void;
|
||||||
|
|
@ -680,6 +688,7 @@ export interface DebugControllerEvents {
|
||||||
'inspectRequested': DebugControllerInspectRequestedEvent;
|
'inspectRequested': DebugControllerInspectRequestedEvent;
|
||||||
'stateChanged': DebugControllerStateChangedEvent;
|
'stateChanged': DebugControllerStateChangedEvent;
|
||||||
'sourceChanged': DebugControllerSourceChangedEvent;
|
'sourceChanged': DebugControllerSourceChangedEvent;
|
||||||
|
'paused': DebugControllerPausedEvent;
|
||||||
'browsersChanged': DebugControllerBrowsersChangedEvent;
|
'browsersChanged': DebugControllerBrowsersChangedEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -700,6 +700,8 @@ DebugController:
|
||||||
|
|
||||||
hideHighlight:
|
hideHighlight:
|
||||||
|
|
||||||
|
resume:
|
||||||
|
|
||||||
kill:
|
kill:
|
||||||
|
|
||||||
closeAllBrowsers:
|
closeAllBrowsers:
|
||||||
|
|
@ -722,7 +724,10 @@ DebugController:
|
||||||
actions:
|
actions:
|
||||||
type: array?
|
type: array?
|
||||||
items: string
|
items: string
|
||||||
|
|
||||||
|
paused:
|
||||||
|
parameters:
|
||||||
|
paused: boolean
|
||||||
|
|
||||||
# Deprecated
|
# Deprecated
|
||||||
browsersChanged:
|
browsersChanged:
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,10 @@ export class Backend extends EventEmitter {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async initialize() {
|
||||||
|
await this._send('initialize', { codegenId: 'playwright-test', sdkLanguage: 'javascript' });
|
||||||
|
}
|
||||||
|
|
||||||
async close() {
|
async close() {
|
||||||
await this._transport.closeAndWait();
|
await this._transport.closeAndWait();
|
||||||
}
|
}
|
||||||
|
|
@ -165,6 +169,10 @@ export class Backend extends EventEmitter {
|
||||||
await this._send('hideHighlight');
|
await this._send('hideHighlight');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resume() {
|
||||||
|
this._send('resume');
|
||||||
|
}
|
||||||
|
|
||||||
async kill() {
|
async kill() {
|
||||||
this._send('kill');
|
this._send('kill');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ const test = baseTest.extend<Fixtures>({
|
||||||
backend: async ({ wsEndpoint }, use) => {
|
backend: async ({ wsEndpoint }, use) => {
|
||||||
const backend = new Backend();
|
const backend = new Backend();
|
||||||
await backend.connect(wsEndpoint);
|
await backend.connect(wsEndpoint);
|
||||||
|
await backend.initialize();
|
||||||
await use(backend);
|
await use(backend);
|
||||||
await backend.close();
|
await backend.close();
|
||||||
},
|
},
|
||||||
|
|
@ -212,3 +213,16 @@ test('test', async ({ page }) => {
|
||||||
});`
|
});`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should pause and resume', async ({ backend, connectedBrowser }) => {
|
||||||
|
const events = [];
|
||||||
|
backend.on('paused', event => events.push(event));
|
||||||
|
const context = await connectedBrowser._newContextForReuse();
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.setContent('<button>Submit</button>');
|
||||||
|
const pausePromise = page.pause();
|
||||||
|
await expect.poll(() => events[events.length - 1]).toEqual({ paused: true });
|
||||||
|
await backend.resume();
|
||||||
|
await pausePromise;
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue