feat: support experimental doc entries (#13446)
feat: support experimental doc entries - Params/options/members are marked as experimental in the docs. - `experimental.d.ts` is generated that contains all types and includes experimental features. - `experimental.d.ts` is references in our tests so that we can test experimental features. - `fonts` option is restored as experimental.
This commit is contained in:
parent
166675b9c1
commit
20dcc45afa
|
|
@ -960,6 +960,8 @@ An object which specifies clipping of the resulting image. Should have the follo
|
||||||
When set to `"css"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will keep screenshots small. Using `"device"` option will produce a single pixel per each device pixel, so screenhots of high-dpi devices will be twice as large or even larger. Defaults to `"device"`.
|
When set to `"css"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this will keep screenshots small. Using `"device"` option will produce a single pixel per each device pixel, so screenhots of high-dpi devices will be twice as large or even larger. Defaults to `"device"`.
|
||||||
|
|
||||||
## screenshot-option-fonts
|
## screenshot-option-fonts
|
||||||
|
* langs: js
|
||||||
|
* experimental
|
||||||
- `fonts` <[ScreenshotFonts]<"ready"|"nowait">>
|
- `fonts` <[ScreenshotFonts]<"ready"|"nowait">>
|
||||||
|
|
||||||
When set to `"ready"`, screenshot will wait for [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) promise to resolve in all frames. Defaults to `"nowait"`.
|
When set to `"ready"`, screenshot will wait for [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) promise to resolve in all frames. Defaults to `"nowait"`.
|
||||||
|
|
@ -975,6 +977,7 @@ When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`,
|
||||||
- %%-screenshot-option-quality-%%
|
- %%-screenshot-option-quality-%%
|
||||||
- %%-screenshot-option-path-%%
|
- %%-screenshot-option-path-%%
|
||||||
- %%-screenshot-option-scale-%%
|
- %%-screenshot-option-scale-%%
|
||||||
|
- %%-screenshot-option-fonts-%%
|
||||||
- %%-screenshot-option-caret-%%
|
- %%-screenshot-option-caret-%%
|
||||||
- %%-screenshot-option-type-%%
|
- %%-screenshot-option-type-%%
|
||||||
- %%-screenshot-option-mask-%%
|
- %%-screenshot-option-mask-%%
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"./lib/utils/timeoutRunner": "./lib/utils/timeoutRunner.js",
|
"./lib/utils/timeoutRunner": "./lib/utils/timeoutRunner.js",
|
||||||
"./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js",
|
"./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js",
|
||||||
"./lib/remote/playwrightClient": "./lib/remote/playwrightClient.js",
|
"./lib/remote/playwrightClient": "./lib/remote/playwrightClient.js",
|
||||||
"./lib/server": "./lib/server/index.js"
|
"./lib/server": "./lib/server/index.js",
|
||||||
|
"./types/protocol": "./types/protocol.d.ts",
|
||||||
|
"./types/structs": "./types/structs.d.ts"
|
||||||
},
|
},
|
||||||
"types": "types/types.d.ts",
|
"types": "types/types.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,6 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
|
||||||
selector: locator._selector,
|
selector: locator._selector,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
copy.fonts = (options as any)._fonts;
|
|
||||||
const result = await this._elementChannel.screenshot(copy);
|
const result = await this._elementChannel.screenshot(copy);
|
||||||
const buffer = Buffer.from(result.binary, 'base64');
|
const buffer = Buffer.from(result.binary, 'base64');
|
||||||
if (options.path) {
|
if (options.path) {
|
||||||
|
|
|
||||||
|
|
@ -492,7 +492,6 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
||||||
selector: locator._selector,
|
selector: locator._selector,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
copy.fonts = (options as any)._fonts;
|
|
||||||
const result = await this._channel.screenshot(copy);
|
const result = await this._channel.screenshot(copy);
|
||||||
const buffer = Buffer.from(result.binary, 'base64');
|
const buffer = Buffer.from(result.binary, 'base64');
|
||||||
if (options.path) {
|
if (options.path) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import type { Frame } from './frames';
|
||||||
import type { ParsedSelector } from './isomorphic/selectorParser';
|
import type { ParsedSelector } from './isomorphic/selectorParser';
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
import { assert } from '../utils';
|
import { assert, experimentalFeaturesEnabled } from '../utils';
|
||||||
import { MultiMap } from '../utils/multimap';
|
import { MultiMap } from '../utils/multimap';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
@ -322,6 +322,9 @@ function trimClipToSize(clip: types.Rect, size: types.Size): types.Rect {
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateScreenshotOptions(options: ScreenshotOptions): 'png' | 'jpeg' {
|
function validateScreenshotOptions(options: ScreenshotOptions): 'png' | 'jpeg' {
|
||||||
|
if (options.fonts && !experimentalFeaturesEnabled())
|
||||||
|
throw new Error(`To use the experimental option "fonts", set PLAYWRIGHT_EXPERIMENTAL_FEATURES=1 enviroment variable.`);
|
||||||
|
|
||||||
let format: 'png' | 'jpeg' | null = null;
|
let format: 'png' | 'jpeg' | null = null;
|
||||||
// options.type takes precedence over inferring the type from options.path
|
// options.type takes precedence over inferring the type from options.path
|
||||||
// because it may be a 0-length file with no extension created beforehand (i.e. as a temp file).
|
// because it may be a 0-length file with no extension created beforehand (i.e. as a temp file).
|
||||||
|
|
|
||||||
2
packages/playwright-core/types/structs.d.ts
vendored
2
packages/playwright-core/types/structs.d.ts
vendored
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { JSHandle, ElementHandle, Frame, Page, BrowserContext, Locator } from './types';
|
import { JSHandle, ElementHandle, Frame, Page, BrowserContext } from 'playwright-core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be converted to JSON
|
* Can be converted to JSON
|
||||||
|
|
|
||||||
4
packages/playwright-core/types/types.d.ts
vendored
4
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -14,12 +14,12 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from 'playwright-core/types/protocol';
|
||||||
import { ChildProcess } from 'child_process';
|
import { ChildProcess } from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
import { ReadStream } from 'fs';
|
import { ReadStream } from 'fs';
|
||||||
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
|
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from 'playwright-core/types/structs';
|
||||||
|
|
||||||
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
||||||
state?: 'visible'|'attached';
|
state?: 'visible'|'attached';
|
||||||
|
|
|
||||||
1
packages/playwright-test/index.d.ts
vendored
1
packages/playwright-test/index.d.ts
vendored
|
|
@ -14,6 +14,5 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'playwright-core';
|
|
||||||
export * from './types/test';
|
export * from './types/test';
|
||||||
export { default } from './types/test';
|
export { default } from './types/test';
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ export async function toHaveScreenshot(
|
||||||
const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [(pageOrLocator as PageEx), undefined] : [(pageOrLocator as Locator).page() as PageEx, pageOrLocator as LocatorEx];
|
const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [(pageOrLocator as PageEx), undefined] : [(pageOrLocator as Locator).page() as PageEx, pageOrLocator as LocatorEx];
|
||||||
const screenshotOptions = {
|
const screenshotOptions = {
|
||||||
animations: config?.animations ?? 'disabled',
|
animations: config?.animations ?? 'disabled',
|
||||||
_fonts: config?.fonts ?? 'ready',
|
fonts: process.env.PLAYWRIGHT_EXPERIMENTAL_FEATURES ? (config?.fonts ?? 'ready') : undefined,
|
||||||
scale: config?.scale ?? 'css',
|
scale: config?.scale ?? 'css',
|
||||||
caret: config?.caret ?? 'hide',
|
caret: config?.caret ?? 'hide',
|
||||||
...helper.allOptions,
|
...helper.allOptions,
|
||||||
|
|
|
||||||
5
packages/playwright-test/types/test.d.ts
vendored
5
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core';
|
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core';
|
||||||
|
export * from 'playwright-core';
|
||||||
|
|
||||||
export type ReporterDescription =
|
export type ReporterDescription =
|
||||||
['dot'] |
|
['dot'] |
|
||||||
|
|
@ -2917,7 +2918,7 @@ type MakeMatchers<R, T> = BaseMatchers<R, T> & {
|
||||||
ExtraMatchers<T, Locator, LocatorAssertions> &
|
ExtraMatchers<T, Locator, LocatorAssertions> &
|
||||||
ExtraMatchers<T, APIResponse, APIResponseAssertions>;
|
ExtraMatchers<T, APIResponse, APIResponseAssertions>;
|
||||||
|
|
||||||
export declare type Expect = {
|
export type Expect = {
|
||||||
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
|
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
|
||||||
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
|
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
|
||||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
|
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
|
||||||
|
|
@ -2996,12 +2997,14 @@ type SupportedExpectProperties =
|
||||||
'toThrow' |
|
'toThrow' |
|
||||||
'toThrowError'
|
'toThrowError'
|
||||||
|
|
||||||
|
// --- BEGINGLOBAL ---
|
||||||
declare global {
|
declare global {
|
||||||
export namespace PlaywrightTest {
|
export namespace PlaywrightTest {
|
||||||
export interface Matchers<R, T = unknown> {
|
export interface Matchers<R, T = unknown> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// --- ENDGLOBAL ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These tests are executed in Playwright environment that launches the browser
|
* These tests are executed in Playwright environment that launches the browser
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { FullConfig, FullProject, TestStatus, TestError } from './test';
|
import type { FullConfig, FullProject, TestStatus, TestError } from '@playwright/test';
|
||||||
export type { FullConfig, TestStatus, TestError } from './test';
|
export type { FullConfig, TestStatus, TestError } from '@playwright/test';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `Suite` is a group of tests. All tests in Playwright Test form the following hierarchy:
|
* `Suite` is a group of tests. All tests in Playwright Test form the following hierarchy:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line spaced-comment
|
||||||
|
/// <reference path="./experimental.d.ts" />
|
||||||
|
|
||||||
import type { Fixtures } from '@playwright/test';
|
import type { Fixtures } from '@playwright/test';
|
||||||
import type { ChildProcess } from 'child_process';
|
import type { ChildProcess } from 'child_process';
|
||||||
import { execSync, spawn } from 'child_process';
|
import { execSync, spawn } from 'child_process';
|
||||||
|
|
|
||||||
20281
tests/config/experimental.d.ts
vendored
Normal file
20281
tests/config/experimental.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -30,7 +30,8 @@ export class DriverTestMode implements TestMode {
|
||||||
|
|
||||||
async setup() {
|
async setup() {
|
||||||
this._impl = await start({
|
this._impl = await start({
|
||||||
NODE_OPTIONS: undefined // Hide driver process while debugging.
|
NODE_OPTIONS: undefined, // Hide driver process while debugging.
|
||||||
|
PLAYWRIGHT_EXPERIMENTAL_FEATURES: '1',
|
||||||
});
|
});
|
||||||
return this._impl.playwright;
|
return this._impl.playwright;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,9 @@ if (mode === 'service') {
|
||||||
command: 'npx playwright experimental-grid-server',
|
command: 'npx playwright experimental-grid-server',
|
||||||
port: 3333,
|
port: 3333,
|
||||||
reuseExistingServer: true,
|
reuseExistingServer: true,
|
||||||
|
env: {
|
||||||
|
PLAYWRIGHT_EXPERIMENTAL_FEATURES: '1',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +83,9 @@ if (mode === 'service2') {
|
||||||
command: 'npx playwright run-server --port=3333',
|
command: 'npx playwright run-server --port=3333',
|
||||||
port: 3333,
|
port: 3333,
|
||||||
reuseExistingServer: true,
|
reuseExistingServer: true,
|
||||||
|
env: {
|
||||||
|
PLAYWRIGHT_EXPERIMENTAL_FEATURES: '1',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
config.use.connectOptions = {
|
config.use.connectOptions = {
|
||||||
wsEndpoint: 'ws://localhost:3333/',
|
wsEndpoint: 'ws://localhost:3333/',
|
||||||
|
|
|
||||||
|
|
@ -785,13 +785,13 @@ it.describe('page screenshot animations', () => {
|
||||||
const noIconsScreenshot = await page.screenshot();
|
const noIconsScreenshot = await page.screenshot();
|
||||||
// Make sure screenshot times out while webfont is stalled.
|
// Make sure screenshot times out while webfont is stalled.
|
||||||
const error = await page.screenshot({
|
const error = await page.screenshot({
|
||||||
_fonts: 'ready',
|
fonts: 'ready',
|
||||||
timeout: 200,
|
timeout: 200,
|
||||||
} as any).catch(e => e);
|
}).catch(e => e);
|
||||||
expect(error.message).toContain('waiting for fonts to load...');
|
expect(error.message).toContain('waiting for fonts to load...');
|
||||||
expect(error.message).toContain('Timeout 200ms exceeded');
|
expect(error.message).toContain('Timeout 200ms exceeded');
|
||||||
const [iconsScreenshot] = await Promise.all([
|
const [iconsScreenshot] = await Promise.all([
|
||||||
page.screenshot({ _fonts: 'ready' } as any),
|
page.screenshot({ fonts: 'ready' }),
|
||||||
server.serveFile(serverRequest, serverResponse),
|
server.serveFile(serverRequest, serverResponse),
|
||||||
]);
|
]);
|
||||||
expect(iconsScreenshot).toMatchSnapshot('screenshot-web-font.png', {
|
expect(iconsScreenshot).toMatchSnapshot('screenshot-web-font.png', {
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ test('should fail to screenshot a page with infinite animation', async ({ runInl
|
||||||
test('should disable animations by default', async ({ runInlineTest }, testInfo) => {
|
test('should disable animations by default', async ({ runInlineTest }, testInfo) => {
|
||||||
const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html'));
|
const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html'));
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
|
...playwrightConfig({}),
|
||||||
'a.spec.js': `
|
'a.spec.js': `
|
||||||
pwt.test('is a test', async ({ page }) => {
|
pwt.test('is a test', async ({ page }) => {
|
||||||
await page.goto('${cssTransitionURL}');
|
await page.goto('${cssTransitionURL}');
|
||||||
|
|
@ -156,11 +157,7 @@ test('should report _toHaveScreenshot step with expectation name in title', asyn
|
||||||
}
|
}
|
||||||
module.exports = Reporter;
|
module.exports = Reporter;
|
||||||
`,
|
`,
|
||||||
'playwright.config.ts': `
|
...playwrightConfig({ reporter: './reporter' }),
|
||||||
module.exports = {
|
|
||||||
reporter: './reporter',
|
|
||||||
};
|
|
||||||
`,
|
|
||||||
'a.spec.js': `
|
'a.spec.js': `
|
||||||
pwt.test('is a test', async ({ page }) => {
|
pwt.test('is a test', async ({ page }) => {
|
||||||
// Named expectation.
|
// Named expectation.
|
||||||
|
|
@ -371,8 +368,7 @@ test('should compile with different option combinations', async ({ runTSC }) =>
|
||||||
maxDiffPixelRatio: 0.2,
|
maxDiffPixelRatio: 0.2,
|
||||||
animations: "disabled",
|
animations: "disabled",
|
||||||
omitBackground: true,
|
omitBackground: true,
|
||||||
// TODO: uncomment when enabling "fonts".
|
fonts: "nowait",
|
||||||
// fonts: "nowait",
|
|
||||||
caret: "initial",
|
caret: "initial",
|
||||||
scale: "device",
|
scale: "device",
|
||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
|
|
@ -401,6 +397,7 @@ test('should fail when screenshot is different size', async ({ runInlineTest })
|
||||||
|
|
||||||
test('should fail when given non-png snapshot name', async ({ runInlineTest }) => {
|
test('should fail when given non-png snapshot name', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
|
...playwrightConfig({}),
|
||||||
'a.spec.js': `
|
'a.spec.js': `
|
||||||
pwt.test('is a test', async ({ page }) => {
|
pwt.test('is a test', async ({ page }) => {
|
||||||
await expect(page)._toHaveScreenshot('snapshot.jpeg');
|
await expect(page)._toHaveScreenshot('snapshot.jpeg');
|
||||||
|
|
@ -413,6 +410,7 @@ test('should fail when given non-png snapshot name', async ({ runInlineTest }) =
|
||||||
|
|
||||||
test('should fail when given buffer', async ({ runInlineTest }) => {
|
test('should fail when given buffer', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
|
...playwrightConfig({}),
|
||||||
'a.spec.js': `
|
'a.spec.js': `
|
||||||
pwt.test('is a test', async ({ page }) => {
|
pwt.test('is a test', async ({ page }) => {
|
||||||
await expect(Buffer.from([1]))._toHaveScreenshot();
|
await expect(Buffer.from([1]))._toHaveScreenshot();
|
||||||
|
|
@ -798,6 +796,7 @@ test('should respect maxDiffPixelRatio option', async ({ runInlineTest }) => {
|
||||||
|
|
||||||
test('should throw for invalid maxDiffPixels values', async ({ runInlineTest }) => {
|
test('should throw for invalid maxDiffPixels values', async ({ runInlineTest }) => {
|
||||||
expect((await runInlineTest({
|
expect((await runInlineTest({
|
||||||
|
...playwrightConfig({}),
|
||||||
'a.spec.js': `
|
'a.spec.js': `
|
||||||
pwt.test('is a test', async ({ page }) => {
|
pwt.test('is a test', async ({ page }) => {
|
||||||
await expect(page)._toHaveScreenshot({
|
await expect(page)._toHaveScreenshot({
|
||||||
|
|
@ -810,6 +809,7 @@ test('should throw for invalid maxDiffPixels values', async ({ runInlineTest })
|
||||||
|
|
||||||
test('should throw for invalid maxDiffPixelRatio values', async ({ runInlineTest }) => {
|
test('should throw for invalid maxDiffPixelRatio values', async ({ runInlineTest }) => {
|
||||||
expect((await runInlineTest({
|
expect((await runInlineTest({
|
||||||
|
...playwrightConfig({}),
|
||||||
'a.spec.js': `
|
'a.spec.js': `
|
||||||
pwt.test('is a test', async ({ page }) => {
|
pwt.test('is a test', async ({ page }) => {
|
||||||
await expect(page)._toHaveScreenshot({
|
await expect(page)._toHaveScreenshot({
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class ApiParser {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const clazz = new Documentation.Class(extractLangs(node), name, [], extendsName, extractComments(node));
|
const clazz = new Documentation.Class(extractLangs(node), extractExperimental(node), name, [], extendsName, extractComments(node));
|
||||||
this.classes.set(clazz.name, clazz);
|
this.classes.set(clazz.name, clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,11 +102,11 @@ class ApiParser {
|
||||||
const comments = extractComments(spec);
|
const comments = extractComments(spec);
|
||||||
let member;
|
let member;
|
||||||
if (match[1] === 'event')
|
if (match[1] === 'event')
|
||||||
member = Documentation.Member.createEvent(extractLangs(spec), name, returnType, comments);
|
member = Documentation.Member.createEvent(extractLangs(spec), extractExperimental(spec), name, returnType, comments);
|
||||||
if (match[1] === 'property')
|
if (match[1] === 'property')
|
||||||
member = Documentation.Member.createProperty(extractLangs(spec), name, returnType, comments, !optional);
|
member = Documentation.Member.createProperty(extractLangs(spec), extractExperimental(spec), name, returnType, comments, !optional);
|
||||||
if (['method', 'async method', 'optional method', 'optional async method'].includes(match[1])) {
|
if (['method', 'async method', 'optional method', 'optional async method'].includes(match[1])) {
|
||||||
member = Documentation.Member.createMethod(extractLangs(spec), name, [], returnType, comments);
|
member = Documentation.Member.createMethod(extractLangs(spec), extractExperimental(spec), name, [], returnType, comments);
|
||||||
if (match[1].includes('async'))
|
if (match[1].includes('async'))
|
||||||
member.async = true;
|
member.async = true;
|
||||||
if (match[1].includes('optional'))
|
if (match[1].includes('optional'))
|
||||||
|
|
@ -167,7 +167,7 @@ class ApiParser {
|
||||||
let options = method.argsArray.find(o => o.name === 'options');
|
let options = method.argsArray.find(o => o.name === 'options');
|
||||||
if (!options) {
|
if (!options) {
|
||||||
const type = new Documentation.Type('Object', []);
|
const type = new Documentation.Type('Object', []);
|
||||||
options = Documentation.Member.createProperty({}, 'options', type, undefined, false);
|
options = Documentation.Member.createProperty({}, false /* experimental */, 'options', type, undefined, false);
|
||||||
method.argsArray.push(options);
|
method.argsArray.push(options);
|
||||||
}
|
}
|
||||||
const p = this.parseProperty(spec);
|
const p = this.parseProperty(spec);
|
||||||
|
|
@ -188,7 +188,7 @@ class ApiParser {
|
||||||
const name = text.substring(0, typeStart).replace(/\`/g, '').trim();
|
const name = text.substring(0, typeStart).replace(/\`/g, '').trim();
|
||||||
const comments = extractComments(spec);
|
const comments = extractComments(spec);
|
||||||
const { type, optional } = this.parseType(param);
|
const { type, optional } = this.parseType(param);
|
||||||
return Documentation.Member.createProperty(extractLangs(spec), name, type, comments, !optional);
|
return Documentation.Member.createProperty(extractLangs(spec), extractExperimental(spec), name, type, comments, !optional);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -202,7 +202,7 @@ class ApiParser {
|
||||||
const { name, text } = parseVariable(child.text);
|
const { name, text } = parseVariable(child.text);
|
||||||
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
||||||
const childType = this.parseType(child);
|
const childType = this.parseType(child);
|
||||||
properties.push(Documentation.Member.createProperty({}, name, childType.type, comments, !childType.optional));
|
properties.push(Documentation.Member.createProperty({}, false /* experimental */, name, childType.type, comments, !childType.optional));
|
||||||
}
|
}
|
||||||
const type = Documentation.Type.parse(arg.type, properties);
|
const type = Documentation.Type.parse(arg.type, properties);
|
||||||
return { type, optional: arg.optional };
|
return { type, optional: arg.optional };
|
||||||
|
|
@ -300,13 +300,11 @@ function applyTemplates(body, params) {
|
||||||
* @returns {MarkdownNode[]}
|
* @returns {MarkdownNode[]}
|
||||||
*/
|
*/
|
||||||
function extractComments(item) {
|
function extractComments(item) {
|
||||||
return (item.children || []).filter(c => {
|
return childrenWithoutProperties(item).filter(c => {
|
||||||
if (c.type.startsWith('h'))
|
if (c.type.startsWith('h'))
|
||||||
return false;
|
return false;
|
||||||
if (c.type === 'li' && c.liType === 'default')
|
if (c.type === 'li' && c.liType === 'default')
|
||||||
return false;
|
return false;
|
||||||
if (c.type === 'li' && c.text.startsWith('langs:'))
|
|
||||||
return false;
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -346,12 +344,27 @@ function extractLangs(spec) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MarkdownNode} spec
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function extractExperimental(spec) {
|
||||||
|
for (const child of spec.children) {
|
||||||
|
if (child.type === 'li' && child.liType === 'bullet' && child.text === 'experimental')
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MarkdownNode} spec
|
* @param {MarkdownNode} spec
|
||||||
* @returns {MarkdownNode[]}
|
* @returns {MarkdownNode[]}
|
||||||
*/
|
*/
|
||||||
function childrenWithoutProperties(spec) {
|
function childrenWithoutProperties(spec) {
|
||||||
return spec.children.filter(c => c.liType !== 'bullet' || !c.text.startsWith('langs'));
|
return (spec.children || []).filter(c => {
|
||||||
|
const isProperty = c.liType === 'bullet' && (c.text.startsWith('langs:') || c.text === 'experimental');
|
||||||
|
return !isProperty;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ class Documentation {
|
||||||
* @return {!Documentation}
|
* @return {!Documentation}
|
||||||
*/
|
*/
|
||||||
mergeWith(documentation) {
|
mergeWith(documentation) {
|
||||||
return new Documentation([...this.classesArray, ...documentation.classesArray]);
|
return new Documentation([...this.classesArray, ...documentation.classesArray].map(cls => cls.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -108,6 +108,18 @@ class Documentation {
|
||||||
this.index();
|
this.index();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterOutExperimental() {
|
||||||
|
const classesArray = [];
|
||||||
|
for (const clazz of this.classesArray) {
|
||||||
|
if (clazz.experimental)
|
||||||
|
continue;
|
||||||
|
clazz.filterOutExperimental();
|
||||||
|
classesArray.push(clazz);
|
||||||
|
}
|
||||||
|
this.classesArray = classesArray;
|
||||||
|
this.index();
|
||||||
|
}
|
||||||
|
|
||||||
index() {
|
index() {
|
||||||
for (const cls of this.classesArray) {
|
for (const cls of this.classesArray) {
|
||||||
this.classes.set(cls.name, cls);
|
this.classes.set(cls.name, cls);
|
||||||
|
|
@ -149,23 +161,28 @@ class Documentation {
|
||||||
clazz.visit(item => item.comment = generateSourceCodeComment(item.spec));
|
clazz.visit(item => item.comment = generateSourceCodeComment(item.spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Documentation(this.classesArray.map(cls => cls.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Documentation.Class = class {
|
Documentation.Class = class {
|
||||||
/**
|
/**
|
||||||
* @param {Langs} langs
|
* @param {Langs} langs
|
||||||
|
* @param {boolean} experimental
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {!Array<!Documentation.Member>} membersArray
|
* @param {!Array<!Documentation.Member>} membersArray
|
||||||
* @param {?string=} extendsName
|
* @param {?string=} extendsName
|
||||||
* @param {MarkdownNode[]=} spec
|
* @param {MarkdownNode[]=} spec
|
||||||
*/
|
*/
|
||||||
constructor(langs, name, membersArray, extendsName = null, spec = undefined) {
|
constructor(langs, experimental, name, membersArray, extendsName = null, spec = undefined) {
|
||||||
this.langs = langs;
|
this.langs = langs;
|
||||||
|
this.experimental = experimental;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.membersArray = membersArray;
|
this.membersArray = membersArray;
|
||||||
this.spec = spec;
|
this.spec = spec;
|
||||||
this.extends = extendsName;
|
this.extends = extendsName;
|
||||||
this.comment = '';
|
this.comment = '';
|
||||||
this.index();
|
this.index();
|
||||||
const match = name.match(/(API|JS|CDP|[A-Z])(.*)/);
|
const match = name.match(/(API|JS|CDP|[A-Z])(.*)/);
|
||||||
this.varName = match[1].toLowerCase() + match[2];
|
this.varName = match[1].toLowerCase() + match[2];
|
||||||
|
|
@ -204,6 +221,12 @@ Documentation.Class = class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
const cls = new Documentation.Class(this.langs, this.experimental, this.name, this.membersArray.map(m => m.clone()), this.extends, this.spec);
|
||||||
|
cls.comment = this.comment;
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} lang
|
* @param {string} lang
|
||||||
*/
|
*/
|
||||||
|
|
@ -218,6 +241,17 @@ Documentation.Class = class {
|
||||||
this.membersArray = membersArray;
|
this.membersArray = membersArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterOutExperimental() {
|
||||||
|
const membersArray = [];
|
||||||
|
for (const member of this.membersArray) {
|
||||||
|
if (member.experimental)
|
||||||
|
continue;
|
||||||
|
member.filterOutExperimental();
|
||||||
|
membersArray.push(member);
|
||||||
|
}
|
||||||
|
this.membersArray = membersArray;
|
||||||
|
}
|
||||||
|
|
||||||
validateOrder(errors, cls) {
|
validateOrder(errors, cls) {
|
||||||
const members = this.membersArray;
|
const members = this.membersArray;
|
||||||
// Events should go first.
|
// Events should go first.
|
||||||
|
|
@ -280,15 +314,17 @@ Documentation.Member = class {
|
||||||
/**
|
/**
|
||||||
* @param {string} kind
|
* @param {string} kind
|
||||||
* @param {Langs} langs
|
* @param {Langs} langs
|
||||||
|
* @param {boolean} experimental
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {?Documentation.Type} type
|
* @param {?Documentation.Type} type
|
||||||
* @param {!Array<!Documentation.Member>} argsArray
|
* @param {!Array<!Documentation.Member>} argsArray
|
||||||
* @param {MarkdownNode[]=} spec
|
* @param {MarkdownNode[]=} spec
|
||||||
* @param {boolean=} required
|
* @param {boolean=} required
|
||||||
*/
|
*/
|
||||||
constructor(kind, langs, name, type, argsArray, spec = undefined, required = true) {
|
constructor(kind, langs, experimental, name, type, argsArray, spec = undefined, required = true) {
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
this.langs = langs;
|
this.langs = langs;
|
||||||
|
this.experimental = experimental;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.spec = spec;
|
this.spec = spec;
|
||||||
|
|
@ -355,13 +391,27 @@ Documentation.Member = class {
|
||||||
overriddenArg.filterForLanguage(lang);
|
overriddenArg.filterForLanguage(lang);
|
||||||
if (overriddenArg.name === 'options' && !overriddenArg.type.properties.length)
|
if (overriddenArg.name === 'options' && !overriddenArg.type.properties.length)
|
||||||
continue;
|
continue;
|
||||||
|
overriddenArg.type.filterForLanguage(lang);
|
||||||
argsArray.push(overriddenArg);
|
argsArray.push(overriddenArg);
|
||||||
}
|
}
|
||||||
this.argsArray = argsArray;
|
this.argsArray = argsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterOutExperimental() {
|
||||||
|
this.type.filterOutExperimental();
|
||||||
|
const argsArray = [];
|
||||||
|
for (const arg of this.argsArray) {
|
||||||
|
if (arg.experimental)
|
||||||
|
continue;
|
||||||
|
arg.type.filterOutExperimental();
|
||||||
|
argsArray.push(arg);
|
||||||
|
}
|
||||||
|
this.argsArray = argsArray;
|
||||||
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
const result = new Documentation.Member(this.kind, this.langs, this.name, this.type, this.argsArray, this.spec, this.required);
|
const result = new Documentation.Member(this.kind, this.langs, this.experimental, this.name, this.type.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required);
|
||||||
|
result.alias = this.alias;
|
||||||
result.async = this.async;
|
result.async = this.async;
|
||||||
result.paramOrOption = this.paramOrOption;
|
result.paramOrOption = this.paramOrOption;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -369,37 +419,40 @@ Documentation.Member = class {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Langs} langs
|
* @param {Langs} langs
|
||||||
|
* @param {boolean} experimental
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {!Array<!Documentation.Member>} argsArray
|
* @param {!Array<!Documentation.Member>} argsArray
|
||||||
* @param {?Documentation.Type} returnType
|
* @param {?Documentation.Type} returnType
|
||||||
* @param {MarkdownNode[]=} spec
|
* @param {MarkdownNode[]=} spec
|
||||||
* @return {!Documentation.Member}
|
* @return {!Documentation.Member}
|
||||||
*/
|
*/
|
||||||
static createMethod(langs, name, argsArray, returnType, spec) {
|
static createMethod(langs, experimental, name, argsArray, returnType, spec) {
|
||||||
return new Documentation.Member('method', langs, name, returnType, argsArray, spec);
|
return new Documentation.Member('method', langs, experimental, name, returnType, argsArray, spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Langs} langs
|
* @param {!Langs} langs
|
||||||
|
* @param {boolean} experimental
|
||||||
* @param {!string} name
|
* @param {!string} name
|
||||||
* @param {!Documentation.Type} type
|
* @param {!Documentation.Type} type
|
||||||
* @param {!MarkdownNode[]=} spec
|
* @param {!MarkdownNode[]=} spec
|
||||||
* @param {boolean=} required
|
* @param {boolean=} required
|
||||||
* @return {!Documentation.Member}
|
* @return {!Documentation.Member}
|
||||||
*/
|
*/
|
||||||
static createProperty(langs, name, type, spec, required) {
|
static createProperty(langs, experimental, name, type, spec, required) {
|
||||||
return new Documentation.Member('property', langs, name, type, [], spec, required);
|
return new Documentation.Member('property', langs, experimental, name, type, [], spec, required);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Langs} langs
|
* @param {Langs} langs
|
||||||
|
* @param {boolean} experimental
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {?Documentation.Type=} type
|
* @param {?Documentation.Type=} type
|
||||||
* @param {MarkdownNode[]=} spec
|
* @param {MarkdownNode[]=} spec
|
||||||
* @return {!Documentation.Member}
|
* @return {!Documentation.Member}
|
||||||
*/
|
*/
|
||||||
static createEvent(langs, name, type = null, spec) {
|
static createEvent(langs, experimental, name, type = null, spec) {
|
||||||
return new Documentation.Member('event', langs, name, type, [], spec);
|
return new Documentation.Member('event', langs, experimental, name, type, [], spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -488,16 +541,17 @@ Documentation.Type = class {
|
||||||
*/
|
*/
|
||||||
constructor(name, properties) {
|
constructor(name, properties) {
|
||||||
this.name = name.replace(/^\[/, '').replace(/\]$/, '');
|
this.name = name.replace(/^\[/, '').replace(/\]$/, '');
|
||||||
|
/** @type {Documentation.Member[] | undefined} */
|
||||||
this.properties = this.name === 'Object' ? properties : undefined;
|
this.properties = this.name === 'Object' ? properties : undefined;
|
||||||
/** @type {Documentation.Type[]} | undefined */
|
/** @type {Documentation.Type[] | undefined} */
|
||||||
this.union;
|
this.union;
|
||||||
/** @type {Documentation.Type[]} | undefined */
|
/** @type {Documentation.Type[] | undefined} */
|
||||||
this.args;
|
this.args;
|
||||||
/** @type {Documentation.Type} | undefined */
|
/** @type {Documentation.Type | undefined} */
|
||||||
this.returnType;
|
this.returnType;
|
||||||
/** @type {Documentation.Type[]} | undefined */
|
/** @type {Documentation.Type[] | undefined} */
|
||||||
this.templates;
|
this.templates;
|
||||||
/** @type {string | undefined } */
|
/** @type {string | undefined} */
|
||||||
this.expression;
|
this.expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -510,6 +564,20 @@ Documentation.Type = class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
const type = new Documentation.Type(this.name, this.properties ? this.properties.map(prop => prop.clone()) : undefined);
|
||||||
|
if (this.union)
|
||||||
|
type.union = this.union.map(type => type.clone());
|
||||||
|
if (this.args)
|
||||||
|
type.args = this.args.map(type => type.clone());
|
||||||
|
if (this.returnType)
|
||||||
|
type.returnType = this.returnType.clone();
|
||||||
|
if (this.templates)
|
||||||
|
type.templates = this.templates.map(type => type.clone());
|
||||||
|
type.expression = this.expression;
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Documentation.Member[]}
|
* @returns {Documentation.Member[]}
|
||||||
*/
|
*/
|
||||||
|
|
@ -550,6 +618,19 @@ Documentation.Type = class {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterOutExperimental() {
|
||||||
|
if (!this.properties)
|
||||||
|
return;
|
||||||
|
const properties = [];
|
||||||
|
for (const prop of this.properties) {
|
||||||
|
if (prop.experimental)
|
||||||
|
continue;
|
||||||
|
prop.filterOutExperimental();
|
||||||
|
properties.push(prop);
|
||||||
|
}
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Documentation.Type[]} result
|
* @param {Documentation.Type[]} result
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,6 @@ const { parseOverrides } = require('./parseOverrides');
|
||||||
const exported = require('./exported.json');
|
const exported = require('./exported.json');
|
||||||
const { parseApi } = require('../doclint/api_parser');
|
const { parseApi } = require('../doclint/api_parser');
|
||||||
|
|
||||||
/** @typedef {import('../doclint/documentation').Member} Member */
|
|
||||||
|
|
||||||
Error.stackTraceLimit = 50;
|
Error.stackTraceLimit = 50;
|
||||||
|
|
||||||
class TypesGenerator {
|
class TypesGenerator {
|
||||||
|
|
@ -38,10 +36,11 @@ class TypesGenerator {
|
||||||
* overridesToDocsClassMapping?: Map<string, string>,
|
* overridesToDocsClassMapping?: Map<string, string>,
|
||||||
* ignoreMissing?: Set<string>,
|
* ignoreMissing?: Set<string>,
|
||||||
* doNotExportClassNames?: Set<string>,
|
* doNotExportClassNames?: Set<string>,
|
||||||
|
* includeExperimental?: boolean,
|
||||||
* }} options
|
* }} options
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
/** @type {Array<{name: string, properties: Member[]}>} */
|
/** @type {Array<{name: string, properties: Documentation.Member[]}>} */
|
||||||
this.objectDefinitions = [];
|
this.objectDefinitions = [];
|
||||||
/** @type {Set<string>} */
|
/** @type {Set<string>} */
|
||||||
this.handledMethods = new Set();
|
this.handledMethods = new Set();
|
||||||
|
|
@ -50,6 +49,10 @@ class TypesGenerator {
|
||||||
this.overridesToDocsClassMapping = options.overridesToDocsClassMapping || new Map();
|
this.overridesToDocsClassMapping = options.overridesToDocsClassMapping || new Map();
|
||||||
this.ignoreMissing = options.ignoreMissing || new Set();
|
this.ignoreMissing = options.ignoreMissing || new Set();
|
||||||
this.doNotExportClassNames = options.doNotExportClassNames || new Set();
|
this.doNotExportClassNames = options.doNotExportClassNames || new Set();
|
||||||
|
this.documentation.filterForLanguage('js');
|
||||||
|
if (!options.includeExperimental)
|
||||||
|
this.documentation.filterOutExperimental();
|
||||||
|
this.documentation.copyDocsFromSuperclasses([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,9 +60,6 @@ class TypesGenerator {
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async generateTypes(overridesFile) {
|
async generateTypes(overridesFile) {
|
||||||
this.documentation.filterForLanguage('js');
|
|
||||||
this.documentation.copyDocsFromSuperclasses([]);
|
|
||||||
|
|
||||||
const createMarkdownLink = (member, text) => {
|
const createMarkdownLink = (member, text) => {
|
||||||
const className = toKebabCase(member.clazz.name);
|
const className = toKebabCase(member.clazz.name);
|
||||||
const memberName = toKebabCase(member.name);
|
const memberName = toKebabCase(member.name);
|
||||||
|
|
@ -238,7 +238,7 @@ class TypesGenerator {
|
||||||
type,
|
type,
|
||||||
params,
|
params,
|
||||||
eventName,
|
eventName,
|
||||||
comment: value.comment
|
comment: value.comment,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return descriptions;
|
return descriptions;
|
||||||
|
|
@ -366,10 +366,21 @@ class TypesGenerator {
|
||||||
return this.stringifySimpleType(type, indent, ...namespace);
|
return this.stringifySimpleType(type, indent, ...namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Documentation.Member[]} properties
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string=} indent
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
stringifyObjectType(properties, name, indent = '') {
|
stringifyObjectType(properties, name, indent = '') {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
parts.push(`{`);
|
parts.push(`{`);
|
||||||
parts.push(properties.map(member => `${this.memberJSDOC(member, indent + ' ')}${this.nameForProperty(member)}${this.argsFromMember(member, indent + ' ', name)}: ${this.stringifyComplexType(member.type, indent + ' ', name, member.name)};`).join('\n\n'));
|
parts.push(properties.map(member => {
|
||||||
|
const comment = this.memberJSDOC(member, indent + ' ');
|
||||||
|
const args = this.argsFromMember(member, indent + ' ', name);
|
||||||
|
const type = this.stringifyComplexType(member.type, indent + ' ', name, member.name);
|
||||||
|
return `${comment}${this.nameForProperty(member)}${args}: ${type};`;
|
||||||
|
}).join('\n\n'));
|
||||||
parts.push(indent + '}');
|
parts.push(indent + '}');
|
||||||
return parts.join('\n');
|
return parts.join('\n');
|
||||||
}
|
}
|
||||||
|
|
@ -476,15 +487,124 @@ class TypesGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
(async function () {
|
(async function () {
|
||||||
let hadChanges = false;
|
const coreDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
|
||||||
|
const testDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-api'), path.join(PROJECT_DIR, 'docs', 'src', 'api', 'params.md'));
|
||||||
|
const reporterDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-reporter-api'));
|
||||||
|
const assertionClasses = new Set(['LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} includeExperimental
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function generateCoreTypes(includeExperimental) {
|
||||||
|
const documentation = coreDocumentation.clone();
|
||||||
|
const generator = new TypesGenerator({
|
||||||
|
documentation,
|
||||||
|
classNamesToGenerate: new Set(coreDocumentation.classesArray.map(cls => cls.name).filter(name => !assertionClasses.has(name) && name !== 'PlaywrightAssertions')),
|
||||||
|
includeExperimental,
|
||||||
|
});
|
||||||
|
let types = await generator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
|
||||||
|
const namedDevices = Object.keys(devices).map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`).join('\n');
|
||||||
|
types += [
|
||||||
|
`type Devices = {`,
|
||||||
|
namedDevices,
|
||||||
|
` [key: string]: DeviceDescriptor;`,
|
||||||
|
`}`,
|
||||||
|
``,
|
||||||
|
`export interface ChromiumBrowserContext extends BrowserContext { }`,
|
||||||
|
`export interface ChromiumBrowser extends Browser { }`,
|
||||||
|
`export interface FirefoxBrowser extends Browser { }`,
|
||||||
|
`export interface WebKitBrowser extends Browser { }`,
|
||||||
|
`export interface ChromiumCoverage extends Coverage { }`,
|
||||||
|
``,
|
||||||
|
].join('\n');
|
||||||
|
for (const [key, value] of Object.entries(exported))
|
||||||
|
types = types.replace(new RegExp('\\b' + key + '\\b', 'g'), value);
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} includeExperimental
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function generateTestTypes(includeExperimental) {
|
||||||
|
const documentation = coreDocumentation.mergeWith(testDocumentation);
|
||||||
|
const generator = new TypesGenerator({
|
||||||
|
documentation,
|
||||||
|
classNamesToGenerate: new Set(['TestError', 'TestInfo', 'WorkerInfo', ...assertionClasses]),
|
||||||
|
overridesToDocsClassMapping: new Map([
|
||||||
|
['TestType', 'Test'],
|
||||||
|
['Config', 'TestConfig'],
|
||||||
|
['FullConfig', 'TestConfig'],
|
||||||
|
['Project', 'TestProject'],
|
||||||
|
['PlaywrightWorkerOptions', 'TestOptions'],
|
||||||
|
['PlaywrightTestOptions', 'TestOptions'],
|
||||||
|
['PlaywrightWorkerArgs', 'Fixtures'],
|
||||||
|
['PlaywrightTestArgs', 'Fixtures'],
|
||||||
|
]),
|
||||||
|
ignoreMissing: new Set([
|
||||||
|
'FullConfig.version',
|
||||||
|
'FullConfig.rootDir',
|
||||||
|
'SuiteFunction',
|
||||||
|
'TestFunction',
|
||||||
|
'PlaywrightWorkerOptions.defaultBrowserType',
|
||||||
|
'PlaywrightWorkerArgs.playwright',
|
||||||
|
'Matchers',
|
||||||
|
]),
|
||||||
|
doNotExportClassNames: new Set(assertionClasses),
|
||||||
|
includeExperimental,
|
||||||
|
});
|
||||||
|
return await generator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} includeExperimental
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function generateReporterTypes(includeExperimental) {
|
||||||
|
const documentation = coreDocumentation.mergeWith(testDocumentation).mergeWith(reporterDocumentation);
|
||||||
|
const generator = new TypesGenerator({
|
||||||
|
documentation,
|
||||||
|
classNamesToGenerate: new Set(reporterDocumentation.classesArray.map(cls => cls.name)),
|
||||||
|
ignoreMissing: new Set(['FullResult']),
|
||||||
|
includeExperimental,
|
||||||
|
});
|
||||||
|
return await generator.generateTypes(path.join(__dirname, 'overrides-testReporter.d.ts'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateExperimentalTypes() {
|
||||||
|
const core = await generateCoreTypes(true);
|
||||||
|
const test = await generateTestTypes(true);
|
||||||
|
const reporter = await generateReporterTypes(true);
|
||||||
|
const lines = [
|
||||||
|
`// This file is generated by ${__filename.substring(path.join(__dirname, '..', '..').length).split(path.sep).join(path.posix.sep)}`,
|
||||||
|
`declare module 'playwright-core' {`,
|
||||||
|
...core.split('\n'),
|
||||||
|
`}`,
|
||||||
|
`declare module '@playwright/test' {`,
|
||||||
|
...test.split('\n'),
|
||||||
|
`}`,
|
||||||
|
`declare module '@playwright/test/reporter' {`,
|
||||||
|
...reporter.split('\n'),
|
||||||
|
`}`,
|
||||||
|
];
|
||||||
|
const cutFrom = lines.findIndex(line => line.includes('BEGINGLOBAL'));
|
||||||
|
const cutTo = lines.findIndex(line => line.includes('ENDGLOBAL'));
|
||||||
|
lines.splice(cutFrom, cutTo - cutFrom + 1);
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} filePath
|
* @param {string} filePath
|
||||||
* @param {string} content
|
* @param {string} content
|
||||||
|
* @param {boolean} removeTrailingWhiteSpace
|
||||||
*/
|
*/
|
||||||
function writeFile(filePath, content) {
|
function writeFile(filePath, content, removeTrailingWhiteSpace) {
|
||||||
|
content = content.replace(/\r\n/g, '\n');
|
||||||
|
if (removeTrailingWhiteSpace)
|
||||||
|
content = content.replace(/( +)\n/g, '\n'); // remove trailing whitespace
|
||||||
if (os.platform() === 'win32')
|
if (os.platform() === 'win32')
|
||||||
content = content.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
content = content.replace(/\n/g, '\r\n');
|
||||||
const existing = fs.readFileSync(filePath, 'utf8');
|
const existing = fs.readFileSync(filePath, 'utf8');
|
||||||
if (existing === content)
|
if (existing === content)
|
||||||
return;
|
return;
|
||||||
|
|
@ -493,82 +613,18 @@ class TypesGenerator {
|
||||||
fs.writeFileSync(filePath, content, 'utf8');
|
fs.writeFileSync(filePath, content, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hadChanges = false;
|
||||||
const coreTypesDir = path.join(PROJECT_DIR, 'packages', 'playwright-core', 'types');
|
const coreTypesDir = path.join(PROJECT_DIR, 'packages', 'playwright-core', 'types');
|
||||||
if (!fs.existsSync(coreTypesDir))
|
if (!fs.existsSync(coreTypesDir))
|
||||||
fs.mkdirSync(coreTypesDir)
|
fs.mkdirSync(coreTypesDir)
|
||||||
const testTypesDir = path.join(PROJECT_DIR, 'packages', 'playwright-test', 'types');
|
const testTypesDir = path.join(PROJECT_DIR, 'packages', 'playwright-test', 'types');
|
||||||
if (!fs.existsSync(testTypesDir))
|
if (!fs.existsSync(testTypesDir))
|
||||||
fs.mkdirSync(testTypesDir)
|
fs.mkdirSync(testTypesDir)
|
||||||
writeFile(path.join(coreTypesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'packages', 'playwright-core', 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'));
|
writeFile(path.join(coreTypesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'packages', 'playwright-core', 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'), false);
|
||||||
|
writeFile(path.join(coreTypesDir, 'types.d.ts'), await generateCoreTypes(false), true);
|
||||||
const assertionClasses = new Set(['LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions']);
|
writeFile(path.join(testTypesDir, 'test.d.ts'), await generateTestTypes(false), true);
|
||||||
const apiDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
|
writeFile(path.join(testTypesDir, 'testReporter.d.ts'), await generateReporterTypes(false), true);
|
||||||
apiDocumentation.index();
|
writeFile(path.join(PROJECT_DIR, 'tests', 'config', 'experimental.d.ts'), await generateExperimentalTypes(), true);
|
||||||
const apiTypesGenerator = new TypesGenerator({
|
|
||||||
documentation: apiDocumentation,
|
|
||||||
classNamesToGenerate: new Set(apiDocumentation.classesArray.map(cls => cls.name).filter(name => !assertionClasses.has(name) && name !== 'PlaywrightAssertions')),
|
|
||||||
});
|
|
||||||
let apiTypes = await apiTypesGenerator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
|
|
||||||
const namedDevices = Object.keys(devices).map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`).join('\n');
|
|
||||||
apiTypes += [
|
|
||||||
`type Devices = {`,
|
|
||||||
namedDevices,
|
|
||||||
` [key: string]: DeviceDescriptor;`,
|
|
||||||
`}`,
|
|
||||||
``,
|
|
||||||
`export interface ChromiumBrowserContext extends BrowserContext { }`,
|
|
||||||
`export interface ChromiumBrowser extends Browser { }`,
|
|
||||||
`export interface FirefoxBrowser extends Browser { }`,
|
|
||||||
`export interface WebKitBrowser extends Browser { }`,
|
|
||||||
`export interface ChromiumCoverage extends Coverage { }`,
|
|
||||||
``,
|
|
||||||
].join('\n');
|
|
||||||
for (const [key, value] of Object.entries(exported))
|
|
||||||
apiTypes = apiTypes.replace(new RegExp('\\b' + key + '\\b', 'g'), value);
|
|
||||||
apiTypes = apiTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
|
|
||||||
writeFile(path.join(coreTypesDir, 'types.d.ts'), apiTypes);
|
|
||||||
|
|
||||||
const testOnlyDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-api'), path.join(PROJECT_DIR, 'docs', 'src', 'api', 'params.md'));
|
|
||||||
const testDocumentation = apiDocumentation.mergeWith(testOnlyDocumentation);
|
|
||||||
const testTypesGenerator = new TypesGenerator({
|
|
||||||
documentation: testDocumentation,
|
|
||||||
classNamesToGenerate: new Set(['TestError', 'TestInfo', 'WorkerInfo', ...assertionClasses]),
|
|
||||||
overridesToDocsClassMapping: new Map([
|
|
||||||
['TestType', 'Test'],
|
|
||||||
['Config', 'TestConfig'],
|
|
||||||
['FullConfig', 'TestConfig'],
|
|
||||||
['Project', 'TestProject'],
|
|
||||||
['PlaywrightWorkerOptions', 'TestOptions'],
|
|
||||||
['PlaywrightTestOptions', 'TestOptions'],
|
|
||||||
['PlaywrightWorkerArgs', 'Fixtures'],
|
|
||||||
['PlaywrightTestArgs', 'Fixtures'],
|
|
||||||
]),
|
|
||||||
ignoreMissing: new Set([
|
|
||||||
'FullConfig.version',
|
|
||||||
'FullConfig.rootDir',
|
|
||||||
'SuiteFunction',
|
|
||||||
'TestFunction',
|
|
||||||
'PlaywrightWorkerOptions.defaultBrowserType',
|
|
||||||
'PlaywrightWorkerArgs.playwright',
|
|
||||||
'Matchers',
|
|
||||||
]),
|
|
||||||
doNotExportClassNames: new Set(assertionClasses),
|
|
||||||
});
|
|
||||||
let testTypes = await testTypesGenerator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'));
|
|
||||||
testTypes = testTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
|
|
||||||
writeFile(path.join(testTypesDir, 'test.d.ts'), testTypes);
|
|
||||||
|
|
||||||
const testReporterOnlyDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-reporter-api'));
|
|
||||||
const testReporterDocumentation = testDocumentation.mergeWith(testReporterOnlyDocumentation);
|
|
||||||
const testReporterTypesGenerator = new TypesGenerator({
|
|
||||||
documentation: testReporterDocumentation,
|
|
||||||
classNamesToGenerate: new Set(testReporterOnlyDocumentation.classesArray.map(cls => cls.name)),
|
|
||||||
ignoreMissing: new Set(['FullResult']),
|
|
||||||
});
|
|
||||||
let testReporterTypes = await testReporterTypesGenerator.generateTypes(path.join(__dirname, 'overrides-testReporter.d.ts'));
|
|
||||||
testReporterTypes = testReporterTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
|
|
||||||
writeFile(path.join(testTypesDir, 'testReporter.d.ts'), testReporterTypes);
|
|
||||||
|
|
||||||
process.exit(hadChanges && process.argv.includes('--check-clean') ? 1 : 0);
|
process.exit(hadChanges && process.argv.includes('--check-clean') ? 1 : 0);
|
||||||
})().catch(e => {
|
})().catch(e => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
||||||
5
utils/generate_types/overrides-test.d.ts
vendored
5
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core';
|
import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core';
|
||||||
|
export * from 'playwright-core';
|
||||||
|
|
||||||
export type ReporterDescription =
|
export type ReporterDescription =
|
||||||
['dot'] |
|
['dot'] |
|
||||||
|
|
@ -371,7 +372,7 @@ type MakeMatchers<R, T> = BaseMatchers<R, T> & {
|
||||||
ExtraMatchers<T, Locator, LocatorAssertions> &
|
ExtraMatchers<T, Locator, LocatorAssertions> &
|
||||||
ExtraMatchers<T, APIResponse, APIResponseAssertions>;
|
ExtraMatchers<T, APIResponse, APIResponseAssertions>;
|
||||||
|
|
||||||
export declare type Expect = {
|
export type Expect = {
|
||||||
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
|
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T>;
|
||||||
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
|
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T>;
|
||||||
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
|
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number }) => BaseMatchers<Promise<void>, T> & {
|
||||||
|
|
@ -450,12 +451,14 @@ type SupportedExpectProperties =
|
||||||
'toThrow' |
|
'toThrow' |
|
||||||
'toThrowError'
|
'toThrowError'
|
||||||
|
|
||||||
|
// --- BEGINGLOBAL ---
|
||||||
declare global {
|
declare global {
|
||||||
export namespace PlaywrightTest {
|
export namespace PlaywrightTest {
|
||||||
export interface Matchers<R, T = unknown> {
|
export interface Matchers<R, T = unknown> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// --- ENDGLOBAL ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These tests are executed in Playwright environment that launches the browser
|
* These tests are executed in Playwright environment that launches the browser
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { FullConfig, FullProject, TestStatus, TestError } from './test';
|
import type { FullConfig, FullProject, TestStatus, TestError } from '@playwright/test';
|
||||||
export type { FullConfig, TestStatus, TestError } from './test';
|
export type { FullConfig, TestStatus, TestError } from '@playwright/test';
|
||||||
|
|
||||||
export interface Suite {
|
export interface Suite {
|
||||||
project(): FullProject | undefined;
|
project(): FullProject | undefined;
|
||||||
|
|
|
||||||
4
utils/generate_types/overrides.d.ts
vendored
4
utils/generate_types/overrides.d.ts
vendored
|
|
@ -13,12 +13,12 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from 'playwright-core/types/protocol';
|
||||||
import { ChildProcess } from 'child_process';
|
import { ChildProcess } from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
import { ReadStream } from 'fs';
|
import { ReadStream } from 'fs';
|
||||||
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
|
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from 'playwright-core/types/structs';
|
||||||
|
|
||||||
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
|
||||||
state?: 'visible'|'attached';
|
state?: 'visible'|'attached';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue