chore: harden markdown link validation (#30221)

This commit is contained in:
Max Schmitt 2024-04-03 17:51:32 +02:00 committed by GitHub
parent 0734d1e733
commit 010bc29a3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 57 additions and 45 deletions

View file

@ -6,9 +6,6 @@
Error is raised whenever certain operations are terminated abnormally, e.g. Error is raised whenever certain operations are terminated abnormally, e.g.
browser closes while [`method: Page.evaluate`] is running. All Playwright exceptions browser closes while [`method: Page.evaluate`] is running. All Playwright exceptions
inherit from this class. inherit from this class.
- [error.message](./class-error.md#errormessage)
- [error.name](./class-error.md#errorname)
- [error.stack](./class-error.md#errorstack)
## property: Error.message ## property: Error.message
* since: v1.11 * since: v1.11

View file

@ -30,7 +30,7 @@ To add a [GitHub Actions](https://docs.github.com/en/actions) file first create
#### You will learn #### You will learn
* langs: python, java, csharp * langs: python, java, csharp
- [How to run tests on push/pull_request](/ci.md#on-pushpull_request) - [How to run tests on push/pull_request](/ci-intro.md#on-pushpull_request)
- [How to view test logs](/ci-intro.md#viewing-test-logs) - [How to view test logs](/ci-intro.md#viewing-test-logs)
- [How to view the trace](/ci-intro.md#viewing-the-trace) - [How to view the trace](/ci-intro.md#viewing-the-trace)

View file

@ -80,14 +80,14 @@ npx playwright test --debug
``` ```
#### Debug one test on all browsers #### Debug one test on all browsers
To debug one test on a specific line run the test command followed by the name of the test file and the line number of the test you want to debug, followed by the `--debug` flag. This will run a single test in each browser configured in your [`playwright.config`](/test-configuration.md#multiple-browsers) and open the inspector. To debug one test on a specific line run the test command followed by the name of the test file and the line number of the test you want to debug, followed by the `--debug` flag. This will run a single test in each browser configured in your [`playwright.config`](./test-projects.md#configure-projects-for-multiple-browsers) and open the inspector.
```bash ```bash
npx playwright test example.spec.ts:10 --debug npx playwright test example.spec.ts:10 --debug
``` ```
#### Debug on a specific browser #### Debug on a specific browser
In Playwright you can configure projects in your [`playwright.config`](/test-configuration.md#multiple-browsers). Once configured you can then debug your tests on a specific browser or mobile viewport using the `--project` flag followed by the name of the project configured in your `playwright.config`. In Playwright you can configure projects in your [`playwright.config`](./test-projects.md#configure-projects-for-multiple-browsers). Once configured you can then debug your tests on a specific browser or mobile viewport using the `--project` flag followed by the name of the project configured in your `playwright.config`.
```bash ```bash
npx playwright test --project=chromium --debug npx playwright test --project=chromium --debug

View file

@ -644,7 +644,7 @@ This version was also tested against the following stable channels:
### New APIs & changes ### New APIs & changes
- Default assertions timeout now can be changed with [`setDefaultAssertionTimeout`](./test-assertions#playwright-assertions-set-default-assertion-timeout). - Default assertions timeout now can be changed with [`setDefaultAssertionTimeout`](./api/class-playwrightassertions#playwright-assertions-set-default-assertion-timeout).
### Announcements ### Announcements

View file

@ -262,7 +262,7 @@ test('pass', async ({ page }) => {
}); });
``` ```
See the documentation [for a full example](./test-assertions.md#add-custom-matchers-using-expectextend). See the documentation [for a full example](./test-assertions#add-custom-matchers-using-expectextend).
### Merge test fixtures ### Merge test fixtures
@ -1753,7 +1753,7 @@ This version was also tested against the following stable channels:
### Locator Improvements ### Locator Improvements
- [`method: Locator.dragTo`] - [`method: Locator.dragTo`]
- [`expect(locator).toBeChecked({ checked })`](./test-assertions#locator-assertions-to-be-checked) - [`expect(locator).toBeChecked({ checked })`](./api/class-locatorassertions#locator-assertions-to-be-checked)
- Each locator can now be optionally filtered by the text it contains: - Each locator can now be optionally filtered by the text it contains:
```js ```js
await page.locator('li', { hasText: 'my item' }).locator('button').click(); await page.locator('li', { hasText: 'my item' }).locator('button').click();
@ -1908,7 +1908,7 @@ Playwright Trace Viewer is now **available online** at https://trace.playwright.
- [`testInfo.parallelIndex`](./api/class-testinfo#test-info-parallel-index) - [`testInfo.parallelIndex`](./api/class-testinfo#test-info-parallel-index)
- [`testInfo.titlePath`](./api/class-testinfo#test-info-title-path) - [`testInfo.titlePath`](./api/class-testinfo#test-info-title-path)
- [`testOptions.trace`](./api/class-testoptions#test-options-trace) has new options - [`testOptions.trace`](./api/class-testoptions#test-options-trace) has new options
- [`expect.toMatchSnapshot`](./test-assertions#expectvaluetomatchsnapshotname-options) supports subdirectories - [`expect.toMatchSnapshot`](./api/class-genericassertions.md) supports subdirectories
- [`reporter.printsToStdio()`](./api/class-reporter#reporter-prints-to-stdio) - [`reporter.printsToStdio()`](./api/class-reporter#reporter-prints-to-stdio)
@ -2189,25 +2189,25 @@ By default, the timeout for assertions is not set, so it'll wait forever, until
List of all new assertions: List of all new assertions:
- [`expect(locator).toBeChecked()`](./test-assertions#expectlocatortobechecked) - [`expect(locator).toBeChecked()`](./api/class-locatorassertions#locator-assertions-to-be-checked)
- [`expect(locator).toBeDisabled()`](./test-assertions#expectlocatortobedisabled) - [`expect(locator).toBeDisabled()`](./api/class-locatorassertions#locator-assertions-to-be-disabled)
- [`expect(locator).toBeEditable()`](./test-assertions#expectlocatortobeeditable) - [`expect(locator).toBeEditable()`](./api/class-locatorassertions#locator-assertions-to-be-editable)
- [`expect(locator).toBeEmpty()`](./test-assertions#expectlocatortobeempty) - [`expect(locator).toBeEmpty()`](./api/class-locatorassertions#locator-assertions-to-be-empty)
- [`expect(locator).toBeEnabled()`](./test-assertions#expectlocatortobeenabled) - [`expect(locator).toBeEnabled()`](./api/class-locatorassertions#locator-assertions-to-be-enabled)
- [`expect(locator).toBeFocused()`](./test-assertions#expectlocatortobefocused) - [`expect(locator).toBeFocused()`](./api/class-locatorassertions#locator-assertions-to-be-focused)
- [`expect(locator).toBeHidden()`](./test-assertions#expectlocatortobehidden) - [`expect(locator).toBeHidden()`](./api/class-locatorassertions#locator-assertions-to-be-hidden)
- [`expect(locator).toBeVisible()`](./test-assertions#expectlocatortobevisible) - [`expect(locator).toBeVisible()`](./api/class-locatorassertions#locator-assertions-to-be-visible)
- [`expect(locator).toContainText(text, options?)`](./test-assertions#expectlocatortocontaintexttext-options) - [`expect(locator).toContainText(text, options?)`](./api/class-locatorassertions#locator-assertions-to-contain-text)
- [`expect(locator).toHaveAttribute(name, value)`](./test-assertions#expectlocatortohaveattributename-value) - [`expect(locator).toHaveAttribute(name, value)`](./api/class-locatorassertions#locator-assertions-to-have-attribute)
- [`expect(locator).toHaveClass(expected)`](./test-assertions#expectlocatortohaveclassexpected) - [`expect(locator).toHaveClass(expected)`](./api/class-locatorassertions#locator-assertions-to-have-class)
- [`expect(locator).toHaveCount(count)`](./test-assertions#expectlocatortohavecountcount) - [`expect(locator).toHaveCount(count)`](./api/class-locatorassertions#locator-assertions-to-have-count)
- [`expect(locator).toHaveCSS(name, value)`](./test-assertions#expectlocatortohavecssname-value) - [`expect(locator).toHaveCSS(name, value)`](./api/class-locatorassertions#locator-assertions-to-have-css)
- [`expect(locator).toHaveId(id)`](./test-assertions#expectlocatortohaveidid) - [`expect(locator).toHaveId(id)`](./api/class-locatorassertions#locator-assertions-to-have-id)
- [`expect(locator).toHaveJSProperty(name, value)`](./test-assertions#expectlocatortohavejspropertyname-value) - [`expect(locator).toHaveJSProperty(name, value)`](./api/class-locatorassertions#locator-assertions-to-have-js-property)
- [`expect(locator).toHaveText(expected, options)`](./test-assertions#expectlocatortohavetextexpected-options) - [`expect(locator).toHaveText(expected, options)`](./api/class-locatorassertions#locator-assertions-to-have-text)
- [`expect(page).toHaveTitle(title)`](./test-assertions#expectpagetohavetitletitle) - [`expect(page).toHaveTitle(title)`](./api/class-pageassertions#page-assertions-to-have-title)
- [`expect(page).toHaveURL(url)`](./test-assertions#expectpagetohaveurlurl) - [`expect(page).toHaveURL(url)`](./api/class-pageassertions#page-assertions-to-have-url)
- [`expect(locator).toHaveValue(value)`](./test-assertions#expectlocatortohavevaluevalue) - [`expect(locator).toHaveValue(value)`](./api/class-locatorassertions#locator-assertions-to-have-value)
#### ⛓ Serial mode with [`describe.serial`](./api/class-test#test-describe-serial) #### ⛓ Serial mode with [`describe.serial`](./api/class-test#test-describe-serial)
@ -2243,7 +2243,7 @@ Step information is exposed in reporters API.
#### 🌎 Launch web server before running tests #### 🌎 Launch web server before running tests
To launch a server during the tests, use the [`webServer`](./test-webserver) option in the configuration file. The server will wait for a given url to be available before running the tests, and the url will be passed over to Playwright as a [`baseURL`](./api/class-fixtures#fixtures-base-url) when creating a context. To launch a server during the tests, use the [`webServer`](./test-webserver) option in the configuration file. The server will wait for a given url to be available before running the tests, and the url will be passed over to Playwright as a [`baseURL`](./api/class-testoptions#test-options-base-url) when creating a context.
```ts title="playwright.config.ts" ```ts title="playwright.config.ts"
import { defineConfig } from '@playwright/test'; import { defineConfig } from '@playwright/test';
@ -2272,7 +2272,7 @@ Learn more in the [documentation](./test-webserver).
#### Playwright Test #### Playwright Test
- **⚡️ Introducing [Reporter API](https://github.com/microsoft/playwright/blob/65a9037461ffc15d70cdc2055832a0c5512b227c/packages/playwright-test/types/testReporter.d.ts)** which is already used to create an [Allure Playwright reporter](https://github.com/allure-framework/allure-js/pull/297). - **⚡️ Introducing [Reporter API](https://github.com/microsoft/playwright/blob/65a9037461ffc15d70cdc2055832a0c5512b227c/packages/playwright-test/types/testReporter.d.ts)** which is already used to create an [Allure Playwright reporter](https://github.com/allure-framework/allure-js/pull/297).
- **⛺️ New [`baseURL` fixture](./test-configuration#basic-options)** to support relative paths in tests. - **⛺️ New [`baseURL` fixture](./test-configuration#basic-configuration)** to support relative paths in tests.
#### Playwright #### Playwright

View file

@ -89,7 +89,7 @@ If your custom reporter does not print anything to the terminal, implement [`met
**Merged report API notes** **Merged report API notes**
When merging mutliple [`blob`](./test-reporters#blob-reporter) reports via [`merge-reports`](./test-sharding#merge-reports-cli) CLI When merging multiple [`blob`](../test-reporters#blob-reporter) reports via [`merge-reports`](../test-sharding#merge-reports-cli) CLI
command, the same [Reporter] API is called to produce final reports and all existing reporters command, the same [Reporter] API is called to produce final reports and all existing reporters
should work without any changes. There some subtle differences though which might affect some custom should work without any changes. There some subtle differences though which might affect some custom
reporters. reporters.

View file

@ -99,7 +99,7 @@ await expect(page.getByText('the lion king')).toBeVisible();
await expect(page.getByText('the mummy')).toBeHidden(); await expect(page.getByText('the mummy')).toBeHidden();
``` ```
When you cannot find a suitable assertion, use [`expect.poll`](./test-assertions#polling) instead. When you cannot find a suitable assertion, use [`expect.poll`](./test-assertions#expectpoll) instead.
```js ```js
await expect.poll(async () => { await expect.poll(async () => {

View file

@ -22,7 +22,7 @@ Playwright Trace Viewer is a GUI tool that lets you explore recorded Playwright
## Recording a Trace ## Recording a Trace
By default the [playwright.config](/test-configuration.md#record-test-trace) file will contain the configuration needed to create a `trace.zip` file for each test. Traces are setup to run `on-first-retry` meaning they will be run on the first retry of a failed test. Also `retries` are set to 2 when running on CI and 0 locally. This means the traces will be recorded on the first retry of a failed test but not on the first run and not on the second retry. By default the [playwright.config](./trace-viewer.md#recording-a-trace-on-ci) file will contain the configuration needed to create a `trace.zip` file for each test. Traces are setup to run `on-first-retry` meaning they will be run on the first retry of a failed test. Also `retries` are set to 2 when running on CI and 0 locally. This means the traces will be recorded on the first retry of a failed test but not on the first run and not on the second retry.
```js title="playwright.config.ts" ```js title="playwright.config.ts"
import { defineConfig } from '@playwright/test'; import { defineConfig } from '@playwright/test';

View file

@ -336,8 +336,8 @@ export interface FullResult {
* *
* **Merged report API notes** * **Merged report API notes**
* *
* When merging mutliple [`blob`](https://playwright.dev/docs/api/test-reporters#blob-reporter) reports via * When merging multiple [`blob`](https://playwright.dev/docs/test-reporters#blob-reporter) reports via
* [`merge-reports`](https://playwright.dev/docs/api/test-sharding#merge-reports-cli) CLI command, the same {@link Reporter} API is called to * [`merge-reports`](https://playwright.dev/docs/test-sharding#merge-reports-cli) CLI command, the same {@link Reporter} API is called to
* produce final reports and all existing reporters should work without any changes. There some subtle differences * produce final reports and all existing reporters should work without any changes. There some subtle differences
* though which might affect some custom reporters. * though which might affect some custom reporters.
* - Projects from different shards are always kept as separate {@link TestProject} objects. E.g. if project * - Projects from different shards are always kept as separate {@link TestProject} objects. E.g. if project

View file

@ -133,14 +133,14 @@ async function run() {
for (const lang of langs) { for (const lang of langs) {
try { try {
let documentation = parseApi(path.join(documentationRoot, 'api')); let documentation = parseApi(path.join(documentationRoot, 'api'));
documentation.filterForLanguage(lang);
if (lang === 'js') { if (lang === 'js') {
const testDocumentation = parseApi(path.join(documentationRoot, 'test-api'), path.join(documentationRoot, 'api', 'params.md')); documentation = documentation.mergeWith(
testDocumentation.filterForLanguage('js'); parseApi(path.join(documentationRoot, 'test-api'), path.join(documentationRoot, 'api', 'params.md'))
const testReporterDocumentation = parseApi(path.join(documentationRoot, 'test-reporter-api')); ).mergeWith(
testReporterDocumentation.filterForLanguage('js'); parseApi(path.join(documentationRoot, 'test-reporter-api'))
documentation = documentation.mergeWith(testDocumentation).mergeWith(testReporterDocumentation); );
} }
documentation.filterForLanguage(lang);
// This validates member links. // This validates member links.
documentation.setLinkRenderer(() => undefined); documentation.setLinkRenderer(() => undefined);
@ -153,8 +153,18 @@ async function run() {
for (const cls of documentation.classesArray) { for (const cls of documentation.classesArray) {
const filePath = path.join(documentationRoot, 'api', 'class-' + cls.name.toLowerCase() + '.md'); const filePath = path.join(documentationRoot, 'api', 'class-' + cls.name.toLowerCase() + '.md');
for (const member of cls.membersArray) for (const member of cls.membersArray) {
mdSections.add(filePath + '#' + toKebabCase(cls.name).toLowerCase() + '-' + toKebabCase(member.name).toLowerCase()); const memberHash = filePath + '#' + toKebabCase(cls.name).toLowerCase() + '-' + toKebabCase(member.name).toLowerCase()
mdSections.add(memberHash);
for (const arg of member.argsArray) {
mdSections.add(memberHash + '-option-' + toKebabCase(arg.name).toLowerCase());
if (arg.name === "options" && arg.type) {
for (const option of arg.type.deepProperties())
mdSections.add(memberHash + '-option-' + toKebabCase(option.name).toLowerCase());
}
}
}
for (const event of cls.eventsArray) for (const event of cls.eventsArray)
mdSections.add(filePath + '#' + toKebabCase(cls.name).toLowerCase() + '-event-' + toKebabCase(event.name).toLowerCase()); mdSections.add(filePath + '#' + toKebabCase(cls.name).toLowerCase() + '-event-' + toKebabCase(event.name).toLowerCase());
} }
@ -211,7 +221,8 @@ async function run() {
} }
if (!node.text) if (!node.text)
return; return;
for (const [, mdLinkName, mdLink] of node.text.matchAll(/\[([\w\s\d]+)\]\((.*?)\)/g)) { // Match links in a lax way (.+), so they can include spaces, backticks etc.
for (const [, mdLinkName, mdLink] of node.text.matchAll(/\[(.+)\]\((.*?)\)/g)) {
const isExternal = mdLink.startsWith('http://') || mdLink.startsWith('https://'); const isExternal = mdLink.startsWith('http://') || mdLink.startsWith('https://');
if (isExternal) if (isExternal)
continue; continue;
@ -296,6 +307,10 @@ async function getBrowserVersions() {
return result; return result;
} }
/**
* @param {string} text
* @returns {string}
*/
function mdSectionHash(text) { function mdSectionHash(text) {
return text.toLowerCase().replace(/\s/g, '-').replace(/[^-_a-z0-9]/g, '').replace(/^-+/, ''); return text.toLowerCase().replace(/\s/g, '-').replace(/[^-_a-z0-9]/g, '').replace(/^-+/, '');
} }