feat(fetch): support options in playwright._newRequest (#9061)
This commit is contained in:
parent
c673ef5330
commit
79eb7744bc
|
|
@ -83,6 +83,35 @@ class PlaywrightExample
|
|||
}
|
||||
```
|
||||
|
||||
## async method: Playwright._newRequest
|
||||
* langs: js
|
||||
- returns: <[FetchRequest]>
|
||||
|
||||
**experimental** Creates new instances of [FetchRequest].
|
||||
|
||||
### option: Playwright._newRequest.useragent = %%-context-option-useragent-%%
|
||||
|
||||
### option: Playwright._newRequest.extraHTTPHeaders = %%-context-option-extrahttpheaders-%%
|
||||
|
||||
### option: Playwright._newRequest.httpCredentials = %%-context-option-httpcredentials-%%
|
||||
|
||||
### option: Playwright._newRequest.proxy = %%-browser-option-proxy-%%
|
||||
|
||||
### option: Playwright._newRequest.timeout
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds to wait for the response. Defaults to
|
||||
`30000` (30 seconds). Pass `0` to disable timeout.
|
||||
|
||||
### option: Playwright._newRequest.ignoreHTTPSErrors = %%-context-option-ignorehttpserrors-%%
|
||||
|
||||
### option: Playwright._newRequest.baseURL
|
||||
- `baseURL` <[string]>
|
||||
|
||||
When using [`method: FetchRequest.get`], [`method: FetchRequest.post`], [`method: FetchRequest.fetch`] it takes the base URL in consideration by using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the corresponding URL. Examples:
|
||||
* baseURL: `http://localhost:3000` and sending rquest to `/bar.html` results in `http://localhost:3000/bar.html`
|
||||
* baseURL: `http://localhost:3000/foo/` and sending rquest to `./bar.html` results in `http://localhost:3000/foo/bar.html`
|
||||
|
||||
## property: Playwright.chromium
|
||||
- type: <[BrowserType]>
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,14 @@ import util from 'util';
|
|||
import * as channels from '../protocol/channels';
|
||||
import { TimeoutError } from '../utils/errors';
|
||||
import { createSocket } from '../utils/netUtils';
|
||||
import { headersObjectToArray } from '../utils/utils';
|
||||
import { Android } from './android';
|
||||
import { BrowserType } from './browserType';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Electron } from './electron';
|
||||
import { FetchRequest } from './fetch';
|
||||
import { Selectors, SelectorsOwner, sharedSelectors } from './selectors';
|
||||
import { Size } from './types';
|
||||
import { NewRequestOptions, Size } from './types';
|
||||
const dnsLookupAsync = util.promisify(dns.lookup);
|
||||
|
||||
type DeviceDescriptor = {
|
||||
|
|
@ -69,9 +70,12 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel, channel
|
|||
this.selectors._addChannel(this._selectorsOwner);
|
||||
}
|
||||
|
||||
async _newRequest(options?: {}): Promise<FetchRequest> {
|
||||
async _newRequest(options: NewRequestOptions = {}): Promise<FetchRequest> {
|
||||
return await this._wrapApiCall(async (channel: channels.PlaywrightChannel) => {
|
||||
return FetchRequest.from((await channel.newRequest({})).request);
|
||||
return FetchRequest.from((await channel.newRequest({
|
||||
...options,
|
||||
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined
|
||||
})).request);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import * as channels from '../protocol/channels';
|
||||
import type { Size } from '../common/types';
|
||||
export { Size, Point, Rect, Quad, URLMatch, TimeoutOptions, HeadersArray } from '../common/types';
|
||||
export { Size, Point, Rect, Quad, URLMatch, TimeoutOptions, HeadersArray, NewRequestOptions } from '../common/types';
|
||||
|
||||
type LoggerSeverity = 'verbose' | 'info' | 'warning' | 'error';
|
||||
export interface Logger {
|
||||
|
|
|
|||
|
|
@ -22,3 +22,20 @@ export type URLMatch = string | RegExp | ((url: URL) => boolean);
|
|||
export type TimeoutOptions = { timeout?: number };
|
||||
export type NameValue = { name: string, value: string };
|
||||
export type HeadersArray = NameValue[];
|
||||
export type NewRequestOptions = {
|
||||
baseURL?: string;
|
||||
extraHTTPHeaders?: { [key: string]: string; };
|
||||
httpCredentials?: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
proxy?: {
|
||||
server: string;
|
||||
bypass?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
};
|
||||
timeout?: number;
|
||||
userAgent?: string;
|
||||
};
|
||||
|
|
@ -16,18 +16,18 @@
|
|||
|
||||
import net, { AddressInfo } from 'net';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { GlobalFetchRequest } from '../server/fetch';
|
||||
import { Playwright } from '../server/playwright';
|
||||
import * as types from '../server/types';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { SocksConnection, SocksConnectionClient } from '../utils/socksProxy';
|
||||
import { createGuid } from '../utils/utils';
|
||||
import { AndroidDispatcher } from './androidDispatcher';
|
||||
import { BrowserTypeDispatcher } from './browserTypeDispatcher';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { ElectronDispatcher } from './electronDispatcher';
|
||||
import { SelectorsDispatcher } from './selectorsDispatcher';
|
||||
import * as types from '../server/types';
|
||||
import { SocksConnection, SocksConnectionClient } from '../utils/socksProxy';
|
||||
import { createGuid } from '../utils/utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { GlobalFetchRequest } from '../server/fetch';
|
||||
import { FetchRequestDispatcher } from './networkDispatchers';
|
||||
import { SelectorsDispatcher } from './selectorsDispatcher';
|
||||
|
||||
export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.PlaywrightInitializer, channels.PlaywrightEvents> implements channels.PlaywrightChannel {
|
||||
private _socksProxy: SocksProxy | undefined;
|
||||
|
|
@ -75,7 +75,7 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
|
|||
}
|
||||
|
||||
async newRequest(params: channels.PlaywrightNewRequestParams, metadata?: channels.Metadata): Promise<channels.PlaywrightNewRequestResult> {
|
||||
const request = new GlobalFetchRequest(this._object);
|
||||
const request = new GlobalFetchRequest(this._object, params);
|
||||
return { request: FetchRequestDispatcher.from(this._scope, request) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,10 +322,38 @@ export type PlaywrightSocksEndOptions = {
|
|||
};
|
||||
export type PlaywrightSocksEndResult = void;
|
||||
export type PlaywrightNewRequestParams = {
|
||||
baseURL?: string,
|
||||
userAgent?: string,
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
extraHTTPHeaders?: NameValue[],
|
||||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
},
|
||||
proxy?: {
|
||||
server: string,
|
||||
bypass?: string,
|
||||
username?: string,
|
||||
password?: string,
|
||||
},
|
||||
timeout?: number,
|
||||
};
|
||||
export type PlaywrightNewRequestOptions = {
|
||||
baseURL?: string,
|
||||
userAgent?: string,
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
extraHTTPHeaders?: NameValue[],
|
||||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
},
|
||||
proxy?: {
|
||||
server: string,
|
||||
bypass?: string,
|
||||
username?: string,
|
||||
password?: string,
|
||||
},
|
||||
timeout?: number,
|
||||
};
|
||||
export type PlaywrightNewRequestResult = {
|
||||
request: FetchRequestChannel,
|
||||
|
|
|
|||
|
|
@ -455,7 +455,26 @@ Playwright:
|
|||
|
||||
newRequest:
|
||||
parameters:
|
||||
baseURL: string?
|
||||
userAgent: string?
|
||||
ignoreHTTPSErrors: boolean?
|
||||
extraHTTPHeaders:
|
||||
type: array?
|
||||
items: NameValue
|
||||
httpCredentials:
|
||||
type: object?
|
||||
properties:
|
||||
username: string
|
||||
password: string
|
||||
proxy:
|
||||
type: object?
|
||||
properties:
|
||||
server: string
|
||||
bypass: string?
|
||||
username: string?
|
||||
password: string?
|
||||
timeout: number?
|
||||
|
||||
returns:
|
||||
request: FetchRequest
|
||||
|
||||
|
|
|
|||
|
|
@ -195,7 +195,21 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
uid: tString,
|
||||
});
|
||||
scheme.PlaywrightNewRequestParams = tObject({
|
||||
baseURL: tOptional(tString),
|
||||
userAgent: tOptional(tString),
|
||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||
extraHTTPHeaders: tOptional(tArray(tType('NameValue'))),
|
||||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
})),
|
||||
proxy: tOptional(tObject({
|
||||
server: tString,
|
||||
bypass: tOptional(tString),
|
||||
username: tOptional(tString),
|
||||
password: tOptional(tString),
|
||||
})),
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.SelectorsRegisterParams = tObject({
|
||||
name: tString,
|
||||
|
|
|
|||
|
|
@ -14,24 +14,25 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import url from 'url';
|
||||
import zlib from 'zlib';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import * as types from './types';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { pipeline, Readable, Transform } from 'stream';
|
||||
import url from 'url';
|
||||
import zlib from 'zlib';
|
||||
import { HTTPCredentials } from '../../types/types';
|
||||
import { NameValue, NewRequestOptions } from '../common/types';
|
||||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import { createGuid, isFilePayload, monotonicTime } from '../utils/utils';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { MultipartFormData } from './formData';
|
||||
import { SdkObject } from './instrumentation';
|
||||
import { Playwright } from './playwright';
|
||||
import * as types from './types';
|
||||
import { HeadersArray, ProxySettings } from './types';
|
||||
import { HTTPCredentials } from '../../types/types';
|
||||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import { MultipartFormData } from './formData';
|
||||
|
||||
|
||||
type FetchRequestOptions = {
|
||||
export type FetchRequestOptions = {
|
||||
userAgent: string;
|
||||
extraHTTPHeaders?: HeadersArray;
|
||||
httpCredentials?: HTTPCredentials;
|
||||
|
|
@ -336,8 +337,29 @@ export class BrowserContextFetchRequest extends FetchRequest {
|
|||
|
||||
|
||||
export class GlobalFetchRequest extends FetchRequest {
|
||||
constructor(playwright: Playwright) {
|
||||
private readonly _options: FetchRequestOptions;
|
||||
constructor(playwright: Playwright, options: Omit<NewRequestOptions, 'extraHTTPHeaders'> & { extraHTTPHeaders?: NameValue[] }) {
|
||||
super(playwright);
|
||||
const timeoutSettings = new TimeoutSettings();
|
||||
if (options.timeout !== undefined)
|
||||
timeoutSettings.setDefaultTimeout(options.timeout);
|
||||
const proxy = options.proxy;
|
||||
if (proxy?.server) {
|
||||
let url = proxy?.server.trim();
|
||||
if (!/^\w+:\/\//.test(url))
|
||||
url = 'http://' + url;
|
||||
proxy.server = url;
|
||||
}
|
||||
this._options = {
|
||||
baseURL: options.baseURL,
|
||||
userAgent: options.userAgent || '',
|
||||
extraHTTPHeaders: options.extraHTTPHeaders,
|
||||
ignoreHTTPSErrors: !!options.ignoreHTTPSErrors,
|
||||
httpCredentials: options.httpCredentials,
|
||||
proxy,
|
||||
timeoutSettings,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
|
|
@ -345,14 +367,7 @@ export class GlobalFetchRequest extends FetchRequest {
|
|||
}
|
||||
|
||||
_defaultOptions(): FetchRequestOptions {
|
||||
return {
|
||||
userAgent: '',
|
||||
extraHTTPHeaders: undefined,
|
||||
proxy: undefined,
|
||||
timeoutSettings: new TimeoutSettings(),
|
||||
ignoreHTTPSErrors: false,
|
||||
baseURL: undefined,
|
||||
};
|
||||
return this._options;
|
||||
}
|
||||
|
||||
async _addCookies(cookies: types.SetNetworkCookieParam[]): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -42,19 +42,6 @@ it.afterAll(() => {
|
|||
http.globalAgent = prevAgent;
|
||||
});
|
||||
|
||||
it('global get should work', async ({playwright, context, server}) => {
|
||||
const request = await playwright._newRequest();
|
||||
const response = await request.get(server.PREFIX + '/simple.json');
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(response.statusText()).toBe('OK');
|
||||
expect(response.ok()).toBeTruthy();
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.headers()['content-type']).toBe('application/json; charset=utf-8');
|
||||
expect(response.headersArray()).toContainEqual({ name: 'Content-Type', value: 'application/json; charset=utf-8' });
|
||||
expect(await response.text()).toBe('{"foo": "bar"}\n');
|
||||
});
|
||||
|
||||
it('get should work', async ({context, server}) => {
|
||||
const response = await context._request.get(server.PREFIX + '/simple.json');
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
|
|
@ -706,15 +693,6 @@ it('should dispose when context closes', async function({context, server}) {
|
|||
expect(error.message).toContain('Response has been disposed');
|
||||
});
|
||||
|
||||
it('should dispose global request', async function({playwright, context, server}) {
|
||||
const request = await playwright._newRequest();
|
||||
const response = await request.get(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({ foo: 'bar' });
|
||||
await request.dispose();
|
||||
const error = await response.body().catch(e => e);
|
||||
expect(error.message).toContain('Response has been disposed');
|
||||
});
|
||||
|
||||
it('should throw on invalid first argument', async function({context}) {
|
||||
const error = await context._request.get({} as any).catch(e => e);
|
||||
expect(error.message).toContain('First argument must be either URL string or Request');
|
||||
|
|
|
|||
145
tests/global-fetch.spec.ts
Normal file
145
tests/global-fetch.spec.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* 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 http from 'http';
|
||||
import { expect, playwrightTest as it } from './config/browserTest';
|
||||
|
||||
it.skip(({ mode }) => mode !== 'default');
|
||||
|
||||
let prevAgent: http.Agent;
|
||||
it.beforeAll(() => {
|
||||
prevAgent = http.globalAgent;
|
||||
http.globalAgent = new http.Agent({
|
||||
// @ts-expect-error
|
||||
lookup: (hostname, options, callback) => {
|
||||
if (hostname === 'localhost' || hostname.endsWith('playwright.dev'))
|
||||
callback(null, '127.0.0.1', 4);
|
||||
else
|
||||
throw new Error(`Failed to resolve hostname: ${hostname}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it.afterAll(() => {
|
||||
http.globalAgent = prevAgent;
|
||||
});
|
||||
|
||||
for (const method of ['get', 'post', 'fetch']) {
|
||||
it(`${method} should work`, async ({playwright, server}) => {
|
||||
const request = await playwright._newRequest();
|
||||
const response = await request[method](server.PREFIX + '/simple.json');
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(response.statusText()).toBe('OK');
|
||||
expect(response.ok()).toBeTruthy();
|
||||
expect(response.url()).toBe(server.PREFIX + '/simple.json');
|
||||
expect(response.headers()['content-type']).toBe('application/json; charset=utf-8');
|
||||
expect(response.headersArray()).toContainEqual({ name: 'Content-Type', value: 'application/json; charset=utf-8' });
|
||||
expect(await response.text()).toBe('{"foo": "bar"}\n');
|
||||
});
|
||||
|
||||
it(`should dispose global ${method} request`, async function({playwright, context, server}) {
|
||||
const request = await playwright._newRequest();
|
||||
const response = await request.get(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({ foo: 'bar' });
|
||||
await request.dispose();
|
||||
const error = await response.body().catch(e => e);
|
||||
expect(error.message).toContain('Response has been disposed');
|
||||
});
|
||||
}
|
||||
|
||||
it('should support global userAgent option', async ({playwright, server}) => {
|
||||
const request = await playwright._newRequest({ userAgent: 'My Agent'});
|
||||
const [serverRequest, response] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
request.get(server.EMPTY_PAGE)
|
||||
]);
|
||||
expect(response.ok()).toBeTruthy();
|
||||
expect(response.url()).toBe(server.EMPTY_PAGE);
|
||||
expect(serverRequest.headers['user-agent']).toBe('My Agent');
|
||||
});
|
||||
|
||||
it('should support global timeout option', async ({playwright, server}) => {
|
||||
const request = await playwright._newRequest({ timeout: 1});
|
||||
server.setRoute('/empty.html', (req, res) => {});
|
||||
const error = await request.get(server.EMPTY_PAGE).catch(e => e);
|
||||
expect(error.message).toContain('Request timed out after 1ms');
|
||||
});
|
||||
|
||||
it('should propagate extra http headers with redirects', async ({playwright, server}) => {
|
||||
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||
server.setRedirect('/b/c/redirect2', '/simple.json');
|
||||
const request = await playwright._newRequest({ extraHTTPHeaders: { 'My-Secret': 'Value' }});
|
||||
const [req1, req2, req3] = await Promise.all([
|
||||
server.waitForRequest('/a/redirect1'),
|
||||
server.waitForRequest('/b/c/redirect2'),
|
||||
server.waitForRequest('/simple.json'),
|
||||
request.get(`${server.PREFIX}/a/redirect1`),
|
||||
]);
|
||||
expect(req1.headers['my-secret']).toBe('Value');
|
||||
expect(req2.headers['my-secret']).toBe('Value');
|
||||
expect(req3.headers['my-secret']).toBe('Value');
|
||||
});
|
||||
|
||||
it('should support global httpCredentials option', async ({playwright, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const request1 = await playwright._newRequest();
|
||||
const response1 = await request1.get(server.EMPTY_PAGE);
|
||||
expect(response1.status()).toBe(401);
|
||||
await request1.dispose();
|
||||
|
||||
const request2 = await playwright._newRequest({ httpCredentials: { username: 'user', password: 'pass' }});
|
||||
const response2 = await request2.get(server.EMPTY_PAGE);
|
||||
expect(response2.status()).toBe(200);
|
||||
await request2.dispose();
|
||||
});
|
||||
|
||||
it('should return error with wrong credentials', async ({playwright, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const request = await playwright._newRequest({ httpCredentials: { username: 'user', password: 'wrong' }});
|
||||
const response2 = await request.get(server.EMPTY_PAGE);
|
||||
expect(response2.status()).toBe(401);
|
||||
});
|
||||
|
||||
it('should pass proxy credentials', async ({playwright, server, proxyServer}) => {
|
||||
proxyServer.forwardTo(server.PORT);
|
||||
let auth;
|
||||
proxyServer.setAuthHandler(req => {
|
||||
auth = req.headers['proxy-authorization'];
|
||||
return !!auth;
|
||||
});
|
||||
const request = await playwright._newRequest({
|
||||
proxy: { server: `localhost:${proxyServer.PORT}`, username: 'user', password: 'secret' }
|
||||
});
|
||||
const response = await request.get('http://non-existent.com/simple.json');
|
||||
expect(proxyServer.connectHosts).toContain('non-existent.com:80');
|
||||
expect(auth).toBe('Basic ' + Buffer.from('user:secret').toString('base64'));
|
||||
expect(await response.json()).toEqual({foo: 'bar'});
|
||||
await request.dispose();
|
||||
});
|
||||
|
||||
it('should support global ignoreHTTPSErrors option', async ({playwright, httpsServer}) => {
|
||||
const request = await playwright._newRequest({ ignoreHTTPSErrors: true });
|
||||
const response = await request.get(httpsServer.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
|
||||
it('should resolve url relative to gobal baseURL option', async ({playwright, server}) => {
|
||||
const request = await playwright._newRequest({ baseURL: server.PREFIX });
|
||||
const response = await request.get('/empty.html');
|
||||
expect(response.url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
99
types/types.d.ts
vendored
99
types/types.d.ts
vendored
|
|
@ -10360,7 +10360,6 @@ type AccessibilityNode = {
|
|||
children?: AccessibilityNode[];
|
||||
}
|
||||
|
||||
export const selectors: Selectors;
|
||||
export const devices: Devices & DeviceDescriptor[];
|
||||
|
||||
//@ts-ignore this will be any if electron is not installed
|
||||
|
|
@ -10669,12 +10668,8 @@ export type AndroidKey =
|
|||
'Copy' |
|
||||
'Paste';
|
||||
|
||||
export const chromium: BrowserType;
|
||||
export const firefox: BrowserType;
|
||||
export const webkit: BrowserType;
|
||||
export const _electron: Electron;
|
||||
export const _android: Android;
|
||||
export const _newRequest: () => Promise<FetchRequest>;
|
||||
|
||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||
export {};
|
||||
|
|
@ -13227,6 +13222,100 @@ export interface Mouse {
|
|||
wheel(deltaX: number, deltaY: number): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* **experimental** Creates new instances of [FetchRequest].
|
||||
* @param options
|
||||
*/
|
||||
export const _newRequest: (options?: {
|
||||
/**
|
||||
* When using
|
||||
* [fetchRequest.get(urlOrRequest[, options])](https://playwright.dev/docs/api/class-fetchrequest#fetch-request-get),
|
||||
* [fetchRequest.post(urlOrRequest[, options])](https://playwright.dev/docs/api/class-fetchrequest#fetch-request-post),
|
||||
* [fetchRequest.fetch(urlOrRequest[, options])](https://playwright.dev/docs/api/class-fetchrequest#fetch-request-fetch) it
|
||||
* takes the base URL in consideration by using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL)
|
||||
* constructor for building the corresponding URL. Examples:
|
||||
* - baseURL: `http://localhost:3000` and sending rquest to `/bar.html` results in `http://localhost:3000/bar.html`
|
||||
* - baseURL: `http://localhost:3000/foo/` and sending rquest to `./bar.html` results in
|
||||
* `http://localhost:3000/foo/bar.html`
|
||||
*/
|
||||
baseURL?: string;
|
||||
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
extraHTTPHeaders?: { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
*/
|
||||
httpCredentials?: {
|
||||
username: string;
|
||||
|
||||
password: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.
|
||||
*/
|
||||
ignoreHTTPSErrors?: boolean;
|
||||
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
proxy?: {
|
||||
/**
|
||||
* Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example `http://myproxy.com:3128` or
|
||||
* `socks5://myproxy.com:3128`. Short form `myproxy.com:3128` is considered an HTTP proxy.
|
||||
*/
|
||||
server: string;
|
||||
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example `".com, chromium.org, .domain.com"`.
|
||||
*/
|
||||
bypass?: string;
|
||||
|
||||
/**
|
||||
* Optional username to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
username?: string;
|
||||
|
||||
/**
|
||||
* Optional password to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
password?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the response. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* Specific user agent to use in this context.
|
||||
*/
|
||||
userAgent?: string;
|
||||
}) => Promise<FetchRequest>;
|
||||
|
||||
/**
|
||||
* This object can be used to launch or connect to Chromium, returning instances of [Browser].
|
||||
*/
|
||||
export const chromium: BrowserType;
|
||||
|
||||
/**
|
||||
* This object can be used to launch or connect to Firefox, returning instances of [Browser].
|
||||
*/
|
||||
export const firefox: BrowserType;
|
||||
|
||||
/**
|
||||
* Selectors can be used to install custom selector engines. See [Working with selectors](https://playwright.dev/docs/selectors) for more
|
||||
* information.
|
||||
*/
|
||||
export const selectors: Selectors;
|
||||
|
||||
/**
|
||||
* This object can be used to launch or connect to WebKit, returning instances of [Browser].
|
||||
*/
|
||||
export const webkit: BrowserType;
|
||||
/**
|
||||
* Whenever the page sends a request for a network resource the following sequence of events are emitted by [Page]:
|
||||
* - [page.on('request')](https://playwright.dev/docs/api/class-page#page-event-request) emitted when the request is
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ function listMethods(rootNames, apiFileName) {
|
|||
function shouldSkipMethodByName(className, methodName) {
|
||||
if (methodName === '_request' && (className === 'BrowserContext' || className === 'Page'))
|
||||
return false;
|
||||
if (methodName === '_newRequest' && className === 'Playwright')
|
||||
return false;
|
||||
if (methodName.startsWith('_') || methodName === 'T' || methodName === 'toString')
|
||||
return true;
|
||||
if (/** @type {any} */(EventEmitter).prototype.hasOwnProperty(methodName))
|
||||
|
|
|
|||
|
|
@ -110,11 +110,18 @@ class TypesGenerator {
|
|||
});
|
||||
|
||||
const classes = this.documentation.classesArray.filter(cls => !handledClasses.has(cls.name));
|
||||
{
|
||||
const playwright = this.documentation.classesArray.find(c => c.name === 'Playwright');
|
||||
playwright.membersArray = playwright.membersArray.filter(member => !['errors', 'devices'].includes(member.name));
|
||||
playwright.index();
|
||||
}
|
||||
return [
|
||||
`// This file is generated by ${__filename.substring(path.join(__dirname, '..', '..').length).split(path.sep).join(path.posix.sep)}`,
|
||||
overrides,
|
||||
'',
|
||||
docsOnlyClassMapping ? '' : classes.map(classDesc => this.classToString(classDesc)).join('\n'),
|
||||
docsOnlyClassMapping ? '' : classes.map(classDesc => {
|
||||
return (classDesc.name === 'Playwright') ? this.classBody(classDesc, true) : this.classToString(classDesc);
|
||||
}).join('\n'),
|
||||
this.objectDefinitionsToString(overrides),
|
||||
'',
|
||||
].join('\n');
|
||||
|
|
@ -215,21 +222,23 @@ class TypesGenerator {
|
|||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {boolean=} exportMembersAsGlobals
|
||||
*/
|
||||
classBody(classDesc) {
|
||||
const parts = [];
|
||||
classBody(classDesc, exportMembersAsGlobals) {
|
||||
let parts = [];
|
||||
const eventDescriptions = this.createEventDescriptions(classDesc);
|
||||
const commentForMethod = {
|
||||
off: 'Removes an event listener added by `on` or `addListener`.',
|
||||
removeListener: 'Removes an event listener added by `on` or `addListener`.',
|
||||
once: 'Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event.'
|
||||
}
|
||||
const indent = exportMembersAsGlobals ? '' : ' ';
|
||||
for (const method of ['on', 'once', 'addListener', 'removeListener', 'off']) {
|
||||
for (const {eventName, params, comment} of eventDescriptions) {
|
||||
if ((method === 'on' || method === 'addListener') && comment)
|
||||
parts.push(this.writeComment(comment, ' '));
|
||||
parts.push(this.writeComment(comment, indent));
|
||||
else
|
||||
parts.push(this.writeComment(commentForMethod[method], ' '));
|
||||
parts.push(this.writeComment(commentForMethod[method], indent));
|
||||
parts.push(` ${method}(event: '${eventName}', listener: (${params}) => void): this;\n`);
|
||||
}
|
||||
}
|
||||
|
|
@ -242,20 +251,24 @@ class TypesGenerator {
|
|||
const parts = [];
|
||||
for (const {eventName, params, comment, type} of eventDescriptions) {
|
||||
if (comment)
|
||||
parts.push(this.writeComment(comment, ' '));
|
||||
parts.push(this.writeComment(comment, indent));
|
||||
parts.push(` ${member.alias}(event: '${eventName}', optionsOrPredicate?: { predicate?: (${params}) => boolean | Promise<boolean>, timeout?: number } | ((${params}) => boolean | Promise<boolean>)): Promise<${type}>;\n`);
|
||||
}
|
||||
|
||||
return parts.join('\n');
|
||||
}
|
||||
const jsdoc = this.memberJSDOC(member, ' ');
|
||||
const args = this.argsFromMember(member, ' ', classDesc.name);
|
||||
let type = this.stringifyComplexType(member.type, ' ', classDesc.name, member.alias);
|
||||
const jsdoc = this.memberJSDOC(member, indent);
|
||||
const args = this.argsFromMember(member, indent, classDesc.name);
|
||||
let type = this.stringifyComplexType(member.type, indent, classDesc.name, member.alias);
|
||||
if (member.async)
|
||||
type = `Promise<${type}>`;
|
||||
// do this late, because we still want object definitions for overridden types
|
||||
if (!this.hasOwnMethod(classDesc, member.alias))
|
||||
return '';
|
||||
if (exportMembersAsGlobals) {
|
||||
const memberType = member.kind === 'method' ? `${args} => ${type}` : type;
|
||||
return `${jsdoc}${exportMembersAsGlobals ? 'export const ' : ''}${member.alias}: ${memberType};`
|
||||
}
|
||||
return `${jsdoc}${member.alias}${args}: ${type};`
|
||||
}).filter(x => x).join('\n\n'));
|
||||
return parts.join('\n');
|
||||
|
|
@ -462,8 +475,6 @@ class TypesGenerator {
|
|||
writeFile(path.join(typesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'));
|
||||
|
||||
const apiDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
|
||||
// Root module types are overridden.
|
||||
apiDocumentation.classesArray = apiDocumentation.classesArray.filter(cls => cls.name !== 'Playwright');
|
||||
apiDocumentation.index();
|
||||
const apiTypesGenerator = new TypesGenerator(apiDocumentation);
|
||||
let apiTypes = await apiTypesGenerator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
|
||||
|
|
|
|||
5
utils/generate_types/overrides.d.ts
vendored
5
utils/generate_types/overrides.d.ts
vendored
|
|
@ -241,7 +241,6 @@ type AccessibilityNode = {
|
|||
children?: AccessibilityNode[];
|
||||
}
|
||||
|
||||
export const selectors: Selectors;
|
||||
export const devices: Devices & DeviceDescriptor[];
|
||||
|
||||
//@ts-ignore this will be any if electron is not installed
|
||||
|
|
@ -343,12 +342,8 @@ export type AndroidKey =
|
|||
'Copy' |
|
||||
'Paste';
|
||||
|
||||
export const chromium: BrowserType;
|
||||
export const firefox: BrowserType;
|
||||
export const webkit: BrowserType;
|
||||
export const _electron: Electron;
|
||||
export const _android: Android;
|
||||
export const _newRequest: () => Promise<FetchRequest>;
|
||||
|
||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||
export {};
|
||||
|
|
|
|||
Loading…
Reference in a new issue