Files
azuredatastudio/test/automation/src/playwrightDriver.ts
Karl Burtram 8a3d08f0de Merge vscode 1.67 (#20883)
* Fix initial build breaks from 1.67 merge (#2514)

* Update yarn lock files

* Update build scripts

* Fix tsconfig

* Build breaks

* WIP

* Update yarn lock files

* Misc breaks

* Updates to package.json

* Breaks

* Update yarn

* Fix breaks

* Breaks

* Build breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Missing file

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Fix several runtime breaks (#2515)

* Missing files

* Runtime breaks

* Fix proxy ordering issue

* Remove commented code

* Fix breaks with opening query editor

* Fix post merge break

* Updates related to setup build and other breaks (#2516)

* Fix bundle build issues

* Update distro

* Fix distro merge and update build JS files

* Disable pipeline steps

* Remove stats call

* Update license name

* Make new RPM dependencies a warning

* Fix extension manager version checks

* Update JS file

* Fix a few runtime breaks

* Fixes

* Fix runtime issues

* Fix build breaks

* Update notebook tests (part 1)

* Fix broken tests

* Linting errors

* Fix hygiene

* Disable lint rules

* Bump distro

* Turn off smoke tests

* Disable integration tests

* Remove failing "activate" test

* Remove failed test assertion

* Disable other broken test

* Disable query history tests

* Disable extension unit tests

* Disable failing tasks
2022-10-19 19:13:18 -07:00

216 lines
7.1 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as playwright from '@playwright/test';
import { join } from 'path';
import { IWindowDriver } from './driver';
import { PageFunction } from 'playwright-core/types/structs';
import { measureAndLog } from './logger';
import { LaunchOptions } from './code';
import { teardown } from './processes';
import { ChildProcess } from 'child_process';
export class PlaywrightDriver {
private static traceCounter = 1;
private static screenShotCounter = 1;
private static readonly vscodeToPlaywrightKey: { [key: string]: string } = {
cmd: 'Meta',
ctrl: 'Control',
shift: 'Shift',
enter: 'Enter',
escape: 'Escape',
right: 'ArrowRight',
up: 'ArrowUp',
down: 'ArrowDown',
left: 'ArrowLeft',
home: 'Home',
esc: 'Escape'
};
constructor(
private readonly application: playwright.Browser | playwright.ElectronApplication,
private readonly context: playwright.BrowserContext,
private readonly page: playwright.Page,
private readonly serverProcess: ChildProcess | undefined,
private readonly options: LaunchOptions
) {
}
async startTracing(name: string): Promise<void> {
if (!this.options.tracing) {
return; // tracing disabled
}
try {
await measureAndLog(this.context.tracing.startChunk({ title: name }), `startTracing for ${name}`, this.options.logger);
} catch (error) {
// Ignore
}
}
async stopTracing(name: string, persist: boolean): Promise<void> {
if (!this.options.tracing) {
return; // tracing disabled
}
try {
let persistPath: string | undefined = undefined;
if (persist) {
persistPath = join(this.options.logsPath, `playwright-trace-${PlaywrightDriver.traceCounter++}-${name.replace(/\s+/g, '-')}.zip`);
}
await measureAndLog(this.context.tracing.stopChunk({ path: persistPath }), `stopTracing for ${name}`, this.options.logger);
// To ensure we have a screenshot at the end where
// it failed, also trigger one explicitly. Tracing
// does not guarantee to give us a screenshot unless
// some driver action ran before.
if (persist) {
await this.takeScreenshot(name);
}
} catch (error) {
// Ignore
}
}
private async takeScreenshot(name: string): Promise<void> {
try {
const persistPath = join(this.options.logsPath, `playwright-screenshot-${PlaywrightDriver.screenShotCounter++}-${name.replace(/\s+/g, '-')}.png`);
await measureAndLog(this.page.screenshot({ path: persistPath, type: 'png' }), 'takeScreenshot', this.options.logger);
} catch (error) {
// Ignore
}
}
async reload() {
await this.page.reload();
}
async exitApplication() {
// Stop tracing
try {
if (this.options.tracing) {
await measureAndLog(this.context.tracing.stop(), 'stop tracing', this.options.logger);
}
} catch (error) {
// Ignore
}
// Web: exit via `close` method
if (this.options.web) {
try {
await measureAndLog(this.application.close(), 'playwright.close()', this.options.logger);
} catch (error) {
this.options.logger.log(`Error closing appliction (${error})`);
}
}
// Desktop: exit via `driver.exitApplication`
else {
try {
await measureAndLog(this.evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger);
} catch (error) {
this.options.logger.log(`Error exiting appliction (${error})`);
}
}
// Server: via `teardown`
if (this.serverProcess) {
await measureAndLog(teardown(this.serverProcess, this.options.logger), 'teardown server process', this.options.logger);
}
}
async dispatchKeybinding(keybinding: string) {
const chords = keybinding.split(' ');
for (let i = 0; i < chords.length; i++) {
const chord = chords[i];
if (i > 0) {
await this.timeout(100);
}
if (keybinding.startsWith('Alt') || keybinding.startsWith('Control') || keybinding.startsWith('Backspace')) {
await this.page.keyboard.press(keybinding);
return;
}
const keys = chord.split('+');
const keysDown: string[] = [];
for (let i = 0; i < keys.length; i++) {
if (keys[i] in PlaywrightDriver.vscodeToPlaywrightKey) {
keys[i] = PlaywrightDriver.vscodeToPlaywrightKey[keys[i]];
}
await this.page.keyboard.down(keys[i]);
keysDown.push(keys[i]);
}
while (keysDown.length > 0) {
await this.page.keyboard.up(keysDown.pop()!);
}
}
await this.timeout(100);
}
async click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined) {
const { x, y } = await this.getElementXY(selector, xoffset, yoffset);
await this.page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0));
}
async setValue(selector: string, text: string) {
return this.page.evaluate(([driver, selector, text]) => driver.setValue(selector, text), [await this.getDriverHandle(), selector, text] as const);
}
async getTitle() {
return this.evaluateWithDriver(([driver]) => driver.getTitle());
}
async isActiveElement(selector: string) {
return this.page.evaluate(([driver, selector]) => driver.isActiveElement(selector), [await this.getDriverHandle(), selector] as const);
}
async getElements(selector: string, recursive: boolean = false) {
return this.page.evaluate(([driver, selector, recursive]) => driver.getElements(selector, recursive), [await this.getDriverHandle(), selector, recursive] as const);
}
async getElementXY(selector: string, xoffset?: number, yoffset?: number) {
return this.page.evaluate(([driver, selector, xoffset, yoffset]) => driver.getElementXY(selector, xoffset, yoffset), [await this.getDriverHandle(), selector, xoffset, yoffset] as const);
}
async typeInEditor(selector: string, text: string) {
return this.page.evaluate(([driver, selector, text]) => driver.typeInEditor(selector, text), [await this.getDriverHandle(), selector, text] as const);
}
async getTerminalBuffer(selector: string) {
return this.page.evaluate(([driver, selector]) => driver.getTerminalBuffer(selector), [await this.getDriverHandle(), selector] as const);
}
async writeInTerminal(selector: string, text: string) {
return this.page.evaluate(([driver, selector, text]) => driver.writeInTerminal(selector, text), [await this.getDriverHandle(), selector, text] as const);
}
async getLocaleInfo() {
return this.evaluateWithDriver(([driver]) => driver.getLocaleInfo());
}
async getLocalizedStrings() {
return this.evaluateWithDriver(([driver]) => driver.getLocalizedStrings());
}
private async evaluateWithDriver<T>(pageFunction: PageFunction<playwright.JSHandle<IWindowDriver>[], T>) {
return this.page.evaluate(pageFunction, [await this.getDriverHandle()]);
}
private timeout(ms: number): Promise<void> {
return new Promise<void>(resolve => setTimeout(resolve, ms));
}
private async getDriverHandle(): Promise<playwright.JSHandle<IWindowDriver>> {
return this.page.evaluateHandle('window.driver');
}
}