Merge branch 'main' into react-18
Signed-off-by: Simon Knott <info@simonknott.de>
This commit is contained in:
commit
93a4c700e8
|
|
@ -1,6 +1,6 @@
|
||||||
# 🎭 Playwright
|
# 🎭 Playwright
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||||
|
|
||||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
||||||
| :--- | :---: | :---: | :---: |
|
| :--- | :---: | :---: | :---: |
|
||||||
| Chromium <!-- GEN:chromium-version -->128.0.6613.27<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Chromium <!-- GEN:chromium-version -->128.0.6613.27<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| Firefox <!-- GEN:firefox-version -->128.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Firefox <!-- GEN:firefox-version -->129.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ A few examples of problems this can catch include:
|
||||||
|
|
||||||
The following examples rely on the [`com.deque.html.axe-core/playwright`](https://mvnrepository.com/artifact/com.deque.html.axe-core/playwright) Maven package which adds support for running the [axe accessibility testing engine](https://www.deque.com/axe/) as part of your Playwright tests.
|
The following examples rely on the [`com.deque.html.axe-core/playwright`](https://mvnrepository.com/artifact/com.deque.html.axe-core/playwright) Maven package which adds support for running the [axe accessibility testing engine](https://www.deque.com/axe/) as part of your Playwright tests.
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But many accessibility problems can only be discovered through manual testing. We recommend using a combination of automated testing, manual accessibility assessments, and inclusive user testing.
|
Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But many accessibility problems can only be discovered through manual testing. We recommend using a combination of automated testing, manual accessibility assessments, and inclusive user testing.
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ All of that could be achieved via [APIRequestContext] methods.
|
||||||
|
|
||||||
The following examples rely on the [`Microsoft.Playwright.MSTest`](./test-runners.md) package which creates a Playwright and Page instance for each test.
|
The following examples rely on the [`Microsoft.Playwright.MSTest`](./test-runners.md) package which creates a Playwright and Page instance for each test.
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Writing API Test
|
## Writing API Test
|
||||||
|
|
||||||
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ A few examples where it may come in handy:
|
||||||
|
|
||||||
All of that could be achieved via [APIRequestContext] methods.
|
All of that could be achieved via [APIRequestContext] methods.
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Writing API Test
|
## Writing API Test
|
||||||
|
|
||||||
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ A few examples where it may come in handy:
|
||||||
|
|
||||||
All of that could be achieved via [APIRequestContext] methods.
|
All of that could be achieved via [APIRequestContext] methods.
|
||||||
|
|
||||||
<!-- TOC3 -->
|
|
||||||
|
|
||||||
## Writing API Test
|
## Writing API Test
|
||||||
|
|
||||||
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ All of that could be achieved via [APIRequestContext] methods.
|
||||||
|
|
||||||
The following examples rely on the [`pytest-playwright`](./test-runners.md) package which add Playwright fixtures to the Pytest test-runner.
|
The following examples rely on the [`pytest-playwright`](./test-runners.md) package which add Playwright fixtures to the Pytest test-runner.
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Writing API Test
|
## Writing API Test
|
||||||
|
|
||||||
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
[APIRequestContext] can send all kinds of HTTP(S) requests over network.
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ With a few lines of code, you can hook up Playwright to your favorite Java test
|
||||||
In [JUnit](https://junit.org/junit5/), you can use Playwright [fixtures](./junit.md#fixtures) to automatically initialize [Playwright], [Browser], [BrowserContext] or [Page]. In the example below, all three test methods use the same
|
In [JUnit](https://junit.org/junit5/), you can use Playwright [fixtures](./junit.md#fixtures) to automatically initialize [Playwright], [Browser], [BrowserContext] or [Page]. In the example below, all three test methods use the same
|
||||||
[Browser]. Each test uses its own [BrowserContext] and [Page].
|
[Browser]. Each test uses its own [BrowserContext] and [Page].
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
package org.example;
|
package org.example;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,6 @@ await Page.ScreenshotAsync(new()
|
||||||
|
|
||||||
[Screenshots API](./api/class-page#page-screenshot) accepts many parameters for image format, clip area, quality, etc. Make sure to check them out.
|
[Screenshots API](./api/class-page#page-screenshot) accepts many parameters for image format, clip area, quality, etc. Make sure to check them out.
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Full page screenshots
|
## Full page screenshots
|
||||||
|
|
||||||
Full page screenshot is a screenshot of a full scrollable page, as if you had a very
|
Full page screenshot is a screenshot of a full scrollable page, as if you had a very
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ Playwright and Browser instances can be reused between tests for better performa
|
||||||
recommend running each test case in a new BrowserContext, this way browser state will be
|
recommend running each test case in a new BrowserContext, this way browser state will be
|
||||||
isolated between the tests.
|
isolated between the tests.
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## JUnit
|
## JUnit
|
||||||
|
|
||||||
In [JUnit](https://junit.org/junit5/) you can initialize [Playwright] and [Browser] in [@BeforeAll](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/BeforeAll.html) method and
|
In [JUnit](https://junit.org/junit5/) you can initialize [Playwright] and [Browser] in [@BeforeAll](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/BeforeAll.html) method and
|
||||||
|
|
|
||||||
|
|
@ -9,25 +9,25 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chromium-tip-of-tree",
|
"name": "chromium-tip-of-tree",
|
||||||
"revision": "1247",
|
"revision": "1248",
|
||||||
"installByDefault": false,
|
"installByDefault": false,
|
||||||
"browserVersion": "129.0.6640.0"
|
"browserVersion": "129.0.6644.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox",
|
"name": "firefox",
|
||||||
"revision": "1458",
|
"revision": "1462",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"browserVersion": "128.0"
|
"browserVersion": "129.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox-beta",
|
"name": "firefox-beta",
|
||||||
"revision": "1461",
|
"revision": "1462",
|
||||||
"installByDefault": false,
|
"installByDefault": false,
|
||||||
"browserVersion": "130.0b2"
|
"browserVersion": "130.0b2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webkit",
|
"name": "webkit",
|
||||||
"revision": "2056",
|
"revision": "2061",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"revisionOverrides": {
|
"revisionOverrides": {
|
||||||
"mac10.14": "1446",
|
"mac10.14": "1446",
|
||||||
|
|
|
||||||
|
|
@ -1592,7 +1592,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Firefox HiDPI": {
|
"Desktop Firefox HiDPI": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
|
@ -1652,7 +1652,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Firefox": {
|
"Desktop Firefox": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1920,
|
"width": 1920,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
|
|
||||||
|
|
@ -220,11 +220,6 @@ export module Protocol {
|
||||||
}|null;
|
}|null;
|
||||||
};
|
};
|
||||||
export type setDefaultViewportReturnValue = void;
|
export type setDefaultViewportReturnValue = void;
|
||||||
export type setScrollbarsHiddenParameters = {
|
|
||||||
browserContextId?: string;
|
|
||||||
hidden: boolean;
|
|
||||||
};
|
|
||||||
export type setScrollbarsHiddenReturnValue = void;
|
|
||||||
export type setInitScriptsParameters = {
|
export type setInitScriptsParameters = {
|
||||||
browserContextId?: string;
|
browserContextId?: string;
|
||||||
scripts: {
|
scripts: {
|
||||||
|
|
@ -1116,7 +1111,6 @@ export module Protocol {
|
||||||
"Browser.setDownloadOptions": Browser.setDownloadOptionsParameters;
|
"Browser.setDownloadOptions": Browser.setDownloadOptionsParameters;
|
||||||
"Browser.setTouchOverride": Browser.setTouchOverrideParameters;
|
"Browser.setTouchOverride": Browser.setTouchOverrideParameters;
|
||||||
"Browser.setDefaultViewport": Browser.setDefaultViewportParameters;
|
"Browser.setDefaultViewport": Browser.setDefaultViewportParameters;
|
||||||
"Browser.setScrollbarsHidden": Browser.setScrollbarsHiddenParameters;
|
|
||||||
"Browser.setInitScripts": Browser.setInitScriptsParameters;
|
"Browser.setInitScripts": Browser.setInitScriptsParameters;
|
||||||
"Browser.addBinding": Browser.addBindingParameters;
|
"Browser.addBinding": Browser.addBindingParameters;
|
||||||
"Browser.grantPermissions": Browser.grantPermissionsParameters;
|
"Browser.grantPermissions": Browser.grantPermissionsParameters;
|
||||||
|
|
@ -1197,7 +1191,6 @@ export module Protocol {
|
||||||
"Browser.setDownloadOptions": Browser.setDownloadOptionsReturnValue;
|
"Browser.setDownloadOptions": Browser.setDownloadOptionsReturnValue;
|
||||||
"Browser.setTouchOverride": Browser.setTouchOverrideReturnValue;
|
"Browser.setTouchOverride": Browser.setTouchOverrideReturnValue;
|
||||||
"Browser.setDefaultViewport": Browser.setDefaultViewportReturnValue;
|
"Browser.setDefaultViewport": Browser.setDefaultViewportReturnValue;
|
||||||
"Browser.setScrollbarsHidden": Browser.setScrollbarsHiddenReturnValue;
|
|
||||||
"Browser.setInitScripts": Browser.setInitScriptsReturnValue;
|
"Browser.setInitScripts": Browser.setInitScriptsReturnValue;
|
||||||
"Browser.addBinding": Browser.addBindingReturnValue;
|
"Browser.addBinding": Browser.addBindingReturnValue;
|
||||||
"Browser.grantPermissions": Browser.grantPermissionsReturnValue;
|
"Browser.grantPermissions": Browser.grantPermissionsReturnValue;
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import * as accessibility from './accessibility';
|
||||||
import { FileChooser } from './fileChooser';
|
import { FileChooser } from './fileChooser';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
import { ProgressController } from './progress';
|
import { ProgressController } from './progress';
|
||||||
import { LongStandingScope, assert, createGuid, isError } from '../utils';
|
import { LongStandingScope, assert, createGuid } from '../utils';
|
||||||
import { ManualPromise } from '../utils/manualPromise';
|
import { ManualPromise } from '../utils/manualPromise';
|
||||||
import { debugLogger } from '../utils/debugLogger';
|
import { debugLogger } from '../utils/debugLogger';
|
||||||
import type { ImageComparatorOptions } from '../utils/comparators';
|
import type { ImageComparatorOptions } from '../utils/comparators';
|
||||||
|
|
@ -851,10 +851,7 @@ export class PageBinding {
|
||||||
}
|
}
|
||||||
context.evaluate(deliverResult, { name, seq, result }).catch(e => debugLogger.log('error', e));
|
context.evaluate(deliverResult, { name, seq, result }).catch(e => debugLogger.log('error', e));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isError(error))
|
context.evaluate(deliverResult, { name, seq, error }).catch(e => debugLogger.log('error', e));
|
||||||
context.evaluate(deliverError, { name, seq, message: error.message, stack: error.stack }).catch(e => debugLogger.log('error', e));
|
|
||||||
else
|
|
||||||
context.evaluate(deliverErrorValue, { name, seq, error }).catch(e => debugLogger.log('error', e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function takeHandle(arg: { name: string, seq: number }) {
|
function takeHandle(arg: { name: string, seq: number }) {
|
||||||
|
|
@ -863,21 +860,13 @@ export class PageBinding {
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deliverResult(arg: { name: string, seq: number, result: any }) {
|
function deliverResult(arg: { name: string, seq: number, result?: any, error?: any }) {
|
||||||
(globalThis as any)[arg.name]['callbacks'].get(arg.seq).resolve(arg.result);
|
const callbacks = (globalThis as any)[arg.name]['callbacks'];
|
||||||
(globalThis as any)[arg.name]['callbacks'].delete(arg.seq);
|
if ('error' in arg)
|
||||||
}
|
callbacks.get(arg.seq).reject(arg.error);
|
||||||
|
else
|
||||||
function deliverError(arg: { name: string, seq: number, message: string, stack: string | undefined }) {
|
callbacks.get(arg.seq).resolve(arg.result);
|
||||||
const error = new Error(arg.message);
|
callbacks.delete(arg.seq);
|
||||||
error.stack = arg.stack;
|
|
||||||
(globalThis as any)[arg.name]['callbacks'].get(arg.seq).reject(error);
|
|
||||||
(globalThis as any)[arg.name]['callbacks'].delete(arg.seq);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deliverErrorValue(arg: { name: string, seq: number, error: any }) {
|
|
||||||
(globalThis as any)[arg.name]['callbacks'].get(arg.seq).reject(arg.error);
|
|
||||||
(globalThis as any)[arg.name]['callbacks'].delete(arg.seq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4452,19 +4452,23 @@ might return multiple quads for inline nodes.
|
||||||
savedResultIndex?: number;
|
savedResultIndex?: number;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets whether the given URL should be in the list of blackboxed scripts, which are ignored when pausing/stepping/debugging.
|
* Sets whether the given URL should be in the list of blackboxed scripts, which are ignored when pausing.
|
||||||
*/
|
*/
|
||||||
export type setShouldBlackboxURLParameters = {
|
export type setShouldBlackboxURLParameters = {
|
||||||
url: string;
|
url: string;
|
||||||
shouldBlackbox: boolean;
|
shouldBlackbox: boolean;
|
||||||
/**
|
/**
|
||||||
* If true, <code>url</code> is case sensitive.
|
* If <code>true</code>, <code>url</code> is case sensitive.
|
||||||
*/
|
*/
|
||||||
caseSensitive?: boolean;
|
caseSensitive?: boolean;
|
||||||
/**
|
/**
|
||||||
* If true, treat <code>url</code> as regular expression.
|
* If <code>true</code>, treat <code>url</code> as regular expression.
|
||||||
*/
|
*/
|
||||||
isRegex?: boolean;
|
isRegex?: boolean;
|
||||||
|
/**
|
||||||
|
* If provided, limits where in the script the debugger will skip pauses. Expected structure is a repeated <code>[startLine, startColumn, endLine, endColumn]</code>. Ignored if <code>shouldBlackbox</code> is <code>false</code>.
|
||||||
|
*/
|
||||||
|
sourceRanges?: number[];
|
||||||
}
|
}
|
||||||
export type setShouldBlackboxURLReturnValue = {
|
export type setShouldBlackboxURLReturnValue = {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
packages/playwright/types/test.d.ts
vendored
4
packages/playwright/types/test.d.ts
vendored
|
|
@ -4815,9 +4815,9 @@ export type Fixtures<T extends KeyValue = {}, W extends KeyValue = {}, PT extend
|
||||||
} & {
|
} & {
|
||||||
[K in keyof PT]?: TestFixtureValue<PT[K], T & W & PT & PW> | [TestFixtureValue<PT[K], T & W & PT & PW>, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }];
|
[K in keyof PT]?: TestFixtureValue<PT[K], T & W & PT & PW> | [TestFixtureValue<PT[K], T & W & PT & PW>, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }];
|
||||||
} & {
|
} & {
|
||||||
[K in Exclude<keyof W, keyof PW>]?: [WorkerFixtureValue<W[K], W & PW>, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
[K in keyof W]?: [WorkerFixtureValue<W[K], W & PW>, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||||
} & {
|
} & {
|
||||||
[K in Exclude<keyof T, keyof PT>]?: TestFixtureValue<T[K], T & W & PT & PW> | [TestFixtureValue<T[K], T & W & PT & PW>, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
[K in keyof T]?: TestFixtureValue<T[K], T & W & PT & PW> | [TestFixtureValue<T[K], T & W & PT & PW>, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||||
};
|
};
|
||||||
|
|
||||||
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@
|
||||||
import '@web/common.css';
|
import '@web/common.css';
|
||||||
import { applyTheme } from '@web/theme';
|
import { applyTheme } from '@web/theme';
|
||||||
import '@web/third_party/vscode/codicon.css';
|
import '@web/third_party/vscode/codicon.css';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom/client';
|
||||||
import { Main } from './main';
|
import { Main } from './main';
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
applyTheme();
|
applyTheme();
|
||||||
// TODO: we'd like to migrate this to React 18, but concurrent mode seems to break some of our tests.
|
ReactDOM.createRoot(document.querySelector('#root')!).render(<Main/>);
|
||||||
ReactDOM.render(<Main/>, document.querySelector('#root'));
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,14 @@ export const Main: React.FC = ({
|
||||||
window.playwrightSetSources = setSources;
|
window.playwrightSetSources = setSources;
|
||||||
window.playwrightSetPaused = setPaused;
|
window.playwrightSetPaused = setPaused;
|
||||||
window.playwrightUpdateLogs = callLogs => {
|
window.playwrightUpdateLogs = callLogs => {
|
||||||
const newLog = new Map<string, CallLog>(log);
|
setLog(log => {
|
||||||
for (const callLog of callLogs) {
|
const newLog = new Map<string, CallLog>(log);
|
||||||
callLog.reveal = !log.has(callLog.id);
|
for (const callLog of callLogs) {
|
||||||
newLog.set(callLog.id, callLog);
|
callLog.reveal = !log.has(callLog.id);
|
||||||
}
|
newLog.set(callLog.id, callLog);
|
||||||
setLog(newLog);
|
}
|
||||||
|
return newLog;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
window.playwrightSourcesEchoForTest = sources;
|
window.playwrightSourcesEchoForTest = sources;
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ export const AttachmentsTab: React.FunctionComponent<{
|
||||||
})}
|
})}
|
||||||
{attachments.size ? <div className='attachments-section'>Attachments</div> : undefined}
|
{attachments.size ? <div className='attachments-section'>Attachments</div> : undefined}
|
||||||
{[...attachments.values()].map((a, i) => {
|
{[...attachments.values()].map((a, i) => {
|
||||||
return <div className='attachment-item' key={`attachment-${i}`}>
|
return <div className='attachment-item' key={attachmentKey(a, i)}>
|
||||||
<ExpandableAttachment attachment={a} />
|
<ExpandableAttachment attachment={a} />
|
||||||
</div>;
|
</div>;
|
||||||
})}
|
})}
|
||||||
|
|
@ -154,3 +154,7 @@ function downloadURL(attachment: Attachment) {
|
||||||
params.dct = attachment.contentType;
|
params.dct = attachment.contentType;
|
||||||
return attachmentURL(attachment, params);
|
return attachmentURL(attachment, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function attachmentKey(attachment: Attachment, index: number) {
|
||||||
|
return index + '-' + (attachment.sha1 ? `sha1-` + attachment.sha1 : `path-` + attachment.path);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,6 @@ export const LogTab: React.FunctionComponent<{
|
||||||
<span className='log-list-duration'>{entry.time}</span>
|
<span className='log-list-duration'>{entry.time}</span>
|
||||||
{entry.message}
|
{entry.message}
|
||||||
</div>}
|
</div>}
|
||||||
noHighlightOnHover={true}
|
notSelectable={true}
|
||||||
/>;
|
/>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,9 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.source-tab-file-name div {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
}, [onOpenExternally, location]);
|
}, [onOpenExternally, location]);
|
||||||
|
|
||||||
const showStackFrames = (stack?.length ?? 0) > 1;
|
const showStackFrames = (stack?.length ?? 0) > 1;
|
||||||
|
const shortFileName = getFileName(fileName);
|
||||||
|
|
||||||
return <SplitView
|
return <SplitView
|
||||||
sidebarSize={200}
|
sidebarSize={200}
|
||||||
|
|
@ -102,8 +103,10 @@ export const SourceTab: React.FunctionComponent<{
|
||||||
sidebarHidden={!showStackFrames}
|
sidebarHidden={!showStackFrames}
|
||||||
main={<div className='vbox' data-testid='source-code'>
|
main={<div className='vbox' data-testid='source-code'>
|
||||||
{ fileName && <Toolbar>
|
{ fileName && <Toolbar>
|
||||||
<span className='source-tab-file-name'>{fileName}</span>
|
<div className='source-tab-file-name' title={fileName}>
|
||||||
<CopyToClipboard description='Copy filename' value={getFileName(fileName)}/>
|
<div>{shortFileName}</div>
|
||||||
|
</div>
|
||||||
|
<CopyToClipboard description='Copy filename' value={shortFileName}/>
|
||||||
{location && <ToolbarButton icon='link-external' title='Open in VS Code' onClick={openExternally}></ToolbarButton>}
|
{location && <ToolbarButton icon='link-external' title='Open in VS Code' onClick={openExternally}></ToolbarButton>}
|
||||||
</Toolbar> }
|
</Toolbar> }
|
||||||
<CodeMirrorWrapper text={source.content || ''} language='javascript' highlight={highlight} revealLine={targetLine} readOnly={true} lineNumbers={true} />
|
<CodeMirrorWrapper text={source.content || ''} language='javascript' highlight={highlight} revealLine={targetLine} readOnly={true} lineNumbers={true} />
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ export function GridView<T>(model: GridViewProps<T>) {
|
||||||
onIconClicked={model.onIconClicked}
|
onIconClicked={model.onIconClicked}
|
||||||
noItemsMessage={model.noItemsMessage}
|
noItemsMessage={model.noItemsMessage}
|
||||||
dataTestId={model.dataTestId}
|
dataTestId={model.dataTestId}
|
||||||
noHighlightOnHover={model.noHighlightOnHover}
|
notSelectable={model.notSelectable}
|
||||||
></ListView>
|
></ListView>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-view-content.not-selectable > .list-view-entry {
|
||||||
|
cursor: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.list-view-entry.highlighted:not(.selected) {
|
.list-view-entry.highlighted:not(.selected) {
|
||||||
background-color: var(--vscode-list-inactiveSelectionBackground) !important;
|
background-color: var(--vscode-list-inactiveSelectionBackground) !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './listView.css';
|
import './listView.css';
|
||||||
|
import { clsx } from '@web/uiUtils';
|
||||||
|
|
||||||
export type ListViewProps<T> = {
|
export type ListViewProps<T> = {
|
||||||
name: string,
|
name: string,
|
||||||
|
|
@ -36,7 +37,7 @@ export type ListViewProps<T> = {
|
||||||
onIconClicked?: (item: T, index: number) => void,
|
onIconClicked?: (item: T, index: number) => void,
|
||||||
noItemsMessage?: string,
|
noItemsMessage?: string,
|
||||||
dataTestId?: string,
|
dataTestId?: string,
|
||||||
noHighlightOnHover?: boolean,
|
notSelectable?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollPositions = new Map<string, number>();
|
const scrollPositions = new Map<string, number>();
|
||||||
|
|
@ -60,7 +61,7 @@ export function ListView<T>({
|
||||||
onIconClicked,
|
onIconClicked,
|
||||||
noItemsMessage,
|
noItemsMessage,
|
||||||
dataTestId,
|
dataTestId,
|
||||||
noHighlightOnHover,
|
notSelectable,
|
||||||
}: ListViewProps<T>) {
|
}: ListViewProps<T>) {
|
||||||
const itemListRef = React.useRef<HTMLDivElement>(null);
|
const itemListRef = React.useRef<HTMLDivElement>(null);
|
||||||
const [highlightedItem, setHighlightedItem] = React.useState<any>();
|
const [highlightedItem, setHighlightedItem] = React.useState<any>();
|
||||||
|
|
@ -85,9 +86,9 @@ export function ListView<T>({
|
||||||
itemListRef.current.scrollTop = scrollPositions.get(name) || 0;
|
itemListRef.current.scrollTop = scrollPositions.get(name) || 0;
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
return <div className={`list-view vbox ` + name + '-list-view' } role={items.length > 0 ? 'list' : undefined} data-testid={dataTestId || (name + '-list')}>
|
return <div className={clsx(`list-view vbox`, name + '-list-view')} role={items.length > 0 ? 'list' : undefined} data-testid={dataTestId || (name + '-list')}>
|
||||||
<div
|
<div
|
||||||
className='list-view-content'
|
className={clsx('list-view-content', notSelectable && 'not-selectable')}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onKeyDown={event => {
|
onKeyDown={event => {
|
||||||
if (selectedItem && event.key === 'Enter') {
|
if (selectedItem && event.key === 'Enter') {
|
||||||
|
|
@ -134,18 +135,19 @@ export function ListView<T>({
|
||||||
>
|
>
|
||||||
{noItemsMessage && items.length === 0 && <div className='list-view-empty'>{noItemsMessage}</div>}
|
{noItemsMessage && items.length === 0 && <div className='list-view-empty'>{noItemsMessage}</div>}
|
||||||
{items.map((item, index) => {
|
{items.map((item, index) => {
|
||||||
const selectedSuffix = selectedItem === item ? ' selected' : '';
|
|
||||||
const highlightedSuffix = !noHighlightOnHover && highlightedItem === item ? ' highlighted' : '';
|
|
||||||
const errorSuffix = isError?.(item, index) ? ' error' : '';
|
|
||||||
const warningSuffix = isWarning?.(item, index) ? ' warning' : '';
|
|
||||||
const infoSuffix = isInfo?.(item, index) ? ' info' : '';
|
|
||||||
const indentation = indent?.(item, index) || 0;
|
const indentation = indent?.(item, index) || 0;
|
||||||
const rendered = render(item, index);
|
const rendered = render(item, index);
|
||||||
return <div
|
return <div
|
||||||
key={id?.(item, index) || index}
|
key={id?.(item, index) || index}
|
||||||
onDoubleClick={() => onAccepted?.(item, index)}
|
onDoubleClick={() => onAccepted?.(item, index)}
|
||||||
role='listitem'
|
role='listitem'
|
||||||
className={'list-view-entry' + selectedSuffix + highlightedSuffix + errorSuffix + warningSuffix + infoSuffix}
|
className={clsx(
|
||||||
|
'list-view-entry',
|
||||||
|
selectedItem === item && 'selected',
|
||||||
|
!notSelectable && highlightedItem === item && 'highlighted',
|
||||||
|
isError?.(item, index) && 'error',
|
||||||
|
isWarning?.(item, index) && 'warning',
|
||||||
|
isInfo?.(item, index) && 'info')}
|
||||||
onClick={() => onSelected?.(item, index)}
|
onClick={() => onSelected?.(item, index)}
|
||||||
onMouseEnter={() => setHighlightedItem(item)}
|
onMouseEnter={() => setHighlightedItem(item)}
|
||||||
onMouseLeave={() => setHighlightedItem(undefined)}
|
onMouseLeave={() => setHighlightedItem(undefined)}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { expect, playwrightTest as base } from '../config/browserTest';
|
||||||
import { kTargetClosedErrorMessage } from 'tests/config/errors';
|
import { kTargetClosedErrorMessage } from 'tests/config/errors';
|
||||||
|
|
||||||
const it = base.extend({
|
const it = base.extend({
|
||||||
context: async () => {
|
context: async ({}, use) => {
|
||||||
throw new Error('global fetch tests should not use context');
|
throw new Error('global fetch tests should not use context');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,9 @@ it('should work with Cross-Origin-Opener-Policy after redirect', async ({ page,
|
||||||
expect(firstRequest.url()).toBe(server.PREFIX + '/redirect');
|
expect(firstRequest.url()).toBe(server.PREFIX + '/redirect');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly cancel Cross-Origin-Opener-Policy navigation', async ({ page, server }) => {
|
it('should properly cancel Cross-Origin-Opener-Policy navigation', {
|
||||||
|
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32107' },
|
||||||
|
}, async ({ page, server }) => {
|
||||||
server.setRoute('/empty.html', (req, res) => {
|
server.setRoute('/empty.html', (req, res) => {
|
||||||
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
||||||
res.end();
|
res.end();
|
||||||
|
|
|
||||||
|
|
@ -393,9 +393,12 @@ it('should continue preload link requests', async ({ page, server, browserName }
|
||||||
expect(color).toBe('rgb(255, 192, 203)');
|
expect(color).toBe('rgb(255, 192, 203)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('continue should propagate headers to redirects', async ({ page, server, browserName }) => {
|
it('continue should propagate headers to redirects', {
|
||||||
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28758' });
|
annotation: [
|
||||||
it.fixme(browserName === 'firefox');
|
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28758' },
|
||||||
|
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32045' },
|
||||||
|
]
|
||||||
|
}, async ({ page, server }) => {
|
||||||
await server.setRedirect('/redirect', '/empty.html');
|
await server.setRedirect('/redirect', '/empty.html');
|
||||||
await page.route('**/redirect', route => {
|
await page.route('**/redirect', route => {
|
||||||
void route.continue({
|
void route.continue({
|
||||||
|
|
@ -413,9 +416,11 @@ it('continue should propagate headers to redirects', async ({ page, server, brow
|
||||||
});
|
});
|
||||||
|
|
||||||
it('redirected requests should report overridden headers', {
|
it('redirected requests should report overridden headers', {
|
||||||
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31351' }
|
annotation: [
|
||||||
}, async ({ page, server, browserName }) => {
|
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31351' },
|
||||||
it.fixme(browserName === 'firefox');
|
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32045' },
|
||||||
|
]
|
||||||
|
}, async ({ page, server }) => {
|
||||||
await server.setRedirect('/redirect', '/empty.html');
|
await server.setRedirect('/redirect', '/empty.html');
|
||||||
await page.route('**/redirect', route => {
|
await page.route('**/redirect', route => {
|
||||||
const headers = route.request().headers();
|
const headers = route.request().headers();
|
||||||
|
|
@ -433,9 +438,12 @@ it('redirected requests should report overridden headers', {
|
||||||
expect((await response.request().allHeaders())['custom']).toBe('value');
|
expect((await response.request().allHeaders())['custom']).toBe('value');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('continue should delete headers on redirects', async ({ page, server, browserName }) => {
|
it('continue should delete headers on redirects', {
|
||||||
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/13106' });
|
annotation: [
|
||||||
it.fixme(browserName === 'firefox');
|
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/13106' },
|
||||||
|
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32045' },
|
||||||
|
]
|
||||||
|
}, async ({ page, server }) => {
|
||||||
await page.goto(server.PREFIX + '/empty.html');
|
await page.goto(server.PREFIX + '/empty.html');
|
||||||
server.setRoute('/something', (request, response) => {
|
server.setRoute('/something', (request, response) => {
|
||||||
response.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
|
response.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { test, expect } from './playwright-test-fixtures';
|
||||||
test('should check types of fixtures', async ({ runTSC }) => {
|
test('should check types of fixtures', async ({ runTSC }) => {
|
||||||
const result = await runTSC({
|
const result = await runTSC({
|
||||||
'helper.ts': `
|
'helper.ts': `
|
||||||
import { test as base, expect } from '@playwright/test';
|
import { test as base, expect, Page } from '@playwright/test';
|
||||||
export type MyOptions = { foo: string, bar: number };
|
export type MyOptions = { foo: string, bar: number };
|
||||||
export const test = base.extend<{ foo: string }, { bar: number }>({
|
export const test = base.extend<{ foo: string }, { bar: number }>({
|
||||||
foo: 'foo',
|
foo: 'foo',
|
||||||
|
|
@ -71,7 +71,7 @@ test('should check types of fixtures', async ({ runTSC }) => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
baz: true,
|
baz: true,
|
||||||
});
|
});
|
||||||
const fail9 = test.extend<{ foo: string }>({
|
const fail9 = test.extend({
|
||||||
foo: [ async ({}, use) => {
|
foo: [ async ({}, use) => {
|
||||||
await use('foo');
|
await use('foo');
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|
@ -100,7 +100,21 @@ test('should check types of fixtures', async ({ runTSC }) => {
|
||||||
return y;
|
return y;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
|
const chain1 = base.extend({
|
||||||
|
page: async ({ page }, use) => {
|
||||||
|
await use(page);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const chain2 = chain1.extend<{ pageAsUser: Page }>({
|
||||||
|
pageAsUser: async ({ page }, use) => {
|
||||||
|
// @ts-expect-error
|
||||||
|
const x: number = page;
|
||||||
|
// @ts-expect-error
|
||||||
|
await use(x);
|
||||||
|
},
|
||||||
|
});
|
||||||
`,
|
`,
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
import { MyOptions } from './helper';
|
import { MyOptions } from './helper';
|
||||||
|
|
|
||||||
4
utils/generate_types/overrides-test.d.ts
vendored
4
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -144,9 +144,9 @@ export type Fixtures<T extends KeyValue = {}, W extends KeyValue = {}, PT extend
|
||||||
} & {
|
} & {
|
||||||
[K in keyof PT]?: TestFixtureValue<PT[K], T & W & PT & PW> | [TestFixtureValue<PT[K], T & W & PT & PW>, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }];
|
[K in keyof PT]?: TestFixtureValue<PT[K], T & W & PT & PW> | [TestFixtureValue<PT[K], T & W & PT & PW>, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }];
|
||||||
} & {
|
} & {
|
||||||
[K in Exclude<keyof W, keyof PW>]?: [WorkerFixtureValue<W[K], W & PW>, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
[K in keyof W]?: [WorkerFixtureValue<W[K], W & PW>, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||||
} & {
|
} & {
|
||||||
[K in Exclude<keyof T, keyof PT>]?: TestFixtureValue<T[K], T & W & PT & PW> | [TestFixtureValue<T[K], T & W & PT & PW>, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
[K in keyof T]?: TestFixtureValue<T[K], T & W & PT & PW> | [TestFixtureValue<T[K], T & W & PT & PW>, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||||
};
|
};
|
||||||
|
|
||||||
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue