mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: generate JSDoc for testing types from md reference (#7799)
This commit is contained in:
parent
cc43f9339f
commit
34b96a5759
2807
types/test.d.ts
vendored
2807
types/test.d.ts
vendored
File diff suppressed because it is too large
Load Diff
280
types/testReporter.d.ts
vendored
280
types/testReporter.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
// This file is generated by /utils/generate_types/index.js
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
@ -18,187 +19,195 @@ import type { FullConfig, TestStatus, TestError } from './test';
|
||||
export type { FullConfig, TestStatus, TestError } from './test';
|
||||
|
||||
/**
|
||||
* Location where TestCase or Suite was defined.
|
||||
* Represents a location in the source code where [TestCase] or [Suite] is defined.
|
||||
*/
|
||||
export interface Location {
|
||||
/**
|
||||
* Path to the file.
|
||||
* Path to the source file.
|
||||
*/
|
||||
file: string;
|
||||
|
||||
/**
|
||||
* Line number in the file.
|
||||
* Line number in the source file.
|
||||
*/
|
||||
line: number;
|
||||
|
||||
/**
|
||||
* Column number in the file.
|
||||
* Column number in the source file.
|
||||
*/
|
||||
column: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of tests. All tests are reported in the following hierarchy:
|
||||
* - Root suite
|
||||
* - Project suite #1 (for each project)
|
||||
* - File suite #1 (for each file in the project)
|
||||
* - Suites for any describe() calls
|
||||
* - TestCase #1 defined in the file or describe() group
|
||||
* - TestCase #2
|
||||
* ... < more test cases >
|
||||
* `Suite` is a group of tests. All tests in Playwright Test form the following hierarchy:
|
||||
* - Root suite has a child suite for each [TestProject].
|
||||
* - Project suite #1. Has a child suite for each test file in the project.
|
||||
* - File suite #1
|
||||
* - [TestCase] #1
|
||||
* - [TestCase] #2
|
||||
* - Suite corresponding to a
|
||||
* [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe) group
|
||||
* - [TestCase] #1 in a group
|
||||
* - [TestCase] #2 in a group
|
||||
* - < more test cases ... >
|
||||
* - File suite #2
|
||||
* ... < more file suites >
|
||||
* - Second project suite
|
||||
* ... < more project suites >
|
||||
* - < more file suites ... >
|
||||
* - Project suite #2
|
||||
* - < more project suites ... >
|
||||
*
|
||||
* Reporter is given a root suite in the
|
||||
* [reporter.onBegin(config, suite)](https://playwright.dev/docs/api/class-reporter#reporter-on-begin) method.
|
||||
*/
|
||||
export interface Suite {
|
||||
/**
|
||||
* Suite title:
|
||||
* - Empty for root suite.
|
||||
* - Project name for project suite.
|
||||
* - File path for file suite.
|
||||
* - Title passed to describe() for describe suites.
|
||||
* Suite title.
|
||||
* - Empty for root suite.
|
||||
* - Project name for project suite.
|
||||
* - File path for file suite.
|
||||
* - Title passed to [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe) for a
|
||||
* group suite.
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Location where the suite is defined.
|
||||
* Location in the source where the suite is defined. Missing for root and project suites.
|
||||
*/
|
||||
location?: Location;
|
||||
|
||||
/**
|
||||
* Child suites.
|
||||
* Child suites. See [Suite] for the hierarchy of suites.
|
||||
*/
|
||||
suites: Suite[];
|
||||
|
||||
/**
|
||||
* Test cases in the suite. Note that only test cases defined directly in this suite
|
||||
* are in the list. Any test cases defined in nested describe() groups are listed
|
||||
* in the child `suites`.
|
||||
* Test cases in the suite. Note that only test cases defined directly in this suite are in the list. Any test cases
|
||||
* defined in nested [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe) groups are
|
||||
* listed in the child [suite.suites](https://playwright.dev/docs/api/class-suite#suite-suites).
|
||||
*/
|
||||
tests: TestCase[];
|
||||
|
||||
/**
|
||||
* A list of titles from the root down to this suite.
|
||||
* Returns a list of titles from the root down to this suite.
|
||||
*/
|
||||
titlePath(): string[];
|
||||
|
||||
/**
|
||||
* Returns the list of all test cases in this suite and its descendants.
|
||||
* Returns the list of all test cases in this suite and its descendants, as opposite to
|
||||
* [suite.tests](https://playwright.dev/docs/api/class-suite#suite-tests).
|
||||
*/
|
||||
allTests(): TestCase[];
|
||||
}
|
||||
|
||||
/**
|
||||
* `TestCase` corresponds to a test() call in a test file. When a single test() is
|
||||
* running in multiple projects or repeated multiple times, it will have multiple
|
||||
* `TestCase` objects in corresponding projects' suites.
|
||||
* `TestCase` corresponds to every [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call)
|
||||
* call in a test file. When a single
|
||||
* [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call) is running in multiple projects
|
||||
* or repeated multiple times, it will have multiple `TestCase` objects in corresponding projects' suites.
|
||||
*/
|
||||
export interface TestCase {
|
||||
/**
|
||||
* Test title as passed to the test() call.
|
||||
* Test title as passed to the [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call)
|
||||
* call.
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Location where the test is defined.
|
||||
* Location in the source where the test is defined.
|
||||
*/
|
||||
location: Location;
|
||||
|
||||
/**
|
||||
* A list of titles from the root down to this test.
|
||||
* Returns a list of titles from the root down to this test.
|
||||
*/
|
||||
titlePath(): string[];
|
||||
|
||||
/**
|
||||
* Expected status.
|
||||
* - Tests marked as test.skip() or test.fixme() are expected to be 'skipped'.
|
||||
* - Tests marked as test.fail() are expected to be 'failed'.
|
||||
* - Other tests are expected to be 'passed'.
|
||||
* Expected test status.
|
||||
* - Tests marked as [test.skip([condition, description])](https://playwright.dev/docs/api/class-test#test-skip) or
|
||||
* [test.fixme([condition, description])](https://playwright.dev/docs/api/class-test#test-fixme) are expected to be
|
||||
* `'skipped'`.
|
||||
* - Tests marked as [test.fail([condition, description])](https://playwright.dev/docs/api/class-test#test-fail) are
|
||||
* expected to be `'failed'`.
|
||||
* - Other tests are expected to be `'passed'`.
|
||||
*
|
||||
* See also [testResult.status](https://playwright.dev/docs/api/class-testresult#test-result-status) for the actual status.
|
||||
*/
|
||||
expectedStatus: TestStatus;
|
||||
|
||||
/**
|
||||
* The timeout given to the test. Affected by timeout in the configuration file,
|
||||
* and calls to test.setTimeout() or test.slow().
|
||||
* The timeout given to the test. Affected by
|
||||
* [testConfig.timeout](https://playwright.dev/docs/api/class-testconfig#test-config-timeout),
|
||||
* [testProject.timeout](https://playwright.dev/docs/api/class-testproject#test-project-timeout),
|
||||
* [test.setTimeout(timeout)](https://playwright.dev/docs/api/class-test#test-set-timeout),
|
||||
* [test.slow([condition, description])](https://playwright.dev/docs/api/class-test#test-slow) and
|
||||
* [testInfo.setTimeout(timeout)](https://playwright.dev/docs/api/class-testinfo#test-info-set-timeout).
|
||||
*/
|
||||
timeout: number;
|
||||
|
||||
/**
|
||||
* Annotations collected for this test. For example, calling
|
||||
* `test.skip(true, 'just because')` will produce an annotation
|
||||
* `{ type: 'skip', description: 'just because' }`.
|
||||
* The list of annotations applicable to the current test. Includes annotations from the test, annotations from all
|
||||
* [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe) groups the test belongs to
|
||||
* and file-level annotations for the test file.
|
||||
*
|
||||
* Annotations are available during test execution through
|
||||
* [testInfo.annotations](https://playwright.dev/docs/api/class-testinfo#test-info-annotations).
|
||||
*
|
||||
* Learn more about [test annotations](https://playwright.dev/docs/test-annotations).
|
||||
*/
|
||||
annotations: { type: string, description?: string }[];
|
||||
|
||||
/**
|
||||
* The maxmium number of retries given to this test in the configuration.
|
||||
* The maximum number of retries given to this test in the configuration.
|
||||
*
|
||||
* Learn more about [test retries](https://playwright.dev/docs/test-retries).
|
||||
*/
|
||||
retries: number;
|
||||
|
||||
/**
|
||||
* Results for each run of this test.
|
||||
*/
|
||||
results: TestResult[];
|
||||
|
||||
/**
|
||||
* Testing outcome for this test. Note that outcome does not directly match to the status:
|
||||
* - Test that is expected to fail and actually fails is 'expected'.
|
||||
* - Test that passes on a second retry is 'flaky'.
|
||||
* Testing outcome for this test. Note that outcome is not the same as
|
||||
* [testResult.status](https://playwright.dev/docs/api/class-testresult#test-result-status):
|
||||
* - Test that is expected to fail and actually fails is `'expected'`.
|
||||
* - Test that passes on a second retry is `'flaky'`.
|
||||
*/
|
||||
outcome(): 'skipped' | 'expected' | 'unexpected' | 'flaky';
|
||||
|
||||
/**
|
||||
* Whether the test is considered running fine.
|
||||
* Non-ok tests fail the test run with non-zero exit code.
|
||||
* Whether the test is considered running fine. Non-ok tests fail the test run with non-zero exit code.
|
||||
*/
|
||||
ok(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A result of a single test run.
|
||||
* A result of a single [TestCase] run.
|
||||
*/
|
||||
export interface TestResult {
|
||||
/**
|
||||
* When test is retries multiple times, each retry attempt is given a sequential number.
|
||||
*
|
||||
* Learn more about [test retries](https://playwright.dev/docs/test-retries).
|
||||
*/
|
||||
retry: number;
|
||||
|
||||
/**
|
||||
* Index of the worker where the test was run.
|
||||
*
|
||||
* Learn more about [parallelism and sharding](https://playwright.dev/docs/test-parallel) with Playwright Test.
|
||||
*/
|
||||
workerIndex: number;
|
||||
|
||||
/**
|
||||
* Test run start time.
|
||||
* Start time of this particular test run.
|
||||
*/
|
||||
startTime: Date;
|
||||
|
||||
/**
|
||||
* Running time in milliseconds.
|
||||
*/
|
||||
duration: number;
|
||||
|
||||
/**
|
||||
* The status of this test result.
|
||||
* The status of this test result. See also
|
||||
* [testCase.expectedStatus](https://playwright.dev/docs/api/class-testcase#test-case-expected-status).
|
||||
*/
|
||||
status?: TestStatus;
|
||||
|
||||
/**
|
||||
* An error from this test result, if any.
|
||||
* An error thrown during the test execution, if any.
|
||||
*/
|
||||
error?: TestError;
|
||||
|
||||
/**
|
||||
* Any attachments created during the test run.
|
||||
* The list of files or buffers attached during the test execution through
|
||||
* [testInfo.attachments](https://playwright.dev/docs/api/class-testinfo#test-info-attachments).
|
||||
*/
|
||||
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[];
|
||||
|
||||
/**
|
||||
* Anything written to the standard output during the test run.
|
||||
*/
|
||||
stdout: (string | Buffer)[];
|
||||
|
||||
/**
|
||||
* Anything written to the standard error during the test run.
|
||||
*/
|
||||
@ -220,47 +229,138 @@ export interface FullResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test runner notifies reporter about various events during the test run.
|
||||
* Test runner notifies the reporter about various events during test execution. All methods of the reporter are optional.
|
||||
*
|
||||
* You can create a custom reporter my implementing a class with some of the reporter methods. Make sure to export this
|
||||
* class as default.
|
||||
*
|
||||
* ```js js-flavor=js
|
||||
* // my-awesome-reporter.js
|
||||
* // @ts-check
|
||||
*
|
||||
* /** @implements {import('@playwright/test/reporter').Reporter} *\/
|
||||
* class MyReporter {
|
||||
* onBegin(config, suite) {
|
||||
* console.log(`Starting the run with ${suite.allTests().length} tests`);
|
||||
* }
|
||||
*
|
||||
* onTestBegin(test) {
|
||||
* console.log(`Starting test ${test.title}`);
|
||||
* }
|
||||
*
|
||||
* onTestEnd(test, result) {
|
||||
* console.log(`Finished test ${test.title}: ${result.status}`);
|
||||
* }
|
||||
*
|
||||
* onEnd(result) {
|
||||
* console.log(`Finished the run: ${result.status}`);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* module.exports = MyReporter;
|
||||
* ```
|
||||
*
|
||||
* ```js js-flavor=ts
|
||||
* // playwright.config.ts
|
||||
* import { Reporter } from '@playwright/test/reporter';
|
||||
*
|
||||
* class MyReporter implements Reporter {
|
||||
* onBegin(config, suite) {
|
||||
* console.log(`Starting the run with ${suite.allTests().length} tests`);
|
||||
* }
|
||||
*
|
||||
* onTestBegin(test) {
|
||||
* console.log(`Starting test ${test.title}`);
|
||||
* }
|
||||
*
|
||||
* onTestEnd(test, result) {
|
||||
* console.log(`Finished test ${test.title}: ${result.status}`);
|
||||
* }
|
||||
*
|
||||
* onEnd(result) {
|
||||
* console.log(`Finished the run: ${result.status}`);
|
||||
* }
|
||||
* }
|
||||
* export default MyReporter;
|
||||
* ```
|
||||
*
|
||||
* Now use this reporter with [testConfig.reporter](https://playwright.dev/docs/api/class-testconfig#test-config-reporter).
|
||||
*
|
||||
* ```js js-flavor=js
|
||||
* // playwright.config.js
|
||||
* // @ts-check
|
||||
*
|
||||
* /** @type {import('@playwright/test').PlaywrightTestConfig} *\/
|
||||
* const config = {
|
||||
* reporter: './my-awesome-reporter.js',
|
||||
* };
|
||||
*
|
||||
* module.exports = config;
|
||||
* ```
|
||||
*
|
||||
* ```js js-flavor=ts
|
||||
* // playwright.config.ts
|
||||
* import { PlaywrightTestConfig } from '@playwright/test';
|
||||
*
|
||||
* const config: PlaywrightTestConfig = {
|
||||
* reporter: './my-awesome-reporter.ts',
|
||||
* };
|
||||
* export default config;
|
||||
* ```
|
||||
*
|
||||
* Learn more about [reporters](https://playwright.dev/docs/test-reporters).
|
||||
*/
|
||||
export interface Reporter {
|
||||
/**
|
||||
* Called once before running tests.
|
||||
* All tests have been already discovered and put into a hierarchy, see `Suite` description.
|
||||
* Called once before running tests. All tests have been already discovered and put into a hierarchy of [Suite]s.
|
||||
* @param config Resolved configuration.
|
||||
* @param suite The root suite that contains all projects, files and test cases.
|
||||
*/
|
||||
onBegin?(config: FullConfig, suite: Suite): void;
|
||||
|
||||
/**
|
||||
* Called after a test has been started in the worker process.
|
||||
* @param test Test that has been started.
|
||||
*/
|
||||
onTestBegin?(test: TestCase): void;
|
||||
|
||||
/**
|
||||
* Called when something has been written to the standard output in the worker process.
|
||||
* When `test` is given, output happened while the test was running.
|
||||
* @param chunk Output chunk.
|
||||
* @param test Test that was running. Note that output may happen when to test is running, in which case this will be [void].
|
||||
*/
|
||||
onStdOut?(chunk: string | Buffer, test?: TestCase): void;
|
||||
|
||||
/**
|
||||
* Called when something has been written to the standard error in the worker process.
|
||||
* When `test` is given, output happened while the test was running.
|
||||
* @param chunk Output chunk.
|
||||
* @param test Test that was running. Note that output may happen when to test is running, in which case this will be [void].
|
||||
*/
|
||||
onStdErr?(chunk: string | Buffer, test?: TestCase): void;
|
||||
|
||||
/**
|
||||
* Called after a test has been finished in the worker process.
|
||||
* @param test Test that has been finished.
|
||||
* @param result Result of the test run.
|
||||
*/
|
||||
onTestEnd?(test: TestCase, result: TestResult): void;
|
||||
|
||||
/**
|
||||
* Called on some global error, for example unhandled expection in the worker process.
|
||||
* Called on some global error, for example unhandled exception in the worker process.
|
||||
* @param error The error.
|
||||
*/
|
||||
onError?(error: TestError): void;
|
||||
|
||||
/**
|
||||
* Called after all tests has been run, or when testing has been interrupted.
|
||||
* Called after all tests has been run, or testing has been interrupted. Note that this method may return a [Promise] and
|
||||
* Playwright Test will await it.
|
||||
* @param result Result of the full test run. - `'passed'` - Everything went as expected.
|
||||
* - `'failed'` - Any test has failed.
|
||||
* - `'timedout'` - The
|
||||
* [testConfig.globalTimeout](https://playwright.dev/docs/api/class-testconfig#test-config-global-timeout) has been
|
||||
* reached.
|
||||
* - `'interrupted'` - Interrupted by the user.
|
||||
*/
|
||||
onEnd?(result: FullResult): void | Promise<void>;
|
||||
}
|
||||
|
||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||
export {};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -26,419 +26,482 @@ const {parseOverrides} = require('./parseOverrides');
|
||||
const exported = require('./exported.json');
|
||||
const { parseApi } = require('../doclint/api_parser');
|
||||
|
||||
const objectDefinitions = [];
|
||||
const handledMethods = new Set();
|
||||
/** @type {Documentation} */
|
||||
let documentation;
|
||||
let hadChanges = false;
|
||||
/** @typedef {import('../doclint/documentation').Member} Member */
|
||||
|
||||
Error.stackTraceLimit = 50;
|
||||
|
||||
class TypesGenerator {
|
||||
/**
|
||||
* @param {Documentation} documentation
|
||||
*/
|
||||
constructor(documentation) {
|
||||
/** @type {Array<{name: string, properties: Member[]}>} */
|
||||
this.objectDefinitions = [];
|
||||
/** @type {Set<string>} */
|
||||
this.handledMethods = new Set();
|
||||
this.documentation = documentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} overridesFile
|
||||
* @param {Map<string, string>=} docsOnlyClassMapping
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async generateTypes(overridesFile, docsOnlyClassMapping) {
|
||||
this.documentation.filterForLanguage('js');
|
||||
this.documentation.copyDocsFromSuperclasses([]);
|
||||
|
||||
const createMarkdownLink = (member, text) => {
|
||||
const className = toKebabCase(member.clazz.name);
|
||||
const memberName = toKebabCase(member.name);
|
||||
let hash = null
|
||||
if (member.kind === 'property' || member.kind === 'method')
|
||||
hash = `${className}-${memberName}`.toLowerCase();
|
||||
else if (member.kind === 'event')
|
||||
hash = `${className}-event-${memberName}`.toLowerCase();
|
||||
return `[${text}](https://playwright.dev/docs/api/class-${member.clazz.name.toLowerCase()}#${hash})`;
|
||||
};
|
||||
this.documentation.setLinkRenderer(item => {
|
||||
const { clazz, member, param, option } = item;
|
||||
if (param)
|
||||
return `\`${param}\``;
|
||||
if (option)
|
||||
return `\`${option}\``;
|
||||
if (clazz)
|
||||
return `[${clazz.name}]`;
|
||||
if (member.kind === 'method')
|
||||
return createMarkdownLink(member, `${member.clazz.varName}.${member.alias}(${this.renderJSSignature(member.argsArray)})`);
|
||||
if (member.kind === 'event')
|
||||
return createMarkdownLink(member, `${member.clazz.varName}.on('${member.alias.toLowerCase()}')`);
|
||||
if (member.kind === 'property')
|
||||
return createMarkdownLink(member, `${member.clazz.varName}.${member.alias}`);
|
||||
throw new Error('Unknown member kind ' + member.kind);
|
||||
});
|
||||
this.documentation.generateSourceCodeComments();
|
||||
|
||||
const handledClasses = new Set();
|
||||
|
||||
const overrides = await parseOverrides(overridesFile, className => {
|
||||
const docClass = this.docClassForName(className, docsOnlyClassMapping);
|
||||
if (!docClass)
|
||||
return '';
|
||||
handledClasses.add(className);
|
||||
return this.writeComment(docClass.comment) + '\n';
|
||||
}, (className, methodName) => {
|
||||
const docClass = this.docClassForName(className, docsOnlyClassMapping);
|
||||
const method = docClass ? docClass.membersArray.find(m => m.alias === methodName) : undefined;
|
||||
if (docsOnlyClassMapping && !method)
|
||||
return '';
|
||||
this.handledMethods.add(`${className}.${methodName}`);
|
||||
if (!method) {
|
||||
if (new Set(['on', 'addListener', 'off', 'removeListener', 'once']).has(methodName))
|
||||
return '';
|
||||
throw new Error(`Unknown override method "${className}.${methodName}"`);
|
||||
}
|
||||
return this.memberJSDOC(method, ' ').trimLeft();
|
||||
}, (className) => {
|
||||
const docClass = this.docClassForName(className, docsOnlyClassMapping);
|
||||
return (!docsOnlyClassMapping && docClass) ? this.classBody(docClass) : '';
|
||||
});
|
||||
|
||||
const classes = this.documentation.classesArray.filter(cls => !handledClasses.has(cls.name));
|
||||
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'),
|
||||
this.objectDefinitionsToString(overrides),
|
||||
'',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Map<string, string> | undefined} docsOnlyClassMapping
|
||||
*/
|
||||
docClassForName(name, docsOnlyClassMapping) {
|
||||
name = (docsOnlyClassMapping ? docsOnlyClassMapping.get(name) : undefined) || name;
|
||||
const docClass = this.documentation.classes.get(name);
|
||||
if (!docClass && !docsOnlyClassMapping)
|
||||
throw new Error(`Unknown override class ${name}`);
|
||||
return docClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} overriddes
|
||||
*/
|
||||
objectDefinitionsToString(overriddes) {
|
||||
let definition;
|
||||
const parts = [];
|
||||
const internalWords = new Set(overriddes.split(/[^\w$]/g));
|
||||
while ((definition = this.objectDefinitions.pop())) {
|
||||
const {name, properties} = definition;
|
||||
const shouldExport = !!exported[name];
|
||||
const usedInternally = internalWords.has(name);
|
||||
if (!usedInternally && !shouldExport)
|
||||
continue;
|
||||
parts.push(`${shouldExport ? 'export ' : ''}interface ${name} ${this.stringifyObjectType(properties, name, '')}\n`)
|
||||
}
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
nameForProperty(member) {
|
||||
return (member.required || member.alias.startsWith('...')) ? member.alias : member.alias + '?';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
classToString(classDesc) {
|
||||
const parts = [];
|
||||
if (classDesc.comment) {
|
||||
parts.push(this.writeComment(classDesc.comment))
|
||||
}
|
||||
parts.push(`export interface ${classDesc.name} ${classDesc.extends ? `extends ${classDesc.extends} ` : ''}{`);
|
||||
parts.push(this.classBody(classDesc));
|
||||
parts.push('}\n');
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
*/
|
||||
argNameForType(type) {
|
||||
if (type === 'void')
|
||||
return null;
|
||||
if (type.includes('{'))
|
||||
return 'data';
|
||||
return (type[0].toLowerCase() + type.slice(1)).replace(/\|/g, 'Or');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
hasUniqueEvents(classDesc) {
|
||||
if (!classDesc.events.size)
|
||||
return false;
|
||||
const parent = this.parentClass(classDesc);
|
||||
if (!parent)
|
||||
return true;
|
||||
return Array.from(classDesc.events.keys()).some(eventName => !parent.events.has(eventName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
createEventDescriptions(classDesc) {
|
||||
if (!this.hasUniqueEvents(classDesc))
|
||||
return [];
|
||||
const descriptions = [];
|
||||
for (let [eventName, value] of classDesc.events) {
|
||||
eventName = eventName.toLowerCase();
|
||||
const type = this.stringifyComplexType(value && value.type, '', classDesc.name, eventName, 'payload');
|
||||
const argName = this.argNameForType(type);
|
||||
const params = argName ? `${argName}: ${type}` : '';
|
||||
descriptions.push({
|
||||
type,
|
||||
params,
|
||||
eventName,
|
||||
comment: value.comment
|
||||
});
|
||||
}
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
classBody(classDesc) {
|
||||
const 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.'
|
||||
}
|
||||
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, ' '));
|
||||
else
|
||||
parts.push(this.writeComment(commentForMethod[method], ' '));
|
||||
parts.push(` ${method}(event: '${eventName}', listener: (${params}) => void): this;\n`);
|
||||
}
|
||||
}
|
||||
|
||||
const members = classDesc.membersArray.filter(member => member.kind !== 'event');
|
||||
parts.push(members.map(member => {
|
||||
if (member.kind === 'event')
|
||||
return '';
|
||||
if (member.alias === 'waitForEvent') {
|
||||
const parts = [];
|
||||
for (const {eventName, params, comment, type} of eventDescriptions) {
|
||||
if (comment)
|
||||
parts.push(this.writeComment(comment, ' '));
|
||||
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);
|
||||
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 '';
|
||||
return `${jsdoc}${member.alias}${args}: ${type};`
|
||||
}).filter(x => x).join('\n\n'));
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {string} methodName
|
||||
*/
|
||||
hasOwnMethod(classDesc, methodName) {
|
||||
if (this.handledMethods.has(`${classDesc.name}.${methodName}`))
|
||||
return false;
|
||||
while (classDesc = this.parentClass(classDesc)) {
|
||||
if (classDesc.members.has(methodName))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
parentClass(classDesc) {
|
||||
if (!classDesc.extends)
|
||||
return null;
|
||||
return this.documentation.classes.get(classDesc.extends);
|
||||
}
|
||||
|
||||
writeComment(comment, indent = '') {
|
||||
const parts = [];
|
||||
const out = [];
|
||||
const pushLine = (line) => {
|
||||
if (line || out[out.length - 1])
|
||||
out.push(line)
|
||||
};
|
||||
let skipExample = false;
|
||||
for (let line of comment.split('\n')) {
|
||||
const match = line.match(/```(\w+)/);
|
||||
if (match) {
|
||||
const lang = match[1];
|
||||
skipExample = !["html", "yml", "bash", "js"].includes(lang);
|
||||
} else if (skipExample && line.trim().startsWith('```')) {
|
||||
skipExample = false;
|
||||
continue;
|
||||
}
|
||||
if (!skipExample)
|
||||
pushLine(line);
|
||||
}
|
||||
comment = out.join('\n');
|
||||
comment = comment.replace(/\[([^\]]+)\]\(\.\/([^\)]+)\)/g, (match, p1, p2) => {
|
||||
return `[${p1}](https://playwright.dev/docs/${p2.replace('.md', '')})`;
|
||||
});
|
||||
|
||||
parts.push(indent + '/**');
|
||||
parts.push(...comment.split('\n').map(line => indent + ' * ' + line.replace(/\*\//g, '*\\/')));
|
||||
parts.push(indent + ' */');
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type} type
|
||||
*/
|
||||
stringifyComplexType(type, indent, ...namespace) {
|
||||
if (!type)
|
||||
return 'void';
|
||||
return this.stringifySimpleType(type, indent, ...namespace);
|
||||
}
|
||||
|
||||
stringifyObjectType(properties, name, indent = '') {
|
||||
const parts = [];
|
||||
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(indent + '}');
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type=} type
|
||||
* @returns{string}
|
||||
*/
|
||||
stringifySimpleType(type, indent = '', ...namespace) {
|
||||
if (!type)
|
||||
return 'void';
|
||||
if (type.name === 'Object' && type.templates) {
|
||||
const keyType = this.stringifySimpleType(type.templates[0], indent, ...namespace);
|
||||
const valueType = this.stringifySimpleType(type.templates[1], indent, ...namespace);
|
||||
return `{ [key: ${keyType}]: ${valueType}; }`;
|
||||
}
|
||||
let out = type.name;
|
||||
if (out === 'int' || out === 'float')
|
||||
out = 'number';
|
||||
|
||||
if (type.name === 'Object' && type.properties && type.properties.length) {
|
||||
const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join('');
|
||||
const shouldExport = exported[name];
|
||||
const properties = namespace[namespace.length -1] === 'options' ? type.sortedProperties() : type.properties;
|
||||
this.objectDefinitions.push({name, properties});
|
||||
if (shouldExport) {
|
||||
out = name;
|
||||
} else {
|
||||
out = this.stringifyObjectType(properties, name, indent);
|
||||
}
|
||||
}
|
||||
|
||||
if (type.args) {
|
||||
const stringArgs = type.args.map(a => ({
|
||||
type: this.stringifySimpleType(a, indent, ...namespace),
|
||||
name: a.name.toLowerCase()
|
||||
}));
|
||||
out = `((${stringArgs.map(({name, type}) => `${name}: ${type}`).join(', ')}) => ${this.stringifySimpleType(type.returnType, indent, ...namespace)})`;
|
||||
} else if (type.name === 'function') {
|
||||
out = 'Function';
|
||||
}
|
||||
if (out === 'path')
|
||||
return 'string';
|
||||
if (out === 'Any')
|
||||
return 'any';
|
||||
if (type.templates)
|
||||
out += '<' + type.templates.map(t => this.stringifySimpleType(t, indent, ...namespace)).join(', ') + '>';
|
||||
if (type.union)
|
||||
out = type.union.map(t => this.stringifySimpleType(t, indent, ...namespace)).join('|');
|
||||
return out.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} member
|
||||
*/
|
||||
argsFromMember(member, indent, ...namespace) {
|
||||
if (member.kind === 'property')
|
||||
return '';
|
||||
return '(' + member.argsArray.map(arg => `${this.nameForProperty(arg)}: ${this.stringifyComplexType(arg.type, indent, ...namespace, member.name, arg.name)}`).join(', ') + ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} member
|
||||
* @param {string} indent
|
||||
*/
|
||||
memberJSDOC(member, indent) {
|
||||
const lines = [];
|
||||
if (member.comment)
|
||||
lines.push(...member.comment.split('\n'));
|
||||
if (member.deprecated)
|
||||
lines.push('@deprecated');
|
||||
lines.push(...member.argsArray.map(arg => `@param ${arg.alias.replace(/\./g, '')} ${arg.comment.replace('\n', ' ')}`));
|
||||
if (!lines.length)
|
||||
return indent;
|
||||
return this.writeComment(lines.join('\n'), indent) + '\n' + indent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member[]} args
|
||||
*/
|
||||
renderJSSignature(args) {
|
||||
const tokens = [];
|
||||
let hasOptional = false;
|
||||
for (const arg of args) {
|
||||
const name = arg.alias;
|
||||
const optional = !arg.required;
|
||||
if (tokens.length) {
|
||||
if (optional && !hasOptional)
|
||||
tokens.push(`[, ${name}`);
|
||||
else
|
||||
tokens.push(`, ${name}`);
|
||||
} else {
|
||||
if (optional && !hasOptional)
|
||||
tokens.push(`[${name}`);
|
||||
else
|
||||
tokens.push(`${name}`);
|
||||
}
|
||||
hasOptional = hasOptional || optional;
|
||||
}
|
||||
if (hasOptional)
|
||||
tokens.push(']');
|
||||
return tokens.join('');
|
||||
}
|
||||
}
|
||||
|
||||
(async function() {
|
||||
let hadChanges = false;
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @param {string} content
|
||||
*/
|
||||
function writeFile(filePath, content) {
|
||||
if (os.platform() === 'win32')
|
||||
content = content.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
const existing = fs.readFileSync(filePath, 'utf8');
|
||||
if (existing === content)
|
||||
return;
|
||||
hadChanges = true;
|
||||
console.error(`Writing //${path.relative(PROJECT_DIR, filePath)}`);
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
}
|
||||
|
||||
const typesDir = path.join(PROJECT_DIR, 'types');
|
||||
if (!fs.existsSync(typesDir))
|
||||
fs.mkdirSync(typesDir)
|
||||
writeFile(path.join(typesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'));
|
||||
documentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
|
||||
documentation.filterForLanguage('js');
|
||||
documentation.copyDocsFromSuperclasses([]);
|
||||
const createMarkdownLink = (member, text) => {
|
||||
const className = toKebabCase(member.clazz.name);
|
||||
const memberName = toKebabCase(member.name);
|
||||
let hash = null
|
||||
if (member.kind === 'property' || member.kind === 'method')
|
||||
hash = `${className}-${memberName}`.toLowerCase();
|
||||
else if (member.kind === 'event')
|
||||
hash = `${className}-event-${memberName}`.toLowerCase();
|
||||
return `[${text}](https://playwright.dev/docs/api/class-${member.clazz.name.toLowerCase()}#${hash})`;
|
||||
};
|
||||
documentation.setLinkRenderer(item => {
|
||||
const { clazz, member, param, option } = item;
|
||||
if (param)
|
||||
return `\`${param}\``;
|
||||
if (option)
|
||||
return `\`${option}\``;
|
||||
if (clazz)
|
||||
return `[${clazz.name}]`;
|
||||
if (member.kind === 'method')
|
||||
return createMarkdownLink(member, `${member.clazz.varName}.${member.alias}(${renderJSSignature(member.argsArray)})`);
|
||||
if (member.kind === 'event')
|
||||
return createMarkdownLink(member, `${member.clazz.varName}.on('${member.alias.toLowerCase()}')`);
|
||||
if (member.kind === 'property')
|
||||
return createMarkdownLink(member, `${member.clazz.varName}.${member.alias}`);
|
||||
throw new Error('Unknown member kind ' + member.kind);
|
||||
});
|
||||
documentation.generateSourceCodeComments();
|
||||
|
||||
const apiDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
|
||||
// Root module types are overridden.
|
||||
const playwrightClass = documentation.classes.get('Playwright');
|
||||
documentation.classes.delete('Playwright');
|
||||
documentation.classesArray.splice(documentation.classesArray.indexOf(playwrightClass), 1);
|
||||
|
||||
const handledClasses = new Set();
|
||||
|
||||
function docClassForName(name) {
|
||||
const docClass = documentation.classes.get(name);
|
||||
if (!docClass)
|
||||
throw new Error(`Unknown override class "${name}"`);
|
||||
return docClass;
|
||||
}
|
||||
const overrides = await parseOverrides(className => {
|
||||
handledClasses.add(className);
|
||||
return writeComment(docClassForName(className).comment) + '\n';
|
||||
}, (className, methodName) => {
|
||||
const docClass = docClassForName(className);
|
||||
const method = docClass.methodsArray.find(m => m.alias === methodName);
|
||||
handledMethods.add(`${className}.${methodName}`);
|
||||
if (!method) {
|
||||
if (new Set(['on', 'addListener', 'off', 'removeListener', 'once']).has(methodName))
|
||||
return '';
|
||||
throw new Error(`Unknown override method "${className}.${methodName}"`);
|
||||
}
|
||||
return memberJSDOC(method, ' ').trimLeft();
|
||||
}, (className) => {
|
||||
return classBody(docClassForName(className));
|
||||
});
|
||||
const classes = documentation.classesArray.filter(cls => !handledClasses.has(cls.name));
|
||||
let output = `// This file is generated by ${__filename.substring(path.join(__dirname, '..', '..').length).split(path.sep).join(path.posix.sep)}
|
||||
${overrides}
|
||||
|
||||
${classes.map(classDesc => classToString(classDesc)).join('\n')}
|
||||
${objectDefinitionsToString(overrides)}
|
||||
${generateDevicesTypes()}
|
||||
|
||||
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 { }
|
||||
`;
|
||||
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'));
|
||||
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))
|
||||
output = output.replace(new RegExp('\\b' + key + '\\b', 'g'), value);
|
||||
// remove trailing whitespace
|
||||
output = output.replace(/( +)\n/g, '\n');
|
||||
writeFile(path.join(typesDir, 'types.d.ts'), output);
|
||||
apiTypes = apiTypes.replace(new RegExp('\\b' + key + '\\b', 'g'), value);
|
||||
apiTypes = apiTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
|
||||
writeFile(path.join(typesDir, '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(testDocumentation);
|
||||
const testClassMapping = new Map([
|
||||
['TestType', 'Test'],
|
||||
['Config', 'TestConfig'],
|
||||
['FullConfig', 'TestConfig'],
|
||||
['Project', 'TestProject'],
|
||||
['PlaywrightWorkerOptions', 'Fixtures'],
|
||||
['PlaywrightTestOptions', 'Fixtures'],
|
||||
['PlaywrightWorkerArgs', 'Fixtures'],
|
||||
['PlaywrightTestArgs', 'Fixtures'],
|
||||
]);
|
||||
let testTypes = await testTypesGenerator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'), testClassMapping);
|
||||
testTypes = testTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
|
||||
writeFile(path.join(typesDir, '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(testReporterDocumentation);
|
||||
let testReporterTypes = await testReporterTypesGenerator.generateTypes(path.join(__dirname, 'overrides-testReporter.d.ts'), new Map());
|
||||
testReporterTypes = testReporterTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
|
||||
writeFile(path.join(typesDir, 'testReporter.d.ts'), testReporterTypes);
|
||||
|
||||
process.exit(hadChanges && process.argv.includes('--check-clean') ? 1 : 0);
|
||||
})().catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
function writeFile(filePath, content) {
|
||||
if (os.platform() === 'win32')
|
||||
content = content.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
const existing = fs.readFileSync(filePath, 'utf8');
|
||||
if (existing === content)
|
||||
return;
|
||||
hadChanges = true;
|
||||
console.error(`Writing //${path.relative(PROJECT_DIR, filePath)}`);
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} overriddes
|
||||
*/
|
||||
function objectDefinitionsToString(overriddes) {
|
||||
let definition;
|
||||
const parts = [];
|
||||
const internalWords = new Set(overriddes.split(/[^\w$]/g));
|
||||
while ((definition = objectDefinitions.pop())) {
|
||||
const {name, properties} = definition;
|
||||
const shouldExport = !!exported[name];
|
||||
const usedInternally = internalWords.has(name);
|
||||
if (!usedInternally && !shouldExport)
|
||||
continue;
|
||||
parts.push(`${shouldExport ? 'export ' : ''}interface ${name} ${stringifyObjectType(properties, name, '')}\n`)
|
||||
}
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
function nameForProperty(member) {
|
||||
return (member.required || member.alias.startsWith('...')) ? member.alias : member.alias + '?';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
function classToString(classDesc) {
|
||||
const parts = [];
|
||||
if (classDesc.comment) {
|
||||
parts.push(writeComment(classDesc.comment))
|
||||
}
|
||||
parts.push(`export interface ${classDesc.name} ${classDesc.extends ? `extends ${classDesc.extends} ` : ''}{`);
|
||||
parts.push(classBody(classDesc));
|
||||
parts.push('}\n');
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
*/
|
||||
function argNameForType(type) {
|
||||
if (type === 'void')
|
||||
return null;
|
||||
if (type.includes('{'))
|
||||
return 'data';
|
||||
return (type[0].toLowerCase() + type.slice(1)).replace(/\|/g, 'Or');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
function hasUniqueEvents(classDesc) {
|
||||
if (!classDesc.events.size)
|
||||
return false;
|
||||
const parent = parentClass(classDesc);
|
||||
if (!parent)
|
||||
return true;
|
||||
return Array.from(classDesc.events.keys()).some(eventName => !parent.events.has(eventName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
function createEventDescriptions(classDesc) {
|
||||
if (!hasUniqueEvents(classDesc))
|
||||
return [];
|
||||
const descriptions = [];
|
||||
for (let [eventName, value] of classDesc.events) {
|
||||
eventName = eventName.toLowerCase();
|
||||
const type = stringifyComplexType(value && value.type, '', classDesc.name, eventName, 'payload');
|
||||
const argName = argNameForType(type);
|
||||
const params = argName ? `${argName}: ${type}` : '';
|
||||
descriptions.push({
|
||||
type,
|
||||
params,
|
||||
eventName,
|
||||
comment: value.comment
|
||||
});
|
||||
}
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
function classBody(classDesc) {
|
||||
const parts = [];
|
||||
const eventDescriptions = 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.'
|
||||
}
|
||||
for (const method of ['on', 'once', 'addListener', 'removeListener', 'off']) {
|
||||
for (const {eventName, params, comment} of eventDescriptions) {
|
||||
if ((method === 'on' || method === 'addListener') && comment)
|
||||
parts.push(writeComment(comment, ' '));
|
||||
else
|
||||
parts.push(writeComment(commentForMethod[method], ' '));
|
||||
parts.push(` ${method}(event: '${eventName}', listener: (${params}) => void): this;\n`);
|
||||
}
|
||||
}
|
||||
|
||||
const members = classDesc.membersArray.filter(member => member.kind !== 'event');
|
||||
parts.push(members.map(member => {
|
||||
if (member.kind === 'event')
|
||||
return '';
|
||||
if (member.alias === 'waitForEvent') {
|
||||
const parts = [];
|
||||
for (const {eventName, params, comment, type} of eventDescriptions) {
|
||||
if (comment)
|
||||
parts.push(writeComment(comment, ' '));
|
||||
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 = memberJSDOC(member, ' ');
|
||||
const args = argsFromMember(member, ' ', classDesc.name);
|
||||
let type = stringifyComplexType(member.type, ' ', classDesc.name, member.alias);
|
||||
if (member.async)
|
||||
type = `Promise<${type}>`;
|
||||
// do this late, because we still want object definitions for overridden types
|
||||
if (!hasOwnMethod(classDesc, member.alias))
|
||||
return '';
|
||||
return `${jsdoc}${member.alias}${args}: ${type};`
|
||||
}).filter(x => x).join('\n\n'));
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {string} methodName
|
||||
*/
|
||||
function hasOwnMethod(classDesc, methodName) {
|
||||
if (handledMethods.has(`${classDesc.name}.${methodName}`))
|
||||
return false;
|
||||
while (classDesc = parentClass(classDesc)) {
|
||||
if (classDesc.members.has(methodName))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
*/
|
||||
function parentClass(classDesc) {
|
||||
if (!classDesc.extends)
|
||||
return null;
|
||||
return documentation.classes.get(classDesc.extends);
|
||||
}
|
||||
|
||||
function writeComment(comment, indent = '') {
|
||||
const parts = [];
|
||||
const out = [];
|
||||
const pushLine = (line) => {
|
||||
if (line || out[out.length - 1])
|
||||
out.push(line)
|
||||
};
|
||||
let skipExample = false;
|
||||
for (let line of comment.split('\n')) {
|
||||
const match = line.match(/```(\w+)/);
|
||||
if (match) {
|
||||
const lang = match[1];
|
||||
skipExample = !["html", "yml", "bash", "js"].includes(lang);
|
||||
} else if (skipExample && line.trim().startsWith('```')) {
|
||||
skipExample = false;
|
||||
continue;
|
||||
}
|
||||
if (!skipExample)
|
||||
pushLine(line);
|
||||
}
|
||||
comment = out.join('\n');
|
||||
comment = comment.replace(/\[([^\]]+)\]\(\.\/([^\)]+)\)/g, (match, p1, p2) => {
|
||||
return `[${p1}](https://playwright.dev/docs/${p2.replace('.md', '')})`;
|
||||
});
|
||||
|
||||
parts.push(indent + '/**');
|
||||
parts.push(...comment.split('\n').map(line => indent + ' * ' + line.replace(/\*\//g, '*\\/')));
|
||||
parts.push(indent + ' */');
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type} type
|
||||
*/
|
||||
function stringifyComplexType(type, indent, ...namespace) {
|
||||
if (!type)
|
||||
return 'void';
|
||||
return stringifySimpleType(type, indent, ...namespace);
|
||||
}
|
||||
|
||||
function stringifyObjectType(properties, name, indent = '') {
|
||||
const parts = [];
|
||||
parts.push(`{`);
|
||||
parts.push(properties.map(member => `${memberJSDOC(member, indent + ' ')}${nameForProperty(member)}${argsFromMember(member, indent + ' ', name)}: ${stringifyComplexType(member.type, indent + ' ', name, member.name)};`).join('\n\n'));
|
||||
parts.push(indent + '}');
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type=} type
|
||||
* @returns{string}
|
||||
*/
|
||||
function stringifySimpleType(type, indent = '', ...namespace) {
|
||||
if (!type)
|
||||
return 'void';
|
||||
if (type.name === 'Object' && type.templates) {
|
||||
const keyType = stringifySimpleType(type.templates[0], indent, ...namespace);
|
||||
const valueType = stringifySimpleType(type.templates[1], indent, ...namespace);
|
||||
return `{ [key: ${keyType}]: ${valueType}; }`;
|
||||
}
|
||||
let out = type.name;
|
||||
if (out === 'int' || out === 'float')
|
||||
out = 'number';
|
||||
|
||||
if (type.name === 'Object' && type.properties && type.properties.length) {
|
||||
const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join('');
|
||||
const shouldExport = exported[name];
|
||||
const properties = namespace[namespace.length -1] === 'options' ? type.sortedProperties() : type.properties;
|
||||
objectDefinitions.push({name, properties: properties});
|
||||
if (shouldExport) {
|
||||
out = name;
|
||||
} else {
|
||||
out = stringifyObjectType(properties, name, indent);
|
||||
}
|
||||
}
|
||||
|
||||
if (type.args) {
|
||||
const stringArgs = type.args.map(a => ({
|
||||
type: stringifySimpleType(a, indent, ...namespace),
|
||||
name: a.name.toLowerCase()
|
||||
}));
|
||||
out = `((${stringArgs.map(({name, type}) => `${name}: ${type}`).join(', ')}) => ${stringifySimpleType(type.returnType, indent, ...namespace)})`;
|
||||
} else if (type.name === 'function') {
|
||||
out = 'Function';
|
||||
}
|
||||
if (out === 'path')
|
||||
return 'string';
|
||||
if (out === 'Any')
|
||||
return 'any';
|
||||
if (type.templates)
|
||||
out += '<' + type.templates.map(t => stringifySimpleType(t, indent, ...namespace)).join(', ') + '>';
|
||||
if (type.union)
|
||||
out = type.union.map(t => stringifySimpleType(t, indent, ...namespace)).join('|');
|
||||
return out.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} member
|
||||
*/
|
||||
function argsFromMember(member, indent, ...namespace) {
|
||||
if (member.kind === 'property')
|
||||
return '';
|
||||
return '(' + member.argsArray.map(arg => `${nameForProperty(arg)}: ${stringifyComplexType(arg.type, indent, ...namespace, member.name, arg.name)}`).join(', ') + ')';
|
||||
}
|
||||
/**
|
||||
* @param {Documentation.Member} member
|
||||
* @param {string} indent
|
||||
*/
|
||||
function memberJSDOC(member, indent) {
|
||||
const lines = [];
|
||||
if (member.comment)
|
||||
lines.push(...member.comment.split('\n'));
|
||||
if (member.deprecated)
|
||||
lines.push('@deprecated');
|
||||
lines.push(...member.argsArray.map(arg => `@param ${arg.alias.replace(/\./g, '')} ${arg.comment.replace('\n', ' ')}`));
|
||||
if (!lines.length)
|
||||
return indent;
|
||||
return writeComment(lines.join('\n'), indent) + '\n' + indent;
|
||||
}
|
||||
|
||||
function generateDevicesTypes() {
|
||||
const namedDevices =
|
||||
Object.keys(devices)
|
||||
.map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`)
|
||||
.join('\n');
|
||||
return `type Devices = {
|
||||
${namedDevices}
|
||||
[key: string]: DeviceDescriptor;
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member[]} args
|
||||
*/
|
||||
function renderJSSignature(args) {
|
||||
const tokens = [];
|
||||
let hasOptional = false;
|
||||
for (const arg of args) {
|
||||
const name = arg.alias;
|
||||
const optional = !arg.required;
|
||||
if (tokens.length) {
|
||||
if (optional && !hasOptional)
|
||||
tokens.push(`[, ${name}`);
|
||||
else
|
||||
tokens.push(`, ${name}`);
|
||||
} else {
|
||||
if (optional && !hasOptional)
|
||||
tokens.push(`[${name}`);
|
||||
else
|
||||
tokens.push(`${name}`);
|
||||
}
|
||||
hasOptional = hasOptional || optional;
|
||||
}
|
||||
if (hasOptional)
|
||||
tokens.push(']');
|
||||
return tokens.join('');
|
||||
}
|
||||
|
||||
338
utils/generate_types/overrides-test.d.ts
vendored
Normal file
338
utils/generate_types/overrides-test.d.ts
vendored
Normal file
@ -0,0 +1,338 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials } from './types';
|
||||
import type { Expect } from './testExpect';
|
||||
|
||||
export type { Expect } from './testExpect';
|
||||
|
||||
export type ReporterDescription =
|
||||
['dot'] |
|
||||
['line'] |
|
||||
['list'] |
|
||||
['junit'] | ['junit', { outputFile?: string, stripANSIControlSequences?: boolean }] |
|
||||
['json'] | ['json', { outputFile?: string }] |
|
||||
['null'] |
|
||||
[string] | [string, any];
|
||||
|
||||
export type Shard = { total: number, current: number } | null;
|
||||
export type ReportSlowTests = { max: number, threshold: number } | null;
|
||||
export type PreserveOutput = 'always' | 'never' | 'failures-only';
|
||||
export type UpdateSnapshots = 'all' | 'none' | 'missing';
|
||||
|
||||
type FixtureDefine<TestArgs extends KeyValue = {}, WorkerArgs extends KeyValue = {}> = { test: TestType<TestArgs, WorkerArgs>, fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs> };
|
||||
|
||||
type ExpectSettings = {
|
||||
toMatchSnapshot?: {
|
||||
// Pixel match threshold.
|
||||
threshold?: number
|
||||
}
|
||||
};
|
||||
|
||||
interface TestProject {
|
||||
expect?: ExpectSettings;
|
||||
metadata?: any;
|
||||
name?: string;
|
||||
outputDir?: string;
|
||||
repeatEach?: number;
|
||||
retries?: number;
|
||||
testDir?: string;
|
||||
testIgnore?: string | RegExp | (string | RegExp)[];
|
||||
testMatch?: string | RegExp | (string | RegExp)[];
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export interface Project<TestArgs = {}, WorkerArgs = {}> extends TestProject {
|
||||
define?: FixtureDefine | FixtureDefine[];
|
||||
use?: Fixtures<{}, {}, TestArgs, WorkerArgs>;
|
||||
}
|
||||
|
||||
export type FullProject<TestArgs = {}, WorkerArgs = {}> = Required<Project<TestArgs, WorkerArgs>>;
|
||||
|
||||
export type LaunchConfig = {
|
||||
/**
|
||||
* Shell command to start. For example `npm run start`.
|
||||
*/
|
||||
command: string,
|
||||
/**
|
||||
* The port that your http server is expected to appear on. If specified it does wait until it accepts connections.
|
||||
*/
|
||||
waitForPort?: number,
|
||||
/**
|
||||
* How long to wait for the process to start up and be available in milliseconds. Defaults to 60000.
|
||||
*/
|
||||
waitForPortTimeout?: number,
|
||||
/**
|
||||
* If true it will verify that the given port via `waitForPort` is available and throw otherwise.
|
||||
* This should commonly set to !!process.env.CI to allow the local dev server when running tests locally.
|
||||
*/
|
||||
strict?: boolean
|
||||
/**
|
||||
* Environment variables, process.env by default
|
||||
*/
|
||||
env?: Record<string, string>,
|
||||
/**
|
||||
* Current working directory of the spawned process. Default is process.cwd().
|
||||
*/
|
||||
cwd?: string,
|
||||
};
|
||||
|
||||
type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never });
|
||||
|
||||
interface TestConfig {
|
||||
forbidOnly?: boolean;
|
||||
globalSetup?: string;
|
||||
globalTeardown?: string;
|
||||
globalTimeout?: number;
|
||||
grep?: RegExp | RegExp[];
|
||||
grepInvert?: RegExp | RegExp[];
|
||||
maxFailures?: number;
|
||||
preserveOutput?: PreserveOutput;
|
||||
projects?: Project[];
|
||||
quiet?: boolean;
|
||||
reporter?: LiteralUnion<'list'|'dot'|'line'|'json'|'junit'|'null', string> | ReporterDescription[];
|
||||
reportSlowTests?: ReportSlowTests;
|
||||
shard?: Shard;
|
||||
updateSnapshots?: UpdateSnapshots;
|
||||
_launch?: LaunchConfig | LaunchConfig[];
|
||||
workers?: number;
|
||||
|
||||
expect?: ExpectSettings;
|
||||
metadata?: any;
|
||||
name?: string;
|
||||
outputDir?: string;
|
||||
repeatEach?: number;
|
||||
retries?: number;
|
||||
testDir?: string;
|
||||
testIgnore?: string | RegExp | (string | RegExp)[];
|
||||
testMatch?: string | RegExp | (string | RegExp)[];
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig {
|
||||
projects?: Project<TestArgs, WorkerArgs>[];
|
||||
define?: FixtureDefine | FixtureDefine[];
|
||||
use?: Fixtures<{}, {}, TestArgs, WorkerArgs>;
|
||||
}
|
||||
|
||||
export interface FullConfig {
|
||||
forbidOnly: boolean;
|
||||
globalSetup: string | null;
|
||||
globalTeardown: string | null;
|
||||
globalTimeout: number;
|
||||
grep: RegExp | RegExp[];
|
||||
grepInvert: RegExp | RegExp[] | null;
|
||||
maxFailures: number;
|
||||
preserveOutput: PreserveOutput;
|
||||
projects: FullProject[];
|
||||
reporter: ReporterDescription[];
|
||||
reportSlowTests: ReportSlowTests;
|
||||
rootDir: string;
|
||||
quiet: boolean;
|
||||
shard: Shard;
|
||||
updateSnapshots: UpdateSnapshots;
|
||||
workers: number;
|
||||
_launch: LaunchConfig[];
|
||||
}
|
||||
|
||||
export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped';
|
||||
|
||||
export interface TestError {
|
||||
message?: string;
|
||||
stack?: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface WorkerInfo {
|
||||
config: FullConfig;
|
||||
project: FullProject;
|
||||
workerIndex: number;
|
||||
}
|
||||
|
||||
export interface TestInfo {
|
||||
config: FullConfig;
|
||||
project: FullProject;
|
||||
workerIndex: number;
|
||||
|
||||
title: string;
|
||||
file: string;
|
||||
line: number;
|
||||
column: number;
|
||||
fn: Function;
|
||||
|
||||
skip(): void;
|
||||
skip(condition: boolean): void;
|
||||
skip(condition: boolean, description: string): void;
|
||||
|
||||
fixme(): void;
|
||||
fixme(condition: boolean): void;
|
||||
fixme(condition: boolean, description: string): void;
|
||||
|
||||
fail(): void;
|
||||
fail(condition: boolean): void;
|
||||
fail(condition: boolean, description: string): void;
|
||||
|
||||
slow(): void;
|
||||
slow(condition: boolean): void;
|
||||
slow(condition: boolean, description: string): void;
|
||||
|
||||
setTimeout(timeout: number): void;
|
||||
expectedStatus: TestStatus;
|
||||
timeout: number;
|
||||
annotations: { type: string, description?: string }[];
|
||||
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[];
|
||||
repeatEachIndex: number;
|
||||
retry: number;
|
||||
duration: number;
|
||||
status?: TestStatus;
|
||||
error?: TestError;
|
||||
stdout: (string | Buffer)[];
|
||||
stderr: (string | Buffer)[];
|
||||
snapshotSuffix: string;
|
||||
outputDir: string;
|
||||
snapshotPath: (snapshotName: string) => string;
|
||||
outputPath: (...pathSegments: string[]) => string;
|
||||
}
|
||||
|
||||
interface SuiteFunction {
|
||||
(title: string, callback: () => void): void;
|
||||
}
|
||||
|
||||
interface TestFunction<TestArgs> {
|
||||
(title: string, testFunction: (args: TestArgs, testInfo: TestInfo) => Promise<void> | void): void;
|
||||
}
|
||||
|
||||
export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue> extends TestFunction<TestArgs & WorkerArgs> {
|
||||
only: TestFunction<TestArgs & WorkerArgs>;
|
||||
describe: SuiteFunction & {
|
||||
only: SuiteFunction;
|
||||
};
|
||||
skip(): void;
|
||||
skip(condition: boolean): void;
|
||||
skip(condition: boolean, description: string): void;
|
||||
skip(callback: (args: TestArgs & WorkerArgs) => boolean): void;
|
||||
skip(callback: (args: TestArgs & WorkerArgs) => boolean, description: string): void;
|
||||
fixme(): void;
|
||||
fixme(condition: boolean): void;
|
||||
fixme(condition: boolean, description: string): void;
|
||||
fixme(callback: (args: TestArgs & WorkerArgs) => boolean): void;
|
||||
fixme(callback: (args: TestArgs & WorkerArgs) => boolean, description: string): void;
|
||||
fail(): void;
|
||||
fail(condition: boolean): void;
|
||||
fail(condition: boolean, description: string): void;
|
||||
fail(callback: (args: TestArgs & WorkerArgs) => boolean): void;
|
||||
fail(callback: (args: TestArgs & WorkerArgs) => boolean, description: string): void;
|
||||
slow(): void;
|
||||
slow(condition: boolean): void;
|
||||
slow(condition: boolean, description: string): void;
|
||||
slow(callback: (args: TestArgs & WorkerArgs) => boolean): void;
|
||||
slow(callback: (args: TestArgs & WorkerArgs) => boolean, description: string): void;
|
||||
setTimeout(timeout: number): void;
|
||||
beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
|
||||
afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
|
||||
beforeAll(inner: (args: WorkerArgs, workerInfo: WorkerInfo) => Promise<any> | any): void;
|
||||
afterAll(inner: (args: WorkerArgs, workerInfo: WorkerInfo) => Promise<any> | any): void;
|
||||
use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void;
|
||||
expect: Expect;
|
||||
declare<T extends KeyValue = {}, W extends KeyValue = {}>(): TestType<TestArgs & T, WorkerArgs & W>;
|
||||
extend<T, W extends KeyValue = {}>(fixtures: Fixtures<T, W, TestArgs, WorkerArgs>): TestType<TestArgs & T, WorkerArgs & W>;
|
||||
}
|
||||
|
||||
type KeyValue = { [key: string]: any };
|
||||
export type TestFixture<R, Args extends KeyValue> = (args: Args, use: (r: R) => Promise<void>, testInfo: TestInfo) => any;
|
||||
export type WorkerFixture<R, Args extends KeyValue> = (args: Args, use: (r: R) => Promise<void>, workerInfo: WorkerInfo) => any;
|
||||
type TestFixtureValue<R, Args> = R | TestFixture<R, Args>;
|
||||
type WorkerFixtureValue<R, Args> = R | WorkerFixture<R, Args>;
|
||||
export type Fixtures<T extends KeyValue = {}, W extends KeyValue = {}, PT extends KeyValue = {}, PW extends KeyValue = {}> = {
|
||||
[K in keyof PW]?: WorkerFixtureValue<PW[K], W & PW>;
|
||||
} & {
|
||||
[K in keyof PT]?: TestFixtureValue<PT[K], T & W & PT & PW>;
|
||||
} & {
|
||||
[K in keyof W]?: [WorkerFixtureValue<W[K], W & PW>, { scope: 'worker', auto?: boolean }];
|
||||
} & {
|
||||
[K in keyof T]?: TestFixtureValue<T[K], T & W & PT & PW> | [TestFixtureValue<T[K], T & W & PT & PW>, { scope?: 'test', auto?: boolean }];
|
||||
};
|
||||
|
||||
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
||||
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
||||
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
||||
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
||||
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
||||
|
||||
export interface PlaywrightWorkerOptions {
|
||||
browserName: BrowserName;
|
||||
defaultBrowserType: BrowserName;
|
||||
headless: boolean | undefined;
|
||||
channel: BrowserChannel | undefined;
|
||||
launchOptions: LaunchOptions;
|
||||
}
|
||||
|
||||
export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-video';
|
||||
|
||||
export interface PlaywrightTestOptions {
|
||||
screenshot: 'off' | 'on' | 'only-on-failure';
|
||||
trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace';
|
||||
video: VideoMode | { mode: VideoMode, size: ViewportSize };
|
||||
acceptDownloads: boolean | undefined;
|
||||
bypassCSP: boolean | undefined;
|
||||
colorScheme: ColorScheme | undefined;
|
||||
deviceScaleFactor: number | undefined;
|
||||
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
||||
geolocation: Geolocation | undefined;
|
||||
hasTouch: boolean | undefined;
|
||||
httpCredentials: HTTPCredentials | undefined;
|
||||
ignoreHTTPSErrors: boolean | undefined;
|
||||
isMobile: boolean | undefined;
|
||||
javaScriptEnabled: boolean | undefined;
|
||||
locale: string | undefined;
|
||||
offline: boolean | undefined;
|
||||
permissions: string[] | undefined;
|
||||
proxy: Proxy | undefined;
|
||||
storageState: StorageState | undefined;
|
||||
timezoneId: string | undefined;
|
||||
userAgent: string | undefined;
|
||||
viewport: ViewportSize | null | undefined;
|
||||
baseURL: string | undefined;
|
||||
contextOptions: BrowserContextOptions;
|
||||
}
|
||||
|
||||
|
||||
export interface PlaywrightWorkerArgs {
|
||||
playwright: typeof import('..');
|
||||
browser: Browser;
|
||||
}
|
||||
|
||||
export interface PlaywrightTestArgs {
|
||||
context: BrowserContext;
|
||||
page: Page;
|
||||
}
|
||||
|
||||
export type PlaywrightTestProject<TestArgs = {}, WorkerArgs = {}> = Project<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>;
|
||||
export type PlaywrightTestConfig<TestArgs = {}, WorkerArgs = {}> = Config<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>;
|
||||
|
||||
/**
|
||||
* These tests are executed in Playwright environment that launches the browser
|
||||
* and provides a fresh page to each test.
|
||||
*/
|
||||
export const test: TestType<PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
|
||||
export default test;
|
||||
|
||||
export const _baseTest: TestType<{}, {}>;
|
||||
export const expect: Expect;
|
||||
|
||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||
export {};
|
||||
85
utils/generate_types/overrides-testReporter.d.ts
vendored
Normal file
85
utils/generate_types/overrides-testReporter.d.ts
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { FullConfig, TestStatus, TestError } from './test';
|
||||
export type { FullConfig, TestStatus, TestError } from './test';
|
||||
|
||||
export interface Location {
|
||||
file: string;
|
||||
line: number;
|
||||
column: number;
|
||||
}
|
||||
|
||||
export interface Suite {
|
||||
title: string;
|
||||
location?: Location;
|
||||
suites: Suite[];
|
||||
tests: TestCase[];
|
||||
titlePath(): string[];
|
||||
allTests(): TestCase[];
|
||||
}
|
||||
|
||||
export interface TestCase {
|
||||
title: string;
|
||||
location: Location;
|
||||
titlePath(): string[];
|
||||
expectedStatus: TestStatus;
|
||||
timeout: number;
|
||||
annotations: { type: string, description?: string }[];
|
||||
retries: number;
|
||||
results: TestResult[];
|
||||
outcome(): 'skipped' | 'expected' | 'unexpected' | 'flaky';
|
||||
ok(): boolean;
|
||||
}
|
||||
|
||||
export interface TestResult {
|
||||
retry: number;
|
||||
workerIndex: number;
|
||||
startTime: Date;
|
||||
duration: number;
|
||||
status?: TestStatus;
|
||||
error?: TestError;
|
||||
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[];
|
||||
stdout: (string | Buffer)[];
|
||||
stderr: (string | Buffer)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of the full test run.
|
||||
*/
|
||||
export interface FullResult {
|
||||
/**
|
||||
* Status:
|
||||
* - 'passed' - everything went as expected.
|
||||
* - 'failed' - any test has failed.
|
||||
* - 'timedout' - the global time has been reached.
|
||||
* - 'interrupted' - interrupted by the user.
|
||||
*/
|
||||
status: 'passed' | 'failed' | 'timedout' | 'interrupted';
|
||||
}
|
||||
|
||||
export interface Reporter {
|
||||
onBegin?(config: FullConfig, suite: Suite): void;
|
||||
onTestBegin?(test: TestCase): void;
|
||||
onStdOut?(chunk: string | Buffer, test?: TestCase): void;
|
||||
onStdErr?(chunk: string | Buffer, test?: TestCase): void;
|
||||
onTestEnd?(test: TestCase, result: TestResult): void;
|
||||
onError?(error: TestError): void;
|
||||
onEnd?(result: FullResult): void | Promise<void>;
|
||||
}
|
||||
|
||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||
export {};
|
||||
@ -16,13 +16,14 @@
|
||||
|
||||
const path = require('path');
|
||||
const ts = require('typescript');
|
||||
|
||||
/**
|
||||
* @param {(className: string) => string} commentForClass
|
||||
* @param {(className: string, methodName: string) => string} commentForMethod
|
||||
* @param {(className: string) => string} extraForClass
|
||||
* @param {string} filePath
|
||||
* @param {(className: string) => string} commentForClass
|
||||
* @param {(className: string, methodName: string) => string} commentForMethod
|
||||
* @param {(className: string) => string} extraForClass
|
||||
*/
|
||||
async function parseOverrides(commentForClass, commentForMethod, extraForClass) {
|
||||
const filePath = path.join(__dirname, 'overrides.d.ts');
|
||||
async function parseOverrides(filePath, commentForClass, commentForMethod, extraForClass) {
|
||||
const program = ts.createProgram({
|
||||
rootNames: [filePath],
|
||||
options: {
|
||||
@ -75,7 +76,9 @@ async function parseOverrides(commentForClass, commentForMethod, extraForClass)
|
||||
for (const [name, member] of symbol.members || []) {
|
||||
if (member.flags & ts.SymbolFlags.TypeParameter)
|
||||
continue;
|
||||
const pos = member.valueDeclaration.getStart(file, false)
|
||||
if (!member.valueDeclaration)
|
||||
continue;
|
||||
const pos = member.valueDeclaration.getStart(file, false);
|
||||
replacers.push({
|
||||
pos,
|
||||
text: commentForMethod(className, name),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user