Merge branch 'main' into expect-extendImmutable

This commit is contained in:
Simon Knott 2024-09-02 16:46:17 +02:00
commit 5164a7e892
No known key found for this signature in database
GPG key ID: 8CEDC00028084AEC
31 changed files with 743 additions and 180 deletions

View file

@ -1,6 +1,6 @@
# 🎭 Playwright
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-128.0.6613.36-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-129.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-18.0-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop --> [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord)
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-129.0.6668.22-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-129.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-18.0-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop --> [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord)
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
@ -8,7 +8,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->128.0.6613.36<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->129.0.6668.22<!-- 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 -->129.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |

View file

@ -80,14 +80,14 @@ By default, Playwright will look up a closest tsconfig for each imported file by
```sh
# Playwright will choose tsconfig automatically
npx playwrigh test
npx playwright test
```
Alternatively, you can specify a single tsconfig file to use in the command line, and Playwright will use it for all imported files, not only test files.
```sh
# Pass a specific tsconfig
npx playwrigh test --tsconfig=tsconfig.test.json
npx playwright test --tsconfig=tsconfig.test.json
```
## Manually compile tests with TypeScript

8
package-lock.json generated
View file

@ -6820,9 +6820,9 @@
}
},
"node_modules/svelte": {
"version": "4.2.9",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.9.tgz",
"integrity": "sha512-hsoB/WZGEPFXeRRLPhPrbRz67PhP6sqYgvwcAs+gWdSQSvNDw+/lTeUJSWe5h2xC97Fz/8QxAOqItwBzNJPU8w==",
"version": "4.2.19",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz",
"integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==",
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
@ -8060,7 +8060,7 @@
"playwright": "cli.js"
},
"devDependencies": {
"svelte": "^4.2.8"
"svelte": "^4.2.19"
},
"engines": {
"node": ">=18"

View file

@ -75,11 +75,16 @@ export const AttachmentLink: React.FunctionComponent<{
attachment: TestAttachment,
href?: string,
linkName?: string,
}> = ({ attachment, href, linkName }) => {
openInNewTab?: boolean,
}> = ({ attachment, href, linkName, openInNewTab }) => {
return <TreeItem title={<span>
{attachment.contentType === kMissingContentType ? icons.warning() : icons.attachment()}
{attachment.path && <a href={href || attachment.path} download={downloadFileNameForAttachment(attachment)}>{linkName || attachment.name}</a>}
{!attachment.path && <span>{linkifyText(attachment.name)}</span>}
{!attachment.path && (
openInNewTab
? <a href={URL.createObjectURL(new Blob([attachment.body!], { type: attachment.contentType }))} target='_blank' rel='noreferrer' onClick={e => e.stopPropagation()}>{attachment.name}</a>
: <span>{linkifyText(attachment.name)}</span>
)}
</span>} loadChildren={attachment.body ? () => {
return [<div key={1} className='attachment-body'><CopyToClipboard value={attachment.body!}/>{linkifyText(attachment.body!)}</div>];
} : undefined} depth={0} style={{ lineHeight: '32px' }}></TreeItem>;

View file

@ -67,15 +67,16 @@ export const TestResultView: React.FC<{
anchor: 'video' | 'diff' | '',
}> = ({ result, anchor }) => {
const { screenshots, videos, traces, otherAttachments, diffs } = React.useMemo(() => {
const { screenshots, videos, traces, otherAttachments, diffs, htmls } = React.useMemo(() => {
const attachments = result?.attachments || [];
const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/')));
const videos = attachments.filter(a => a.name === 'video');
const traces = attachments.filter(a => a.name === 'trace');
const htmls = attachments.filter(a => a.contentType.startsWith('text/html'));
const otherAttachments = new Set<TestAttachment>(attachments);
[...screenshots, ...videos, ...traces].forEach(a => otherAttachments.delete(a));
[...screenshots, ...videos, ...traces, ...htmls].forEach(a => otherAttachments.delete(a));
const diffs = groupImageDiffs(screenshots);
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs };
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs, htmls };
}, [result]);
const videoRef = React.useRef<HTMLDivElement>(null);
@ -135,7 +136,10 @@ export const TestResultView: React.FC<{
</div>)}
</AutoChip>}
{!!otherAttachments.size && <AutoChip header='Attachments'>
{!!(otherAttachments.size + htmls.length) && <AutoChip header='Attachments'>
{[...htmls].map((a, i) => (
<AttachmentLink key={`html-link-${i}`} attachment={a} openInNewTab />)
)}
{[...otherAttachments].map((a, i) => <AttachmentLink key={`attachment-link-${i}`} attachment={a}></AttachmentLink>)}
</AutoChip>}
</div>;

View file

@ -3,15 +3,15 @@
"browsers": [
{
"name": "chromium",
"revision": "1131",
"revision": "1133",
"installByDefault": true,
"browserVersion": "128.0.6613.36"
"browserVersion": "129.0.6668.22"
},
{
"name": "chromium-tip-of-tree",
"revision": "1254",
"revision": "1255",
"installByDefault": false,
"browserVersion": "130.0.6681.0"
"browserVersion": "130.0.6684.0"
},
{
"name": "firefox",

View file

@ -1131,17 +1131,21 @@ using Audits.issueAdded event.
}
/**
* Defines commands and events for browser extensions. Available if the client
is connected using the --remote-debugging-pipe flag and
the --enable-unsafe-extension-debugging flag is set.
* Defines commands and events for browser extensions.
*/
export module Extensions {
/**
* Storage areas.
*/
export type StorageArea = "session"|"local"|"sync"|"managed";
/**
* Installs an unpacked extension from the filesystem similar to
--load-extension CLI flags. Returns extension ID once the extension
has been installed.
has been installed. Available if the client is connected using the
--remote-debugging-pipe flag and the --enable-unsafe-extension-debugging
flag is set.
*/
export type loadUnpackedParameters = {
/**
@ -1155,6 +1159,81 @@ has been installed.
*/
id: string;
}
/**
* Gets data from extension storage in the given `storageArea`. If `keys` is
specified, these are used to filter the result.
*/
export type getStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to retrieve data from.
*/
storageArea: StorageArea;
/**
* Keys to retrieve.
*/
keys?: string[];
}
export type getStorageItemsReturnValue = {
data: { [key: string]: string };
}
/**
* Removes `keys` from extension storage in the given `storageArea`.
*/
export type removeStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to remove data from.
*/
storageArea: StorageArea;
/**
* Keys to remove.
*/
keys: string[];
}
export type removeStorageItemsReturnValue = {
}
/**
* Clears extension storage in the given `storageArea`.
*/
export type clearStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to remove data from.
*/
storageArea: StorageArea;
}
export type clearStorageItemsReturnValue = {
}
/**
* Sets `values` in extension storage in the given `storageArea`. The provided `values`
will be merged with existing values in the storage area.
*/
export type setStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to set data in.
*/
storageArea: StorageArea;
/**
* Values to set.
*/
values: { [key: string]: string };
}
export type setStorageItemsReturnValue = {
}
}
/**
@ -2532,16 +2611,6 @@ stylesheet rules) this rule came from.
*/
style: CSSStyle;
}
/**
* CSS position-fallback rule representation.
*/
export interface CSSPositionFallbackRule {
name: Value;
/**
* List of keyframes.
*/
tryRules: CSSTryRule[];
}
/**
* CSS @position-try rule representation.
*/
@ -2888,10 +2957,6 @@ attributes) for a DOM node identified by `nodeId`.
* A list of CSS keyframed animations matching this node.
*/
cssKeyframesRules?: CSSKeyframesRule[];
/**
* A list of CSS position fallbacks matching this node.
*/
cssPositionFallbackRules?: CSSPositionFallbackRule[];
/**
* A list of CSS @position-try rules matching this node, based on the position-try-fallbacks property.
*/
@ -3496,7 +3561,7 @@ front-end.
/**
* Pseudo element type.
*/
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-next-button"|"scroll-prev-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
/**
* Shadow root type.
*/
@ -3646,6 +3711,13 @@ The property is always undefined now.
compatibilityMode?: CompatibilityMode;
assignedSlot?: BackendNode;
}
/**
* A structure to hold the top-level node of a detached tree and an array of its retained descendants.
*/
export interface DetachedElementInfo {
treeNode: Node;
retainedNodeIds: NodeId[];
}
/**
* A structure holding an RGBA color.
*/
@ -4693,6 +4765,17 @@ File wrapper.
export type getFileInfoReturnValue = {
path: string;
}
/**
* Returns list of detached nodes
*/
export type getDetachedDomNodesParameters = {
}
export type getDetachedDomNodesReturnValue = {
/**
* The list of detached nodes
*/
detachedNodes: DetachedElementInfo[];
}
/**
* Enables console to refer to the node with given id via $x (see Command Line API for more details
$x functions).
@ -11369,7 +11452,7 @@ as an ad.
* All Permissions Policy features. This enum should match the one defined
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
*/
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking";
export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking";
/**
* Reason for a permissions policy feature to be disabled.
*/
@ -11784,7 +11867,7 @@ Example URLs: http://www.google.com/file.html -> "google.com"
*/
fixed?: number;
}
export type ClientNavigationReason = "formSubmissionGet"|"formSubmissionPost"|"httpHeaderRefresh"|"scriptInitiated"|"metaTagRefresh"|"pageBlockInterstitial"|"reload"|"anchorClick";
export type ClientNavigationReason = "anchorClick"|"formSubmissionGet"|"formSubmissionPost"|"httpHeaderRefresh"|"initialFrameNavigation"|"metaTagRefresh"|"other"|"pageBlockInterstitial"|"reload"|"scriptInitiated";
export type ClientNavigationDisposition = "currentTab"|"newTab"|"newWindow"|"download";
export interface InstallabilityErrorArgument {
/**
@ -12298,6 +12381,10 @@ when bfcache navigation fails.
* Frame's new url.
*/
url: string;
/**
* Navigation type
*/
navigationType: "fragment"|"historyApi"|"other";
}
/**
* Compressed image data requested by the `startScreencast`.
@ -16922,7 +17009,7 @@ possible for multiple rule sets and links to trigger a single attempt.
/**
* List of FinalStatus reasons for Prerender2.
*/
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl"|"JavaScriptInterfaceAdded"|"JavaScriptInterfaceRemoved"|"AllPrerenderingCanceled"|"WindowClosed";
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl"|"JavaScriptInterfaceAdded"|"JavaScriptInterfaceRemoved"|"AllPrerenderingCanceled"|"WindowClosed"|"SlowNetwork"|"OtherPrerenderedPageActivated";
/**
* Preloading status values, see also PreloadingTriggeringOutcome. This
status is shared by prefetchStatusUpdated and prerenderStatusUpdated.
@ -17270,6 +17357,101 @@ supported yet.
}
}
/**
* This domain allows configuring virtual Bluetooth devices to test
the web-bluetooth API.
*/
export module BluetoothEmulation {
/**
* Indicates the various states of Central.
*/
export type CentralState = "absent"|"powered-off"|"powered-on";
/**
* Stores the manufacturer data
*/
export interface ManufacturerData {
/**
* Company identifier
https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml
https://usb.org/developers
*/
key: number;
/**
* Manufacturer-specific data
*/
data: binary;
}
/**
* Stores the byte data of the advertisement packet sent by a Bluetooth device.
*/
export interface ScanRecord {
name?: string;
uuids?: string[];
/**
* Stores the external appearance description of the device.
*/
appearance?: number;
/**
* Stores the transmission power of a broadcasting device.
*/
txPower?: number;
/**
* Key is the company identifier and the value is an array of bytes of
manufacturer specific data.
*/
manufacturerData?: ManufacturerData[];
}
/**
* Stores the advertisement packet information that is sent by a Bluetooth device.
*/
export interface ScanEntry {
deviceAddress: string;
rssi: number;
scanRecord: ScanRecord;
}
/**
* Enable the BluetoothEmulation domain.
*/
export type enableParameters = {
/**
* State of the simulated central.
*/
state: CentralState;
}
export type enableReturnValue = {
}
/**
* Disable the BluetoothEmulation domain.
*/
export type disableParameters = {
}
export type disableReturnValue = {
}
/**
* Simulates a peripheral with |address|, |name| and |knownServiceUuids|
that has already been connected to the system.
*/
export type simulatePreconnectedPeripheralParameters = {
address: string;
name: string;
manufacturerData: ManufacturerData[];
knownServiceUuids: string[];
}
export type simulatePreconnectedPeripheralReturnValue = {
}
/**
* Simulates an advertisement packet described in |entry| being received by
the central.
*/
export type simulateAdvertisementParameters = {
entry: ScanEntry;
}
export type simulateAdvertisementReturnValue = {
}
}
/**
* This domain is deprecated - use Runtime or Log instead.
*/
@ -20122,6 +20304,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastParameters;
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
"Extensions.loadUnpacked": Extensions.loadUnpackedParameters;
"Extensions.getStorageItems": Extensions.getStorageItemsParameters;
"Extensions.removeStorageItems": Extensions.removeStorageItemsParameters;
"Extensions.clearStorageItems": Extensions.clearStorageItemsParameters;
"Extensions.setStorageItems": Extensions.setStorageItemsParameters;
"Autofill.trigger": Autofill.triggerParameters;
"Autofill.setAddresses": Autofill.setAddressesParameters;
"Autofill.disable": Autofill.disableParameters;
@ -20232,6 +20418,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledParameters;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesParameters;
"DOM.getFileInfo": DOM.getFileInfoParameters;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesParameters;
"DOM.setInspectedNode": DOM.setInspectedNodeParameters;
"DOM.setNodeName": DOM.setNodeNameParameters;
"DOM.setNodeValue": DOM.setNodeValueParameters;
@ -20616,6 +20803,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppParameters;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppParameters;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsParameters;
"BluetoothEmulation.enable": BluetoothEmulation.enableParameters;
"BluetoothEmulation.disable": BluetoothEmulation.disableParameters;
"BluetoothEmulation.simulatePreconnectedPeripheral": BluetoothEmulation.simulatePreconnectedPeripheralParameters;
"BluetoothEmulation.simulateAdvertisement": BluetoothEmulation.simulateAdvertisementParameters;
"Console.clearMessages": Console.clearMessagesParameters;
"Console.disable": Console.disableParameters;
"Console.enable": Console.enableParameters;
@ -20722,6 +20913,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastReturnValue;
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
"Extensions.loadUnpacked": Extensions.loadUnpackedReturnValue;
"Extensions.getStorageItems": Extensions.getStorageItemsReturnValue;
"Extensions.removeStorageItems": Extensions.removeStorageItemsReturnValue;
"Extensions.clearStorageItems": Extensions.clearStorageItemsReturnValue;
"Extensions.setStorageItems": Extensions.setStorageItemsReturnValue;
"Autofill.trigger": Autofill.triggerReturnValue;
"Autofill.setAddresses": Autofill.setAddressesReturnValue;
"Autofill.disable": Autofill.disableReturnValue;
@ -20832,6 +21027,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledReturnValue;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesReturnValue;
"DOM.getFileInfo": DOM.getFileInfoReturnValue;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesReturnValue;
"DOM.setInspectedNode": DOM.setInspectedNodeReturnValue;
"DOM.setNodeName": DOM.setNodeNameReturnValue;
"DOM.setNodeValue": DOM.setNodeValueReturnValue;
@ -21216,6 +21412,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppReturnValue;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppReturnValue;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsReturnValue;
"BluetoothEmulation.enable": BluetoothEmulation.enableReturnValue;
"BluetoothEmulation.disable": BluetoothEmulation.disableReturnValue;
"BluetoothEmulation.simulatePreconnectedPeripheral": BluetoothEmulation.simulatePreconnectedPeripheralReturnValue;
"BluetoothEmulation.simulateAdvertisement": BluetoothEmulation.simulateAdvertisementReturnValue;
"Console.clearMessages": Console.clearMessagesReturnValue;
"Console.disable": Console.disableReturnValue;
"Console.enable": Console.enableReturnValue;

View file

@ -110,7 +110,7 @@
"defaultBrowserType": "webkit"
},
"Galaxy S5": {
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -121,7 +121,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -132,7 +132,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S8": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 740
@ -143,7 +143,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S8 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 740,
"height": 360
@ -154,7 +154,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S9+": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 320,
"height": 658
@ -165,7 +165,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S9+ landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 658,
"height": 320
@ -176,7 +176,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy Tab S4": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"viewport": {
"width": 712,
"height": 1138
@ -187,7 +187,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy Tab S4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"viewport": {
"width": 1138,
"height": 712
@ -1098,7 +1098,7 @@
"defaultBrowserType": "webkit"
},
"LG Optimus L70": {
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 384,
"height": 640
@ -1109,7 +1109,7 @@
"defaultBrowserType": "chromium"
},
"LG Optimus L70 landscape": {
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 384
@ -1120,7 +1120,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 550": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 640,
"height": 360
@ -1131,7 +1131,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 550 landscape": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 360,
"height": 640
@ -1142,7 +1142,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 950": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 360,
"height": 640
@ -1153,7 +1153,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 950 landscape": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 640,
"height": 360
@ -1164,7 +1164,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 10": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"viewport": {
"width": 800,
"height": 1280
@ -1175,7 +1175,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 10 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"viewport": {
"width": 1280,
"height": 800
@ -1186,7 +1186,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 4": {
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 384,
"height": 640
@ -1197,7 +1197,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 384
@ -1208,7 +1208,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -1219,7 +1219,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -1230,7 +1230,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5X": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1241,7 +1241,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5X landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1252,7 +1252,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1263,7 +1263,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1274,7 +1274,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6P": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1285,7 +1285,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6P landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1296,7 +1296,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 7": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"viewport": {
"width": 600,
"height": 960
@ -1307,7 +1307,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 7 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"viewport": {
"width": 960,
"height": 600
@ -1362,7 +1362,7 @@
"defaultBrowserType": "webkit"
},
"Pixel 2": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 411,
"height": 731
@ -1373,7 +1373,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 2 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 731,
"height": 411
@ -1384,7 +1384,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 2 XL": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 411,
"height": 823
@ -1395,7 +1395,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 2 XL landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 823,
"height": 411
@ -1406,7 +1406,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 3": {
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 393,
"height": 786
@ -1417,7 +1417,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 3 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 786,
"height": 393
@ -1428,7 +1428,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 353,
"height": 745
@ -1439,7 +1439,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 745,
"height": 353
@ -1450,7 +1450,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4a (5G)": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"screen": {
"width": 412,
"height": 892
@ -1465,7 +1465,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4a (5G) landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"screen": {
"height": 892,
"width": 412
@ -1480,7 +1480,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"screen": {
"width": 393,
"height": 851
@ -1495,7 +1495,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"screen": {
"width": 851,
"height": 393
@ -1510,7 +1510,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 7": {
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"screen": {
"width": 412,
"height": 915
@ -1525,7 +1525,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 7 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"screen": {
"width": 915,
"height": 412
@ -1540,7 +1540,7 @@
"defaultBrowserType": "chromium"
},
"Moto G4": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -1551,7 +1551,7 @@
"defaultBrowserType": "chromium"
},
"Moto G4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -1562,7 +1562,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Chrome HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"screen": {
"width": 1792,
"height": 1120
@ -1577,7 +1577,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Edge HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36 Edg/128.0.6613.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36 Edg/129.0.6668.22",
"screen": {
"width": 1792,
"height": 1120
@ -1622,7 +1622,7 @@
"defaultBrowserType": "webkit"
},
"Desktop Chrome": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36",
"screen": {
"width": 1920,
"height": 1080
@ -1637,7 +1637,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Edge": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.36 Safari/537.36 Edg/128.0.6613.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.22 Safari/537.36 Edg/129.0.6668.22",
"screen": {
"width": 1920,
"height": 1080

View file

@ -34,7 +34,8 @@ import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } fr
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
import type { Language } from '../../utils/isomorphic/locatorGenerators';
import { cacheNormalizedWhitespaces, escapeHTML, escapeHTMLAttribute, normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils';
import { generateSimpleDom, generateSimpleDomNode, selectorForSimpleDomNodeId } from './simpleDom';
import { selectorForSimpleDomNodeId, generateSimpleDomNode } from './simpleDom';
import type { SimpleDomNode } from './simpleDom';
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
@ -79,15 +80,12 @@ export class InjectedScript {
endAriaCaches,
escapeHTML,
escapeHTMLAttribute,
generateSimpleDom: generateSimpleDom.bind(undefined, this),
generateSimpleDomNode: generateSimpleDomNode.bind(undefined, this),
getAriaRole,
getElementAccessibleDescription,
getElementAccessibleName,
isElementVisible,
isInsideScope,
normalizeWhiteSpace,
selectorForSimpleDomNodeId: selectorForSimpleDomNodeId.bind(undefined, this),
};
// eslint-disable-next-line no-restricted-globals
@ -1314,6 +1312,17 @@ export class InjectedScript {
}
throw this.createStacklessError('Unknown expect matcher: ' + expression);
}
generateSimpleDomNode(selector: string): SimpleDomNode | undefined {
const element = this.querySelector(this.parseSelector(selector), this.document.documentElement, true);
if (!element)
return;
return generateSimpleDomNode(this, element);
}
selectorForSimpleDomNodeId(nodeId: string) {
return selectorForSimpleDomNodeId(this, nodeId);
}
}
const autoClosingTags = new Set(['AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR']);

View file

@ -24,8 +24,8 @@ import clipPaths from './clipPaths';
import type { SimpleDomNode } from '../simpleDom';
interface RecorderDelegate {
performAction?(action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode): Promise<void>;
recordAction?(action: actions.Action, simpleDomNode?: SimpleDomNode): Promise<void>;
performAction?(action: actions.PerformOnRecordAction): Promise<void>;
recordAction?(action: actions.Action): Promise<void>;
setSelector?(selector: string): Promise<void>;
setMode?(mode: Mode): Promise<void>;
setOverlayState?(state: OverlayState): Promise<void>;
@ -931,7 +931,6 @@ export class Recorder {
testIdAttributeName: 'data-testid',
language: 'javascript',
overlay: { offsetX: 0 },
generateSimpleDom: false,
};
readonly document: Document;
private _delegate: RecorderDelegate = {};
@ -1186,13 +1185,11 @@ export class Recorder {
}
async performAction(action: actions.PerformOnRecordAction) {
const simpleDomNode = this._generateSimpleDomNode(action);
await this._delegate.performAction?.(action, simpleDomNode).catch(() => {});
await this._delegate.performAction?.(action).catch(() => {});
}
recordAction(action: actions.Action) {
const simpleDomNode = this._generateSimpleDomNode(action);
void this._delegate.recordAction?.(action, simpleDomNode);
void this._delegate.recordAction?.(action);
}
setOverlayState(state: { offsetX: number; }) {
@ -1202,18 +1199,6 @@ export class Recorder {
setSelector(selector: string) {
void this._delegate.setSelector?.(selector);
}
private _generateSimpleDomNode(action: actions.Action): SimpleDomNode | undefined {
if (!this.state.generateSimpleDom)
return;
if (!('selector' in action))
return;
const element = this.injectedScript.querySelector(this.injectedScript.parseSelector(action.selector), this.document.documentElement, true);
if (!element)
return;
return this.injectedScript.utils.generateSimpleDomNode(element);
}
}
class Dialog {

View file

@ -77,10 +77,11 @@ function generate(injectedScript: InjectedScript, target?: Element): { dom: Simp
const name = injectedScript.utils.getElementAccessibleName(element, false);
const structuralId = String(++lastId);
elements.set(structuralId, element);
const tag = renderTag(injectedScript, role, name, structuralId, { value });
if (element === target)
resultTarget = { tag, id: structuralId };
tokens.push(tag);
tokens.push(renderTag(injectedScript, role, name, structuralId, { value }));
if (element === target) {
const tagNoValue = renderTag(injectedScript, role, name, structuralId);
resultTarget = { tag: tagNoValue, id: structuralId };
}
return;
}
}

View file

@ -72,7 +72,7 @@ export class Recorder implements InstrumentationListener {
constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams) {
this._mode = params.mode || 'none';
this._contextRecorder = new ContextRecorder(context, params);
this._contextRecorder = new ContextRecorder(context, params, {});
this._context = context;
this._omitCallTracking = !!params.omitCallTracking;
this._debugger = context.debugger();
@ -160,7 +160,6 @@ export class Recorder implements InstrumentationListener {
language: this._currentLanguage,
testIdAttributeName: this._contextRecorder.testIdAttributeName(),
overlay: this._overlayState,
generateSimpleDom: false,
};
return uiState;
});

View file

@ -25,7 +25,6 @@ import type { ActionInContext, FrameDescription, LanguageGeneratorOptions, Langu
import { languageSet } from '../codegen/languages';
import type { Dialog } from '../dialog';
import { Frame } from '../frames';
import type { SimpleDomNode } from '../injected/simpleDom';
import { Page } from '../page';
import type * as actions from './recorderActions';
import { performAction } from './recorderRunner';
@ -35,6 +34,10 @@ import { generateCode } from '../codegen/language';
type BindingSource = { frame: Frame, page: Page };
export interface ContextRecorderDelegate {
rewriteActionInContext?(pageAliases: Map<Page, string>, actionInContext: ActionInContext): Promise<void>;
}
export class ContextRecorder extends EventEmitter {
static Events = {
Change: 'change'
@ -48,15 +51,17 @@ export class ContextRecorder extends EventEmitter {
private _timers = new Set<NodeJS.Timeout>();
private _context: BrowserContext;
private _params: channels.BrowserContextRecorderSupplementEnableParams;
private _delegate: ContextRecorderDelegate;
private _recorderSources: Source[];
private _throttledOutputFile: ThrottledFile | null = null;
private _orderedLanguages: LanguageGenerator[] = [];
private _listeners: RegisteredListener[] = [];
constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams) {
constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams, delegate: ContextRecorderDelegate) {
super();
this._context = context;
this._params = params;
this._delegate = delegate;
this._recorderSources = [];
const language = params.language || context.attribution.playwright.options.sdkLanguage;
this.setOutput(language, params.outputFile);
@ -134,11 +139,11 @@ export class ContextRecorder extends EventEmitter {
// Input actions that potentially lead to navigation are intercepted on the page and are
// performed by the Playwright.
await this._context.exposeBinding('__pw_recorderPerformAction', false,
(source: BindingSource, action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode) => this._performAction(source.frame, action, simpleDomNode));
(source: BindingSource, action: actions.PerformOnRecordAction) => this._performAction(source.frame, action));
// Other non-essential actions are simply being recorded.
await this._context.exposeBinding('__pw_recorderRecordAction', false,
(source: BindingSource, action: actions.Action, simpleDomNode?: SimpleDomNode) => this._recordAction(source.frame, action, simpleDomNode));
(source: BindingSource, action: actions.Action) => this._recordAction(source.frame, action));
await this._context.extendInjectedScript(recorderSource.source);
}
@ -218,7 +223,7 @@ export class ContextRecorder extends EventEmitter {
return this._params.testIdAttributeName || this._context.selectors().testIdAttributeName() || 'data-testid';
}
private async _performAction(frame: Frame, action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode) {
private async _performAction(frame: Frame, action: actions.PerformOnRecordAction) {
// Commit last action so that no further signals are added to it.
this._collection.commitLastAction();
@ -226,9 +231,11 @@ export class ContextRecorder extends EventEmitter {
const actionInContext: ActionInContext = {
frame: frameDescription,
action,
description: undefined, // TODO: generate description based on simple dom node.
description: undefined,
};
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
this._collection.willPerformAction(actionInContext);
const success = await performAction(this._pageAliases, actionInContext);
if (success) {
@ -239,7 +246,7 @@ export class ContextRecorder extends EventEmitter {
}
}
private async _recordAction(frame: Frame, action: actions.Action, simpleDomNode?: SimpleDomNode) {
private async _recordAction(frame: Frame, action: actions.Action) {
// Commit last action so that no further signals are added to it.
this._collection.commitLastAction();
@ -247,8 +254,11 @@ export class ContextRecorder extends EventEmitter {
const actionInContext: ActionInContext = {
frame: frameDescription,
action,
description: undefined, // TODO: generate description based on simple dom node.
description: undefined,
};
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
this._setCommittedAfterTimeout(actionInContext);
this._collection.addAction(actionInContext);
}

View file

@ -16,6 +16,10 @@
import type { CallMetadata } from '../instrumentation';
import type { CallLog, CallLogStatus } from '@recorder/recorderTypes';
import type { Page } from '../page';
import type { ActionInContext } from '../codegen/types';
import type { Frame } from '../frames';
import type * as actions from './recorderActions';
export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog {
let title = metadata.apiName || metadata.method;
@ -48,3 +52,23 @@ export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus)
export function buildFullSelector(framePath: string[], selector: string) {
return [...framePath, selector].join(' >> internal:control=enter-frame >> ');
}
export function mainFrameForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext): Frame {
const pageAlias = actionInContext.frame.pageAlias;
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
if (!page)
throw new Error('Internal error: page not found');
return page.mainFrame();
}
export async function frameForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext, action: actions.ActionWithSelector): Promise<Frame> {
const pageAlias = actionInContext.frame.pageAlias;
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
if (!page)
throw new Error('Internal error: page not found');
const fullSelector = buildFullSelector(actionInContext.frame.framePath, action.selector);
const result = await page.mainFrame().selectors.resolveFrameForSelector(fullSelector);
if (!result)
throw new Error('Internal error: frame not found');
return result.frame;
}

View file

@ -1131,17 +1131,21 @@ using Audits.issueAdded event.
}
/**
* Defines commands and events for browser extensions. Available if the client
is connected using the --remote-debugging-pipe flag and
the --enable-unsafe-extension-debugging flag is set.
* Defines commands and events for browser extensions.
*/
export module Extensions {
/**
* Storage areas.
*/
export type StorageArea = "session"|"local"|"sync"|"managed";
/**
* Installs an unpacked extension from the filesystem similar to
--load-extension CLI flags. Returns extension ID once the extension
has been installed.
has been installed. Available if the client is connected using the
--remote-debugging-pipe flag and the --enable-unsafe-extension-debugging
flag is set.
*/
export type loadUnpackedParameters = {
/**
@ -1155,6 +1159,81 @@ has been installed.
*/
id: string;
}
/**
* Gets data from extension storage in the given `storageArea`. If `keys` is
specified, these are used to filter the result.
*/
export type getStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to retrieve data from.
*/
storageArea: StorageArea;
/**
* Keys to retrieve.
*/
keys?: string[];
}
export type getStorageItemsReturnValue = {
data: { [key: string]: string };
}
/**
* Removes `keys` from extension storage in the given `storageArea`.
*/
export type removeStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to remove data from.
*/
storageArea: StorageArea;
/**
* Keys to remove.
*/
keys: string[];
}
export type removeStorageItemsReturnValue = {
}
/**
* Clears extension storage in the given `storageArea`.
*/
export type clearStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to remove data from.
*/
storageArea: StorageArea;
}
export type clearStorageItemsReturnValue = {
}
/**
* Sets `values` in extension storage in the given `storageArea`. The provided `values`
will be merged with existing values in the storage area.
*/
export type setStorageItemsParameters = {
/**
* ID of extension.
*/
id: string;
/**
* StorageArea to set data in.
*/
storageArea: StorageArea;
/**
* Values to set.
*/
values: { [key: string]: string };
}
export type setStorageItemsReturnValue = {
}
}
/**
@ -2532,16 +2611,6 @@ stylesheet rules) this rule came from.
*/
style: CSSStyle;
}
/**
* CSS position-fallback rule representation.
*/
export interface CSSPositionFallbackRule {
name: Value;
/**
* List of keyframes.
*/
tryRules: CSSTryRule[];
}
/**
* CSS @position-try rule representation.
*/
@ -2888,10 +2957,6 @@ attributes) for a DOM node identified by `nodeId`.
* A list of CSS keyframed animations matching this node.
*/
cssKeyframesRules?: CSSKeyframesRule[];
/**
* A list of CSS position fallbacks matching this node.
*/
cssPositionFallbackRules?: CSSPositionFallbackRule[];
/**
* A list of CSS @position-try rules matching this node, based on the position-try-fallbacks property.
*/
@ -3496,7 +3561,7 @@ front-end.
/**
* Pseudo element type.
*/
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-next-button"|"scroll-prev-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
/**
* Shadow root type.
*/
@ -3646,6 +3711,13 @@ The property is always undefined now.
compatibilityMode?: CompatibilityMode;
assignedSlot?: BackendNode;
}
/**
* A structure to hold the top-level node of a detached tree and an array of its retained descendants.
*/
export interface DetachedElementInfo {
treeNode: Node;
retainedNodeIds: NodeId[];
}
/**
* A structure holding an RGBA color.
*/
@ -4693,6 +4765,17 @@ File wrapper.
export type getFileInfoReturnValue = {
path: string;
}
/**
* Returns list of detached nodes
*/
export type getDetachedDomNodesParameters = {
}
export type getDetachedDomNodesReturnValue = {
/**
* The list of detached nodes
*/
detachedNodes: DetachedElementInfo[];
}
/**
* Enables console to refer to the node with given id via $x (see Command Line API for more details
$x functions).
@ -11369,7 +11452,7 @@ as an ad.
* All Permissions Policy features. This enum should match the one defined
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
*/
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking";
export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking";
/**
* Reason for a permissions policy feature to be disabled.
*/
@ -11784,7 +11867,7 @@ Example URLs: http://www.google.com/file.html -> "google.com"
*/
fixed?: number;
}
export type ClientNavigationReason = "formSubmissionGet"|"formSubmissionPost"|"httpHeaderRefresh"|"scriptInitiated"|"metaTagRefresh"|"pageBlockInterstitial"|"reload"|"anchorClick";
export type ClientNavigationReason = "anchorClick"|"formSubmissionGet"|"formSubmissionPost"|"httpHeaderRefresh"|"initialFrameNavigation"|"metaTagRefresh"|"other"|"pageBlockInterstitial"|"reload"|"scriptInitiated";
export type ClientNavigationDisposition = "currentTab"|"newTab"|"newWindow"|"download";
export interface InstallabilityErrorArgument {
/**
@ -12298,6 +12381,10 @@ when bfcache navigation fails.
* Frame's new url.
*/
url: string;
/**
* Navigation type
*/
navigationType: "fragment"|"historyApi"|"other";
}
/**
* Compressed image data requested by the `startScreencast`.
@ -16922,7 +17009,7 @@ possible for multiple rule sets and links to trigger a single attempt.
/**
* List of FinalStatus reasons for Prerender2.
*/
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl"|"JavaScriptInterfaceAdded"|"JavaScriptInterfaceRemoved"|"AllPrerenderingCanceled"|"WindowClosed";
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl"|"JavaScriptInterfaceAdded"|"JavaScriptInterfaceRemoved"|"AllPrerenderingCanceled"|"WindowClosed"|"SlowNetwork"|"OtherPrerenderedPageActivated";
/**
* Preloading status values, see also PreloadingTriggeringOutcome. This
status is shared by prefetchStatusUpdated and prerenderStatusUpdated.
@ -17270,6 +17357,101 @@ supported yet.
}
}
/**
* This domain allows configuring virtual Bluetooth devices to test
the web-bluetooth API.
*/
export module BluetoothEmulation {
/**
* Indicates the various states of Central.
*/
export type CentralState = "absent"|"powered-off"|"powered-on";
/**
* Stores the manufacturer data
*/
export interface ManufacturerData {
/**
* Company identifier
https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml
https://usb.org/developers
*/
key: number;
/**
* Manufacturer-specific data
*/
data: binary;
}
/**
* Stores the byte data of the advertisement packet sent by a Bluetooth device.
*/
export interface ScanRecord {
name?: string;
uuids?: string[];
/**
* Stores the external appearance description of the device.
*/
appearance?: number;
/**
* Stores the transmission power of a broadcasting device.
*/
txPower?: number;
/**
* Key is the company identifier and the value is an array of bytes of
manufacturer specific data.
*/
manufacturerData?: ManufacturerData[];
}
/**
* Stores the advertisement packet information that is sent by a Bluetooth device.
*/
export interface ScanEntry {
deviceAddress: string;
rssi: number;
scanRecord: ScanRecord;
}
/**
* Enable the BluetoothEmulation domain.
*/
export type enableParameters = {
/**
* State of the simulated central.
*/
state: CentralState;
}
export type enableReturnValue = {
}
/**
* Disable the BluetoothEmulation domain.
*/
export type disableParameters = {
}
export type disableReturnValue = {
}
/**
* Simulates a peripheral with |address|, |name| and |knownServiceUuids|
that has already been connected to the system.
*/
export type simulatePreconnectedPeripheralParameters = {
address: string;
name: string;
manufacturerData: ManufacturerData[];
knownServiceUuids: string[];
}
export type simulatePreconnectedPeripheralReturnValue = {
}
/**
* Simulates an advertisement packet described in |entry| being received by
the central.
*/
export type simulateAdvertisementParameters = {
entry: ScanEntry;
}
export type simulateAdvertisementReturnValue = {
}
}
/**
* This domain is deprecated - use Runtime or Log instead.
*/
@ -20122,6 +20304,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastParameters;
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
"Extensions.loadUnpacked": Extensions.loadUnpackedParameters;
"Extensions.getStorageItems": Extensions.getStorageItemsParameters;
"Extensions.removeStorageItems": Extensions.removeStorageItemsParameters;
"Extensions.clearStorageItems": Extensions.clearStorageItemsParameters;
"Extensions.setStorageItems": Extensions.setStorageItemsParameters;
"Autofill.trigger": Autofill.triggerParameters;
"Autofill.setAddresses": Autofill.setAddressesParameters;
"Autofill.disable": Autofill.disableParameters;
@ -20232,6 +20418,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledParameters;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesParameters;
"DOM.getFileInfo": DOM.getFileInfoParameters;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesParameters;
"DOM.setInspectedNode": DOM.setInspectedNodeParameters;
"DOM.setNodeName": DOM.setNodeNameParameters;
"DOM.setNodeValue": DOM.setNodeValueParameters;
@ -20616,6 +20803,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppParameters;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppParameters;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsParameters;
"BluetoothEmulation.enable": BluetoothEmulation.enableParameters;
"BluetoothEmulation.disable": BluetoothEmulation.disableParameters;
"BluetoothEmulation.simulatePreconnectedPeripheral": BluetoothEmulation.simulatePreconnectedPeripheralParameters;
"BluetoothEmulation.simulateAdvertisement": BluetoothEmulation.simulateAdvertisementParameters;
"Console.clearMessages": Console.clearMessagesParameters;
"Console.disable": Console.disableParameters;
"Console.enable": Console.enableParameters;
@ -20722,6 +20913,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastReturnValue;
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
"Extensions.loadUnpacked": Extensions.loadUnpackedReturnValue;
"Extensions.getStorageItems": Extensions.getStorageItemsReturnValue;
"Extensions.removeStorageItems": Extensions.removeStorageItemsReturnValue;
"Extensions.clearStorageItems": Extensions.clearStorageItemsReturnValue;
"Extensions.setStorageItems": Extensions.setStorageItemsReturnValue;
"Autofill.trigger": Autofill.triggerReturnValue;
"Autofill.setAddresses": Autofill.setAddressesReturnValue;
"Autofill.disable": Autofill.disableReturnValue;
@ -20832,6 +21027,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledReturnValue;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesReturnValue;
"DOM.getFileInfo": DOM.getFileInfoReturnValue;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesReturnValue;
"DOM.setInspectedNode": DOM.setInspectedNodeReturnValue;
"DOM.setNodeName": DOM.setNodeNameReturnValue;
"DOM.setNodeValue": DOM.setNodeValueReturnValue;
@ -21216,6 +21412,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppReturnValue;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppReturnValue;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsReturnValue;
"BluetoothEmulation.enable": BluetoothEmulation.enableReturnValue;
"BluetoothEmulation.disable": BluetoothEmulation.disableReturnValue;
"BluetoothEmulation.simulatePreconnectedPeripheral": BluetoothEmulation.simulatePreconnectedPeripheralReturnValue;
"BluetoothEmulation.simulateAdvertisement": BluetoothEmulation.simulateAdvertisementReturnValue;
"Console.clearMessages": Console.clearMessagesReturnValue;
"Console.disable": Console.disableReturnValue;
"Console.enable": Console.enableReturnValue;

View file

@ -34,7 +34,7 @@
"@sveltejs/vite-plugin-svelte": "^3.0.1"
},
"devDependencies": {
"svelte": "^4.2.8"
"svelte": "^4.2.19"
},
"bin": {
"playwright": "cli.js"

View file

@ -44,7 +44,7 @@ export interface TestServerInterface {
installBrowsers(params: {}): Promise<void>;
runGlobalSetup(params: {}): Promise<{
runGlobalSetup(params: { outputDir?: string }): Promise<{
report: ReportEntry[],
status: reporterTypes.FullResult['status']
}>;
@ -81,6 +81,7 @@ export interface TestServerInterface {
locations?: string[];
grep?: string;
grepInvert?: string;
outputDir?: string;
}): Promise<{
report: ReportEntry[],
status: reporterTypes.FullResult['status']

View file

@ -148,7 +148,10 @@ class TestServerDispatcher implements TestServerInterface {
async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> {
await this.runGlobalTeardown();
const { config, error } = await this._loadConfig();
const overrides: ConfigCLIOverrides = {
outputDir: params.outputDir,
};
const { config, error } = await this._loadConfig(overrides);
if (!config) {
const { reporter, report } = await this._collectingInternalReporter();
// Produce dummy config when it has an error.
@ -256,6 +259,7 @@ class TestServerDispatcher implements TestServerInterface {
const overrides: ConfigCLIOverrides = {
repeatEach: 1,
retries: 0,
outputDir: params.outputDir,
};
const { config, error } = await this._loadConfig(overrides);
if (!config) {

View file

@ -30,7 +30,7 @@ export async function detectChangedTestFiles(baseCommit: string, configDir: stri
const unknownRevision = error.output.some(line => line?.includes('unknown revision'));
if (unknownRevision) {
const isShallowClone = childProcess.execSync('git rev-parse --is-shallow-repository', { encoding: 'utf-8', stdio: 'pipe' }).trim() === 'true';
const isShallowClone = childProcess.execSync('git rev-parse --is-shallow-repository', { encoding: 'utf-8', stdio: 'pipe', cwd: configDir }).trim() === 'true';
if (isShallowClone) {
throw new Error([
`The repository is a shallow clone and does not have '${baseCommit}' available locally.`,

View file

@ -1825,8 +1825,10 @@ type TestDetailsAnnotation = {
description?: string;
};
type TestDetailsTag = `@${string}`;
export type TestDetails = {
tag?: string | string[];
tag?: TestDetailsTag | TestDetailsTag[];
annotation?: TestDetailsAnnotation | TestDetailsAnnotation[];
}
@ -6565,15 +6567,17 @@ type MakeMatchers<R, T, ExtendedMatchers> = {
rejects: MakeMatchers<Promise<R>, any, ExtendedMatchers>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>>;
type PollMatchers<R, T, ExtendedMatchers> = {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: PollMatchers<R, T, ExtendedMatchers>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>;
export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, ExtendedMatchers>;
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T, ExtendedMatchers>;
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => BaseMatchers<Promise<void>, T> & {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: BaseMatchers<Promise<void>, T>;
};
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => PollMatchers<Promise<void>,T, ExtendedMatchers>
extend<MoreMatchers extends Record<string, (this: ExpectMatcherState, receiver: any, ...args: any[]) => MatcherReturnType | Promise<MatcherReturnType>>>(matchers: MoreMatchers): Expect<ExtendedMatchers & MoreMatchers>;
configure: (configuration: {
message?: string,

View file

@ -51,7 +51,6 @@ export type UIState = {
language: Language;
testIdAttributeName: string;
overlay: OverlayState;
generateSimpleDom: boolean;
};
export type CallLogStatus = 'in-progress' | 'done' | 'error' | 'paused';

View file

@ -59,7 +59,7 @@ const RequestTab: React.FunctionComponent<{
React.useEffect(() => {
const readResources = async () => {
if (resource.request.postData) {
const requestContentTypeHeader = resource.request.headers.find(q => q.name === 'Content-Type');
const requestContentTypeHeader = resource.request.headers.find(q => q.name.toLowerCase() === 'content-type');
const requestContentType = requestContentTypeHeader ? requestContentTypeHeader.value : '';
if (resource.request.postData._sha1) {
const response = await fetch(`sha1/${resource.request.postData._sha1}`);

View file

@ -254,7 +254,6 @@ export const InspectModeController: React.FunctionComponent<{
language: sdkLanguage,
testIdAttributeName,
overlay: { offsetX: 0 },
generateSimpleDom: false,
}, {
async setSelector(selector: string) {
setHighlightedLocator(asLocator(sdkLanguage, frameSelector + selector));

View file

@ -205,12 +205,14 @@ export const UIModeView: React.FC<{}> = ({
interceptStdio: true,
watchTestDirs: true
});
const { status, report } = await testServerConnection.runGlobalSetup({});
const { status, report } = await testServerConnection.runGlobalSetup({
outputDir: queryParams.outputDir,
});
teleSuiteUpdater.processGlobalReport(report);
if (status !== 'passed')
return;
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert });
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert, outputDir: queryParams.outputDir });
teleSuiteUpdater.processListReport(result.report);
testServerConnection.onReport(params => {
@ -333,7 +335,7 @@ export const UIModeView: React.FC<{}> = ({
commandQueue.current = commandQueue.current.then(async () => {
setIsLoading(true);
try {
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert });
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert, outputDir: queryParams.outputDir });
teleSuiteUpdater.processListReport(result.report);
} catch (e) {
// eslint-disable-next-line no-console

View file

@ -13,6 +13,25 @@
</style>
<script src='script.js'></script>
<script>fetch('/api/endpoint')</script>
<script>
const body = JSON.stringify({
data: {
key: 'value',
array: ['value-1', 'value-2'],
},
});
fetch('/post-data-1', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body,
});
fetch('/post-data-2', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body,
});
</script>
</head>
<body>
<h1>Network Tab Test</h1>

View file

@ -44,7 +44,10 @@ test('should poll predicate', async ({ runInlineTest }) => {
test('should compile', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
import { test, expect as baseExpect } from '@playwright/test';
const expect = baseExpect.extend({
toBeWithinRange() { return { message: () => "is within range", pass: true }; },
})
test('should poll sync predicate', async ({ page }) => {
let i = 0;
test.expect.poll(() => ++i).toBe(3);
@ -57,6 +60,7 @@ test('should compile', async ({ runTSC }) => {
return ++i;
}).toBe(3);
test.expect.poll(() => Promise.resolve(++i)).toBe(3);
expect.poll(() => Promise.resolve(++i)).toBeWithinRange();
// @ts-expect-error
await test.expect.poll(() => page.locator('foo')).toBeEnabled();

View file

@ -802,6 +802,32 @@ for (const useIntermediateMergeReport of [false] as const) {
await expect(page.locator('.attachment-body')).toHaveText(['foo', '{"foo":1}', 'utf16 encoded']);
});
test('should have link for opening HTML attachments in new tab', async ({ runInlineTest, page, showReport }) => {
const result = await runInlineTest({
'a.test.js': `
import { test, expect } from '@playwright/test';
test('passing', async ({ page }, testInfo) => {
testInfo.attach('axe-report.html', {
contentType: 'text/html',
body: '<h1>Axe Report</h1>',
});
});
`,
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
expect(result.exitCode).toBe(0);
await showReport();
await page.getByText('passing', { exact: true }).click();
const [newTab] = await Promise.all([
page.waitForEvent('popup'),
page.getByText('axe-report.html', { exact: true }).click(),
]);
await expect(newTab).toHaveURL(/^blob:/);
await expect(newTab.getByText('Axe Report')).toBeVisible();
});
test('should use file-browser friendly extensions for buffer attachments based on contentType', async ({ runInlineTest, showReport, page }, testInfo) => {
const result = await runInlineTest({
'a.test.js': `

View file

@ -147,6 +147,18 @@ test('should enforce @ symbol', async ({ runInlineTest }) => {
expect(result.output).toContain(`Error: Tag must start with "@" symbol, got "foo" instead.`);
});
test('types should enforce @ symbol', async ({ runTSC }) => {
const result = await runTSC({
'stdio.spec.ts': `
import { test, expect } from '@playwright/test';
test('test1', { tag: 'foo' }, () => {
});
`
});
expect(result.exitCode).toBe(2);
expect(result.output).toContain('error TS2322: Type \'"foo"\' is not assignable to type \'`@${string}` | `@${string}`[] | undefined');
});
test('should be included in testInfo', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
'a.test.ts': `

View file

@ -93,3 +93,45 @@ test('should filter network requests by url', async ({ runUITest, server }) => {
await expect(networkItems).toHaveCount(1);
await expect(networkItems.getByText('font.woff2')).toBeVisible();
});
test('should format JSON request body', async ({ runUITest, server }) => {
const { page } = await runUITest({
'network-tab.test.ts': `
import { test, expect } from '@playwright/test';
test('network tab test', async ({ page }) => {
await page.goto('${server.PREFIX}/network-tab/network.html');
});
`,
});
await page.getByText('network tab test').dblclick();
await page.getByText('Network', { exact: true }).click();
await page.getByText('post-data-1').click();
await expect(page.locator('.CodeMirror-code .CodeMirror-line').allInnerTexts()).resolves.toEqual([
'{',
' "data": {',
' "key": "value",',
' "array": [',
' "value-1",',
' "value-2"',
' ]',
' }',
'}',
]);
await page.getByText('post-data-2').click();
await expect(page.locator('.CodeMirror-code .CodeMirror-line').allInnerTexts()).resolves.toEqual([
'{',
' "data": {',
' "key": "value",',
' "array": [',
' "value-1",',
' "value-2"',
' ]',
' }',
'}',
]);
});

View file

@ -19,7 +19,7 @@ import path from 'path';
test.describe.configure({ mode: 'parallel', retries });
test('should run global setup and teardown', async ({ runUITest }) => {
test('should run global setup and teardown', async ({ runUITest }, testInfo) => {
const { page, testProcess } = await runUITest({
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';
@ -29,26 +29,36 @@ test('should run global setup and teardown', async ({ runUITest }) => {
});
`,
'globalSetup.ts': `
export default () => console.log('\\n%%from-global-setup');
import { basename } from "node:path";
export default (config) => {
console.log('\\n%%from-global-setup');
console.log("setupOutputDir: " + basename(config.projects[0].outputDir));
};
`,
'globalTeardown.ts': `
export default () => console.log('\\n%%from-global-teardown');
export default (config) => {
console.log('\\n%%from-global-teardown');
console.log('%%' + JSON.stringify(config));
};
`,
'a.test.js': `
import { test, expect } from '@playwright/test';
test('should work', async ({}) => {});
`
});
}, undefined, { additionalArgs: ['--output=foo'] });
await page.getByTitle('Run all').click();
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
await page.getByTitle('Toggle output').click();
await expect(page.getByTestId('output')).toContainText('from-global-setup');
const output = page.getByTestId('output');
await expect(output).toContainText('from-global-setup');
await expect(output).toContainText('setupOutputDir: foo');
await page.close();
await expect.poll(() => testProcess.outputLines()).toEqual([
'from-global-teardown',
]);
await expect.poll(() => testProcess.outputLines()).toContain('from-global-teardown');
const teardownConfig = JSON.parse(testProcess.outputLines()[1]);
expect(teardownConfig.projects[0].outputDir).toEqual(testInfo.outputPath('foo'));
});
test('should teardown on sigint', async ({ runUITest, nodeVersion }) => {

View file

@ -70,8 +70,10 @@ type TestDetailsAnnotation = {
description?: string;
};
type TestDetailsTag = `@${string}`;
export type TestDetails = {
tag?: string | string[];
tag?: TestDetailsTag | TestDetailsTag[];
annotation?: TestDetailsAnnotation | TestDetailsAnnotation[];
}
@ -403,15 +405,17 @@ type MakeMatchers<R, T, ExtendedMatchers> = {
rejects: MakeMatchers<Promise<R>, any, ExtendedMatchers>;
} & IfAny<T, AllMatchers<R, T>, SpecificMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>>;
type PollMatchers<R, T, ExtendedMatchers> = {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: PollMatchers<R, T, ExtendedMatchers>;
} & BaseMatchers<R, T> & ToUserMatcherObject<ExtendedMatchers, T>;
export type Expect<ExtendedMatchers = {}> = {
<T = unknown>(actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers<void, T, ExtendedMatchers>;
soft: <T = unknown>(actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers<void, T, ExtendedMatchers>;
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => BaseMatchers<Promise<void>, T> & {
/**
* If you know how to test something, `.not` lets you test its opposite.
*/
not: BaseMatchers<Promise<void>, T>;
};
poll: <T = unknown>(actual: () => T | Promise<T>, messageOrOptions?: string | { message?: string, timeout?: number, intervals?: number[] }) => PollMatchers<Promise<void>, T, ExtendedMatchers>;
extend<MoreMatchers extends Record<string, (this: ExpectMatcherState, receiver: any, ...args: any[]) => MatcherReturnType | Promise<MatcherReturnType>>>(matchers: MoreMatchers): Expect<ExtendedMatchers & MoreMatchers>;
configure: (configuration: {
message?: string,