Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions projects/core/.visual/dialog.center-alignment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions projects/core/.visual/dialog.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions projects/core/.visual/dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions projects/core/.visual/dialog.scroll-height.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions projects/core/.visual/toast.body-anchor.png

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a chromium change I think here. Not specific to this PR I don't think as I hit this in another wip PR as well.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions projects/internals/vite/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import type { UserConfig } from 'vite';
import type { Page } from 'playwright';

export declare const libraryBuildConfig: UserConfig;
export declare const libraryNodeBuildConfig: UserConfig;
Expand Down Expand Up @@ -40,8 +41,35 @@ export declare const visualRunner: {
waitFor?: (waitForFunction: (...args: unknown[]) => Promise<unknown>) => Promise<void>;
}
): Promise<{ maxDiffPercentage: number }>;
inspect<Result>(
name: string,
content: string,
inspectPage: (page: Page) => Result | Promise<Result>,
options?: { deviceScaleFactor?: number }
): Promise<Result>;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
runWebGPUSmoke(name: string): Promise<WebGPUVisualDiagnostics>;
};

export interface WebGPUVisualDiagnostics {
browserVersion: string;
chromiumArgs: string[];
secureContext: boolean;
adapterInfo: {
vendor: string;
architecture: string;
device: string;
description: string;
isFallbackAdapter?: boolean;
} | null;
software: boolean;
workCompleted: boolean;
clearFrame: boolean;
clearPixel?: number[];
expectedPixel?: number[];
format?: string;
error?: string;
}

export declare const ssrRunner: {
render(content: unknown): Promise<string>;
};
Expand Down
38 changes: 20 additions & 18 deletions projects/internals/vite/src/runners/lighthouse.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,25 @@ import lighthouse from 'lighthouse';
import fs from 'fs';
import { resolve } from 'path';
import { VitePlaywrightRunner, buildPage } from './playwright.js';
import { withSoftwareWebGPUChromiumArgs } from './webgpu.js';

const output = process.env.CI ? ['json'] : ['json', 'html'];
const RUNNER_ID = 'lighthouse';
const LIGHTHOUSE_FLAGS = { logLevel: 'error', output };
const CHROMIUM_ARGS = withSoftwareWebGPUChromiumArgs([
'--headless',
'--remote-debugging-port=9222',
'--disable-dev-shm-usage',
'--disable-extensions',
'--disable-background-networking',
'--disable-default-apps',
'--disable-sync',
'--disable-translate',
'--metrics-recording-only',
'--mute-audio',
'--no-first-run',
'--safebrowsing-disable-auto-update'
]);
const LIGHTHOUSE_CONFIG = {
// https://github.com/GoogleChrome/lighthouse/blob/main/core/config/default-config.js
extends: 'lighthouse:default',
Expand All @@ -22,24 +37,11 @@ const LIGHTHOUSE_CONFIG = {
};

export class LighthouseRunner {
#runner = new VitePlaywrightRunner({
runnerID: RUNNER_ID,
chromiumArgs: [
'--headless',
'--remote-debugging-port=9222',
'--disable-dev-shm-usage',
'--disable-gpu',
'--disable-extensions',
'--disable-background-networking',
'--disable-default-apps',
'--disable-sync',
'--disable-translate',
'--metrics-recording-only',
'--mute-audio',
'--no-first-run',
'--safebrowsing-disable-auto-update'
]
});
#runner;

constructor({ chromiumArgs = CHROMIUM_ARGS } = {}) {
this.#runner = new VitePlaywrightRunner({ runnerID: RUNNER_ID, chromiumArgs });
}

async open() {
await this.#runner.open();
Expand Down
66 changes: 63 additions & 3 deletions projects/internals/vite/src/runners/playwright.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { preview, build } from 'vite';

const resolve = rel => path.resolve(process.cwd(), rel);
const DEFAULT_PORT = 4176;
export const VIEWPORT_WIDTH = 1180;
export const VIEWPORT_HEIGHT = 820;

process.env.NODE_ENV = 'production';

Expand All @@ -26,6 +28,10 @@ export class VitePlaywrightRunner {
return this.#page;
}

get browserVersion() {
return this.#browser?.version();
}

get port() {
return this.#serverPort ?? this.#storedPort ?? DEFAULT_PORT;
}
Expand Down Expand Up @@ -96,7 +102,9 @@ export class VitePlaywrightRunner {
throw error;
});
console.log('playwright-runner: creating context');
this.#page = await (await this.#browser.newContext({ viewport: { width: 1180, height: 820 } })).newPage();
this.#page = await (
await this.#browser.newContext({ viewport: { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT } })
).newPage();
this.#page.on('crash', data => console.error('playwright-runner: browser crashed', data));
console.log('playwright-runner: creating server');
this.#server = await preview({
Expand Down Expand Up @@ -137,8 +145,7 @@ export async function buildPage(testName, runnerID, render) {
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true });

let scriptIdx = 0;
const scriptRe = /<script\s+type=["']module["']>([\s\S]*?)<\/script>/g;
html = html.replace(scriptRe, (_match, content) => {
html = replaceInlineModuleScripts(html, content => {
const name = `_entry${scriptIdx++}.js`;
fs.writeFileSync(path.join(tmpDir, name), content.trim());
return `<script type="module" src="./${name}"></script>`;
Expand Down Expand Up @@ -177,3 +184,56 @@ export async function buildPage(testName, runnerID, render) {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
}

/** Replaces inline module scripts with a forward-only scan of the HTML. */
export function replaceInlineModuleScripts(html, replaceScript) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit overkill to satisfy the security bot check

const parts = [];
let copiedUntil = 0;
let searchFrom = 0;

while (searchFrom < html.length) {
const scriptStart = html.indexOf('<script', searchFrom);
if (scriptStart === -1) break;

const contentStart = getInlineModuleScriptContentStart(html, scriptStart);
if (contentStart === undefined) {
searchFrom = scriptStart + '<script'.length;
continue;
}

const scriptEnd = html.indexOf('</script>', contentStart);
if (scriptEnd === -1) break;

parts.push(html.slice(copiedUntil, scriptStart));
parts.push(replaceScript(html.slice(contentStart, scriptEnd)));
copiedUntil = scriptEnd + '</script>'.length;
searchFrom = copiedUntil;
}

parts.push(html.slice(copiedUntil));
return parts.join('');
}

function getInlineModuleScriptContentStart(html, scriptStart) {
let cursor = scriptStart + '<script'.length;
if (!isWhitespace(html[cursor])) return undefined;

while (isWhitespace(html[cursor])) cursor += 1;
if (!html.startsWith('type=', cursor)) return undefined;
cursor += 'type='.length;

const quote = html[cursor];
if (quote !== '"' && quote !== "'") return undefined;

const moduleStart = cursor + 1;
const moduleEnd = moduleStart + 'module'.length;
if (!html.startsWith('module', moduleStart) || html[moduleEnd] !== quote || html[moduleEnd + 1] !== '>') {
return undefined;
}

return moduleEnd + 2;
}

function isWhitespace(character) {
return character !== undefined && character.trim() === '';
}
Loading