feat(pw-web): introduce platform.ts to absract between node and browser platforms (#392)
This commit is contained in:
parent
f1b825e6a2
commit
9c966c8b19
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { BrowserContext, BrowserContextOptions } from './browserContext';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import { EventEmitter } from './platform';
|
||||
|
||||
export class Browser extends EventEmitter {
|
||||
newContext(options?: BrowserContextOptions): Promise<BrowserContext> { throw new Error('Not implemented'); }
|
||||
|
|
|
|||
|
|
@ -19,16 +19,17 @@ import * as extract from 'extract-zip';
|
|||
import * as fs from 'fs';
|
||||
import * as ProxyAgent from 'https-proxy-agent';
|
||||
import * as path from 'path';
|
||||
import * as platform from './platform';
|
||||
// @ts-ignore
|
||||
import { getProxyForUrl } from 'proxy-from-env';
|
||||
import * as removeRecursive from 'rimraf';
|
||||
import * as URL from 'url';
|
||||
import { assert, helper } from './helper';
|
||||
import { assert } from './helper';
|
||||
|
||||
const readdirAsync = helper.promisify(fs.readdir.bind(fs));
|
||||
const mkdirAsync = helper.promisify(fs.mkdir.bind(fs));
|
||||
const unlinkAsync = helper.promisify(fs.unlink.bind(fs));
|
||||
const chmodAsync = helper.promisify(fs.chmod.bind(fs));
|
||||
const readdirAsync = platform.promisify(fs.readdir.bind(fs));
|
||||
const mkdirAsync = platform.promisify(fs.mkdir.bind(fs));
|
||||
const unlinkAsync = platform.promisify(fs.unlink.bind(fs));
|
||||
const chmodAsync = platform.promisify(fs.chmod.bind(fs));
|
||||
|
||||
function existsAsync(filePath) {
|
||||
let fulfill = null;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { CRPage } from './crPage';
|
|||
import * as browser from '../browser';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import * as platform from '../platform';
|
||||
import { CRWorker } from './features/crWorkers';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import { readProtocolStream } from './crProtocolHelper';
|
||||
|
|
@ -268,10 +269,10 @@ export class CRBrowser extends browser.Browser {
|
|||
});
|
||||
}
|
||||
|
||||
async stopTracing(): Promise<Buffer> {
|
||||
async stopTracing(): Promise<platform.BufferType> {
|
||||
assert(this._tracingClient, 'Tracing was not started.');
|
||||
let fulfill: (buffer: Buffer) => void;
|
||||
const contentPromise = new Promise<Buffer>(x => fulfill = x);
|
||||
let fulfill: (buffer: platform.BufferType) => void;
|
||||
const contentPromise = new Promise<platform.BufferType>(x => fulfill = x);
|
||||
this._tracingClient.once('Tracing.tracingComplete', event => {
|
||||
readProtocolStream(this._tracingClient, event.stream, this._tracingPath).then(fulfill);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,19 +15,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as debug from 'debug';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as platform from '../platform';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import { assert } from '../helper';
|
||||
import { Protocol } from './protocol';
|
||||
|
||||
const debugProtocol = debug('playwright:protocol');
|
||||
const debugProtocol = platform.debug('playwright:protocol');
|
||||
|
||||
export const ConnectionEvents = {
|
||||
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
||||
};
|
||||
|
||||
export class CRConnection extends EventEmitter {
|
||||
export class CRConnection extends platform.EventEmitter {
|
||||
private _lastId = 0;
|
||||
private _transport: ConnectionTransport;
|
||||
private _sessions = new Map<string, CRSession>();
|
||||
|
|
@ -113,7 +112,7 @@ export const CRSessionEvents = {
|
|||
Disconnected: Symbol('Events.CDPSession.Disconnected')
|
||||
};
|
||||
|
||||
export class CRSession extends EventEmitter {
|
||||
export class CRSession extends platform.EventEmitter {
|
||||
_connection: CRConnection;
|
||||
private _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||
private _targetType: string;
|
||||
|
|
|
|||
|
|
@ -19,15 +19,16 @@ import * as fs from 'fs';
|
|||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import * as platform from '../platform';
|
||||
import { BrowserFetcher, BrowserFetcherOptions } from '../browserFetcher';
|
||||
import { TimeoutError } from '../errors';
|
||||
import { assert, helper } from '../helper';
|
||||
import { assert } from '../helper';
|
||||
import { launchProcess, waitForLine } from '../processLauncher';
|
||||
import { ConnectionTransport, PipeTransport, SlowMoTransport, WebSocketTransport } from '../transport';
|
||||
import { CRBrowser } from './crBrowser';
|
||||
import { BrowserServer } from '../browser';
|
||||
|
||||
const mkdtempAsync = helper.promisify(fs.mkdtemp);
|
||||
const mkdtempAsync = platform.promisify(fs.mkdtemp);
|
||||
|
||||
const CHROME_PROFILE_PATH = path.join(os.tmpdir(), 'playwright_dev_profile-');
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { assert, debugError, helper, RegisteredListener } from '../helper';
|
|||
import { Protocol } from './protocol';
|
||||
import * as network from '../network';
|
||||
import * as frames from '../frames';
|
||||
import * as platform from '../platform';
|
||||
import { Credentials } from '../types';
|
||||
|
||||
export class CRNetworkManager {
|
||||
|
|
@ -188,7 +189,7 @@ export class CRNetworkManager {
|
|||
const remoteAddress: network.RemoteAddress = { ip: responsePayload.remoteIPAddress, port: responsePayload.remotePort };
|
||||
const getResponseBody = async () => {
|
||||
const response = await this._client.send('Network.getResponseBody', { requestId: request._requestId });
|
||||
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||
return platform.Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||
};
|
||||
return new network.Response(request.request, responsePayload.status, responsePayload.statusText, headersObject(responsePayload.headers), remoteAddress, getResponseBody);
|
||||
}
|
||||
|
|
@ -261,7 +262,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
event.request.url, event.type.toLowerCase(), event.request.method, event.request.postData, headersObject(event.request.headers));
|
||||
}
|
||||
|
||||
async continue(overrides: { headers?: {[key: string]: string}; } = {}) {
|
||||
async continue(overrides: { headers?: network.Headers; } = {}) {
|
||||
await this._client.send('Fetch.continueRequest', {
|
||||
requestId: this._interceptionId,
|
||||
headers: overrides.headers ? headersArray(overrides.headers) : undefined,
|
||||
|
|
@ -272,8 +273,8 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
});
|
||||
}
|
||||
|
||||
async fulfill(response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) {
|
||||
const responseBody = response.body && helper.isString(response.body) ? Buffer.from(/** @type {string} */(response.body)) : /** @type {?Buffer} */(response.body || null);
|
||||
async fulfill(response: { status: number; headers: network.Headers; contentType: string; body: (string | platform.BufferType); }) {
|
||||
const responseBody = response.body && helper.isString(response.body) ? platform.Buffer.from(response.body) : (response.body || null);
|
||||
|
||||
const responseHeaders: { [s: string]: string; } = {};
|
||||
if (response.headers) {
|
||||
|
|
@ -283,7 +284,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
if (response.contentType)
|
||||
responseHeaders['content-type'] = response.contentType;
|
||||
if (responseBody && !('content-length' in responseHeaders))
|
||||
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
||||
responseHeaders['content-length'] = String(platform.Buffer.byteLength(responseBody));
|
||||
|
||||
await this._client.send('Fetch.fulfillRequest', {
|
||||
requestId: this._interceptionId,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import { BrowserContext } from '../browserContext';
|
|||
import * as types from '../types';
|
||||
import { ConsoleMessage } from '../console';
|
||||
import * as accessibility from '../accessibility';
|
||||
import * as platform from '../platform';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
||||
|
|
@ -375,11 +376,11 @@ export class CRPage implements PageDelegate {
|
|||
await this._client.send('Emulation.setDefaultBackgroundColorOverride', { color });
|
||||
}
|
||||
|
||||
async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions): Promise<Buffer> {
|
||||
async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions): Promise<platform.BufferType> {
|
||||
await this._client.send('Page.bringToFront', {});
|
||||
const clip = options.clip ? { ...options.clip, scale: 1 } : undefined;
|
||||
const result = await this._client.send('Page.captureScreenshot', { format, quality: options.quality, clip });
|
||||
return Buffer.from(result.data, 'base64');
|
||||
return platform.Buffer.from(result.data, 'base64');
|
||||
}
|
||||
|
||||
async resetViewport(): Promise<void> {
|
||||
|
|
@ -494,7 +495,7 @@ export class ChromiumPage extends Page {
|
|||
this._networkManager = new CRNetworkManager(client, this);
|
||||
}
|
||||
|
||||
async pdf(options?: PDFOptions): Promise<Buffer> {
|
||||
async pdf(options?: PDFOptions): Promise<platform.BufferType> {
|
||||
return this._pdf.generate(options);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,15 +14,11 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import {helper, assert, debugError} from '../helper';
|
||||
|
||||
import { assert, debugError } from '../helper';
|
||||
import { CRSession } from './crConnection';
|
||||
import { Protocol } from './protocol';
|
||||
|
||||
const openAsync = helper.promisify(fs.open);
|
||||
const writeAsync = helper.promisify(fs.write);
|
||||
const closeAsync = helper.promisify(fs.close);
|
||||
|
||||
import * as platform from '../platform';
|
||||
|
||||
export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string {
|
||||
if (exceptionDetails.exception)
|
||||
|
|
@ -69,26 +65,26 @@ export async function releaseObject(client: CRSession, remoteObject: Protocol.Ru
|
|||
});
|
||||
}
|
||||
|
||||
export async function readProtocolStream(client: CRSession, handle: string, path: string | null): Promise<Buffer> {
|
||||
export async function readProtocolStream(client: CRSession, handle: string, path: string | null): Promise<platform.BufferType> {
|
||||
let eof = false;
|
||||
let file;
|
||||
let fd;
|
||||
if (path)
|
||||
file = await openAsync(path, 'w');
|
||||
fd = await platform.openFdAsync(path, 'w');
|
||||
const bufs = [];
|
||||
while (!eof) {
|
||||
const response = await client.send('IO.read', {handle});
|
||||
eof = response.eof;
|
||||
const buf = Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined);
|
||||
const buf = platform.Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined);
|
||||
bufs.push(buf);
|
||||
if (path)
|
||||
await writeAsync(file, buf);
|
||||
await platform.writeFdAsync(fd, buf);
|
||||
}
|
||||
if (path)
|
||||
await closeAsync(file);
|
||||
await platform.closeFdAsync(fd);
|
||||
await client.send('IO.close', {handle});
|
||||
let resultBuffer = null;
|
||||
try {
|
||||
resultBuffer = Buffer.concat(bufs);
|
||||
resultBuffer = platform.Buffer.concat(bufs);
|
||||
} finally {
|
||||
return resultBuffer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
import { assert, helper } from '../../helper';
|
||||
import { CRSession } from '../crConnection';
|
||||
import { readProtocolStream } from '../crProtocolHelper';
|
||||
import * as platform from '../../platform';
|
||||
|
||||
export type PDFOptions = {
|
||||
scale?: number,
|
||||
|
|
@ -91,7 +92,7 @@ export class CRPDF {
|
|||
this._client = client;
|
||||
}
|
||||
|
||||
async generate(options: PDFOptions = {}): Promise<Buffer> {
|
||||
async generate(options: PDFOptions = {}): Promise<platform.BufferType> {
|
||||
const {
|
||||
scale = 1,
|
||||
displayHeaderFooter = false,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { EventEmitter } from '../../platform';
|
||||
import { CRSession, CRConnection } from '../crConnection';
|
||||
import { debugError } from '../../helper';
|
||||
import { Protocol } from '../protocol';
|
||||
|
|
|
|||
11
src/dom.ts
11
src/dom.ts
|
|
@ -23,10 +23,7 @@ import * as zsSelectorEngineSource from './generated/zsSelectorEngineSource';
|
|||
import { assert, helper, debugError } from './helper';
|
||||
import Injected from './injected/injected';
|
||||
import { Page } from './page';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const readFileAsync = helper.promisify(fs.readFile);
|
||||
import * as platform from './platform';
|
||||
|
||||
export class FrameExecutionContext extends js.ExecutionContext {
|
||||
readonly frame: frames.Frame;
|
||||
|
|
@ -409,9 +406,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
const filePayloads = await Promise.all(files.map(async item => {
|
||||
if (typeof item === 'string') {
|
||||
const file: types.FilePayload = {
|
||||
name: path.basename(item),
|
||||
name: platform.basename(item),
|
||||
type: 'application/octet-stream',
|
||||
data: (await readFileAsync(item)).toString('base64')
|
||||
data: await platform.readFileAsync(item, 'base64')
|
||||
};
|
||||
return file;
|
||||
}
|
||||
|
|
@ -445,7 +442,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
return this._page._delegate.getBoundingBox(this);
|
||||
}
|
||||
|
||||
async screenshot(options?: types.ElementScreenshotOptions): Promise<string | Buffer> {
|
||||
async screenshot(options?: types.ElementScreenshotOptions): Promise<string | platform.BufferType> {
|
||||
return this._page._screenshotter.screenshotElement(this, options);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@
|
|||
*/
|
||||
|
||||
import {assert} from '../helper';
|
||||
import {EventEmitter} from 'events';
|
||||
import * as debug from 'debug';
|
||||
import * as platform from '../platform';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import { Protocol } from './protocol';
|
||||
const debugProtocol = debug('playwright:protocol');
|
||||
|
||||
const debugProtocol = platform.debug('playwright:protocol');
|
||||
|
||||
export const ConnectionEvents = {
|
||||
Disconnected: Symbol('Disconnected'),
|
||||
};
|
||||
|
||||
export class FFConnection extends EventEmitter {
|
||||
export class FFConnection extends platform.EventEmitter {
|
||||
private _lastId: number;
|
||||
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
||||
private _transport: ConnectionTransport;
|
||||
|
|
@ -131,7 +131,7 @@ export const FFSessionEvents = {
|
|||
Disconnected: Symbol('Disconnected')
|
||||
};
|
||||
|
||||
export class FFSession extends EventEmitter {
|
||||
export class FFSession extends platform.EventEmitter {
|
||||
_connection: FFConnection;
|
||||
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
||||
private _targetType: string;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { FFSession } from './ffConnection';
|
|||
import { Page } from '../page';
|
||||
import * as network from '../network';
|
||||
import * as frames from '../frames';
|
||||
import * as platform from '../platform';
|
||||
|
||||
export class FFNetworkManager {
|
||||
private _session: FFSession;
|
||||
|
|
@ -76,7 +77,7 @@ export class FFNetworkManager {
|
|||
});
|
||||
if (response.evicted)
|
||||
throw new Error(`Response body for ${request.request.method()} ${request.request.url()} was evicted!`);
|
||||
return Buffer.from(response.base64body, 'base64');
|
||||
return platform.Buffer.from(response.base64body, 'base64');
|
||||
};
|
||||
const headers: network.Headers = {};
|
||||
for (const {name, value} of event.headers)
|
||||
|
|
@ -168,7 +169,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
});
|
||||
}
|
||||
|
||||
async fulfill(response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) {
|
||||
async fulfill(response: { status: number; headers: network.Headers; contentType: string; body: (string | platform.BufferType); }) {
|
||||
throw new Error('Fulfill is not supported in Firefox');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { getAccessibilityTree } from './ffAccessibility';
|
|||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import * as accessibility from '../accessibility';
|
||||
import * as platform from '../platform';
|
||||
|
||||
export class FFPage implements PageDelegate {
|
||||
readonly rawMouse: RawMouseImpl;
|
||||
|
|
@ -272,13 +273,13 @@ export class FFPage implements PageDelegate {
|
|||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions): Promise<Buffer> {
|
||||
async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions): Promise<platform.BufferType> {
|
||||
const { data } = await this._session.send('Page.screenshot', {
|
||||
mimeType: ('image/' + format) as ('image/png' | 'image/jpeg'),
|
||||
fullPage: options.fullPage,
|
||||
clip: options.clip,
|
||||
});
|
||||
return Buffer.from(data, 'base64');
|
||||
return platform.Buffer.from(data, 'base64');
|
||||
}
|
||||
|
||||
async resetViewport(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
|
||||
import * as types from './types';
|
||||
import * as fs from 'fs';
|
||||
import * as js from './javascript';
|
||||
import * as dom from './dom';
|
||||
import * as network from './network';
|
||||
|
|
@ -26,8 +25,7 @@ import { TimeoutError } from './errors';
|
|||
import { Events } from './events';
|
||||
import { Page } from './page';
|
||||
import { ConsoleMessage } from './console';
|
||||
|
||||
const readFileAsync = helper.promisify(fs.readFile);
|
||||
import * as platform from './platform';
|
||||
|
||||
type ContextType = 'main' | 'utility';
|
||||
type ContextData = {
|
||||
|
|
@ -507,7 +505,7 @@ export class Frame {
|
|||
if (url !== null)
|
||||
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement();
|
||||
if (path !== null) {
|
||||
let contents = await readFileAsync(path, 'utf8');
|
||||
let contents = await platform.readFileAsync(path, 'utf8');
|
||||
contents += '//# sourceURL=' + path.replace(/\n/g, '');
|
||||
return (await context.evaluateHandle(addScriptContent, contents, type)).asElement();
|
||||
}
|
||||
|
|
@ -557,7 +555,7 @@ export class Frame {
|
|||
return (await context.evaluateHandle(addStyleUrl, url)).asElement();
|
||||
|
||||
if (path !== null) {
|
||||
let contents = await readFileAsync(path, 'utf8');
|
||||
let contents = await platform.readFileAsync(path, 'utf8');
|
||||
contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
|
||||
return (await context.evaluateHandle(addStyleContent, contents)).asElement();
|
||||
}
|
||||
|
|
@ -909,7 +907,7 @@ class LifecycleWatcher {
|
|||
}
|
||||
|
||||
_urlMatches(urlString: string): boolean {
|
||||
return !this._urlMatch || helper.urlMatches(urlString, this._urlMatch);
|
||||
return !this._urlMatch || platform.urlMatches(urlString, this._urlMatch);
|
||||
}
|
||||
|
||||
setExpectedDocumentId(documentId: string, url: string) {
|
||||
|
|
|
|||
|
|
@ -15,15 +15,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as debug from 'debug';
|
||||
import * as types from './types';
|
||||
import * as kurl from 'url';
|
||||
import { TimeoutError } from './errors';
|
||||
import * as platform from './platform';
|
||||
|
||||
export const debugError = debug(`playwright:error`);
|
||||
export const debugError = platform.debug(`playwright:error`);
|
||||
|
||||
export type RegisteredListener = {
|
||||
emitter: NodeJS.EventEmitter;
|
||||
emitter: platform.EventEmitterType;
|
||||
eventName: (string | symbol);
|
||||
handler: (...args: any[]) => void;
|
||||
};
|
||||
|
|
@ -63,7 +61,7 @@ class Helper {
|
|||
}
|
||||
|
||||
static addEventListener(
|
||||
emitter: NodeJS.EventEmitter,
|
||||
emitter: platform.EventEmitterType,
|
||||
eventName: (string | symbol),
|
||||
handler: (...args: any[]) => void): RegisteredListener {
|
||||
emitter.on(eventName, handler);
|
||||
|
|
@ -71,7 +69,7 @@ class Helper {
|
|||
}
|
||||
|
||||
static removeEventListeners(listeners: Array<{
|
||||
emitter: NodeJS.EventEmitter;
|
||||
emitter: platform.EventEmitterType;
|
||||
eventName: (string | symbol);
|
||||
handler: (...args: any[]) => void;
|
||||
}>) {
|
||||
|
|
@ -88,24 +86,8 @@ class Helper {
|
|||
return typeof obj === 'number' || obj instanceof Number;
|
||||
}
|
||||
|
||||
static promisify(nodeFunction: Function): Function {
|
||||
function promisified(...args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function callback(err, ...result) {
|
||||
if (err)
|
||||
return reject(err);
|
||||
if (result.length === 1)
|
||||
return resolve(result[0]);
|
||||
return resolve(result);
|
||||
}
|
||||
nodeFunction.call(null, ...args, callback);
|
||||
});
|
||||
}
|
||||
return promisified;
|
||||
}
|
||||
|
||||
static async waitForEvent(
|
||||
emitter: NodeJS.EventEmitter,
|
||||
emitter: platform.EventEmitterType,
|
||||
eventName: (string | symbol),
|
||||
predicate: Function,
|
||||
timeout: number,
|
||||
|
|
@ -159,22 +141,6 @@ class Helper {
|
|||
clearTimeout(timeoutTimer);
|
||||
}
|
||||
}
|
||||
|
||||
static urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
|
||||
if (match === undefined)
|
||||
return true;
|
||||
if (typeof match === 'string')
|
||||
return match === urlString;
|
||||
if (match instanceof RegExp)
|
||||
return match.test(urlString);
|
||||
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
|
||||
|
||||
try {
|
||||
return match(new kurl.URL(urlString));
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function assert(value: any, message?: string) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import * as frames from './frames';
|
||||
import { assert } from './helper';
|
||||
import * as platform from './platform';
|
||||
|
||||
export type NetworkCookie = {
|
||||
name: string,
|
||||
|
|
@ -202,7 +203,7 @@ export class Request {
|
|||
await this._delegate.abort(errorCode);
|
||||
}
|
||||
|
||||
async fulfill(response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) { // Mocking responses for dataURL requests is not currently supported.
|
||||
async fulfill(response: { status: number; headers: Headers; contentType: string; body: (string | platform.BufferType); }) { // Mocking responses for dataURL requests is not currently supported.
|
||||
if (this.url().startsWith('data:'))
|
||||
return;
|
||||
assert(this._delegate, 'Request Interception is not enabled!');
|
||||
|
|
@ -226,11 +227,11 @@ export type RemoteAddress = {
|
|||
port: number,
|
||||
};
|
||||
|
||||
type GetResponseBodyCallback = () => Promise<Buffer>;
|
||||
type GetResponseBodyCallback = () => Promise<platform.BufferType>;
|
||||
|
||||
export class Response {
|
||||
private _request: Request;
|
||||
private _contentPromise: Promise<Buffer> | null = null;
|
||||
private _contentPromise: Promise<platform.BufferType> | null = null;
|
||||
_finishedPromise: Promise<Error | null>;
|
||||
private _finishedPromiseCallback: any;
|
||||
private _remoteAddress: RemoteAddress;
|
||||
|
|
@ -282,7 +283,7 @@ export class Response {
|
|||
return this._headers;
|
||||
}
|
||||
|
||||
buffer(): Promise<Buffer> {
|
||||
buffer(): Promise<platform.BufferType> {
|
||||
if (!this._contentPromise) {
|
||||
this._contentPromise = this._finishedPromise.then(async error => {
|
||||
if (error)
|
||||
|
|
@ -314,8 +315,8 @@ export class Response {
|
|||
|
||||
export interface RequestDelegate {
|
||||
abort(errorCode: string): Promise<void>;
|
||||
fulfill(response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }): Promise<void>;
|
||||
continue(overrides: { url?: string; method?: string; postData?: string; headers?: { [key: string]: string; }; }): Promise<void>;
|
||||
fulfill(response: { status: number; headers: Headers; contentType: string; body: (string | platform.BufferType); }): Promise<void>;
|
||||
continue(overrides: { url?: string; method?: string; postData?: string; headers?: Headers; }): Promise<void>;
|
||||
}
|
||||
|
||||
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
||||
|
|
|
|||
12
src/page.ts
12
src/page.ts
|
|
@ -15,7 +15,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import * as dom from './dom';
|
||||
import * as frames from './frames';
|
||||
import { assert, debugError, helper } from './helper';
|
||||
|
|
@ -30,6 +29,7 @@ import { BrowserContext } from './browserContext';
|
|||
import { ConsoleMessage, ConsoleMessageLocation } from './console';
|
||||
import Injected from './injected/injected';
|
||||
import * as accessibility from './accessibility';
|
||||
import * as platform from './platform';
|
||||
|
||||
export interface PageDelegate {
|
||||
readonly rawMouse: input.RawMouse;
|
||||
|
|
@ -56,7 +56,7 @@ export interface PageDelegate {
|
|||
getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null>;
|
||||
canScreenshotOutsideViewport(): boolean;
|
||||
setBackgroundColor(color?: { r: number; g: number; b: number; a: number; }): Promise<void>;
|
||||
takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<Buffer>;
|
||||
takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType>;
|
||||
resetViewport(oldSize: types.Size): Promise<void>;
|
||||
|
||||
isElementHandle(remoteObject: any): boolean;
|
||||
|
|
@ -87,7 +87,7 @@ export type FileChooser = {
|
|||
multiple: boolean
|
||||
};
|
||||
|
||||
export class Page extends EventEmitter {
|
||||
export class Page extends platform.EventEmitter {
|
||||
private _closed = false;
|
||||
private _closedCallback: () => void;
|
||||
private _closedPromise: Promise<void>;
|
||||
|
|
@ -336,7 +336,7 @@ export class Page extends EventEmitter {
|
|||
const { timeout = this._timeoutSettings.timeout() } = options;
|
||||
return helper.waitForEvent(this, Events.Page.Request, (request: network.Request) => {
|
||||
if (helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp)
|
||||
return helper.urlMatches(request.url(), urlOrPredicate);
|
||||
return platform.urlMatches(request.url(), urlOrPredicate);
|
||||
return urlOrPredicate(request);
|
||||
}, timeout, this._disconnectedPromise);
|
||||
}
|
||||
|
|
@ -345,7 +345,7 @@ export class Page extends EventEmitter {
|
|||
const { timeout = this._timeoutSettings.timeout() } = options;
|
||||
return helper.waitForEvent(this, Events.Page.Response, (response: network.Response) => {
|
||||
if (helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp)
|
||||
return helper.urlMatches(response.url(), urlOrPredicate);
|
||||
return platform.urlMatches(response.url(), urlOrPredicate);
|
||||
return urlOrPredicate(response);
|
||||
}, timeout, this._disconnectedPromise);
|
||||
}
|
||||
|
|
@ -430,7 +430,7 @@ export class Page extends EventEmitter {
|
|||
await this._delegate.authenticate(credentials);
|
||||
}
|
||||
|
||||
async screenshot(options?: types.ScreenshotOptions): Promise<Buffer> {
|
||||
async screenshot(options?: types.ScreenshotOptions): Promise<platform.BufferType> {
|
||||
return this._screenshotter.screenshotPage(options);
|
||||
}
|
||||
|
||||
|
|
|
|||
221
src/platform.ts
Normal file
221
src/platform.ts
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as nodeEvents from 'events';
|
||||
import * as nodeFS from 'fs';
|
||||
import * as nodePath from 'path';
|
||||
import * as nodeDebug from 'debug';
|
||||
import * as nodeBuffer from 'buffer';
|
||||
import * as mime from 'mime';
|
||||
import * as jpeg from 'jpeg-js';
|
||||
import * as png from 'pngjs';
|
||||
|
||||
import { assert, helper } from './helper';
|
||||
import * as types from './types';
|
||||
|
||||
export const isNode = typeof process === 'object' && !!process && typeof process.versions === 'object' && !!process.versions && !!process.versions.node;
|
||||
|
||||
export function promisify(nodeFunction: Function): Function {
|
||||
assert(isNode);
|
||||
function promisified(...args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function callback(err, ...result) {
|
||||
if (err)
|
||||
return reject(err);
|
||||
if (result.length === 1)
|
||||
return resolve(result[0]);
|
||||
return resolve(result);
|
||||
}
|
||||
nodeFunction.call(null, ...args, callback);
|
||||
});
|
||||
}
|
||||
return promisified;
|
||||
}
|
||||
|
||||
type Listener = (...args: any[]) => void;
|
||||
export const EventEmitter: typeof nodeEvents.EventEmitter = isNode ? nodeEvents.EventEmitter : (
|
||||
class EventEmitterImpl {
|
||||
private _deliveryQueue?: {listener: Listener, args: any[]}[];
|
||||
private _listeners = new Map<string | symbol, Set<Listener>>();
|
||||
|
||||
addListener(event: string | symbol, listener: Listener): this {
|
||||
let set = this._listeners.get(event);
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
this._listeners.set(event, set);
|
||||
}
|
||||
set.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
on(event: string | symbol, listener: Listener): this {
|
||||
return this.addListener(event, listener);
|
||||
}
|
||||
|
||||
once(event: string | symbol, listener: Listener): this {
|
||||
const wrapped = (...args: any[]) => {
|
||||
this.removeListener(event, wrapped);
|
||||
listener(...args);
|
||||
};
|
||||
return this.on(event, wrapped);
|
||||
}
|
||||
|
||||
removeListener(event: string | symbol, listener: Listener): this {
|
||||
const set = this._listeners.get(event);
|
||||
if (set)
|
||||
set.delete(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
emit(event: string | symbol, ...args: any[]): boolean {
|
||||
const set = this._listeners.get(event);
|
||||
if (!set || !set.size)
|
||||
return true;
|
||||
const dispatch = !this._deliveryQueue;
|
||||
if (!this._deliveryQueue)
|
||||
this._deliveryQueue = [];
|
||||
for (const listener of set)
|
||||
this._deliveryQueue.push({ listener, args });
|
||||
if (!dispatch)
|
||||
return true;
|
||||
for (let index = 0; index < this._deliveryQueue.length; index++) {
|
||||
const { listener, args } = this._deliveryQueue[index];
|
||||
listener(...args);
|
||||
}
|
||||
this._deliveryQueue = undefined;
|
||||
return true;
|
||||
}
|
||||
|
||||
listenerCount(event: string | symbol): number {
|
||||
const set = this._listeners.get(event);
|
||||
return set ? set.size : 0;
|
||||
}
|
||||
}
|
||||
) as any as typeof nodeEvents.EventEmitter;
|
||||
export type EventEmitterType = nodeEvents.EventEmitter;
|
||||
|
||||
type DebugType = typeof nodeDebug;
|
||||
export const debug: DebugType = isNode ? nodeDebug : (
|
||||
function debug(namespace: string) {
|
||||
return () => {};
|
||||
}
|
||||
) as any as DebugType;
|
||||
|
||||
export const Buffer: typeof nodeBuffer.Buffer = isNode ? nodeBuffer.Buffer : (
|
||||
class BufferImpl {
|
||||
readonly data: ArrayBuffer;
|
||||
|
||||
static from(data: string | ArrayBuffer, encoding: string = 'utf8'): BufferImpl {
|
||||
return new BufferImpl(data, encoding);
|
||||
}
|
||||
|
||||
static byteLength(buffer: BufferImpl | string, encoding: string = 'utf8'): number {
|
||||
if (helper.isString(buffer))
|
||||
buffer = new BufferImpl(buffer, encoding);
|
||||
return buffer.data.byteLength;
|
||||
}
|
||||
|
||||
static concat(buffers: BufferImpl[]): BufferImpl {
|
||||
if (!buffers.length)
|
||||
return new BufferImpl(new ArrayBuffer(0));
|
||||
if (buffers.length === 1)
|
||||
return buffers[0];
|
||||
const view = new Uint8Array(buffers.reduce((a, b) => a + b.data.byteLength, 0));
|
||||
let offset = 0;
|
||||
for (const buffer of buffers) {
|
||||
view.set(new Uint8Array(buffer.data), offset);
|
||||
offset += buffer.data.byteLength;
|
||||
}
|
||||
return new BufferImpl(view.buffer);
|
||||
}
|
||||
|
||||
constructor(data: string | ArrayBuffer, encoding: string = 'utf8') {
|
||||
if (data instanceof ArrayBuffer) {
|
||||
this.data = data;
|
||||
} else {
|
||||
if (encoding === 'base64') {
|
||||
const binary = atob(data);
|
||||
this.data = new ArrayBuffer(binary.length * 2);
|
||||
const view = new Uint16Array(this.data);
|
||||
for (let i = 0; i < binary.length; i++)
|
||||
view[i] = binary.charCodeAt(i);
|
||||
} else if (encoding === 'utf8') {
|
||||
const encoder = new TextEncoder();
|
||||
this.data = encoder.encode(data).buffer;
|
||||
} else {
|
||||
throw new Error('Unsupported encoding "' + encoding + '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toString(encoding: string = 'utf8'): string {
|
||||
if (encoding === 'base64') {
|
||||
const binary = String.fromCharCode(...new Uint16Array(this.data));
|
||||
return btoa(binary);
|
||||
}
|
||||
const decoder = new TextDecoder(encoding, { fatal: true });
|
||||
return decoder.decode(this.data);
|
||||
}
|
||||
}
|
||||
) as any as typeof nodeBuffer.Buffer;
|
||||
export type BufferType = NodeBuffer;
|
||||
|
||||
function assertFileAccess() {
|
||||
assert(isNode, 'Working with filesystem using "path" is only supported in Node.js');
|
||||
}
|
||||
|
||||
export async function readFileAsync(file: string, encoding: string): Promise<string> {
|
||||
assertFileAccess();
|
||||
return await promisify(nodeFS.readFile)(file, encoding);
|
||||
}
|
||||
|
||||
export async function writeFileAsync(file: string, data: any) {
|
||||
assertFileAccess();
|
||||
return await promisify(nodeFS.writeFile)(file, data);
|
||||
}
|
||||
|
||||
export function basename(file: string): string {
|
||||
assertFileAccess();
|
||||
return nodePath.basename(file);
|
||||
}
|
||||
|
||||
export async function openFdAsync(file: string, flags: string): Promise<number> {
|
||||
assertFileAccess();
|
||||
return await promisify(nodeFS.open)(file, flags);
|
||||
}
|
||||
|
||||
export async function writeFdAsync(fd: number, buffer: Buffer): Promise<void> {
|
||||
assertFileAccess();
|
||||
return await promisify(nodeFS.write)(fd, buffer);
|
||||
}
|
||||
|
||||
export async function closeFdAsync(fd: number): Promise<void> {
|
||||
assertFileAccess();
|
||||
return await promisify(nodeFS.close)(fd);
|
||||
}
|
||||
|
||||
export function getMimeType(file: string): string {
|
||||
assertFileAccess();
|
||||
return mime.getType(file);
|
||||
}
|
||||
|
||||
export function urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
|
||||
if (match === undefined)
|
||||
return true;
|
||||
if (typeof match === 'string')
|
||||
return match === urlString;
|
||||
if (match instanceof RegExp)
|
||||
return match.test(urlString);
|
||||
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
|
||||
|
||||
try {
|
||||
return match(new URL(urlString));
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function pngToJpeg(buffer: Buffer): Buffer {
|
||||
assert(isNode, 'Converting from png to jpeg is only supported in Node.js');
|
||||
return jpeg.encode(png.PNG.sync.read(buffer)).data;
|
||||
}
|
||||
|
|
@ -21,8 +21,9 @@ import * as removeFolder from 'rimraf';
|
|||
import { helper } from './helper';
|
||||
import * as readline from 'readline';
|
||||
import { TimeoutError } from './errors';
|
||||
import * as platform from './platform';
|
||||
|
||||
const removeFolderAsync = helper.promisify(removeFolder);
|
||||
const removeFolderAsync = platform.promisify(removeFolder);
|
||||
|
||||
export type LaunchProcessOptions = {
|
||||
executablePath: string,
|
||||
|
|
|
|||
|
|
@ -15,14 +15,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as mime from 'mime';
|
||||
import * as dom from './dom';
|
||||
import { assert, helper } from './helper';
|
||||
import { assert } from './helper';
|
||||
import * as types from './types';
|
||||
import { Page } from './page';
|
||||
|
||||
const writeFileAsync = helper.promisify(fs.writeFile);
|
||||
import * as platform from './platform';
|
||||
|
||||
export class Screenshotter {
|
||||
private _queue = new TaskQueue();
|
||||
|
|
@ -39,7 +36,7 @@ export class Screenshotter {
|
|||
}
|
||||
}
|
||||
|
||||
async screenshotPage(options: types.ScreenshotOptions = {}): Promise<Buffer> {
|
||||
async screenshotPage(options: types.ScreenshotOptions = {}): Promise<platform.BufferType> {
|
||||
const format = validateScreeshotOptions(options);
|
||||
return this._queue.postTask(async () => {
|
||||
let overridenViewport: types.Viewport | undefined;
|
||||
|
|
@ -82,7 +79,7 @@ export class Screenshotter {
|
|||
});
|
||||
}
|
||||
|
||||
async screenshotElement(handle: dom.ElementHandle, options: types.ElementScreenshotOptions = {}): Promise<Buffer> {
|
||||
async screenshotElement(handle: dom.ElementHandle, options: types.ElementScreenshotOptions = {}): Promise<platform.BufferType> {
|
||||
const format = validateScreeshotOptions(options);
|
||||
const rewrittenOptions: types.ScreenshotOptions = { ...options };
|
||||
return this._queue.postTask(async () => {
|
||||
|
|
@ -121,7 +118,7 @@ export class Screenshotter {
|
|||
});
|
||||
}
|
||||
|
||||
private async _screenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions, viewport: types.Viewport): Promise<Buffer> {
|
||||
private async _screenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType> {
|
||||
const shouldSetDefaultBackground = options.omitBackground && format === 'png';
|
||||
if (shouldSetDefaultBackground)
|
||||
await this._page._delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0});
|
||||
|
|
@ -129,7 +126,7 @@ export class Screenshotter {
|
|||
if (shouldSetDefaultBackground)
|
||||
await this._page._delegate.setBackgroundColor();
|
||||
if (options.path)
|
||||
await writeFileAsync(options.path, buffer);
|
||||
await platform.writeFileAsync(options.path, buffer);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
|
@ -168,7 +165,7 @@ function validateScreeshotOptions(options: types.ScreenshotOptions): 'png' | 'jp
|
|||
assert(options.type === 'png' || options.type === 'jpeg', 'Unknown options.type value: ' + options.type);
|
||||
format = options.type;
|
||||
} else if (options.path) {
|
||||
const mimeType = mime.getType(options.path);
|
||||
const mimeType = platform.getMimeType(options.path);
|
||||
if (mimeType === 'image/png')
|
||||
format = 'png';
|
||||
else if (mimeType === 'image/jpeg')
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
import * as js from './javascript';
|
||||
import * as dom from './dom';
|
||||
import * as kurl from 'url';
|
||||
|
||||
type Boxed<Args extends any[]> = { [Index in keyof Args]: Args[Index] | js.JSHandle<Args[Index]> };
|
||||
type PageFunction<Args extends any[], R = any> = string | ((...args: Args) => R | Promise<R>);
|
||||
|
|
@ -63,7 +62,7 @@ export type Viewport = {
|
|||
hasTouch?: boolean;
|
||||
};
|
||||
|
||||
export type URLMatch = string | RegExp | ((url: kurl.URL) => boolean);
|
||||
export type URLMatch = string | RegExp | ((url: URL) => boolean);
|
||||
|
||||
export type Credentials = {
|
||||
username: string;
|
||||
|
|
|
|||
|
|
@ -16,13 +16,12 @@
|
|||
*/
|
||||
|
||||
import { assert } from '../helper';
|
||||
import * as debug from 'debug';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as platform from '../platform';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import { Protocol } from './protocol';
|
||||
|
||||
const debugProtocol = debug('playwright:protocol');
|
||||
const debugWrappedMessage = require('debug')('wrapped');
|
||||
const debugProtocol = platform.debug('playwright:protocol');
|
||||
const debugWrappedMessage = platform.debug('wrapped');
|
||||
|
||||
export const WKConnectionEvents = {
|
||||
PageProxyCreated: Symbol('ConnectionEvents.PageProxyCreated'),
|
||||
|
|
@ -35,7 +34,7 @@ export const WKPageProxySessionEvents = {
|
|||
DidCommitProvisionalTarget: Symbol('PageProxyEvents.DidCommitProvisionalTarget'),
|
||||
};
|
||||
|
||||
export class WKConnection extends EventEmitter {
|
||||
export class WKConnection extends platform.EventEmitter {
|
||||
private _lastId = 0;
|
||||
private readonly _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||
private readonly _transport: ConnectionTransport;
|
||||
|
|
@ -137,7 +136,7 @@ export const WKTargetSessionEvents = {
|
|||
Disconnected: Symbol('TargetSessionEvents.Disconnected')
|
||||
};
|
||||
|
||||
export class WKPageProxySession extends EventEmitter {
|
||||
export class WKPageProxySession extends platform.EventEmitter {
|
||||
_connection: WKConnection;
|
||||
private readonly _sessions = new Map<string, WKTargetSession>();
|
||||
private readonly _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||
|
|
@ -215,7 +214,7 @@ export class WKPageProxySession extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
export class WKTargetSession extends EventEmitter {
|
||||
export class WKTargetSession extends platform.EventEmitter {
|
||||
_pageProxySession: WKPageProxySession;
|
||||
private readonly _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||
private readonly _targetType: string;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { Protocol } from './protocol';
|
|||
import * as network from '../network';
|
||||
import * as frames from '../frames';
|
||||
import * as types from '../types';
|
||||
import * as platform from '../platform';
|
||||
|
||||
export class WKNetworkManager {
|
||||
private _session: WKTargetSession;
|
||||
|
|
@ -106,7 +107,7 @@ export class WKNetworkManager {
|
|||
const remoteAddress: network.RemoteAddress = { ip: '', port: 0 };
|
||||
const getResponseBody = async () => {
|
||||
const response = await this._session.send('Network.getResponseBody', { requestId: request._requestId });
|
||||
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||
return platform.Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||
};
|
||||
return new network.Response(request.request, responsePayload.status, responsePayload.statusText, headersObject(responsePayload.headers), remoteAddress, getResponseBody);
|
||||
}
|
||||
|
|
@ -208,7 +209,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
await this._session.send('Network.interceptAsError', { requestId: this._requestId, reason });
|
||||
}
|
||||
|
||||
async fulfill(response: { status: number; headers: {[key: string]: string}; contentType: string; body: (string | Buffer); }) {
|
||||
async fulfill(response: { status: number; headers: network.Headers; contentType: string; body: (string | platform.BufferType); }) {
|
||||
await this._interceptedPromise;
|
||||
|
||||
const base64Encoded = !!response.body && !helper.isString(response.body);
|
||||
|
|
@ -222,7 +223,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
if (response.contentType)
|
||||
responseHeaders['content-type'] = response.contentType;
|
||||
if (responseBody && !('content-length' in responseHeaders))
|
||||
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
||||
responseHeaders['content-length'] = String(platform.Buffer.byteLength(responseBody));
|
||||
|
||||
await this._session.send('Network.interceptWithResponse', {
|
||||
requestId: this._requestId,
|
||||
|
|
|
|||
|
|
@ -30,9 +30,8 @@ import { WKBrowser } from './wkBrowser';
|
|||
import { BrowserContext } from '../browserContext';
|
||||
import { RawMouseImpl, RawKeyboardImpl } from './wkInput';
|
||||
import * as types from '../types';
|
||||
import * as jpeg from 'jpeg-js';
|
||||
import { PNG } from 'pngjs';
|
||||
import * as accessibility from '../accessibility';
|
||||
import * as platform from '../platform';
|
||||
import { getAccessibilityTree } from './wkAccessibility';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
|
@ -386,13 +385,13 @@ export class WKPage implements PageDelegate {
|
|||
this._session.send('Page.setDefaultBackgroundColorOverride', { color });
|
||||
}
|
||||
|
||||
async takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<Buffer> {
|
||||
async takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType> {
|
||||
const rect = options.clip || { x: 0, y: 0, width: viewport.width, height: viewport.height };
|
||||
const result = await this._session.send('Page.snapshotRect', { ...rect, coordinateSystem: options.fullPage ? 'Page' : 'Viewport' });
|
||||
const prefix = 'data:image/png;base64,';
|
||||
let buffer = Buffer.from(result.dataURL.substr(prefix.length), 'base64');
|
||||
let buffer = platform.Buffer.from(result.dataURL.substr(prefix.length), 'base64');
|
||||
if (format === 'jpeg')
|
||||
buffer = jpeg.encode(PNG.sync.read(buffer)).data;
|
||||
buffer = platform.pngToJpeg(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const {helper} = require('../../lib/helper');
|
||||
const rmAsync = helper.promisify(require('rimraf'));
|
||||
const mkdtempAsync = helper.promisify(fs.mkdtemp);
|
||||
const utils = require('../utils');
|
||||
const rmAsync = utils.promisify(require('rimraf'));
|
||||
const mkdtempAsync = utils.promisify(fs.mkdtemp);
|
||||
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
|
||||
|
||||
module.exports.describe = function ({ testRunner, expect, defaultBrowserOptions, playwright }) {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const {helper} = require('../lib/helper');
|
||||
const rmAsync = helper.promisify(require('rimraf'));
|
||||
const mkdtempAsync = helper.promisify(fs.mkdtemp);
|
||||
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
|
||||
const utils = require('./utils');
|
||||
|
||||
module.exports.describe = function({testRunner, expect, defaultBrowserOptions, playwright, WEBKIT}) {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,22 @@ function traceAPICoverage(apiCoverage, events, className, classType) {
|
|||
}
|
||||
|
||||
const utils = module.exports = {
|
||||
promisify: function (nodeFunction) {
|
||||
function promisified(...args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function callback(err, ...result) {
|
||||
if (err)
|
||||
return reject(err);
|
||||
if (result.length === 1)
|
||||
return resolve(result[0]);
|
||||
return resolve(result);
|
||||
}
|
||||
nodeFunction.call(null, ...args, callback);
|
||||
});
|
||||
}
|
||||
return promisified;
|
||||
},
|
||||
|
||||
recordAPICoverage: function(testRunner, api, events) {
|
||||
const coverage = new Map();
|
||||
for (const [className, classType] of Object.entries(api))
|
||||
|
|
@ -96,7 +112,7 @@ const utils = module.exports = {
|
|||
for (const frame of page.frames()) {
|
||||
if (!frames.has(frame))
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,14 +14,6 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const {helper} = require('../../lib/helper');
|
||||
const rmAsync = helper.promisify(require('rimraf'));
|
||||
const mkdtempAsync = helper.promisify(fs.mkdtemp);
|
||||
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
|
||||
const utils = require('../utils');
|
||||
|
||||
module.exports.describe = function ({ testRunner, expect, playwright }) {
|
||||
const {describe, xdescribe, fdescribe} = testRunner;
|
||||
|
|
|
|||
|
|
@ -44,12 +44,12 @@ function checkSources(sources) {
|
|||
});
|
||||
const checker = program.getTypeChecker();
|
||||
const sourceFiles = program.getSourceFiles();
|
||||
const errors = [];
|
||||
/** @type {!Array<!Documentation.Class>} */
|
||||
const classes = [];
|
||||
/** @type {!Map<string, string>} */
|
||||
const inheritance = new Map();
|
||||
sourceFiles.filter(x => !x.fileName.includes('node_modules')).map(x => visit(x));
|
||||
const errors = [];
|
||||
const documentation = new Documentation(recreateClassesWithInheritance(classes, inheritance));
|
||||
|
||||
return {errors, documentation};
|
||||
|
|
@ -98,6 +98,30 @@ function checkSources(sources) {
|
|||
excludeClasses.add(className);
|
||||
}
|
||||
}
|
||||
if (!node.getSourceFile().fileName.endsWith('platform.ts')) {
|
||||
// Only relative imports.
|
||||
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
||||
const module = node.moduleSpecifier.text;
|
||||
if (!module.startsWith('.')) {
|
||||
const lac = ts.getLineAndCharacterOfPosition(node.getSourceFile(), node.moduleSpecifier.pos);
|
||||
errors.push(`Disallowed import "${module}" at ${node.getSourceFile().fileName}:${lac.line + 1}`);
|
||||
}
|
||||
}
|
||||
// No references to external types.
|
||||
if (ts.isTypeReferenceNode(node)) {
|
||||
const isPlatformReference = ts.isQualifiedName(node.typeName) && ts.isIdentifier(node.typeName.left) && node.typeName.left.escapedText === 'platform';
|
||||
if (!isPlatformReference) {
|
||||
const type = checker.getTypeAtLocation(node);
|
||||
if (type.symbol && type.symbol.valueDeclaration) {
|
||||
const source = type.symbol.valueDeclaration.getSourceFile();
|
||||
if (source.fileName.includes('@types')) {
|
||||
const lac = ts.getLineAndCharacterOfPosition(node.getSourceFile(), node.pos);
|
||||
errors.push(`Disallowed type reference "${type.symbol.escapedName}" at ${node.getSourceFile().fileName}:${lac.line + 1}:${lac.character + 1}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue