diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index b808a2fe5e..6cd5bc321d 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -9,9 +9,9 @@ }, { "name": "chromium-tip-of-tree", - "revision": "1269", + "revision": "1271", "installByDefault": false, - "browserVersion": "131.0.6778.0" + "browserVersion": "132.0.6791.0" }, { "name": "firefox", @@ -27,7 +27,7 @@ }, { "name": "webkit", - "revision": "2094", + "revision": "2095", "installByDefault": true, "revisionOverrides": { "mac10.14": "1446", diff --git a/packages/playwright-core/src/server/debugController.ts b/packages/playwright-core/src/server/debugController.ts index 53c6c3d99e..a9a79a49e2 100644 --- a/packages/playwright-core/src/server/debugController.ts +++ b/packages/playwright-core/src/server/debugController.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Mode, Source } from '@recorder/recorderTypes'; +import type { ElementInfo, Mode, Source } from '@recorder/recorderTypes'; import { gracefullyProcessExitDoNotHang } from '../utils/processLauncher'; import type { Browser } from './browser'; import type { BrowserContext } from './browserContext'; @@ -221,9 +221,9 @@ class InspectingRecorderApp extends EmptyRecorderApp { this._debugController = debugController; } - override async setSelector(selector: string): Promise { - const locator: string = asLocator(this._debugController._sdkLanguage, selector); - this._debugController.emit(DebugController.Events.InspectRequested, { selector, locator }); + override async elementPicked(elementInfo: ElementInfo): Promise { + const locator: string = asLocator(this._debugController._sdkLanguage, elementInfo.selector); + this._debugController.emit(DebugController.Events.InspectRequested, { selector: elementInfo.selector, locator }); } override async setSources(sources: Source[]): Promise { diff --git a/packages/playwright-core/src/server/injected/ariaSnapshot.ts b/packages/playwright-core/src/server/injected/ariaSnapshot.ts index 682f48365a..7156d0cce7 100644 --- a/packages/playwright-core/src/server/injected/ariaSnapshot.ts +++ b/packages/playwright-core/src/server/injected/ariaSnapshot.ts @@ -53,7 +53,7 @@ export function generateAriaTree(rootElement: Element): AriaNode { return; const element = node as Element; - if (roleUtils.isElementIgnoredForAria(element)) + if (roleUtils.isElementHiddenForAria(element)) return; const visible = isElementVisible(element); @@ -281,7 +281,7 @@ export function renderAriaTree(ariaNode: AriaNode, options?: { noText?: boolean const visit = (ariaNode: AriaNode | string, indent: string) => { if (typeof ariaNode === 'string') { if (!options?.noText) - lines.push(indent + '- text: ' + escapeYamlString(ariaNode)); + lines.push(indent + '- text: ' + quoteYamlString(ariaNode)); return; } let line = `${indent}- ${ariaNode.role}`; @@ -308,7 +308,7 @@ export function renderAriaTree(ariaNode: AriaNode, options?: { noText?: boolean const stringValue = !ariaNode.children.length || (ariaNode.children?.length === 1 && typeof ariaNode.children[0] === 'string'); if (stringValue) { if (!options?.noText && ariaNode.children.length) - line += ': ' + escapeYamlString(ariaNode.children?.[0] as string); + line += ': ' + quoteYamlString(ariaNode.children?.[0] as string); lines.push(line); return; } @@ -328,36 +328,10 @@ export function renderAriaTree(ariaNode: AriaNode, options?: { noText?: boolean return lines.join('\n'); } -function escapeYamlString(str: string) { - if (str === '') - return '""'; - - const needQuotes = ( - // Starts or ends with whitespace - /^\s|\s$/.test(str) || - // Contains control characters - /[\x00-\x1f]/.test(str) || - // Contains special YAML characters that could cause parsing issues - /[\[\]{}&*!,|>%@`]/.test(str) || - // Contains a colon followed by a space (could be interpreted as a key-value pair) - /:\s/.test(str) || - // Is a YAML boolean or null value - /^(?:y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|null|Null|NULL|~)$/.test(str) || - // Could be interpreted as a number - /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/.test(str) || - // Contains a newline character - /\n/.test(str) || - // Starts with a special character - /^[\-?:,>|%@"`]/.test(str) - ); - - if (needQuotes) { - return `"${str - .replace(/\\/g, '\\\\') - .replace(/"/g, '\\"') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r')}"`; - } - - return str; +function quoteYamlString(str: string) { + return `"${str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r')}"`; } diff --git a/packages/playwright-core/src/server/injected/consoleApi.ts b/packages/playwright-core/src/server/injected/consoleApi.ts index 0287240493..eae874834e 100644 --- a/packages/playwright-core/src/server/injected/consoleApi.ts +++ b/packages/playwright-core/src/server/injected/consoleApi.ts @@ -85,7 +85,11 @@ class ConsoleAPI { inspect: (selector: string) => this._inspect(selector), selector: (element: Element) => this._selector(element), generateLocator: (element: Element, language?: Language) => this._generateLocator(element, language), - ariaSnapshot: (element?: Element) => this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body), + ariaSnapshot: (element?: Element) => { + const snapshot = this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body); + // eslint-disable-next-line no-console + console.log(snapshot); + }, resume: () => this._resume(), ...new Locator(injectedScript, ''), }; diff --git a/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts b/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts index 9a96d8f25d..c48645ad09 100644 --- a/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes'; +import type { ElementInfo, Mode, OverlayState, UIState } from '@recorder/recorderTypes'; import type * as actions from '@recorder/actions'; import type { InjectedScript } from '../injectedScript'; import { Recorder } from './recorder'; @@ -24,7 +24,7 @@ interface Embedder { __pw_recorderPerformAction(action: actions.PerformOnRecordAction): Promise; __pw_recorderRecordAction(action: actions.Action): Promise; __pw_recorderState(): Promise; - __pw_recorderSetSelector(selector: string): Promise; + __pw_recorderElementPicked(element: { selector: string, ariaSnapshot?: string }): Promise; __pw_recorderSetMode(mode: Mode): Promise; __pw_recorderSetOverlayState(state: OverlayState): Promise; __pw_refreshOverlay(): void; @@ -75,8 +75,8 @@ export class PollingRecorder implements RecorderDelegate { await this._embedder.__pw_recorderRecordAction(action); } - async setSelector(selector: string): Promise { - await this._embedder.__pw_recorderSetSelector(selector); + async elementPicked(elementInfo: ElementInfo): Promise { + await this._embedder.__pw_recorderElementPicked(elementInfo); } async setMode(mode: Mode): Promise { diff --git a/packages/playwright-core/src/server/injected/recorder/recorder.ts b/packages/playwright-core/src/server/injected/recorder/recorder.ts index 7c38c6386e..9dfa2cd372 100644 --- a/packages/playwright-core/src/server/injected/recorder/recorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -17,7 +17,7 @@ import type * as actions from '@recorder/actions'; import type { InjectedScript } from '../injectedScript'; import type { Point } from '../../../common/types'; -import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes'; +import type { ElementInfo, Mode, OverlayState, UIState } from '@recorder/recorderTypes'; import type { ElementText } from '../selectorUtils'; import type { Highlight, HighlightOptions } from '../highlight'; import clipPaths from './clipPaths'; @@ -25,7 +25,7 @@ import clipPaths from './clipPaths'; export interface RecorderDelegate { performAction?(action: actions.PerformOnRecordAction): Promise; recordAction?(action: actions.Action): Promise; - setSelector?(selector: string): Promise; + elementPicked?(elementInfo: ElementInfo): Promise; setMode?(mode: Mode): Promise; setOverlayState?(state: OverlayState): Promise; highlightUpdated?(): void; @@ -85,7 +85,7 @@ class InspectTool implements RecorderTool { if (event.button !== 0) return; if (this._hoveredModel?.selector) - this._commit(this._hoveredModel.selector); + this._commit(this._hoveredModel.selector, this._hoveredModel); } onContextMenu(event: MouseEvent) { @@ -93,13 +93,14 @@ class InspectTool implements RecorderTool { && this._hoveredSelectors && this._hoveredSelectors.length > 1) { consumeEvent(event); const selectors = this._hoveredSelectors; + const hoveredModel = this._hoveredModel; this._hoveredModel.tooltipFooter = undefined; this._hoveredModel.tooltipList = selectors.map(selector => this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, selector)); this._hoveredModel.tooltipListItemSelected = (index: number | undefined) => { if (index === undefined) this._reset(true); else - this._commit(selectors[index]); + this._commit(selectors[index], hoveredModel); }; this._recorder.updateHighlight(this._hoveredModel, true); } @@ -181,7 +182,7 @@ class InspectTool implements RecorderTool { this._reset(false); } - private _commit(selector: string) { + private _commit(selector: string, model: HighlightModel) { if (this._assertVisibility) { this._recorder.recordAction({ name: 'assertVisible', @@ -191,7 +192,7 @@ class InspectTool implements RecorderTool { this._recorder.setMode('recording'); this._recorder.overlay?.flashToolSucceeded('assertingVisibility'); } else { - this._recorder.setSelector(selector); + this._recorder.elementPicked(selector, model); } } @@ -1314,8 +1315,9 @@ export class Recorder { void this._delegate.setOverlayState?.(state); } - setSelector(selector: string) { - void this._delegate.setSelector?.(selector); + elementPicked(selector: string, model: HighlightModel) { + const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0]); + void this._delegate.elementPicked?.({ selector, ariaSnapshot }); } } diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index c5e133c069..74ebefee86 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -15,7 +15,7 @@ */ import type * as channels from '@protocol/channels'; -import type { CallLog, CallLogStatus, EventData, Mode, OverlayState, Source, UIState } from '@recorder/recorderTypes'; +import type { CallLog, CallLogStatus, ElementInfo, EventData, Mode, OverlayState, Source, UIState } from '@recorder/recorderTypes'; import * as fs from 'fs'; import type { Point } from '../common/types'; import * as consoleApiSource from '../generated/consoleApiSource'; @@ -168,9 +168,9 @@ export class Recorder implements InstrumentationListener, IRecorder { return uiState; }); - await this._context.exposeBinding('__pw_recorderSetSelector', false, async ({ frame }, selector: string) => { + await this._context.exposeBinding('__pw_recorderElementPicked', false, async ({ frame }, elementInfo: ElementInfo) => { const selectorChain = await generateFrameSelector(frame); - await this._recorderApp?.setSelector(buildFullSelector(selectorChain, selector), true); + await this._recorderApp?.elementPicked({ selector: buildFullSelector(selectorChain, elementInfo.selector), ariaSnapshot: elementInfo.ariaSnapshot }, true); }); await this._context.exposeBinding('__pw_recorderSetMode', false, async ({ frame }, mode: Mode) => { @@ -256,12 +256,10 @@ export class Recorder implements InstrumentationListener, IRecorder { this._currentCallsMetadata.set(metadata, sdkObject); this._updateUserSources(); this.updateCallLog([metadata]); - if (isScreenshotCommand(metadata)) { + if (isScreenshotCommand(metadata)) this.hideHighlightedSelector(); - } else if (metadata.params && metadata.params.selector) { + else if (metadata.params && metadata.params.selector) this._highlightedSelector = metadata.params.selector; - this._recorderApp?.setSelector(this._highlightedSelector).catch(() => {}); - } } async onAfterCall(sdkObject: SdkObject, metadata: CallMetadata) { diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts index 3f9a636579..df27a96c6c 100644 --- a/packages/playwright-core/src/server/recorder/recorderApp.ts +++ b/packages/playwright-core/src/server/recorder/recorderApp.ts @@ -20,7 +20,7 @@ import type { Page } from '../page'; import { ProgressController } from '../progress'; import { EventEmitter } from 'events'; import { serverSideCallMetadata } from '../instrumentation'; -import type { CallLog, Mode, Source } from '@recorder/recorderTypes'; +import type { CallLog, ElementInfo, Mode, Source } from '@recorder/recorderTypes'; import { isUnderTest } from '../../utils'; import { mime } from '../../utilsBundle'; import { syncLocalStorageWithSettings } from '../launchApp'; @@ -35,7 +35,7 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp { async setPaused(paused: boolean): Promise {} async setMode(mode: Mode): Promise {} async setRunningFile(file: string | undefined): Promise {} - async setSelector(selector: string, userGesture?: boolean): Promise {} + async elementPicked(elementInfo: ElementInfo, userGesture?: boolean): Promise {} async updateCallLogs(callLogs: CallLog[]): Promise {} async setSources(sources: Source[]): Promise {} async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise {} @@ -158,18 +158,12 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise { } - async setSelector(selector: string, userGesture?: boolean): Promise { - if (userGesture) { - if (this._recorder?.mode() === 'inspecting') { - this._recorder.setMode('standby'); - this._page.bringToFront(); - } else { - this._recorder?.setMode('recording'); - } - } - await this._page.mainFrame().evaluateExpression(((data: { selector: string, userGesture?: boolean }) => { - window.playwrightSetSelector(data.selector, data.userGesture); - }).toString(), { isFunction: true }, { selector, userGesture }).catch(() => {}); + async elementPicked(elementInfo: ElementInfo, userGesture?: boolean): Promise { + if (userGesture) + this._page.bringToFront(); + await this._page.mainFrame().evaluateExpression(((param: { elementInfo: ElementInfo, userGesture?: boolean }) => { + window.playwrightElementPicked(param.elementInfo, param.userGesture); + }).toString(), { isFunction: true }, { elementInfo, userGesture }).catch(() => {}); } async updateCallLogs(callLogs: CallLog[]): Promise { diff --git a/packages/playwright-core/src/server/recorder/recorderFrontend.ts b/packages/playwright-core/src/server/recorder/recorderFrontend.ts index 9bf48022a7..dbba78ceeb 100644 --- a/packages/playwright-core/src/server/recorder/recorderFrontend.ts +++ b/packages/playwright-core/src/server/recorder/recorderFrontend.ts @@ -15,7 +15,7 @@ */ import type * as actions from '@recorder/actions'; -import type { CallLog, Mode, Source } from '@recorder/recorderTypes'; +import type { CallLog, Mode, Source, ElementInfo } from '@recorder/recorderTypes'; import type { EventEmitter } from 'events'; export interface IRecorder { @@ -29,7 +29,7 @@ export interface IRecorderApp extends EventEmitter { setPaused(paused: boolean): Promise; setMode(mode: Mode): Promise; setRunningFile(file: string | undefined): Promise; - setSelector(selector: string, userGesture?: boolean): Promise; + elementPicked(elementInfo: ElementInfo, userGesture?: boolean): Promise; updateCallLogs(callLogs: CallLog[]): Promise; setSources(sources: Source[]): Promise; setActions(actions: actions.ActionInContext[], sources: Source[]): Promise; diff --git a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts b/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts index e11ef1283a..fcfd0a36c5 100644 --- a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts +++ b/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts @@ -15,7 +15,7 @@ */ import path from 'path'; -import type { CallLog, Mode, Source } from '@recorder/recorderTypes'; +import type { CallLog, ElementInfo, Mode, Source } from '@recorder/recorderTypes'; import { EventEmitter } from 'events'; import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorderFrontend'; import { installRootRedirect, openTraceViewerApp, startTraceViewerServer } from '../trace/viewer/traceViewer'; @@ -70,8 +70,8 @@ export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp this._transport.deliverEvent('setRunningFile', { file }); } - async setSelector(selector: string, userGesture?: boolean): Promise { - this._transport.deliverEvent('setSelector', { selector, userGesture }); + async elementPicked(elementInfo: ElementInfo, userGesture?: boolean): Promise { + this._transport.deliverEvent('elementPicked', { elementInfo, userGesture }); } async updateCallLogs(callLogs: CallLog[]): Promise { diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 7778d81218..faadda45ee 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -56,6 +56,11 @@ const EXECUTABLE_PATHS = { 'mac': ['chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'], 'win': ['chrome-win', 'chrome.exe'], }, + 'chromium-headless-shell': { + 'linux': ['chrome-linux', 'headless_shell'], + 'mac': ['chrome-mac', 'headless_shell'], + 'win': ['chrome-win', 'headless_shell.exe'], + }, 'firefox': { 'linux': ['firefox', 'firefox'], 'mac': ['firefox', 'Nightly.app', 'Contents', 'MacOS', 'firefox'], @@ -104,6 +109,35 @@ const DOWNLOAD_PATHS: Record = { 'mac15-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', 'win64': 'builds/chromium/%s/chromium-win64.zip', }, + 'chromium-headless-shell': { + '': undefined, + 'ubuntu18.04-x64': undefined, + 'ubuntu20.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'ubuntu22.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'ubuntu24.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'ubuntu18.04-arm64': undefined, + 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'ubuntu24.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'debian11-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'debian11-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'debian12-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', + 'debian12-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'mac10.13': undefined, + 'mac10.14': undefined, + 'mac10.15': undefined, + 'mac11': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + 'mac11-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', + 'mac12': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + 'mac12-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', + 'mac13': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + 'mac13-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', + 'mac14': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + 'mac14-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', + 'mac15': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + 'mac15-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', + 'win64': 'builds/chromium/%s/chromium-headless-shell-win64.zip', + }, 'chromium-tip-of-tree': { '': undefined, 'ubuntu18.04-x64': undefined, @@ -343,7 +377,7 @@ type BrowsersJSONDescriptor = { dir: string, }; -function readDescriptors(browsersJSON: BrowsersJSON) { +function readDescriptors(browsersJSON: BrowsersJSON): BrowsersJSONDescriptor[] { return (browsersJSON['browsers']).map(obj => { const name = obj.name; const revisionOverride = (obj.revisionOverrides || {})[hostPlatform]; @@ -367,10 +401,10 @@ function readDescriptors(browsersJSON: BrowsersJSON) { } export type BrowserName = 'chromium' | 'firefox' | 'webkit' | 'bidi'; -type InternalTool = 'ffmpeg' | 'firefox-beta' | 'chromium-tip-of-tree' | 'android'; +type InternalTool = 'ffmpeg' | 'firefox-beta' | 'chromium-tip-of-tree' | 'chromium-headless-shell' |'android'; type BidiChannel = 'bidi-firefox-stable' | 'bidi-firefox-beta' | 'bidi-firefox-nightly' | 'bidi-chrome-canary' | 'bidi-chrome-stable' | 'bidi-chromium'; type ChromiumChannel = 'chrome' | 'chrome-beta' | 'chrome-dev' | 'chrome-canary' | 'msedge' | 'msedge-beta' | 'msedge-dev' | 'msedge-canary'; -const allDownloadable = ['chromium', 'firefox', 'webkit', 'ffmpeg', 'firefox-beta', 'chromium-tip-of-tree']; +const allDownloadable = ['chromium', 'firefox', 'webkit', 'ffmpeg', 'firefox-beta', 'chromium-tip-of-tree', 'chromium-headless-shell']; export interface Executable { type: 'browser' | 'tool' | 'channel'; @@ -445,7 +479,7 @@ export class Registry { executablePath: () => chromiumExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium', chromiumExecutable, chromium.installByDefault, sdkLanguage), installType: chromium.installByDefault ? 'download-by-default' : 'download-on-demand', - _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromium.dir, ['chrome-linux'], [], ['chrome-win']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, chromium.dir, ['chrome-linux'], [], ['chrome-win']), downloadURLs: this._downloadURLs(chromium), browserVersion: chromium.browserVersion, _install: () => this._downloadExecutable(chromium, chromiumExecutable), @@ -453,6 +487,30 @@ export class Registry { _isHermeticInstallation: true, }); + const chromiumHeadlessShellDescriptor: BrowsersJSONDescriptor = { + name: 'chromium-headless-shell', + revision: chromium.revision, + browserVersion: chromium.browserVersion, + dir: chromium.dir.replace(/(.*)(-\d+)$/, '$1-headless-shell$2'), + installByDefault: false + }; + const chromiumHeadlessShellExecutable = findExecutablePath(chromiumHeadlessShellDescriptor.dir, 'chromium-headless-shell'); + this._executables.push({ + type: 'tool', + name: 'chromium-headless-shell', + browserName: 'chromium', + directory: chromiumHeadlessShellDescriptor.dir, + executablePath: () => chromiumHeadlessShellExecutable, + executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-headless-shell', chromiumHeadlessShellExecutable, false, sdkLanguage), + installType: 'download-on-demand', + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, chromiumHeadlessShellDescriptor.dir, ['chrome-linux'], [], ['chrome-win']), + downloadURLs: this._downloadURLs(chromiumHeadlessShellDescriptor), + browserVersion: chromium.browserVersion, + _install: () => this._downloadExecutable(chromiumHeadlessShellDescriptor, chromiumHeadlessShellExecutable), + _dependencyGroup: 'chromium', + _isHermeticInstallation: true, + }); + const chromiumTipOfTree = descriptors.find(d => d.name === 'chromium-tip-of-tree')!; const chromiumTipOfTreeExecutable = findExecutablePath(chromiumTipOfTree.dir, 'chromium'); this._executables.push({ @@ -463,7 +521,7 @@ export class Registry { executablePath: () => chromiumTipOfTreeExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-tip-of-tree', chromiumTipOfTreeExecutable, chromiumTipOfTree.installByDefault, sdkLanguage), installType: chromiumTipOfTree.installByDefault ? 'download-by-default' : 'download-on-demand', - _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumTipOfTree.dir, ['chrome-linux'], [], ['chrome-win']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, chromiumTipOfTree.dir, ['chrome-linux'], [], ['chrome-win']), downloadURLs: this._downloadURLs(chromiumTipOfTree), browserVersion: chromiumTipOfTree.browserVersion, _install: () => this._downloadExecutable(chromiumTipOfTree, chromiumTipOfTreeExecutable), @@ -573,7 +631,7 @@ export class Registry { executablePath: () => chromiumExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium', chromiumExecutable, chromium.installByDefault, sdkLanguage), installType: 'download-on-demand', - _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromium.dir, ['chrome-linux'], [], ['chrome-win']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, chromium.dir, ['chrome-linux'], [], ['chrome-win']), downloadURLs: this._downloadURLs(chromium), browserVersion: chromium.browserVersion, _install: () => this._downloadExecutable(chromium, chromiumExecutable), @@ -591,7 +649,7 @@ export class Registry { executablePath: () => firefoxExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox', firefoxExecutable, firefox.installByDefault, sdkLanguage), installType: firefox.installByDefault ? 'download-by-default' : 'download-on-demand', - _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefox.dir, ['firefox'], [], ['firefox']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, firefox.dir, ['firefox'], [], ['firefox']), downloadURLs: this._downloadURLs(firefox), browserVersion: firefox.browserVersion, _install: () => this._downloadExecutable(firefox, firefoxExecutable), @@ -609,7 +667,7 @@ export class Registry { executablePath: () => firefoxBetaExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox-beta', firefoxBetaExecutable, firefoxBeta.installByDefault, sdkLanguage), installType: firefoxBeta.installByDefault ? 'download-by-default' : 'download-on-demand', - _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefoxBeta.dir, ['firefox'], [], ['firefox']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, firefoxBeta.dir, ['firefox'], [], ['firefox']), downloadURLs: this._downloadURLs(firefoxBeta), browserVersion: firefoxBeta.browserVersion, _install: () => this._downloadExecutable(firefoxBeta, firefoxBetaExecutable), @@ -637,7 +695,7 @@ export class Registry { executablePath: () => webkitExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('webkit', webkitExecutable, webkit.installByDefault, sdkLanguage), installType: webkit.installByDefault ? 'download-by-default' : 'download-on-demand', - _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'webkit', webkit.dir, webkitLinuxLddDirectories, ['libGLESv2.so.2', 'libx264.so'], ['']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, webkit.dir, webkitLinuxLddDirectories, ['libGLESv2.so.2', 'libx264.so'], ['']), downloadURLs: this._downloadURLs(webkit), browserVersion: webkit.browserVersion, _install: () => this._downloadExecutable(webkit, webkitExecutable), @@ -835,7 +893,7 @@ export class Registry { return Array.from(set); } - private async _validateHostRequirements(sdkLanguage: string, browserName: BrowserName, browserDirectory: string, linuxLddDirectories: string[], dlOpenLibraries: string[], windowsExeAndDllDirectories: string[]) { + private async _validateHostRequirements(sdkLanguage: string, browserDirectory: string, linuxLddDirectories: string[], dlOpenLibraries: string[], windowsExeAndDllDirectories: string[]) { if (os.platform() === 'linux') return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map(d => path.join(browserDirectory, d)), dlOpenLibraries); if (os.platform() === 'win32' && os.arch() === 'x64') diff --git a/packages/playwright-core/src/server/registry/nativeDeps.ts b/packages/playwright-core/src/server/registry/nativeDeps.ts index 0f67455d26..d569f13258 100644 --- a/packages/playwright-core/src/server/registry/nativeDeps.ts +++ b/packages/playwright-core/src/server/registry/nativeDeps.ts @@ -359,8 +359,10 @@ export const deps: any = { 'libx264-163', 'libatomic1', 'libevent-2.1-7', + 'libavif13', ], lib2package: { + 'libavif.so.13': 'libavif13', 'libsoup-3.0.so.0': 'libsoup-3.0-0', 'libasound.so.2': 'libasound2', 'libatk-1.0.so.0': 'libatk1.0-0', @@ -566,9 +568,11 @@ export const deps: any = { 'libxkbcommon0', 'libxml2', 'libxslt1.1', - 'libx264-164' + 'libx264-164', + 'libavif16', ], lib2package: { + 'libavif.so.16': 'libavif16', 'libasound.so.2': 'libasound2t64', 'libatk-1.0.so.0': 'libatk1.0-0t64', 'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0t64', @@ -994,8 +998,10 @@ export const deps: any = { 'libxslt1.1', 'libatomic1', 'libevent-2.1-7', + 'libavif15', ], lib2package: { + 'libavif.so.15': 'libavif15', 'libsoup-3.0.so.0': 'libsoup-3.0-0', 'libasound.so.2': 'libasound2', 'libatk-1.0.so.0': 'libatk1.0-0', diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts index 138820baee..66f78bfdc9 100644 --- a/packages/playwright/src/reporters/base.ts +++ b/packages/playwright/src/reporters/base.ts @@ -23,12 +23,6 @@ import { resolveReporterOutputPath } from '../util'; export type TestResultOutput = { chunk: string | Buffer, type: 'stdout' | 'stderr' }; export const kOutputSymbol = Symbol('output'); -type Annotation = { - title: string; - message: string; - location?: Location; -}; - type ErrorDetails = { message: string; location?: Location; @@ -260,9 +254,7 @@ export class BaseReporter implements ReporterV2 { private _printFailures(failures: TestCase[]) { console.log(''); failures.forEach((test, index) => { - console.log(formatFailure(this.config, test, { - index: index + 1, - }).message); + console.log(formatFailure(this.config, test, index + 1)); }); } @@ -285,14 +277,8 @@ export class BaseReporter implements ReporterV2 { } } -export function formatFailure(config: FullConfig, test: TestCase, options: {index?: number, includeStdio?: boolean, includeAttachments?: boolean} = {}): { - message: string, - annotations: Annotation[] -} { - const { index, includeStdio, includeAttachments = true } = options; +export function formatFailure(config: FullConfig, test: TestCase, index?: number): string { const lines: string[] = []; - const title = formatTestTitle(config, test); - const annotations: Annotation[] = []; const header = formatTestHeader(config, test, { indent: ' ', index, mode: 'error' }); lines.push(colors.red(header)); for (const result of test.results) { @@ -307,62 +293,48 @@ export function formatFailure(config: FullConfig, test: TestCase, options: {inde } resultLines.push(...retryLines); resultLines.push(...errors.map(error => '\n' + error.message)); - if (includeAttachments) { - for (let i = 0; i < result.attachments.length; ++i) { - const attachment = result.attachments[i]; - const hasPrintableContent = attachment.contentType.startsWith('text/'); - if (!attachment.path && !hasPrintableContent) - continue; - resultLines.push(''); - resultLines.push(colors.cyan(separator(` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`))); - if (attachment.path) { - const relativePath = path.relative(process.cwd(), attachment.path); - resultLines.push(colors.cyan(` ${relativePath}`)); - // Make this extensible - if (attachment.name === 'trace') { - const packageManagerCommand = getPackageManagerExecCommand(); - resultLines.push(colors.cyan(` Usage:`)); - resultLines.push(''); - resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`)); - resultLines.push(''); - } - } else { - if (attachment.contentType.startsWith('text/') && attachment.body) { - let text = attachment.body.toString(); - if (text.length > 300) - text = text.slice(0, 300) + '...'; - for (const line of text.split('\n')) - resultLines.push(colors.cyan(` ${line}`)); - } - } - resultLines.push(colors.cyan(separator(' '))); - } - } - const output = ((result as any)[kOutputSymbol] || []) as TestResultOutput[]; - if (includeStdio && output.length) { - const outputText = output.map(({ chunk, type }) => { - const text = chunk.toString('utf8'); - if (type === 'stderr') - return colors.red(stripAnsiEscapes(text)); - return text; - }).join(''); + for (let i = 0; i < result.attachments.length; ++i) { + const attachment = result.attachments[i]; + const hasPrintableContent = attachment.contentType.startsWith('text/'); + if (!attachment.path && !hasPrintableContent) + continue; resultLines.push(''); - resultLines.push(colors.gray(separator('--- Test output')) + '\n\n' + outputText + '\n' + separator()); - } - for (const error of errors) { - annotations.push({ - location: error.location, - title, - message: [header, ...retryLines, error.message].join('\n'), - }); + resultLines.push(colors.cyan(separator(` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`))); + if (attachment.path) { + const relativePath = path.relative(process.cwd(), attachment.path); + resultLines.push(colors.cyan(` ${relativePath}`)); + // Make this extensible + if (attachment.name === 'trace') { + const packageManagerCommand = getPackageManagerExecCommand(); + resultLines.push(colors.cyan(` Usage:`)); + resultLines.push(''); + resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`)); + resultLines.push(''); + } + } else { + if (attachment.contentType.startsWith('text/') && attachment.body) { + let text = attachment.body.toString(); + if (text.length > 300) + text = text.slice(0, 300) + '...'; + for (const line of text.split('\n')) + resultLines.push(colors.cyan(` ${line}`)); + } + } + resultLines.push(colors.cyan(separator(' '))); } lines.push(...resultLines); } lines.push(''); - return { - message: lines.join('\n'), - annotations - }; + return lines.join('\n'); +} + +export function formatRetry(result: TestResult) { + const retryLines = []; + if (result.retry) { + retryLines.push(''); + retryLines.push(colors.gray(separator(` Retry #${result.retry}`))); + } + return retryLines; } function quotePathIfNeeded(path: string): string { @@ -428,7 +400,7 @@ export function formatTestTitle(config: FullConfig, test: TestCase, step?: TestS return `${projectTitle}${location} › ${titles.join(' › ')}${stepSuffix(step)}${tags}`; } -function formatTestHeader(config: FullConfig, test: TestCase, options: { indent?: string, index?: number, mode?: 'default' | 'error' } = {}): string { +export function formatTestHeader(config: FullConfig, test: TestCase, options: { indent?: string, index?: number, mode?: 'default' | 'error' } = {}): string { const title = formatTestTitle(config, test); const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`; let fullHeader = header; diff --git a/packages/playwright/src/reporters/github.ts b/packages/playwright/src/reporters/github.ts index dc03677714..c178cce64d 100644 --- a/packages/playwright/src/reporters/github.ts +++ b/packages/playwright/src/reporters/github.ts @@ -16,7 +16,7 @@ import { ms as milliseconds } from 'playwright-core/lib/utilsBundle'; import path from 'path'; -import { BaseReporter, formatError, formatFailure, stripAnsiEscapes } from './base'; +import { BaseReporter, colors, formatError, formatResultFailure, formatRetry, formatTestHeader, formatTestTitle, stripAnsiEscapes } from './base'; import type { TestCase, FullResult, TestError } from '../../types/testReporter'; type GitHubLogType = 'debug' | 'notice' | 'warning' | 'error'; @@ -100,22 +100,23 @@ export class GitHubReporter extends BaseReporter { private _printFailureAnnotations(failures: TestCase[]) { failures.forEach((test, index) => { - const { annotations } = formatFailure(this.config, test, { - index: index + 1, - includeStdio: true, - includeAttachments: false, - }); - annotations.forEach(({ location, title, message }) => { - const options: GitHubLogOptions = { - file: workspaceRelativePath(location?.file || test.location.file), - title, - }; - if (location) { - options.line = location.line; - options.col = location.column; + const title = formatTestTitle(this.config, test); + const header = formatTestHeader(this.config, test, { indent: ' ', index: index + 1, mode: 'error' }); + for (const result of test.results) { + const errors = formatResultFailure(test, result, ' ', colors.enabled); + for (const error of errors) { + const options: GitHubLogOptions = { + file: workspaceRelativePath(error.location?.file || test.location.file), + title, + }; + if (error.location) { + options.line = error.location.line; + options.col = error.location.column; + } + const message = [header, ...formatRetry(result), error.message].join('\n'); + this.githubLogger.error(message, options); } - this.githubLogger.error(message, options); - }); + } }); } } diff --git a/packages/playwright/src/reporters/junit.ts b/packages/playwright/src/reporters/junit.ts index befd652e4b..f193f4f79d 100644 --- a/packages/playwright/src/reporters/junit.ts +++ b/packages/playwright/src/reporters/junit.ts @@ -188,7 +188,7 @@ class JUnitReporter implements ReporterV2 { message: `${path.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`, type: 'FAILURE', }, - text: stripAnsiEscapes(formatFailure(this.config, test).message) + text: stripAnsiEscapes(formatFailure(this.config, test)) }); } diff --git a/packages/playwright/src/reporters/line.ts b/packages/playwright/src/reporters/line.ts index af4c786e16..de5fc61703 100644 --- a/packages/playwright/src/reporters/line.ts +++ b/packages/playwright/src/reporters/line.ts @@ -82,9 +82,7 @@ class LineReporter extends BaseReporter { if (!this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected' || result.status === 'interrupted')) { if (!process.env.PW_TEST_DEBUG_REPORTERS) process.stdout.write(`\u001B[1A\u001B[2K`); - console.log(formatFailure(this.config, test, { - index: ++this._failures - }).message); + console.log(formatFailure(this.config, test, ++this._failures)); console.log(); } } diff --git a/packages/recorder/src/recorder.tsx b/packages/recorder/src/recorder.tsx index a8cd2b2719..2e05b65f6e 100644 --- a/packages/recorder/src/recorder.tsx +++ b/packages/recorder/src/recorder.tsx @@ -14,7 +14,7 @@ limitations under the License. */ -import type { CallLog, Mode, Source } from './recorderTypes'; +import type { CallLog, ElementInfo, Mode, Source } from './recorderTypes'; import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper'; import { SplitView } from '@web/components/splitView'; import { TabbedPane } from '@web/components/tabbedPane'; @@ -44,6 +44,7 @@ export const Recorder: React.FC = ({ const [selectedFileId, setSelectedFileId] = React.useState(); const [runningFileId, setRunningFileId] = React.useState(); const [selectedTab, setSelectedTab] = React.useState('log'); + const [ariaSnapshot, setAriaSnapshot] = React.useState(); const fileId = selectedFileId || runningFileId || sources[0]?.id; @@ -57,11 +58,18 @@ export const Recorder: React.FC = ({ }, [sources, fileId]); const [locator, setLocator] = React.useState(''); - window.playwrightSetSelector = (selector: string, focus?: boolean) => { + window.playwrightElementPicked = (elementInfo: ElementInfo, userGesture?: boolean) => { const language = source.language; - if (focus) + setLocator(asLocator(language, elementInfo.selector)); + setAriaSnapshot(elementInfo.ariaSnapshot); + if (userGesture && selectedTab !== 'locator' && selectedTab !== 'aria') setSelectedTab('locator'); - setLocator(asLocator(language, selector)); + + if (mode === 'inspecting' && selectedTab === 'aria') { + // Keep exploring aria. + } else { + window.dispatch({ event: 'setMode', params: { mode: mode === 'inspecting' ? 'standby' : 'recording' } }).catch(() => { }); + } }; window.playwrightSetRunningFile = setRunningFileId; @@ -94,7 +102,7 @@ export const Recorder: React.FC = ({ }, [paused]); const onEditorChange = React.useCallback((selector: string) => { - if (mode === 'none') + if (mode === 'none' || mode === 'inspecting') window.dispatch({ event: 'setMode', params: { mode: 'standby' } }); setLocator(selector); window.dispatch({ event: 'selectorUpdated', params: { selector } }); @@ -157,7 +165,7 @@ export const Recorder: React.FC = ({ sidebarSize={200} main={} sidebar={ copy(locator)} />] : []} + rightToolbar={selectedTab === 'locator' || selectedTab === 'aria' ? [ copy((selectedTab === 'locator' ? locator : ariaSnapshot) || '')} />] : []} tabs={[ { id: 'locator', @@ -169,6 +177,11 @@ export const Recorder: React.FC = ({ title: 'Log', render: () => }, + { + id: 'aria', + title: 'Accessibility', + render: () => + }, ]} selectedTab={selectedTab} setSelectedTab={setSelectedTab} diff --git a/packages/recorder/src/recorderTypes.ts b/packages/recorder/src/recorderTypes.ts index a0e04a7283..22afde3dad 100644 --- a/packages/recorder/src/recorderTypes.ts +++ b/packages/recorder/src/recorderTypes.ts @@ -29,6 +29,11 @@ export type Mode = | 'assertingValue' | 'assertingSnapshot'; +export type ElementInfo = { + selector: string; + ariaSnapshot: string; +}; + export type EventData = { event: | 'clear' @@ -98,7 +103,7 @@ declare global { playwrightSetOverlayVisible: (visible: boolean) => void; playwrightUpdateLogs: (callLogs: CallLog[]) => void; playwrightSetRunningFile: (file: string | undefined) => void; - playwrightSetSelector: (selector: string, focus?: boolean) => void; + playwrightElementPicked: (elementInfo: ElementInfo, userGesture?: boolean) => void; playwrightSourcesEchoForTest: Source[]; dispatch(data: any): Promise; } diff --git a/packages/recorder/tsconfig.json b/packages/recorder/tsconfig.json index 10f4bfcf2f..16546d6e0d 100644 --- a/packages/recorder/tsconfig.json +++ b/packages/recorder/tsconfig.json @@ -19,6 +19,7 @@ "paths": { "@isomorphic/*": ["../playwright-core/src/utils/isomorphic/*"], "@protocol/*": ["../protocol/src/*"], + "@recorder/*": ["../recorder/src/*"], "@web/*": ["../web/src/*"], } }, diff --git a/packages/trace-viewer/src/ui/snapshotTab.tsx b/packages/trace-viewer/src/ui/snapshotTab.tsx index a4d606723f..56d5b0b2e5 100644 --- a/packages/trace-viewer/src/ui/snapshotTab.tsx +++ b/packages/trace-viewer/src/ui/snapshotTab.tsx @@ -29,6 +29,7 @@ import type { Language } from '@isomorphic/locatorGenerators'; import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser'; import { TabbedPaneTab } from '@web/components/tabbedPane'; import { BrowserFrame } from './browserFrame'; +import type { ElementInfo } from '@recorder/recorderTypes'; export const SnapshotTabsView: React.FunctionComponent<{ action: ActionTraceEvent | undefined, @@ -244,8 +245,8 @@ export const InspectModeController: React.FunctionComponent<{ testIdAttributeName, overlay: { offsetX: 0 }, }, { - async setSelector(selector: string) { - setHighlightedLocator(asLocator(sdkLanguage, frameSelector + selector)); + async elementPicked(elementInfo: ElementInfo) { + setHighlightedLocator(asLocator(sdkLanguage, frameSelector + elementInfo.selector)); }, highlightUpdated() { for (const r of recorders) { diff --git a/packages/web/src/components/codeMirrorModule.tsx b/packages/web/src/components/codeMirrorModule.tsx index 4376e0a18f..ea0953bf8f 100644 --- a/packages/web/src/components/codeMirrorModule.tsx +++ b/packages/web/src/components/codeMirrorModule.tsx @@ -25,6 +25,7 @@ import 'codemirror-shadow-1/mode/python/python'; import 'codemirror-shadow-1/mode/clike/clike'; import 'codemirror-shadow-1/mode/markdown/markdown'; import 'codemirror-shadow-1/addon/mode/simple'; +import 'codemirror-shadow-1/mode/yaml/yaml'; export type CodeMirror = typeof codemirrorType; export default codemirror; diff --git a/tests/library/capabilities.spec.ts b/tests/library/capabilities.spec.ts index 864022a1f4..cdc97d1523 100644 --- a/tests/library/capabilities.spec.ts +++ b/tests/library/capabilities.spec.ts @@ -400,7 +400,7 @@ it('should be able to render avif images', { } }, async ({ page, server, browserName, platform }) => { it.fixme(browserName === 'webkit' && platform === 'win32'); - it.fixme(browserName === 'webkit' && platform === 'linux', 'https://github.com/microsoft/playwright/issues/32673'); + it.skip(browserName === 'webkit' && hostPlatform.startsWith('ubuntu20.04'), 'Ubuntu 20.04 is frozen'); await page.goto(server.EMPTY_PAGE); await page.setContent(``); await expect.poll(() => page.locator('img').boundingBox()).toEqual(expect.objectContaining({ diff --git a/tests/page/page-aria-snapshot.spec.ts b/tests/page/page-aria-snapshot.spec.ts index 88306a1e43..188808a178 100644 --- a/tests/page/page-aria-snapshot.spec.ts +++ b/tests/page/page-aria-snapshot.spec.ts @@ -64,8 +64,8 @@ it('should snapshot list with accessible name', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - list "my list": - - listitem: one - - listitem: two + - listitem: "one" + - listitem: "two" `); }); @@ -92,7 +92,7 @@ it('should allow text nodes', async ({ page }) => { await checkAndMatchSnapshot(page.locator('body'), ` - heading "Microsoft" [level=1] - - text: Open source projects and samples from Microsoft + - text: "Open source projects and samples from Microsoft" `); }); @@ -105,7 +105,7 @@ it('should snapshot details visibility', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - group: Summary + - group: "Summary" `); }); @@ -145,10 +145,10 @@ it('should snapshot integration', async ({ page }) => { await checkAndMatchSnapshot(page.locator('body'), ` - heading "Microsoft" [level=1] - - text: Open source projects and samples from Microsoft + - text: "Open source projects and samples from Microsoft" - list: - listitem: - - group: Verified + - group: "Verified" - listitem: - link "Sponsor" `); @@ -164,7 +164,7 @@ it('should support multiline text', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - paragraph: Line 1 Line 2 Line 3 + - paragraph: "Line 1 Line 2 Line 3" `); await expect(page.locator('body')).toMatchAriaSnapshot(` - paragraph: | @@ -180,7 +180,7 @@ it('should concatenate span text', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - text: One Two Three + - text: "One Two Three" `); }); @@ -190,7 +190,7 @@ it('should concatenate span text 2', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - text: One Two Three + - text: "One Two Three" `); }); @@ -200,7 +200,7 @@ it('should concatenate div text with spaces', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - text: One Two Three + - text: "One Two Three" `); }); @@ -362,12 +362,12 @@ it('should snapshot inner text', async ({ page }) => { await checkAndMatchSnapshot(page.locator('body'), ` - listitem: - - text: a.test.ts + - text: "a.test.ts" - button "Run" - button "Show source" - button "Watch" - listitem: - - text: snapshot 30ms + - text: "snapshot 30ms" - button "Run" - button "Show source" - button "Watch" @@ -382,6 +382,6 @@ it('should include pseudo codepoints', async ({ page, server }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - paragraph: \ueab2hello + - paragraph: "\ueab2hello" `); }); diff --git a/utils/build/build.js b/utils/build/build.js index 56442c0948..7a289bd5ed 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -283,7 +283,7 @@ for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer']) { 'vite', 'build', ...(watchMode ? ['--watch', '--minify=false'] : []), - ...(withSourceMaps ? ['--sourcemap'] : []), + ...(withSourceMaps ? ['--sourcemap=inline'] : []), ], shell: true, cwd: path.join(__dirname, '..', '..', 'packages', webPackage), @@ -299,7 +299,7 @@ steps.push({ 'vite.sw.config.ts', 'build', ...(watchMode ? ['--watch', '--minify=false'] : []), - ...(withSourceMaps ? ['--sourcemap'] : []), + ...(withSourceMaps ? ['--sourcemap=inline'] : []), ], shell: true, cwd: path.join(__dirname, '..', '..', 'packages', 'trace-viewer'),