chore: headless mode for codegen (#12020)
This commit is contained in:
Родитель
66b5cf5ae1
Коммит
5ab7bc3a59
|
@ -38,7 +38,18 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
export class RecorderApp extends EventEmitter {
|
||||
export interface IRecorderApp extends EventEmitter {
|
||||
close(): Promise<void>;
|
||||
setPaused(paused: boolean): Promise<void>;
|
||||
setMode(mode: 'none' | 'recording' | 'inspecting'): Promise<void>;
|
||||
setFile(file: string): Promise<void>;
|
||||
setSelector(selector: string, focus?: boolean): Promise<void>;
|
||||
updateCallLogs(callLogs: CallLog[]): Promise<void>;
|
||||
bringToFront(): void;
|
||||
setSources(sources: Source[]): Promise<void>;
|
||||
}
|
||||
|
||||
export class RecorderApp extends EventEmitter implements IRecorderApp {
|
||||
private _page: Page;
|
||||
readonly wsEndpoint: string | undefined;
|
||||
|
||||
|
@ -85,7 +96,9 @@ export class RecorderApp extends EventEmitter {
|
|||
await mainFrame.goto(internalCallMetadata(), 'https://playwright/index.html');
|
||||
}
|
||||
|
||||
static async open(sdkLanguage: string, headed: boolean): Promise<RecorderApp> {
|
||||
static async open(sdkLanguage: string, headed: boolean): Promise<IRecorderApp> {
|
||||
if (process.env.PW_CODEGEN_NO_INSPECTOR)
|
||||
return new HeadlessRecorderApp();
|
||||
const recorderPlaywright = (require('../../playwright').createPlaywright as typeof import('../../playwright').createPlaywright)('javascript', true);
|
||||
const args = [
|
||||
'--app=data:text/html,',
|
||||
|
@ -163,3 +176,14 @@ export class RecorderApp extends EventEmitter {
|
|||
await this._page.bringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
class HeadlessRecorderApp extends EventEmitter implements IRecorderApp {
|
||||
async close(): Promise<void> {}
|
||||
async setPaused(paused: boolean): Promise<void> {}
|
||||
async setMode(mode: 'none' | 'recording' | 'inspecting'): Promise<void> {}
|
||||
async setFile(file: string): Promise<void> {}
|
||||
async setSelector(selector: string, focus?: boolean): Promise<void> {}
|
||||
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
|
||||
bringToFront(): void {}
|
||||
async setSources(sources: Source[]): Promise<void> {}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import { CSharpLanguageGenerator } from './recorder/csharp';
|
|||
import { PythonLanguageGenerator } from './recorder/python';
|
||||
import * as recorderSource from '../../generated/recorderSource';
|
||||
import * as consoleApiSource from '../../generated/consoleApiSource';
|
||||
import { RecorderApp } from './recorder/recorderApp';
|
||||
import { IRecorderApp, RecorderApp } from './recorder/recorderApp';
|
||||
import { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation';
|
||||
import { Point } from '../../common/types';
|
||||
import { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
|
||||
|
@ -46,7 +46,7 @@ export class RecorderSupplement implements InstrumentationListener {
|
|||
private _context: BrowserContext;
|
||||
private _mode: Mode;
|
||||
private _highlightedSelector = '';
|
||||
private _recorderApp: RecorderApp | null = null;
|
||||
private _recorderApp: IRecorderApp | null = null;
|
||||
private _currentCallsMetadata = new Map<CallMetadata, SdkObject>();
|
||||
private _recorderSources: Source[] = [];
|
||||
private _userSources = new Map<string, Source>();
|
||||
|
@ -317,7 +317,7 @@ class ContextRecorder extends EventEmitter {
|
|||
|
||||
this._recorderSources = [];
|
||||
const generator = new CodeGenerator(context._browser.options.name, !!params.startRecording, params.launchOptions || {}, params.contextOptions || {}, params.device, params.saveStorage);
|
||||
let text = '';
|
||||
const throttledOutputFile = params.outputFile ? new ThrottledFile(params.outputFile) : null;
|
||||
generator.on('change', () => {
|
||||
this._recorderSources = [];
|
||||
for (const languageGenerator of orderedLanguages) {
|
||||
|
@ -330,21 +330,19 @@ class ContextRecorder extends EventEmitter {
|
|||
source.revealLine = source.text.split('\n').length - 1;
|
||||
this._recorderSources.push(source);
|
||||
if (languageGenerator === orderedLanguages[0])
|
||||
text = source.text;
|
||||
throttledOutputFile?.setContent(source.text);
|
||||
}
|
||||
this.emit(ContextRecorder.Events.Change, {
|
||||
sources: this._recorderSources,
|
||||
primaryFileName: primaryLanguage.fileName
|
||||
});
|
||||
});
|
||||
if (params.outputFile) {
|
||||
if (throttledOutputFile) {
|
||||
context.on(BrowserContext.Events.BeforeClose, () => {
|
||||
fs.writeFileSync(params.outputFile!, text);
|
||||
text = '';
|
||||
throttledOutputFile.flush();
|
||||
});
|
||||
process.on('exit', () => {
|
||||
if (text)
|
||||
fs.writeFileSync(params.outputFile!, text);
|
||||
throttledOutputFile.flush();
|
||||
});
|
||||
}
|
||||
this._generator = generator;
|
||||
|
@ -590,3 +588,29 @@ function languageForFile(file: string) {
|
|||
return 'csharp';
|
||||
return 'javascript';
|
||||
}
|
||||
|
||||
class ThrottledFile {
|
||||
private _file: string;
|
||||
private _timer: NodeJS.Timeout | undefined;
|
||||
private _text: string | undefined;
|
||||
|
||||
constructor(file: string) {
|
||||
this._file = file;
|
||||
}
|
||||
|
||||
setContent(text: string) {
|
||||
this._text = text;
|
||||
if (!this._timer)
|
||||
this._timer = setTimeout(() => this.flush(), 1000);
|
||||
}
|
||||
|
||||
flush(): void {
|
||||
if (this._timer) {
|
||||
clearTimeout(this._timer);
|
||||
this._timer = undefined;
|
||||
}
|
||||
if (this._text)
|
||||
fs.writeFileSync(this._file, this._text);
|
||||
this._text = undefined;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче