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 # 🎭 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) ## [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 | | | 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: | | 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: | | 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 ```sh
# Playwright will choose tsconfig automatically # 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. 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 ```sh
# Pass a specific tsconfig # Pass a specific tsconfig
npx playwrigh test --tsconfig=tsconfig.test.json npx playwright test --tsconfig=tsconfig.test.json
``` ```
## Manually compile tests with TypeScript ## Manually compile tests with TypeScript

8
package-lock.json generated
View file

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

View file

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

View file

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

View file

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

View file

@ -1131,17 +1131,21 @@ using Audits.issueAdded event.
} }
/** /**
* Defines commands and events for browser extensions. Available if the client * Defines commands and events for browser extensions.
is connected using the --remote-debugging-pipe flag and
the --enable-unsafe-extension-debugging flag is set.
*/ */
export module Extensions { export module Extensions {
/**
* Storage areas.
*/
export type StorageArea = "session"|"local"|"sync"|"managed";
/** /**
* Installs an unpacked extension from the filesystem similar to * Installs an unpacked extension from the filesystem similar to
--load-extension CLI flags. Returns extension ID once the extension --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 = { export type loadUnpackedParameters = {
/** /**
@ -1155,6 +1159,81 @@ has been installed.
*/ */
id: string; 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; style: CSSStyle;
} }
/**
* CSS position-fallback rule representation.
*/
export interface CSSPositionFallbackRule {
name: Value;
/**
* List of keyframes.
*/
tryRules: CSSTryRule[];
}
/** /**
* CSS @position-try rule representation. * 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. * A list of CSS keyframed animations matching this node.
*/ */
cssKeyframesRules?: CSSKeyframesRule[]; 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. * 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. * 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. * Shadow root type.
*/ */
@ -3646,6 +3711,13 @@ The property is always undefined now.
compatibilityMode?: CompatibilityMode; compatibilityMode?: CompatibilityMode;
assignedSlot?: BackendNode; 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. * A structure holding an RGBA color.
*/ */
@ -4693,6 +4765,17 @@ File wrapper.
export type getFileInfoReturnValue = { export type getFileInfoReturnValue = {
path: string; 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 * Enables console to refer to the node with given id via $x (see Command Line API for more details
$x functions). $x functions).
@ -11369,7 +11452,7 @@ as an ad.
* All Permissions Policy features. This enum should match the one defined * All Permissions Policy features. This enum should match the one defined
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5. 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. * 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; 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 type ClientNavigationDisposition = "currentTab"|"newTab"|"newWindow"|"download";
export interface InstallabilityErrorArgument { export interface InstallabilityErrorArgument {
/** /**
@ -12298,6 +12381,10 @@ when bfcache navigation fails.
* Frame's new url. * Frame's new url.
*/ */
url: string; url: string;
/**
* Navigation type
*/
navigationType: "fragment"|"historyApi"|"other";
} }
/** /**
* Compressed image data requested by the `startScreencast`. * 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. * 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 * Preloading status values, see also PreloadingTriggeringOutcome. This
status is shared by prefetchStatusUpdated and prerenderStatusUpdated. 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. * This domain is deprecated - use Runtime or Log instead.
*/ */
@ -20122,6 +20304,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastParameters; "Audits.checkContrast": Audits.checkContrastParameters;
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters; "Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
"Extensions.loadUnpacked": Extensions.loadUnpackedParameters; "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.trigger": Autofill.triggerParameters;
"Autofill.setAddresses": Autofill.setAddressesParameters; "Autofill.setAddresses": Autofill.setAddressesParameters;
"Autofill.disable": Autofill.disableParameters; "Autofill.disable": Autofill.disableParameters;
@ -20232,6 +20418,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledParameters; "DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledParameters;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesParameters; "DOM.getNodeStackTraces": DOM.getNodeStackTracesParameters;
"DOM.getFileInfo": DOM.getFileInfoParameters; "DOM.getFileInfo": DOM.getFileInfoParameters;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesParameters;
"DOM.setInspectedNode": DOM.setInspectedNodeParameters; "DOM.setInspectedNode": DOM.setInspectedNodeParameters;
"DOM.setNodeName": DOM.setNodeNameParameters; "DOM.setNodeName": DOM.setNodeNameParameters;
"DOM.setNodeValue": DOM.setNodeValueParameters; "DOM.setNodeValue": DOM.setNodeValueParameters;
@ -20616,6 +20803,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppParameters; "PWA.launchFilesInApp": PWA.launchFilesInAppParameters;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppParameters; "PWA.openCurrentPageInApp": PWA.openCurrentPageInAppParameters;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsParameters; "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.clearMessages": Console.clearMessagesParameters;
"Console.disable": Console.disableParameters; "Console.disable": Console.disableParameters;
"Console.enable": Console.enableParameters; "Console.enable": Console.enableParameters;
@ -20722,6 +20913,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastReturnValue; "Audits.checkContrast": Audits.checkContrastReturnValue;
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue; "Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
"Extensions.loadUnpacked": Extensions.loadUnpackedReturnValue; "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.trigger": Autofill.triggerReturnValue;
"Autofill.setAddresses": Autofill.setAddressesReturnValue; "Autofill.setAddresses": Autofill.setAddressesReturnValue;
"Autofill.disable": Autofill.disableReturnValue; "Autofill.disable": Autofill.disableReturnValue;
@ -20832,6 +21027,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledReturnValue; "DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledReturnValue;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesReturnValue; "DOM.getNodeStackTraces": DOM.getNodeStackTracesReturnValue;
"DOM.getFileInfo": DOM.getFileInfoReturnValue; "DOM.getFileInfo": DOM.getFileInfoReturnValue;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesReturnValue;
"DOM.setInspectedNode": DOM.setInspectedNodeReturnValue; "DOM.setInspectedNode": DOM.setInspectedNodeReturnValue;
"DOM.setNodeName": DOM.setNodeNameReturnValue; "DOM.setNodeName": DOM.setNodeNameReturnValue;
"DOM.setNodeValue": DOM.setNodeValueReturnValue; "DOM.setNodeValue": DOM.setNodeValueReturnValue;
@ -21216,6 +21412,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppReturnValue; "PWA.launchFilesInApp": PWA.launchFilesInAppReturnValue;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppReturnValue; "PWA.openCurrentPageInApp": PWA.openCurrentPageInAppReturnValue;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsReturnValue; "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.clearMessages": Console.clearMessagesReturnValue;
"Console.disable": Console.disableReturnValue; "Console.disable": Console.disableReturnValue;
"Console.enable": Console.enableReturnValue; "Console.enable": Console.enableReturnValue;

View file

@ -110,7 +110,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"Galaxy S5": { "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": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -121,7 +121,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S5 landscape": { "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": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -132,7 +132,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S8": { "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": { "viewport": {
"width": 360, "width": 360,
"height": 740 "height": 740
@ -143,7 +143,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S8 landscape": { "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": { "viewport": {
"width": 740, "width": 740,
"height": 360 "height": 360
@ -154,7 +154,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S9+": { "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": { "viewport": {
"width": 320, "width": 320,
"height": 658 "height": 658
@ -165,7 +165,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S9+ landscape": { "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": { "viewport": {
"width": 658, "width": 658,
"height": 320 "height": 320
@ -176,7 +176,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy Tab S4": { "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": { "viewport": {
"width": 712, "width": 712,
"height": 1138 "height": 1138
@ -187,7 +187,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy Tab S4 landscape": { "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": { "viewport": {
"width": 1138, "width": 1138,
"height": 712 "height": 712
@ -1098,7 +1098,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"LG Optimus L70": { "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": { "viewport": {
"width": 384, "width": 384,
"height": 640 "height": 640
@ -1109,7 +1109,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"LG Optimus L70 landscape": { "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": { "viewport": {
"width": 640, "width": 640,
"height": 384 "height": 384
@ -1120,7 +1120,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 550": { "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": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1131,7 +1131,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 550 landscape": { "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": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1142,7 +1142,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 950": { "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": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1153,7 +1153,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 950 landscape": { "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": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1164,7 +1164,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 10": { "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": { "viewport": {
"width": 800, "width": 800,
"height": 1280 "height": 1280
@ -1175,7 +1175,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 10 landscape": { "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": { "viewport": {
"width": 1280, "width": 1280,
"height": 800 "height": 800
@ -1186,7 +1186,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 4": { "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": { "viewport": {
"width": 384, "width": 384,
"height": 640 "height": 640
@ -1197,7 +1197,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 4 landscape": { "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": { "viewport": {
"width": 640, "width": 640,
"height": 384 "height": 384
@ -1208,7 +1208,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5": { "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": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1219,7 +1219,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5 landscape": { "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": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1230,7 +1230,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5X": { "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": { "viewport": {
"width": 412, "width": 412,
"height": 732 "height": 732
@ -1241,7 +1241,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5X landscape": { "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": { "viewport": {
"width": 732, "width": 732,
"height": 412 "height": 412
@ -1252,7 +1252,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6": { "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": { "viewport": {
"width": 412, "width": 412,
"height": 732 "height": 732
@ -1263,7 +1263,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6 landscape": { "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": { "viewport": {
"width": 732, "width": 732,
"height": 412 "height": 412
@ -1274,7 +1274,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6P": { "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": { "viewport": {
"width": 412, "width": 412,
"height": 732 "height": 732
@ -1285,7 +1285,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6P landscape": { "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": { "viewport": {
"width": 732, "width": 732,
"height": 412 "height": 412
@ -1296,7 +1296,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 7": { "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": { "viewport": {
"width": 600, "width": 600,
"height": 960 "height": 960
@ -1307,7 +1307,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 7 landscape": { "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": { "viewport": {
"width": 960, "width": 960,
"height": 600 "height": 600
@ -1362,7 +1362,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"Pixel 2": { "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": { "viewport": {
"width": 411, "width": 411,
"height": 731 "height": 731
@ -1373,7 +1373,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 2 landscape": { "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": { "viewport": {
"width": 731, "width": 731,
"height": 411 "height": 411
@ -1384,7 +1384,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 2 XL": { "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": { "viewport": {
"width": 411, "width": 411,
"height": 823 "height": 823
@ -1395,7 +1395,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 2 XL landscape": { "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": { "viewport": {
"width": 823, "width": 823,
"height": 411 "height": 411
@ -1406,7 +1406,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 3": { "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": { "viewport": {
"width": 393, "width": 393,
"height": 786 "height": 786
@ -1417,7 +1417,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 3 landscape": { "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": { "viewport": {
"width": 786, "width": 786,
"height": 393 "height": 393
@ -1428,7 +1428,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4": { "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": { "viewport": {
"width": 353, "width": 353,
"height": 745 "height": 745
@ -1439,7 +1439,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4 landscape": { "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": { "viewport": {
"width": 745, "width": 745,
"height": 353 "height": 353
@ -1450,7 +1450,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4a (5G)": { "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": { "screen": {
"width": 412, "width": 412,
"height": 892 "height": 892
@ -1465,7 +1465,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4a (5G) landscape": { "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": { "screen": {
"height": 892, "height": 892,
"width": 412 "width": 412
@ -1480,7 +1480,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 5": { "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": { "screen": {
"width": 393, "width": 393,
"height": 851 "height": 851
@ -1495,7 +1495,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 5 landscape": { "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": { "screen": {
"width": 851, "width": 851,
"height": 393 "height": 393
@ -1510,7 +1510,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 7": { "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": { "screen": {
"width": 412, "width": 412,
"height": 915 "height": 915
@ -1525,7 +1525,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 7 landscape": { "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": { "screen": {
"width": 915, "width": 915,
"height": 412 "height": 412
@ -1540,7 +1540,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Moto G4": { "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": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1551,7 +1551,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Moto G4 landscape": { "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": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1562,7 +1562,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Desktop Chrome HiDPI": { "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": { "screen": {
"width": 1792, "width": 1792,
"height": 1120 "height": 1120
@ -1577,7 +1577,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Desktop Edge HiDPI": { "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": { "screen": {
"width": 1792, "width": 1792,
"height": 1120 "height": 1120
@ -1622,7 +1622,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"Desktop Chrome": { "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": { "screen": {
"width": 1920, "width": 1920,
"height": 1080 "height": 1080
@ -1637,7 +1637,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Desktop Edge": { "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": { "screen": {
"width": 1920, "width": 1920,
"height": 1080 "height": 1080

View file

@ -34,7 +34,8 @@ import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } fr
import { asLocator } from '../../utils/isomorphic/locatorGenerators'; import { asLocator } from '../../utils/isomorphic/locatorGenerators';
import type { Language } from '../../utils/isomorphic/locatorGenerators'; import type { Language } from '../../utils/isomorphic/locatorGenerators';
import { cacheNormalizedWhitespaces, escapeHTML, escapeHTMLAttribute, normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils'; 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 }; export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
@ -79,15 +80,12 @@ export class InjectedScript {
endAriaCaches, endAriaCaches,
escapeHTML, escapeHTML,
escapeHTMLAttribute, escapeHTMLAttribute,
generateSimpleDom: generateSimpleDom.bind(undefined, this),
generateSimpleDomNode: generateSimpleDomNode.bind(undefined, this),
getAriaRole, getAriaRole,
getElementAccessibleDescription, getElementAccessibleDescription,
getElementAccessibleName, getElementAccessibleName,
isElementVisible, isElementVisible,
isInsideScope, isInsideScope,
normalizeWhiteSpace, normalizeWhiteSpace,
selectorForSimpleDomNodeId: selectorForSimpleDomNodeId.bind(undefined, this),
}; };
// eslint-disable-next-line no-restricted-globals // eslint-disable-next-line no-restricted-globals
@ -1314,6 +1312,17 @@ export class InjectedScript {
} }
throw this.createStacklessError('Unknown expect matcher: ' + expression); 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']); 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'; import type { SimpleDomNode } from '../simpleDom';
interface RecorderDelegate { interface RecorderDelegate {
performAction?(action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode): Promise<void>; performAction?(action: actions.PerformOnRecordAction): Promise<void>;
recordAction?(action: actions.Action, simpleDomNode?: SimpleDomNode): Promise<void>; recordAction?(action: actions.Action): Promise<void>;
setSelector?(selector: string): Promise<void>; setSelector?(selector: string): Promise<void>;
setMode?(mode: Mode): Promise<void>; setMode?(mode: Mode): Promise<void>;
setOverlayState?(state: OverlayState): Promise<void>; setOverlayState?(state: OverlayState): Promise<void>;
@ -931,7 +931,6 @@ export class Recorder {
testIdAttributeName: 'data-testid', testIdAttributeName: 'data-testid',
language: 'javascript', language: 'javascript',
overlay: { offsetX: 0 }, overlay: { offsetX: 0 },
generateSimpleDom: false,
}; };
readonly document: Document; readonly document: Document;
private _delegate: RecorderDelegate = {}; private _delegate: RecorderDelegate = {};
@ -1186,13 +1185,11 @@ export class Recorder {
} }
async performAction(action: actions.PerformOnRecordAction) { async performAction(action: actions.PerformOnRecordAction) {
const simpleDomNode = this._generateSimpleDomNode(action); await this._delegate.performAction?.(action).catch(() => {});
await this._delegate.performAction?.(action, simpleDomNode).catch(() => {});
} }
recordAction(action: actions.Action) { recordAction(action: actions.Action) {
const simpleDomNode = this._generateSimpleDomNode(action); void this._delegate.recordAction?.(action);
void this._delegate.recordAction?.(action, simpleDomNode);
} }
setOverlayState(state: { offsetX: number; }) { setOverlayState(state: { offsetX: number; }) {
@ -1202,18 +1199,6 @@ export class Recorder {
setSelector(selector: string) { setSelector(selector: string) {
void this._delegate.setSelector?.(selector); 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 { class Dialog {

View file

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

View file

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

View file

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

View file

@ -16,6 +16,10 @@
import type { CallMetadata } from '../instrumentation'; import type { CallMetadata } from '../instrumentation';
import type { CallLog, CallLogStatus } from '@recorder/recorderTypes'; 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 { export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog {
let title = metadata.apiName || metadata.method; let title = metadata.apiName || metadata.method;
@ -48,3 +52,23 @@ export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus)
export function buildFullSelector(framePath: string[], selector: string) { export function buildFullSelector(framePath: string[], selector: string) {
return [...framePath, selector].join(' >> internal:control=enter-frame >> '); 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 * Defines commands and events for browser extensions.
is connected using the --remote-debugging-pipe flag and
the --enable-unsafe-extension-debugging flag is set.
*/ */
export module Extensions { export module Extensions {
/**
* Storage areas.
*/
export type StorageArea = "session"|"local"|"sync"|"managed";
/** /**
* Installs an unpacked extension from the filesystem similar to * Installs an unpacked extension from the filesystem similar to
--load-extension CLI flags. Returns extension ID once the extension --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 = { export type loadUnpackedParameters = {
/** /**
@ -1155,6 +1159,81 @@ has been installed.
*/ */
id: string; 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; style: CSSStyle;
} }
/**
* CSS position-fallback rule representation.
*/
export interface CSSPositionFallbackRule {
name: Value;
/**
* List of keyframes.
*/
tryRules: CSSTryRule[];
}
/** /**
* CSS @position-try rule representation. * 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. * A list of CSS keyframed animations matching this node.
*/ */
cssKeyframesRules?: CSSKeyframesRule[]; 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. * 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. * 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. * Shadow root type.
*/ */
@ -3646,6 +3711,13 @@ The property is always undefined now.
compatibilityMode?: CompatibilityMode; compatibilityMode?: CompatibilityMode;
assignedSlot?: BackendNode; 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. * A structure holding an RGBA color.
*/ */
@ -4693,6 +4765,17 @@ File wrapper.
export type getFileInfoReturnValue = { export type getFileInfoReturnValue = {
path: string; 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 * Enables console to refer to the node with given id via $x (see Command Line API for more details
$x functions). $x functions).
@ -11369,7 +11452,7 @@ as an ad.
* All Permissions Policy features. This enum should match the one defined * All Permissions Policy features. This enum should match the one defined
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5. 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. * 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; 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 type ClientNavigationDisposition = "currentTab"|"newTab"|"newWindow"|"download";
export interface InstallabilityErrorArgument { export interface InstallabilityErrorArgument {
/** /**
@ -12298,6 +12381,10 @@ when bfcache navigation fails.
* Frame's new url. * Frame's new url.
*/ */
url: string; url: string;
/**
* Navigation type
*/
navigationType: "fragment"|"historyApi"|"other";
} }
/** /**
* Compressed image data requested by the `startScreencast`. * 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. * 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 * Preloading status values, see also PreloadingTriggeringOutcome. This
status is shared by prefetchStatusUpdated and prerenderStatusUpdated. 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. * This domain is deprecated - use Runtime or Log instead.
*/ */
@ -20122,6 +20304,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastParameters; "Audits.checkContrast": Audits.checkContrastParameters;
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters; "Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
"Extensions.loadUnpacked": Extensions.loadUnpackedParameters; "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.trigger": Autofill.triggerParameters;
"Autofill.setAddresses": Autofill.setAddressesParameters; "Autofill.setAddresses": Autofill.setAddressesParameters;
"Autofill.disable": Autofill.disableParameters; "Autofill.disable": Autofill.disableParameters;
@ -20232,6 +20418,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledParameters; "DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledParameters;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesParameters; "DOM.getNodeStackTraces": DOM.getNodeStackTracesParameters;
"DOM.getFileInfo": DOM.getFileInfoParameters; "DOM.getFileInfo": DOM.getFileInfoParameters;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesParameters;
"DOM.setInspectedNode": DOM.setInspectedNodeParameters; "DOM.setInspectedNode": DOM.setInspectedNodeParameters;
"DOM.setNodeName": DOM.setNodeNameParameters; "DOM.setNodeName": DOM.setNodeNameParameters;
"DOM.setNodeValue": DOM.setNodeValueParameters; "DOM.setNodeValue": DOM.setNodeValueParameters;
@ -20616,6 +20803,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppParameters; "PWA.launchFilesInApp": PWA.launchFilesInAppParameters;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppParameters; "PWA.openCurrentPageInApp": PWA.openCurrentPageInAppParameters;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsParameters; "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.clearMessages": Console.clearMessagesParameters;
"Console.disable": Console.disableParameters; "Console.disable": Console.disableParameters;
"Console.enable": Console.enableParameters; "Console.enable": Console.enableParameters;
@ -20722,6 +20913,10 @@ Error was thrown.
"Audits.checkContrast": Audits.checkContrastReturnValue; "Audits.checkContrast": Audits.checkContrastReturnValue;
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue; "Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
"Extensions.loadUnpacked": Extensions.loadUnpackedReturnValue; "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.trigger": Autofill.triggerReturnValue;
"Autofill.setAddresses": Autofill.setAddressesReturnValue; "Autofill.setAddresses": Autofill.setAddressesReturnValue;
"Autofill.disable": Autofill.disableReturnValue; "Autofill.disable": Autofill.disableReturnValue;
@ -20832,6 +21027,7 @@ Error was thrown.
"DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledReturnValue; "DOM.setNodeStackTracesEnabled": DOM.setNodeStackTracesEnabledReturnValue;
"DOM.getNodeStackTraces": DOM.getNodeStackTracesReturnValue; "DOM.getNodeStackTraces": DOM.getNodeStackTracesReturnValue;
"DOM.getFileInfo": DOM.getFileInfoReturnValue; "DOM.getFileInfo": DOM.getFileInfoReturnValue;
"DOM.getDetachedDomNodes": DOM.getDetachedDomNodesReturnValue;
"DOM.setInspectedNode": DOM.setInspectedNodeReturnValue; "DOM.setInspectedNode": DOM.setInspectedNodeReturnValue;
"DOM.setNodeName": DOM.setNodeNameReturnValue; "DOM.setNodeName": DOM.setNodeNameReturnValue;
"DOM.setNodeValue": DOM.setNodeValueReturnValue; "DOM.setNodeValue": DOM.setNodeValueReturnValue;
@ -21216,6 +21412,10 @@ Error was thrown.
"PWA.launchFilesInApp": PWA.launchFilesInAppReturnValue; "PWA.launchFilesInApp": PWA.launchFilesInAppReturnValue;
"PWA.openCurrentPageInApp": PWA.openCurrentPageInAppReturnValue; "PWA.openCurrentPageInApp": PWA.openCurrentPageInAppReturnValue;
"PWA.changeAppUserSettings": PWA.changeAppUserSettingsReturnValue; "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.clearMessages": Console.clearMessagesReturnValue;
"Console.disable": Console.disableReturnValue; "Console.disable": Console.disableReturnValue;
"Console.enable": Console.enableReturnValue; "Console.enable": Console.enableReturnValue;

View file

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

View file

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

View file

@ -148,7 +148,10 @@ class TestServerDispatcher implements TestServerInterface {
async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> { async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> {
await this.runGlobalTeardown(); await this.runGlobalTeardown();
const { config, error } = await this._loadConfig(); const overrides: ConfigCLIOverrides = {
outputDir: params.outputDir,
};
const { config, error } = await this._loadConfig(overrides);
if (!config) { if (!config) {
const { reporter, report } = await this._collectingInternalReporter(); const { reporter, report } = await this._collectingInternalReporter();
// Produce dummy config when it has an error. // Produce dummy config when it has an error.
@ -256,6 +259,7 @@ class TestServerDispatcher implements TestServerInterface {
const overrides: ConfigCLIOverrides = { const overrides: ConfigCLIOverrides = {
repeatEach: 1, repeatEach: 1,
retries: 0, retries: 0,
outputDir: params.outputDir,
}; };
const { config, error } = await this._loadConfig(overrides); const { config, error } = await this._loadConfig(overrides);
if (!config) { 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')); const unknownRevision = error.output.some(line => line?.includes('unknown revision'));
if (unknownRevision) { 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) { if (isShallowClone) {
throw new Error([ throw new Error([
`The repository is a shallow clone and does not have '${baseCommit}' available locally.`, `The repository is a shallow clone and does not have '${baseCommit}' available locally.`,

View file

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

View file

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

View file

@ -59,7 +59,7 @@ const RequestTab: React.FunctionComponent<{
React.useEffect(() => { React.useEffect(() => {
const readResources = async () => { const readResources = async () => {
if (resource.request.postData) { 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 : ''; const requestContentType = requestContentTypeHeader ? requestContentTypeHeader.value : '';
if (resource.request.postData._sha1) { if (resource.request.postData._sha1) {
const response = await fetch(`sha1/${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, language: sdkLanguage,
testIdAttributeName, testIdAttributeName,
overlay: { offsetX: 0 }, overlay: { offsetX: 0 },
generateSimpleDom: false,
}, { }, {
async setSelector(selector: string) { async setSelector(selector: string) {
setHighlightedLocator(asLocator(sdkLanguage, frameSelector + selector)); setHighlightedLocator(asLocator(sdkLanguage, frameSelector + selector));

View file

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

View file

@ -13,6 +13,25 @@
</style> </style>
<script src='script.js'></script> <script src='script.js'></script>
<script>fetch('/api/endpoint')</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> </head>
<body> <body>
<h1>Network Tab Test</h1> <h1>Network Tab Test</h1>

View file

@ -44,7 +44,10 @@ test('should poll predicate', async ({ runInlineTest }) => {
test('should compile', async ({ runTSC }) => { test('should compile', async ({ runTSC }) => {
const result = await runTSC({ const result = await runTSC({
'a.spec.ts': ` '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 }) => { test('should poll sync predicate', async ({ page }) => {
let i = 0; let i = 0;
test.expect.poll(() => ++i).toBe(3); test.expect.poll(() => ++i).toBe(3);
@ -57,6 +60,7 @@ test('should compile', async ({ runTSC }) => {
return ++i; return ++i;
}).toBe(3); }).toBe(3);
test.expect.poll(() => Promise.resolve(++i)).toBe(3); test.expect.poll(() => Promise.resolve(++i)).toBe(3);
expect.poll(() => Promise.resolve(++i)).toBeWithinRange();
// @ts-expect-error // @ts-expect-error
await test.expect.poll(() => page.locator('foo')).toBeEnabled(); 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']); 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) => { test('should use file-browser friendly extensions for buffer attachments based on contentType', async ({ runInlineTest, showReport, page }, testInfo) => {
const result = await runInlineTest({ const result = await runInlineTest({
'a.test.js': ` '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.`); 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) => { test('should be included in testInfo', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({ const result = await runInlineTest({
'a.test.ts': ` '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).toHaveCount(1);
await expect(networkItems.getByText('font.woff2')).toBeVisible(); 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.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({ const { page, testProcess } = await runUITest({
'playwright.config.ts': ` 'playwright.config.ts': `
import { defineConfig } from '@playwright/test'; import { defineConfig } from '@playwright/test';
@ -29,26 +29,36 @@ test('should run global setup and teardown', async ({ runUITest }) => {
}); });
`, `,
'globalSetup.ts': ` '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': ` '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': ` 'a.test.js': `
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
test('should work', async ({}) => {}); test('should work', async ({}) => {});
` `
}); }, undefined, { additionalArgs: ['--output=foo'] });
await page.getByTitle('Run all').click(); await page.getByTitle('Run all').click();
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
await page.getByTitle('Toggle output').click(); 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 page.close();
await expect.poll(() => testProcess.outputLines()).toEqual([ await expect.poll(() => testProcess.outputLines()).toContain('from-global-teardown');
'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 }) => { test('should teardown on sigint', async ({ runUITest, nodeVersion }) => {

View file

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