chore: generate more types (#13358)

As a result, fix documentation issues:
- Removed `TestStep.data` that is not a thing.
- Added `TestConfig.name` documentation.
- Refined a lot of optional types in the documentation.
- Aligned `test.fail()` and `test.slow()` docs with `test.skip()`.
- Strict set of docs vs types inconsistensies in the generator
This commit is contained in:
Dmitry Gozman 2022-04-06 13:36:20 -07:00 committed by GitHub
parent d9d826b7f6
commit 4123a55be5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 794 additions and 1067 deletions

View file

@ -9,9 +9,9 @@ title: "Annotations"
Playwright Test supports test annotations to deal with failures, flakiness, skip, focus and tag tests: Playwright Test supports test annotations to deal with failures, flakiness, skip, focus and tag tests:
- [`method: Test.skip#1`] marks the test as irrelevant. Playwright Test does not run such a test. Use this annotation when the test is not applicable in some configuration. - [`method: Test.skip#1`] marks the test as irrelevant. Playwright Test does not run such a test. Use this annotation when the test is not applicable in some configuration.
- [`method: Test.fail`] marks the test as failing. Playwright Test will run this test and ensure it does indeed fail. If the test does not fail, Playwright Test will complain. - [`method: Test.fail#1`] marks the test as failing. Playwright Test will run this test and ensure it does indeed fail. If the test does not fail, Playwright Test will complain.
- [`method: Test.fixme#1`] marks the test as failing. Playwright Test will not run this test, as opposite to the `fail` annotation. Use `fixme` when running the test is slow or crashy. - [`method: Test.fixme#1`] marks the test as failing. Playwright Test will not run this test, as opposite to the `fail` annotation. Use `fixme` when running the test is slow or crashy.
- [`method: Test.slow`] marks the test as slow and triples the test timeout. - [`method: Test.slow#1`] marks the test as slow and triples the test timeout.
Annotations can be used on a single test or a group of tests. Annotations can be conditional, in which case they apply when the condition is truthy. Annotations may depend on test fixtures. There could be multiple annotations on the same test, possibly in different configurations. Annotations can be used on a single test or a group of tests. Annotations can be conditional, in which case they apply when the condition is truthy. Annotations may depend on test fixtures. There could be multiple annotations on the same test, possibly in different configurations.

View file

@ -630,11 +630,9 @@ An object containing fixtures and/or options. Learn more about [fixtures format]
## method: Test.fail ## method: Test.fail#1
Marks a test or a group of tests as "should fail". Playwright Test runs these tests and ensures that they are actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. Unconditonally marks a test as "should fail". Playwright Test runs this test and ensures that it is actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed.
Unconditional fail:
```js js-flavor=js ```js js-flavor=js
const { test, expect } = require('@playwright/test'); const { test, expect } = require('@playwright/test');
@ -654,7 +652,9 @@ test('not yet ready', async ({ page }) => {
}); });
``` ```
Conditional fail a test with an optional description: ## method: Test.fail#2
Conditionally mark a test as "should fail" with an optional description.
```js js-flavor=js ```js js-flavor=js
const { test, expect } = require('@playwright/test'); const { test, expect } = require('@playwright/test');
@ -674,45 +674,56 @@ test('fail in WebKit', async ({ page, browserName }) => {
}); });
``` ```
Conditional fail for all tests in a file or [`method: Test.describe`] group: ### param: Test.fail#2.condition
- `condition` <[boolean]>
```js js-flavor=js Test is marked as "should fail" when the condition is `true`.
const { test, expect } = require('@playwright/test');
test.fail(({ browserName }) => browserName === 'webkit'); ### param: Test.fail#2.description
- `description` <[string]>
test('fail in WebKit 1', async ({ page }) => {
// ...
});
test('fail in WebKit 2', async ({ page }) => {
// ...
});
```
```js js-flavor=ts
import { test, expect } from '@playwright/test';
test.fail(({ browserName }) => browserName === 'webkit');
test('fail in WebKit 1', async ({ page }) => {
// ...
});
test('fail in WebKit 2', async ({ page }) => {
// ...
});
```
### param: Test.fail.condition
- `condition` <[void]|[boolean]|[function]\([Fixtures]\):[boolean]>
Optional condition - either a boolean value, or a function that takes a fixtures object and returns a boolean. Test or tests are marked as "should fail" when the condition is `true`.
### param: Test.fail.description
- `description` <[void]|[string]>
Optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## method: Test.fail#3
Conditionally mark all tests in a file or [`method: Test.describe`] group as "should fail".
```js js-flavor=js
const { test, expect } = require('@playwright/test');
test.fail(({ browserName }) => browserName === 'webkit');
test('fail in WebKit 1', async ({ page }) => {
// ...
});
test('fail in WebKit 2', async ({ page }) => {
// ...
});
```
```js js-flavor=ts
import { test, expect } from '@playwright/test';
test.fail(({ browserName }) => browserName === 'webkit');
test('fail in WebKit 1', async ({ page }) => {
// ...
});
test('fail in WebKit 2', async ({ page }) => {
// ...
});
```
### param: Test.fail#3.condition
- `callback` <[function]\([Fixtures]\):[boolean]>
A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as "should fail" when the return value is `true`.
### param: Test.fail#3.description
- `description` <[string]>
Optional description that will be reflected in a test report.
## method: Test.fixme#1 ## method: Test.fixme#1
@ -824,12 +835,12 @@ test('broken in WebKit', async ({ page, browserName }) => {
### param: Test.fixme#3.condition ### param: Test.fixme#3.condition
- `condition` <[boolean]> - `condition` <[boolean]>
Test or tests are marked as "fixme" when the condition is `true`. Test is marked as "fixme" when the condition is `true`.
### param: Test.fixme#3.description ### param: Test.fixme#3.description
- `description` <[void]|[string]> - `description` <[string]>
An optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
@ -871,9 +882,9 @@ test('broken in WebKit 2', async ({ page }) => {
A function that returns whether to mark as "fixme", based on test fixtures. Test or tests are marked as "fixme" when the return value is `true`. A function that returns whether to mark as "fixme", based on test fixtures. Test or tests are marked as "fixme" when the return value is `true`.
### param: Test.fixme#4.description ### param: Test.fixme#4.description
- `description` <[void]|[string]> - `description` <[string]>
An optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## method: Test.info ## method: Test.info
@ -1089,12 +1100,12 @@ test.beforeEach(async ({ page }) => {
### param: Test.skip#3.condition ### param: Test.skip#3.condition
- `condition` <[boolean]> - `condition` <[boolean]>
A skip condition. Test or tests are skipped when the condition is `true`. A skip condition. Test is skipped when the condition is `true`.
### param: Test.skip#3.description ### param: Test.skip#3.description
- `description` <[void]|[string]> - `description` <[void]|[string]>
An optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
@ -1136,17 +1147,15 @@ test('skip in WebKit 2', async ({ page }) => {
A function that returns whether to skip, based on test fixtures. Test or tests are skipped when the return value is `true`. A function that returns whether to skip, based on test fixtures. Test or tests are skipped when the return value is `true`.
### param: Test.skip#4.description ### param: Test.skip#4.description
- `description` <[void]|[string]> - `description` <[string]>
An optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## method: Test.slow ## method: Test.slow#1
Marks a test or a group of tests as "slow". Slow tests will be given triple the default timeout. Unconditionally marks a test as "slow". Slow test will be given triple the default timeout.
Unconditional slow:
```js js-flavor=js ```js js-flavor=js
const { test, expect } = require('@playwright/test'); const { test, expect } = require('@playwright/test');
@ -1166,7 +1175,9 @@ test('slow test', async ({ page }) => {
}); });
``` ```
Conditional slow a test with an optional description: ## method: Test.slow#2
Conditionally mark a test as "slow" with an optional description. Slow test will be given triple the default timeout.
```js js-flavor=js ```js js-flavor=js
const { test, expect } = require('@playwright/test'); const { test, expect } = require('@playwright/test');
@ -1186,7 +1197,20 @@ test('slow in WebKit', async ({ page, browserName }) => {
}); });
``` ```
Conditional slow for all tests in a file or [`method: Test.describe`] group: ### param: Test.slow#2.condition
- `condition` <[boolean]>
Test is marked as "slow" when the condition is `true`.
### param: Test.slow#2.description
- `description` <[string]>
Optional description that will be reflected in a test report.
## method: Test.slow#3
Conditionally mark all tests in a file or [`method: Test.describe`] group as "slow". Slow tests will be given triple the default timeout.
```js js-flavor=js ```js js-flavor=js
const { test, expect } = require('@playwright/test'); const { test, expect } = require('@playwright/test');
@ -1214,13 +1238,13 @@ test('fail in WebKit 2', async ({ page }) => {
}); });
``` ```
### param: Test.slow.condition ### param: Test.slow#3.condition
- `condition` <[void]|[boolean]|[function]\([Fixtures]\):[boolean]> - `callback` <[function]\([Fixtures]\):[boolean]>
Optional condition - either a boolean value, or a function that takes a fixtures object and returns a boolean. Test or tests are marked as "slow" when the condition is `true`. A function that returns whether to mark as "slow", based on test fixtures. Test or tests are marked as "slow" when the return value is `true`.
### param: Test.slow.description ### param: Test.slow#3.description
- `description` <[void]|[string]> - `description` <[string]>
Optional description that will be reflected in a test report. Optional description that will be reflected in a test report.

View file

@ -245,6 +245,11 @@ export default config;
Any JSON-serializable metadata that will be put directly to the test report. Any JSON-serializable metadata that will be put directly to the test report.
## property: TestConfig.name
- type: <[string]>
Config name is visible in the report and during test execution, unless overridden by [`property: TestProject.name`].
## property: TestConfig.outputDir ## property: TestConfig.outputDir
- type: <[string]> - type: <[string]>

View file

@ -4,16 +4,16 @@
Information about an error thrown during test execution. Information about an error thrown during test execution.
## property: TestError.message ## property: TestError.message
- type: <[void]|[string]> - type: <[string]>
Error message. Set when [Error] (or its subclass) has been thrown. Optional error message. Set when [Error] (or its subclass) has been thrown.
## property: TestError.stack ## property: TestError.stack
- type: <[void]|[string]> - type: <[string]>
Error stack. Set when [Error] (or its subclass) has been thrown. Optional error stack. Set when [Error] (or its subclass) has been thrown.
## property: TestError.value ## property: TestError.value
- type: <[void]|[string]> - type: <[string]>
The value that was thrown. Set when anything except the [Error] (or its subclass) has been thrown. Optional value that was thrown. Set when anything except the [Error] (or its subclass) has been thrown.

View file

@ -25,7 +25,7 @@ test('basic test', async ({ page }, testInfo) => {
## property: TestInfo.annotations ## property: TestInfo.annotations
- type: <[Array]<[Object]>> - type: <[Array]<[Object]>>
- `type` <[string]> Annotation type, for example `'skip'` or `'fail'`. - `type` <[string]> Annotation type, for example `'skip'` or `'fail'`.
- `description` <[void]|[string]> Optional description. - `description` <[string]> Optional description.
The list of annotations applicable to the current test. Includes annotations from the test, annotations from all [`method: Test.describe`] groups the test belongs to and file-level annotations for the test file. The list of annotations applicable to the current test. Includes annotations from the test, annotations from all [`method: Test.describe`] groups the test belongs to and file-level annotations for the test file.
@ -35,8 +35,8 @@ Learn more about [test annotations](../test-annotations.md).
- type: <[Array]<[Object]>> - type: <[Array]<[Object]>>
- `name` <[string]> Attachment name. - `name` <[string]> Attachment name.
- `contentType` <[string]> Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`. - `contentType` <[string]> Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`.
- `path` <[void]|[string]> Optional path on the filesystem to the attached file. - `path` <[string]> Optional path on the filesystem to the attached file.
- `body` <[void]|[Buffer]> Optional attachment body used instead of a file. - `body` <[Buffer]> Optional attachment body used instead of a file.
The list of files or buffers attached to the current test. Some reporters show test attachments. The list of files or buffers attached to the current test. Some reporters show test attachments.
@ -103,7 +103,7 @@ after awaiting the attach call.
- `body` <[string]|[Buffer]> Attachment body. Mutually exclusive with [`option: path`]. - `body` <[string]|[Buffer]> Attachment body. Mutually exclusive with [`option: path`].
### option: TestInfo.attach.contentType ### option: TestInfo.attach.contentType
- `contentType` <[void]|[string]> Optional content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`. If omitted, content type is inferred based on the [`option: path`], or defaults to `text/plain` for [string] attachments and `application/octet-stream` for [Buffer] attachments. - `contentType` <[string]> Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`. If omitted, content type is inferred based on the [`option: path`], or defaults to `text/plain` for [string] attachments and `application/octet-stream` for [Buffer] attachments.
### option: TestInfo.attach.path ### option: TestInfo.attach.path
- `path` <[string]> Path on the filesystem to the attached file. Mutually exclusive with [`option: body`]. - `path` <[string]> Path on the filesystem to the attached file. Mutually exclusive with [`option: body`].
@ -128,9 +128,9 @@ The number of milliseconds the test took to finish. Always zero before the test
## property: TestInfo.error ## property: TestInfo.error
- type: <[void]|[TestError]> - type: <[TestError]>
First error thrown during test execution, if any. This is equal to the first Optional first error thrown during test execution, if any. This is equal to the first
element in [`property: TestInfo.errors`]. element in [`property: TestInfo.errors`].
## property: TestInfo.errors ## property: TestInfo.errors
@ -144,7 +144,7 @@ Errors thrown during test execution, if any.
Expected status for the currently running test. This is usually `'passed'`, except for a few cases: Expected status for the currently running test. This is usually `'passed'`, except for a few cases:
* `'skipped'` for skipped tests, e.g. with [`method: Test.skip#2`]; * `'skipped'` for skipped tests, e.g. with [`method: Test.skip#2`];
* `'failed'` for tests marked as failed with [`method: Test.fail`]. * `'failed'` for tests marked as failed with [`method: Test.fail#1`].
Expected status is usually compared with the actual [`property: TestInfo.status`]: Expected status is usually compared with the actual [`property: TestInfo.status`]:
@ -166,41 +166,52 @@ test.afterEach(async ({}, testInfo) => {
}); });
``` ```
## method: TestInfo.fail ## method: TestInfo.fail#1
Marks the currently running test as "should fail". Playwright Test ensures that this test is actually failing. This is similar to [`method: Test.fail`]. Marks the currently running test as "should fail". Playwright Test runs theis tests and ensures that it is actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. This is similar to [`method: Test.fail#1`].
### param: TestInfo.fail.condition ## method: TestInfo.fail#2
- `condition` <[void]|[boolean]>
Optional condition - the test is marked as "should fail" when the condition is `true`. Conditionally mark the currently running test as "should fail" with an optional description. This is similar to [`method: Test.fail#2`].
### param: TestInfo.fail.description ### param: TestInfo.fail#2.condition
- `description` <[void]|[string]> - `condition` <[boolean]>
Test is marked as "should fail" when the condition is `true`.
### param: TestInfo.fail#2.description
- `description` <[string]>
Optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## property: TestInfo.file ## property: TestInfo.file
- type: <[string]> - type: <[string]>
Absolute path to a file where the currently running test is declared. Absolute path to a file where the currently running test is declared.
## method: TestInfo.fixme
Marks the currently running test as "fixme". The test will be skipped, but the intention is to fix it. This is similar to [`method: Test.fixme#2`]. ## method: TestInfo.fixme#1
### param: TestInfo.fixme.condition Mark a test as "fixme", with the intention to fix it. Test is immediately aborted. This is similar to [`method: Test.fixme#2`].
- `condition` <[void]|[boolean]>
Optional condition - the test is marked as "fixme" when the condition is `true`. ## method: TestInfo.fixme#2
### param: TestInfo.fixme.description Conditionally mark the currently running test as "fixme" with an optional description. This is similar to [`method: Test.fixme#3`].
- `description` <[void]|[string]>
### param: TestInfo.fixme#2.condition
- `condition` <[boolean]>
Test is marked as "fixme" when the condition is `true`.
### param: TestInfo.fixme#2.description
- `description` <[string]>
Optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## property: TestInfo.fn ## property: TestInfo.fn
- type: <[function]\([TestArgs], [TestInfo]\)> - type: <[function]>
Test function as passed to `test(title, testFunction)`. Test function as passed to `test(title, testFunction)`.
@ -248,7 +259,7 @@ test('example test', async ({}, testInfo) => {
> However, this path must stay within the [`property: TestInfo.outputDir`] directory for each test (i.e. `test-results/a-test-title`), otherwise it will throw. > However, this path must stay within the [`property: TestInfo.outputDir`] directory for each test (i.e. `test-results/a-test-title`), otherwise it will throw.
### param: TestInfo.outputPath.pathSegments ### param: TestInfo.outputPath.pathSegments
- `pathSegments` <[string...]> - `...pathSegments` <[Array]<[string]>>
Path segments to append at the end of the resulting path. Path segments to append at the end of the resulting path.
@ -338,34 +349,44 @@ test.beforeEach(async ({ page }, testInfo) => {
Timeout in milliseconds. Timeout in milliseconds.
## method: TestInfo.skip ## method: TestInfo.skip#1
Skips the currently running test. This is similar to [`method: Test.skip#2`]. Unconditionally skip the currently running test. Test is immediately aborted. This is similar to [`method: Test.skip#2`].
### param: TestInfo.skip.condition ## method: TestInfo.skip#2
- `condition` <[void]|[boolean]>
Optional condition - the test is skipped when the condition is `true`. Conditionally skips the currently running test with an optional description. This is similar to [`method: Test.skip#3`].
### param: TestInfo.skip.description ### param: TestInfo.skip#2.condition
- `description` <[void]|[string]> - `condition` <[boolean]>
A skip condition. Test is skipped when the condition is `true`.
### param: TestInfo.skip#2.description
- `description` <[string]>
Optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## method: TestInfo.slow
Marks the currently running test as "slow", giving it triple the default timeout. This is similar to [`method: Test.slow`]. ## method: TestInfo.slow#1
### param: TestInfo.slow.condition Marks the currently running test as "slow", giving it triple the default timeout. This is similar to [`method: Test.slow#1`].
- `condition` <[void]|[boolean]>
Optional condition - the test is marked as "slow" when the condition is `true`. ## method: TestInfo.slow#2
### param: TestInfo.slow.description Conditionally mark the currently running test as "slow" with an optional description, giving it triple the default timeout. This is similar to [`method: Test.slow#2`].
- `description` <[void]|[string]>
### param: TestInfo.slow#2.condition
- `condition` <[boolean]>
Test is marked as "slow" when the condition is `true`.
### param: TestInfo.slow#2.description
- `description` <[string]>
Optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## method: TestInfo.snapshotPath ## method: TestInfo.snapshotPath
- returns: <[string]> - returns: <[string]>
@ -375,7 +396,7 @@ Returns a path to a snapshot file with the given `pathSegments`. Learn more abou
> However, this path must stay within the snapshots directory for each test file (i.e. `a.spec.js-snapshots`), otherwise it will throw. > However, this path must stay within the snapshots directory for each test file (i.e. `a.spec.js-snapshots`), otherwise it will throw.
### param: TestInfo.snapshotPath.pathSegments ### param: TestInfo.snapshotPath.pathSegments
- `pathSegments` <[string...]> - `...pathSegments` <[Array]<[string]>>
The name of the snapshot or the path segments to define the snapshot file path. Snapshots with the same name in the same test file are expected to be the same. The name of the snapshot or the path segments to define the snapshot file path. Snapshots with the same name in the same test file are expected to be the same.

View file

@ -25,14 +25,14 @@ Reporter is given a root suite in the [`method: Reporter.onBegin`] method.
Returns the list of all test cases in this suite and its descendants, as opposite to [`property: Suite.tests`]. Returns the list of all test cases in this suite and its descendants, as opposite to [`property: Suite.tests`].
## property: Suite.location ## property: Suite.location
- type: <[void]|[Location]> - type: <[Location]>
Location in the source where the suite is defined. Missing for root and project suites. Optional location in the source where the suite is defined. Missing for root and project suites.
## property: Suite.parent ## property: Suite.parent
- type: <[void]|[Suite]> - type: <[Suite]>
Parent suite or [void] for the root suite. Optional parent suite, missing for the root suite.
## method: Suite.project ## method: Suite.project
- returns: <[void]|[TestProject]> - returns: <[void]|[TestProject]>

View file

@ -6,7 +6,7 @@
## property: TestCase.annotations ## property: TestCase.annotations
- type: <[Array]<[Object]>> - type: <[Array]<[Object]>>
- `type` <[string]> Annotation type, for example `'skip'` or `'fail'`. - `type` <[string]> Annotation type, for example `'skip'` or `'fail'`.
- `description` <[void]|[string]> Optional description. - `description` <[string]> Optional description.
The list of annotations applicable to the current test. Includes annotations from the test, annotations from all [`method: Test.describe`] groups the test belongs to and file-level annotations for the test file. The list of annotations applicable to the current test. Includes annotations from the test, annotations from all [`method: Test.describe`] groups the test belongs to and file-level annotations for the test file.
@ -19,13 +19,13 @@ Learn more about [test annotations](../test-annotations.md).
Expected test status. Expected test status.
* Tests marked as [`method: Test.skip#1`] or [`method: Test.fixme#1`] are expected to be `'skipped'`. * Tests marked as [`method: Test.skip#1`] or [`method: Test.fixme#1`] are expected to be `'skipped'`.
* Tests marked as [`method: Test.fail`] are expected to be `'failed'`. * Tests marked as [`method: Test.fail#1`] are expected to be `'failed'`.
* Other tests are expected to be `'passed'`. * Other tests are expected to be `'passed'`.
See also [`property: TestResult.status`] for the actual status. See also [`property: TestResult.status`] for the actual status.
## property: TestCase.location ## property: TestCase.location
- type: <[void]|[Location]> - type: <[Location]>
Location in the source where the test is defined. Location in the source where the test is defined.
@ -66,7 +66,7 @@ Learn more about [test retries](../test-retries.md#retries).
## property: TestCase.timeout ## property: TestCase.timeout
- type: <[float]> - type: <[float]>
The timeout given to the test. Affected by [`property: TestConfig.timeout`], [`property: TestProject.timeout`], [`method: Test.setTimeout`], [`method: Test.slow`] and [`method: TestInfo.setTimeout`]. The timeout given to the test. Affected by [`property: TestConfig.timeout`], [`property: TestProject.timeout`], [`method: Test.setTimeout`], [`method: Test.slow#1`] and [`method: TestInfo.setTimeout`].
## property: TestCase.title ## property: TestCase.title
- type: <[string]> - type: <[string]>

View file

@ -7,8 +7,8 @@ A result of a single [TestCase] run.
- type: <[Array]<[Object]>> - type: <[Array]<[Object]>>
- `name` <[string]> Attachment name. - `name` <[string]> Attachment name.
- `contentType` <[string]> Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`. - `contentType` <[string]> Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`.
- `path` <[void]|[string]> Optional path on the filesystem to the attached file. - `path` <[string]> Optional path on the filesystem to the attached file.
- `body` <[void]|[Buffer]> Optional attachment body used instead of a file. - `body` <[Buffer]> Optional attachment body used instead of a file.
The list of files or buffers attached during the test execution through [`property: TestInfo.attachments`]. The list of files or buffers attached during the test execution through [`property: TestInfo.attachments`].
@ -18,9 +18,9 @@ The list of files or buffers attached during the test execution through [`proper
Running time in milliseconds. Running time in milliseconds.
## property: TestResult.error ## property: TestResult.error
- type: <[void]|[TestError]> - type: <[TestError]>
First error thrown during test execution, if any. This is equal to the first Optional first error thrown during test execution, if any. This is equal to the first
element in [`property: TestResult.errors`]. element in [`property: TestResult.errors`].
## property: TestResult.errors ## property: TestResult.errors

View file

@ -18,19 +18,19 @@ Step category to differentiate steps with different origin and verbosity. Built-
Running time in milliseconds. Running time in milliseconds.
## property: TestStep.location ## property: TestStep.location
- type: <[void]|[Location]> - type: <[Location]>
Location in the source where the step is defined. Optional location in the source where the step is defined.
## property: TestStep.error ## property: TestStep.error
- type: <[void]|[TestError]> - type: <[TestError]>
An error thrown during the step execution, if any. Optional error thrown during the step execution, if any.
## property: TestStep.parent ## property: TestStep.parent
- type: <[void]|[TestStep]> - type: <[TestStep]>
Parent step, if any. Optional parent step, if any.
## property: TestStep.startTime ## property: TestStep.startTime
- type: <[Date]> - type: <[Date]>

View file

@ -88,7 +88,7 @@ test('very slow test', async ({ page }) => {
}); });
``` ```
API reference: [`method: Test.setTimeout`] and [`method: Test.slow`]. API reference: [`method: Test.setTimeout`] and [`method: Test.slow#1`].
### Change timeout from a hook ### Change timeout from a hook

View file

@ -239,7 +239,6 @@ export class Dispatcher {
duration: -1, duration: -1,
steps: [], steps: [],
location: params.location, location: params.location,
data: {},
}; };
steps.set(params.stepId, step); steps.set(params.stepId, step);
(parentStep || result).steps.push(step); (parentStep || result).steps.push(step);

File diff suppressed because it is too large Load diff

View file

@ -18,24 +18,6 @@
import type { FullConfig, FullProject, TestStatus, TestError } from './test'; import type { FullConfig, FullProject, TestStatus, TestError } from './test';
export type { FullConfig, TestStatus, TestError } from './test'; export type { FullConfig, TestStatus, TestError } from './test';
/**
* Represents a location in the source code where [TestCase] or [Suite] is defined.
*/
export interface Location {
/**
* Path to the source file.
*/
file: string;
/**
* Line number in the source file.
*/
line: number;
/**
* Column number in the source file.
*/
column: number;
}
/** /**
* `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:
* - Root suite has a child suite for each [TestProject]. * - Root suite has a child suite for each [TestProject].
@ -58,9 +40,37 @@ export interface Location {
*/ */
export interface Suite { export interface Suite {
/** /**
* Parent suite or [void] for the root suite. * Configuration of the project this suite belongs to, or [void] for the root suite.
*/
project(): FullProject | undefined;
/**
* 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(): Array<TestCase>;
/**
* Optional location in the source where the suite is defined. Missing for root and project suites.
*/
location?: Location;
/**
* Optional parent suite, missing for the root suite.
*/ */
parent?: Suite; parent?: Suite;
/**
* Child suites. See [Suite] for the hierarchy of suites.
*/
suites: Array<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 [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: Array<TestCase>;
/** /**
* Suite title. * Suite title.
* - Empty for root suite. * - Empty for root suite.
@ -70,34 +80,11 @@ export interface Suite {
* group suite. * group suite.
*/ */
title: string; title: string;
/**
* Location in the source where the suite is defined. Missing for root and project suites.
*/
location?: Location;
/**
* 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 [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[];
/** /**
* Returns 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[]; titlePath(): Array<string>;}
/**
* 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[];
/**
* Configuration of the project this suite belongs to, or [void] for the root suite.
*/
project(): FullProject | undefined;
}
/** /**
* `TestCase` corresponds to every [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call) * `TestCase` corresponds to every [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call)
@ -106,44 +93,17 @@ export interface Suite {
* or repeated multiple times, it will have multiple `TestCase` objects in corresponding projects' suites. * or repeated multiple times, it will have multiple `TestCase` objects in corresponding projects' suites.
*/ */
export interface TestCase { export interface TestCase {
/**
* Suite this test case belongs to.
*/
parent: Suite;
/**
* Test title as passed to the [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call)
* call.
*/
title: string;
/**
* Location in the source where the test is defined.
*/
location: Location;
/**
* Returns a list of titles from the root down to this test.
*/
titlePath(): string[];
/** /**
* Expected test status. * Expected test status.
* - Tests marked as [test.skip(title, testFunction)](https://playwright.dev/docs/api/class-test#test-skip-1) or * - Tests marked as [test.skip(title, testFunction)](https://playwright.dev/docs/api/class-test#test-skip-1) or
* [test.fixme(title, testFunction)](https://playwright.dev/docs/api/class-test#test-fixme-1) are expected to be * [test.fixme(title, testFunction)](https://playwright.dev/docs/api/class-test#test-fixme-1) are expected to be
* `'skipped'`. * `'skipped'`.
* - Tests marked as [test.fail([condition, description])](https://playwright.dev/docs/api/class-test#test-fail) are * - Tests marked as [test.fail()](https://playwright.dev/docs/api/class-test#test-fail-1) are expected to be `'failed'`.
* expected to be `'failed'`.
* - Other tests are expected to be `'passed'`. * - 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. * See also [testResult.status](https://playwright.dev/docs/api/class-testresult#test-result-status) for the actual status.
*/ */
expectedStatus: TestStatus; expectedStatus: TestStatus;
/**
* 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;
/** /**
* The list of annotations applicable to the current test. Includes annotations from the test, annotations from all * 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 * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe) groups the test belongs to
@ -154,138 +114,164 @@ export interface TestCase {
* *
* Learn more about [test annotations](https://playwright.dev/docs/test-annotations). * Learn more about [test annotations](https://playwright.dev/docs/test-annotations).
*/ */
annotations: { type: string, description?: string }[]; annotations: Array<{
/**
* Annotation type, for example `'skip'` or `'fail'`.
*/
type: string;
/**
* Optional description.
*/
description?: string;
}>;
/** /**
* The maximum number of retries given to this test in the configuration. * Location in the source where the test is defined.
*
* Learn more about [test retries](https://playwright.dev/docs/test-retries#retries).
*/ */
retries: number; location: Location;
/** /**
* Contains the repeat index when running in "repeat each" mode. This mode is enabled by passing `--repeat-each` to the * Whether the test is considered running fine. Non-ok tests fail the test run with non-zero exit code.
* [command line](https://playwright.dev/docs/test-cli).
*/ */
repeatEachIndex: number; ok(): boolean;
/**
* Results for each run of this test.
*/
results: TestResult[];
/** /**
* Testing outcome for this test. Note that outcome is not the same as * 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): * [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 is expected to fail and actually fails is `'expected'`.
* - Test that passes on a second retry is `'flaky'`. * - Test that passes on a second retry is `'flaky'`.
*/ */
outcome(): 'skipped' | 'expected' | 'unexpected' | '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. * Suite this test case belongs to.
*/ */
ok(): boolean; parent: Suite;
}
/**
* Contains the repeat index when running in "repeat each" mode. This mode is enabled by passing `--repeat-each` to the
* [command line](https://playwright.dev/docs/test-cli).
*/
repeatEachIndex: number;
/**
* Results for each run of this test.
*/
results: Array<TestResult>;
/**
* The maximum number of retries given to this test in the configuration.
*
* Learn more about [test retries](https://playwright.dev/docs/test-retries#retries).
*/
retries: number;
/**
* 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()](https://playwright.dev/docs/api/class-test#test-slow-1) and
* [testInfo.setTimeout(timeout)](https://playwright.dev/docs/api/class-testinfo#test-info-set-timeout).
*/
timeout: number;
/**
* Test title as passed to the [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call)
* call.
*/
title: string;
/**
* Returns a list of titles from the root down to this test.
*/
titlePath(): Array<string>;}
/** /**
* A result of a single [TestCase] run. * A result of a single [TestCase] run.
*/ */
export interface TestResult { 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#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;
/**
* Start time of this particular test run.
*/
startTime: Date;
/**
* Running time in milliseconds.
*/
duration: number;
/** /**
* The status of this test result. See also * The status of this test result. See also
* [testCase.expectedStatus](https://playwright.dev/docs/api/class-testcase#test-case-expected-status). * [testCase.expectedStatus](https://playwright.dev/docs/api/class-testcase#test-case-expected-status).
*/ */
status: TestStatus; status: TestStatus;
/**
* First error thrown during test execution, if any. This is equal to the first element in
* [testResult.errors](https://playwright.dev/docs/api/class-testresult#test-result-errors).
*/
error?: TestError;
/**
* Errors thrown during the test execution.
*/
errors: TestError[];
/** /**
* The list of files or buffers attached during the test execution through * The list of files or buffers attached during the test execution through
* [testInfo.attachments](https://playwright.dev/docs/api/class-testinfo#test-info-attachments). * [testInfo.attachments](https://playwright.dev/docs/api/class-testinfo#test-info-attachments).
*/ */
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[]; attachments: Array<{
/** /**
* Anything written to the standard output during the test run. * Attachment name.
*/ */
stdout: (string | Buffer)[]; name: string;
/**
* Anything written to the standard error during the test run. /**
*/ * Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`.
stderr: (string | Buffer)[]; */
/** contentType: string;
* List of steps inside this test run.
*/ /**
steps: TestStep[]; * Optional path on the filesystem to the attached file.
} */
path?: string;
/**
* Optional attachment body used instead of a file.
*/
body?: Buffer;
}>;
/**
* Represents a step in the [TestRun].
*/
export interface TestStep {
/**
* User-friendly test step title.
*/
title: string;
/**
* Returns a list of step titles from the root step down to this step.
*/
titlePath(): string[];
/**
* Location in the source where the step is defined.
*/
location?: Location;
/**
* Parent step, if any.
*/
parent?: TestStep;
/**
* Step category to differentiate steps with different origin and verbosity. Built-in categories are:
* - `hook` for fixtures and hooks initialization and teardown
* - `expect` for expect calls
* - `pw:api` for Playwright API calls.
* - `test.step` for test.step API calls.
*/
category: string,
/**
* Start time of this particular test step.
*/
startTime: Date;
/** /**
* Running time in milliseconds. * Running time in milliseconds.
*/ */
duration: number; duration: number;
/** /**
* An error thrown during the step execution, if any. * Optional first error thrown during test execution, if any. This is equal to the first element in
* [testResult.errors](https://playwright.dev/docs/api/class-testresult#test-result-errors).
*/ */
error?: TestError; error?: TestError;
/** /**
* List of steps inside this step. * Errors thrown during the test execution.
*/ */
steps: TestStep[]; errors: Array<TestError>;
data: { [key: string]: any };
} /**
* 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#retries).
*/
retry: number;
/**
* Start time of this particular test run.
*/
startTime: Date;
/**
* Anything written to the standard error during the test run.
*/
stderr: Array<string|Buffer>;
/**
* Anything written to the standard output during the test run.
*/
stdout: Array<string|Buffer>;
/**
* List of steps inside this test run.
*/
steps: Array<TestStep>;
/**
* 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;}
/** /**
* Result of the full test run. * Result of the full test run.
@ -442,5 +428,78 @@ export interface Reporter {
export {}; export {};
/**
* Represents a location in the source code where [TestCase] or [Suite] is defined.
*/
export interface Location {
/**
* Path to the source file.
*/
file: string;
/**
* Line number in the source file.
*/
line: number;
/**
* Column number in the source file.
*/
column: number;
}
/**
* Represents a step in the [TestRun].
*/
export interface TestStep {
/**
* Step category to differentiate steps with different origin and verbosity. Built-in categories are:
* - `hook` for fixtures and hooks initialization and teardown
* - `expect` for expect calls
* - `pw:api` for Playwright API calls.
* - `test.step` for test.step API calls.
*/
category: string;
/**
* Running time in milliseconds.
*/
duration: number;
/**
* Optional location in the source where the step is defined.
*/
location?: Location;
/**
* Optional error thrown during the step execution, if any.
*/
error?: TestError;
/**
* Optional parent step, if any.
*/
parent?: TestStep;
/**
* Start time of this particular test step.
*/
startTime: Date;
/**
* List of steps inside this step.
*/
steps: Array<TestStep>;
/**
* User-friendly test step title.
*/
title: string;
/**
* Returns a list of step titles from the root step down to this step.
*/
titlePath(): Array<string>;
}

View file

@ -95,13 +95,14 @@ class ApiParser {
if (!returnType) if (!returnType)
returnType = new Documentation.Type('void'); returnType = new Documentation.Type('void');
const comments = extractComments(spec);
let member; let member;
if (match[1] === 'event') if (match[1] === 'event')
member = Documentation.Member.createEvent(extractLangs(spec), name, returnType, extractComments(spec)); member = Documentation.Member.createEvent(extractLangs(spec), name, returnType, comments);
if (match[1] === 'property') if (match[1] === 'property')
member = Documentation.Member.createProperty(extractLangs(spec), name, returnType, extractComments(spec)); member = Documentation.Member.createProperty(extractLangs(spec), name, returnType, comments, guessRequired(md.render(comments)));
if (match[1] === 'method' || match[1] === 'async method') { if (match[1] === 'method' || match[1] === 'async method') {
member = Documentation.Member.createMethod(extractLangs(spec), name, [], returnType, extractComments(spec)); member = Documentation.Member.createMethod(extractLangs(spec), name, [], returnType, comments);
if (match[1] === 'async method') if (match[1] === 'async method')
member.async = true; member.async = true;
} }

View file

@ -32,22 +32,29 @@ Error.stackTraceLimit = 50;
class TypesGenerator { class TypesGenerator {
/** /**
* @param {Documentation} documentation * @param {{
* documentation: Documentation,
* classNames: Set<string>,
* overridesToDocsClassMapping: Map<string, string>,
* ignoreMissing: Set<string>,
* }} options
*/ */
constructor(documentation) { constructor(options) {
/** @type {Array<{name: string, properties: Member[]}>} */ /** @type {Array<{name: string, properties: Member[]}>} */
this.objectDefinitions = []; this.objectDefinitions = [];
/** @type {Set<string>} */ /** @type {Set<string>} */
this.handledMethods = new Set(); this.handledMethods = new Set();
this.documentation = documentation; this.documentation = options.documentation;
this.classNames = options.classNames;
this.overridesToDocsClassMapping = options.overridesToDocsClassMapping;
this.ignoreMissing = options.ignoreMissing;
} }
/** /**
* @param {string} overridesFile * @param {string} overridesFile
* @param {Map<string, string>=} docsOnlyClassMapping
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
async generateTypes(overridesFile, docsOnlyClassMapping) { async generateTypes(overridesFile) {
this.documentation.filterForLanguage('js'); this.documentation.filterForLanguage('js');
this.documentation.copyDocsFromSuperclasses([]); this.documentation.copyDocsFromSuperclasses([]);
@ -82,22 +89,22 @@ class TypesGenerator {
const handledClasses = new Set(); const handledClasses = new Set();
let overrides = await parseOverrides(overridesFile, className => { let overrides = await parseOverrides(overridesFile, className => {
const docClass = this.docClassForName(className, docsOnlyClassMapping); const docClass = this.docClassForName(className);
if (!docClass) if (!docClass)
return ''; return '';
handledClasses.add(className); handledClasses.add(className);
return this.writeComment(docClass.comment) + '\n'; return this.writeComment(docClass.comment) + '\n';
}, (className, methodName, overloadIndex) => { }, (className, methodName, overloadIndex) => {
const docClass = this.docClassForName(className, docsOnlyClassMapping); const docClass = this.docClassForName(className);
let method; let method;
if (docClass) { if (docClass) {
const methods = docClass.membersArray.filter(m => m.alias === methodName && m.kind !== 'event').sort((a, b) => a.overloadIndex - b.overloadIndex); const methods = docClass.membersArray.filter(m => m.alias === methodName && m.kind !== 'event').sort((a, b) => a.overloadIndex - b.overloadIndex);
// Use the last overload when not enough overloads are defined in docs. // Use the last overload when not enough overloads are defined in docs.
method = methods.find(m => m.overloadIndex === overloadIndex) || methods[methods.length - 1]; method = methods.find(m => m.overloadIndex === overloadIndex) || methods[methods.length - 1];
} }
if (docsOnlyClassMapping && !method) if (!method && this.canIgnoreMissingName(`${className}.${methodName}`))
return ''; return '';
this.handledMethods.add(`${className}.${methodName}`); this.handledMethods.add(`${className}.${methodName}#${overloadIndex}`);
if (!method) { if (!method) {
if (new Set(['on', 'addListener', 'off', 'removeListener', 'once']).has(methodName)) if (new Set(['on', 'addListener', 'off', 'removeListener', 'once']).has(methodName))
return ''; return '';
@ -105,12 +112,15 @@ class TypesGenerator {
} }
return this.memberJSDOC(method, ' ').trimLeft(); return this.memberJSDOC(method, ' ').trimLeft();
}, (className) => { }, (className) => {
const docClass = this.docClassForName(className, docsOnlyClassMapping); const docClass = this.docClassForName(className);
return (!docsOnlyClassMapping && docClass) ? this.classBody(docClass) : ''; if (!docClass || !this.classNames.has(docClass.name))
return '';
return this.classBody(docClass);
}); });
const IGNORED_CLASSES = ['PlaywrightAssertions', 'LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions']; const classes = this.documentation.classesArray
const classes = this.documentation.classesArray.filter(cls => !IGNORED_CLASSES.includes(cls.name)).filter(cls => !handledClasses.has(cls.name)); .filter(cls => this.classNames.has(cls.name))
.filter(cls => !handledClasses.has(cls.name));
{ {
const playwright = this.documentation.classesArray.find(c => c.name === 'Playwright'); const playwright = this.documentation.classesArray.find(c => c.name === 'Playwright');
playwright.membersArray = playwright.membersArray.filter(member => !['errors', 'devices'].includes(member.name)); playwright.membersArray = playwright.membersArray.filter(member => !['errors', 'devices'].includes(member.name));
@ -121,7 +131,7 @@ class TypesGenerator {
`// This file is generated by ${__filename.substring(path.join(__dirname, '..', '..').length).split(path.sep).join(path.posix.sep)}`, `// This file is generated by ${__filename.substring(path.join(__dirname, '..', '..').length).split(path.sep).join(path.posix.sep)}`,
overrides, overrides,
'', '',
docsOnlyClassMapping ? '' : classes.map(classDesc => { classes.map(classDesc => {
return (classDesc.name === 'Playwright') ? this.classBody(classDesc, true) : this.classToString(classDesc); return (classDesc.name === 'Playwright') ? this.classBody(classDesc, true) : this.classToString(classDesc);
}).join('\n'), }).join('\n'),
this.objectDefinitionsToString(overrides), this.objectDefinitionsToString(overrides),
@ -131,12 +141,20 @@ class TypesGenerator {
/** /**
* @param {string} name * @param {string} name
* @param {Map<string, string> | undefined} docsOnlyClassMapping
*/ */
docClassForName(name, docsOnlyClassMapping) { canIgnoreMissingName(name) {
name = (docsOnlyClassMapping ? docsOnlyClassMapping.get(name) : undefined) || name; const parts = name.split('.');
const docClass = this.documentation.classes.get(name); // Either the class is ignored, or a specific method.
if (!docClass && !docsOnlyClassMapping) return this.ignoreMissing.has(name) || this.ignoreMissing.has(parts[0]);
}
/**
* @param {string} name
*/
docClassForName(name) {
const mappedName = (this.overridesToDocsClassMapping ? this.overridesToDocsClassMapping.get(name) : undefined) || name;
const docClass = this.documentation.classes.get(mappedName);
if (!docClass && !this.canIgnoreMissingName(name))
throw new Error(`Unknown override class ${name}`); throw new Error(`Unknown override class ${name}`);
return docClass; return docClass;
} }
@ -265,26 +283,26 @@ class TypesGenerator {
if (member.async) if (member.async)
type = `Promise<${type}>`; type = `Promise<${type}>`;
// do this late, because we still want object definitions for overridden types // do this late, because we still want object definitions for overridden types
if (!this.hasOwnMethod(classDesc, member.alias)) if (!this.hasOwnMethod(classDesc, member))
return ''; return '';
if (exportMembersAsGlobals) { if (exportMembersAsGlobals) {
const memberType = member.kind === 'method' ? `${args} => ${type}` : type; const memberType = member.kind === 'method' ? `${args} => ${type}` : type;
return `${jsdoc}${exportMembersAsGlobals ? 'export const ' : ''}${member.alias}: ${memberType};` return `${jsdoc}${exportMembersAsGlobals ? 'export const ' : ''}${member.alias}: ${memberType};`
} }
return `${jsdoc}${member.alias}${args}: ${type};` return `${jsdoc}${member.alias}${member.required ? '' : '?'}${args}: ${type};`
}).filter(x => x).join('\n\n')); }).filter(x => x).join('\n\n'));
return parts.join('\n'); return parts.join('\n');
} }
/** /**
* @param {Documentation.Class} classDesc * @param {Documentation.Class} classDesc
* @param {string} methodName * @param {Documentation.Member} member
*/ */
hasOwnMethod(classDesc, methodName) { hasOwnMethod(classDesc, member) {
if (this.handledMethods.has(`${classDesc.name}.${methodName}`)) if (this.handledMethods.has(`${classDesc.name}.${member.alias}#${member.overloadIndex}`))
return false; return false;
while (classDesc = this.parentClass(classDesc)) { while (classDesc = this.parentClass(classDesc)) {
if (classDesc.members.has(methodName)) if (classDesc.members.has(member.alias))
return false; return false;
} }
return true; return true;
@ -479,9 +497,15 @@ class TypesGenerator {
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'));
const assertionClasses = new Set(['PlaywrightAssertions', 'LocatorAssertions', 'PageAssertions', 'APIResponseAssertions', 'ScreenshotAssertions']);
const apiDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api')); const apiDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
apiDocumentation.index(); apiDocumentation.index();
const apiTypesGenerator = new TypesGenerator(apiDocumentation); const apiTypesGenerator = new TypesGenerator({
documentation: apiDocumentation,
classNames: new Set(apiDocumentation.classesArray.map(cls => cls.name).filter(name => !assertionClasses.has(name))),
overridesToDocsClassMapping: new Map(),
ignoreMissing: new Set(),
});
let apiTypes = await apiTypesGenerator.generateTypes(path.join(__dirname, 'overrides.d.ts')); let apiTypes = await apiTypesGenerator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
const namedDevices = Object.keys(devices).map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`).join('\n'); const namedDevices = Object.keys(devices).map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`).join('\n');
apiTypes += [ apiTypes += [
@ -504,25 +528,41 @@ class TypesGenerator {
const testOnlyDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-api'), path.join(PROJECT_DIR, 'docs', 'src', 'api', 'params.md')); 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 testDocumentation = apiDocumentation.mergeWith(testOnlyDocumentation);
const testTypesGenerator = new TypesGenerator(testDocumentation); const testTypesGenerator = new TypesGenerator({
const testClassMapping = new Map([ documentation: testDocumentation,
['TestType', 'Test'], classNames: new Set(['TestError', 'TestInfo', 'WorkerInfo']),
['Config', 'TestConfig'], overridesToDocsClassMapping: new Map([
['FullConfig', 'TestConfig'], ['TestType', 'Test'],
['Project', 'TestProject'], ['Config', 'TestConfig'],
['PlaywrightWorkerOptions', 'TestOptions'], ['FullConfig', 'TestConfig'],
['PlaywrightTestOptions', 'TestOptions'], ['Project', 'TestProject'],
['PlaywrightWorkerArgs', 'Fixtures'], ['PlaywrightWorkerOptions', 'TestOptions'],
['PlaywrightTestArgs', 'Fixtures'], ['PlaywrightTestOptions', 'TestOptions'],
]); ['PlaywrightWorkerArgs', 'Fixtures'],
let testTypes = await testTypesGenerator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'), testClassMapping); ['PlaywrightTestArgs', 'Fixtures'],
]),
ignoreMissing: new Set([
'FullConfig.version',
'FullConfig.rootDir',
'SuiteFunction',
'TestFunction',
'PlaywrightWorkerOptions.defaultBrowserType',
'PlaywrightWorkerArgs.playwright',
]),
});
let testTypes = await testTypesGenerator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'));
testTypes = testTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace testTypes = testTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
writeFile(path.join(testTypesDir, 'test.d.ts'), testTypes); writeFile(path.join(testTypesDir, 'test.d.ts'), testTypes);
const testReporterOnlyDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-reporter-api')); const testReporterOnlyDocumentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'test-reporter-api'));
const testReporterDocumentation = testDocumentation.mergeWith(testReporterOnlyDocumentation); const testReporterDocumentation = testDocumentation.mergeWith(testReporterOnlyDocumentation);
const testReporterTypesGenerator = new TypesGenerator(testReporterDocumentation); const testReporterTypesGenerator = new TypesGenerator({
let testReporterTypes = await testReporterTypesGenerator.generateTypes(path.join(__dirname, 'overrides-testReporter.d.ts'), new Map()); documentation: testReporterDocumentation,
classNames: new Set(testReporterOnlyDocumentation.classesArray.map(cls => cls.name)),
overridesToDocsClassMapping: new Map(),
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 testReporterTypes = testReporterTypes.replace(/( +)\n/g, '\n'); // remove trailing whitespace
writeFile(path.join(testTypesDir, 'testReporter.d.ts'), testReporterTypes); writeFile(path.join(testTypesDir, 'testReporter.d.ts'), testReporterTypes);

View file

@ -187,67 +187,16 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped'; export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped';
export interface TestError {
message?: string;
stack?: string;
value?: string;
}
export interface WorkerInfo { export interface WorkerInfo {
config: FullConfig; config: FullConfig;
parallelIndex: number;
project: FullProject; project: FullProject;
workerIndex: number;
} }
export interface TestInfo { export interface TestInfo {
config: FullConfig; config: FullConfig;
parallelIndex: number;
project: FullProject; project: FullProject;
workerIndex: number;
title: string;
titlePath: 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; expectedStatus: TestStatus;
timeout: number;
annotations: { type: string, description?: string }[];
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[];
attach(name: string, options?: { contentType?: string, path?: string, body?: string | Buffer }): Promise<void>;
repeatEachIndex: number;
retry: number;
duration: number;
status?: TestStatus; status?: TestStatus;
error?: TestError;
errors: TestError[];
stdout: (string | Buffer)[];
stderr: (string | Buffer)[];
snapshotSuffix: string;
snapshotDir: string;
outputDir: string;
snapshotPath: (...pathSegments: string[]) => string;
outputPath: (...pathSegments: string[]) => string;
} }
interface SuiteFunction { interface SuiteFunction {
@ -280,15 +229,11 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
fixme(condition: boolean, description?: string): void; fixme(condition: boolean, description?: string): void;
fixme(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; fixme(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void;
fail(): void; fail(): void;
fail(condition: boolean): void; fail(condition: boolean, description?: string): void;
fail(condition: boolean, description: string): void; fail(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void;
fail(callback: (args: TestArgs & WorkerArgs) => boolean): void;
fail(callback: (args: TestArgs & WorkerArgs) => boolean, description: string): void;
slow(): void; slow(): void;
slow(condition: boolean): void; slow(condition: boolean, description?: string): void;
slow(condition: boolean, description: string): void; slow(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void;
slow(callback: (args: TestArgs & WorkerArgs) => boolean): void;
slow(callback: (args: TestArgs & WorkerArgs) => boolean, description: string): void;
setTimeout(timeout: number): void; setTimeout(timeout: number): void;
beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;

View file

@ -17,63 +17,16 @@
import type { FullConfig, FullProject, TestStatus, TestError } from './test'; import type { FullConfig, FullProject, TestStatus, TestError } from './test';
export 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 { export interface Suite {
parent?: Suite;
title: string;
location?: Location;
suites: Suite[];
tests: TestCase[];
titlePath(): string[];
allTests(): TestCase[];
project(): FullProject | undefined; project(): FullProject | undefined;
} }
export interface TestCase { export interface TestCase {
parent: Suite;
title: string;
location: Location;
titlePath(): string[];
expectedStatus: TestStatus; expectedStatus: TestStatus;
timeout: number;
annotations: { type: string, description?: string }[];
retries: number;
repeatEachIndex: number;
results: TestResult[];
outcome(): 'skipped' | 'expected' | 'unexpected' | 'flaky';
ok(): boolean;
} }
export interface TestResult { export interface TestResult {
retry: number;
workerIndex: number;
startTime: Date;
duration: number;
status: TestStatus; status: TestStatus;
error?: TestError;
errors: TestError[];
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[];
stdout: (string | Buffer)[];
stderr: (string | Buffer)[];
steps: TestStep[];
}
export interface TestStep {
title: string;
titlePath(): string[];
location?: Location;
parent?: TestStep;
category: string,
startTime: Date;
duration: number;
error?: TestError;
steps: TestStep[];
data: { [key: string]: any };
} }
/** /**

View file

@ -117,7 +117,7 @@ async function parseOverrides(filePath, commentForClass, commentForMethod, extra
pos, pos,
text: commentForMethod(className, `${prefix}`, 0), text: commentForMethod(className, `${prefix}`, 0),
}); });
} else if (!ts.isMethodSignature(node)) { } else if (ts.isIntersectionTypeNode(node) || ts.isTypeLiteralNode(node)) {
ts.forEachChild(node, child => visitProperties(className, prefix, child)); ts.forEachChild(node, child => visitProperties(className, prefix, child));
} }
} }