2025-04-28 16:14:16 -07:00
|
|
|
/**
|
|
|
|
* Copyright (c) Microsoft Corporation.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import * as playwright from 'playwright';
|
|
|
|
|
2025-04-30 23:06:56 +02:00
|
|
|
import { PageSnapshot } from './pageSnapshot.js';
|
2025-04-28 16:14:16 -07:00
|
|
|
|
2025-04-30 23:06:56 +02:00
|
|
|
import type { Context } from './context.js';
|
2025-04-28 16:14:16 -07:00
|
|
|
|
|
|
|
export class Tab {
|
|
|
|
readonly context: Context;
|
|
|
|
readonly page: playwright.Page;
|
|
|
|
private _console: playwright.ConsoleMessage[] = [];
|
|
|
|
private _requests: Map<playwright.Request, playwright.Response | null> = new Map();
|
|
|
|
private _snapshot: PageSnapshot | undefined;
|
|
|
|
private _onPageClose: (tab: Tab) => void;
|
|
|
|
|
|
|
|
constructor(context: Context, page: playwright.Page, onPageClose: (tab: Tab) => void) {
|
|
|
|
this.context = context;
|
|
|
|
this.page = page;
|
|
|
|
this._onPageClose = onPageClose;
|
|
|
|
page.on('console', event => this._console.push(event));
|
|
|
|
page.on('request', request => this._requests.set(request, null));
|
|
|
|
page.on('response', response => this._requests.set(response.request(), response));
|
|
|
|
page.on('framenavigated', frame => {
|
|
|
|
if (!frame.parentFrame())
|
|
|
|
this._clearCollectedArtifacts();
|
|
|
|
});
|
|
|
|
page.on('close', () => this._onClose());
|
|
|
|
page.on('filechooser', chooser => {
|
|
|
|
this.context.setModalState({
|
|
|
|
type: 'fileChooser',
|
|
|
|
description: 'File chooser',
|
|
|
|
fileChooser: chooser,
|
|
|
|
}, this);
|
|
|
|
});
|
|
|
|
page.on('dialog', dialog => this.context.dialogShown(this, dialog));
|
2025-05-02 10:57:31 +02:00
|
|
|
page.on('download', download => {
|
|
|
|
void this.context.downloadStarted(this, download);
|
|
|
|
});
|
2025-04-28 16:14:16 -07:00
|
|
|
page.setDefaultNavigationTimeout(60000);
|
|
|
|
page.setDefaultTimeout(5000);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _clearCollectedArtifacts() {
|
|
|
|
this._console.length = 0;
|
|
|
|
this._requests.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
private _onClose() {
|
|
|
|
this._clearCollectedArtifacts();
|
|
|
|
this._onPageClose(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
async navigate(url: string) {
|
|
|
|
await this.page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
|
|
// Cap load event to 5 seconds, the page is operational at this point.
|
|
|
|
await this.page.waitForLoadState('load', { timeout: 5000 }).catch(() => {});
|
|
|
|
}
|
|
|
|
|
|
|
|
hasSnapshot(): boolean {
|
|
|
|
return !!this._snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshotOrDie(): PageSnapshot {
|
|
|
|
if (!this._snapshot)
|
|
|
|
throw new Error('No snapshot available');
|
|
|
|
return this._snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
console(): playwright.ConsoleMessage[] {
|
|
|
|
return this._console;
|
|
|
|
}
|
|
|
|
|
|
|
|
requests(): Map<playwright.Request, playwright.Response | null> {
|
|
|
|
return this._requests;
|
|
|
|
}
|
|
|
|
|
|
|
|
async captureSnapshot() {
|
|
|
|
this._snapshot = await PageSnapshot.create(this.page);
|
|
|
|
}
|
|
|
|
}
|