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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dist
.lighthouse
**/.visual/dist
**/.visual/.tmp-*
**/.visual/port-*
*.diff.png
coverage
vendor/
Expand Down
3 changes: 3 additions & 0 deletions projects/core/vitest.visual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { libraryVisualTestConfig } from '@internals/vite/configs/visual.js';
export default mergeConfig(libraryVisualTestConfig, {
root: import.meta.dirname,
test: {
fileParallelism: true,
maxWorkers: 2,
maxConcurrency: 1,
include: ['src/**/*.test.visual.ts', '!src/internal/styles/*.test.visual.ts'],
outputFile: {
junit: './coverage/visual/junit.xml'
Expand Down
1 change: 1 addition & 0 deletions projects/internals/vite/src/configs/visual.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const libraryVisualTestConfig = {
}
},
test: {
runner: '@internals/vite/runners/visual-test.js',
retry: 2,
isolate: false,
fileParallelism: false,
Expand Down
12 changes: 3 additions & 9 deletions projects/internals/vite/src/runners/playwright.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,7 @@ export class VitePlaywrightRunner {

async open() {
if (!this.#server) {
if (!fs.existsSync(this.#root)) {
fs.mkdirSync(this.#root);
}

if (!fs.existsSync(this.#dist)) {
fs.mkdirSync(this.#dist);
}
fs.mkdirSync(this.#dist, { recursive: true });

console.log('playwright-runner: launching browser');
// Chromium reserves the debugging port itself, avoiding a free-port race.
Expand Down Expand Up @@ -138,10 +132,10 @@ export class VitePlaywrightRunner {
}
}

async close() {
async close({ collectReports: collect = true } = {}) {
const failures = [];
try {
if (fs.existsSync(this.#dist)) {
if (collect && fs.existsSync(this.#dist)) {
const report = collectReports(this.#root);
fs.writeFileSync(`${this.#dist}/report.json`, JSON.stringify(report, null, 2));
}
Expand Down
21 changes: 21 additions & 0 deletions projects/internals/vite/src/runners/visual-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TestRunner } from 'vitest';
import { visualRunner } from './visual.js';

/** Keeps one browser per worker and closes it after the worker's files finish. */
export default class VisualTestRunner extends TestRunner {
constructor(config) {
super(config);
this.onCleanupWorkerContext?.(() => visualRunner.close());
}

async onAfterRunFiles(files) {
try {
// VM pools do not expose worker cleanup and cannot retain the browser between contexts.
if (!this.onCleanupWorkerContext) {
await visualRunner.close();
}
} finally {
await super.onAfterRunFiles(files);
}
}
}
9 changes: 7 additions & 2 deletions projects/internals/vite/src/runners/visual.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ export class VisualRunner {

constructor({ chromiumArgs = CHROMIUM_ARGS } = {}) {
this.#chromiumArgs = chromiumArgs;
this.#runner = new VitePlaywrightRunner({ runnerID: RUNNER_ID, chromiumArgs });
this.#runner = new VitePlaywrightRunner({
runnerID: RUNNER_ID,
instanceID: process.env.VITEST_POOL_ID ?? '1',
chromiumArgs
});
}

async open() {
await this.#runner.open();
}

async close() {
await this.#runner.close();
// Visual results belong to Vitest; workers must not write a shared runner report.
await this.#runner.close({ collectReports: false });
}

async render(name, content, options = { network: false }) {
Expand Down
7 changes: 1 addition & 6 deletions projects/internals/vite/src/setup/visual.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ import './lit.js';
import { beforeAll } from 'vitest';
import { visualRunner } from '../runners/visual.js';

let runnerInstance = null;

beforeAll(async () => {
if (!runnerInstance) {
runnerInstance = visualRunner;
await runnerInstance.open();
}
await visualRunner.open();
});
3 changes: 3 additions & 0 deletions projects/media/vitest.visual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { libraryVisualTestConfig } from '@internals/vite/configs/visual.js';
export default mergeConfig(libraryVisualTestConfig, {
root: import.meta.dirname,
test: {
fileParallelism: true,
maxWorkers: 2,
maxConcurrency: 1,
include: ['src/**/*.test.visual.ts'],
outputFile: {
junit: './coverage/visual/junit.xml'
Expand Down
42 changes: 42 additions & 0 deletions projects/monaco/src/fixtures.test.visual.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { template as inputTemplate, readyCheck as inputReadyCheck } from './input/input.test.visual.js';
import { template as editorTemplate, readyCheck as editorReadyCheck } from './editor/editor.test.visual.js';
import {
template as diffEditorTemplate,
readyCheck as diffEditorReadyCheck
} from './diff-editor/diff-editor.test.visual.js';
import {
template as diffInputTemplate,
readyCheck as diffInputReadyCheck
} from './diff-input/diff-input.test.visual.js';
import { template as problemsTemplate, readyCheck as problemsReadyCheck } from './problems/problems.test.visual.js';

export async function readyCheck(evaluate: (fn: () => boolean) => Promise<boolean>) {
await inputReadyCheck(evaluate);
await editorReadyCheck(evaluate);
await diffEditorReadyCheck(evaluate);
await diffInputReadyCheck(evaluate);
await problemsReadyCheck(evaluate);
}

export function template(theme: '' | 'dark' = '') {
return /* html */ `
<script type="module">
import '@nvidia-elements/core/forms/define.js';
import '@nvidia-elements/monaco/input/define.js';
import '@nvidia-elements/monaco/editor/define.js';
import '@nvidia-elements/monaco/diff-editor/define.js';
import '@nvidia-elements/monaco/diff-input/define.js';
import '@nvidia-elements/monaco/problems/define.js';
document.documentElement.setAttribute('nve-theme', '${theme} reduced-motion');
</script>
<div nve-layout="column gap:lg align:stretch">
${inputTemplate()}
${editorTemplate()}
${diffEditorTemplate()}
${diffInputTemplate()}
${problemsTemplate()}
</div>`;
}
13 changes: 13 additions & 0 deletions projects/monaco/src/index.dark.test.visual.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { expect, test, describe } from 'vitest';
import { visualRunner } from '@internals/vite';
import { template, readyCheck } from './fixtures.test.visual.js';

describe('monaco input visual', () => {
test('input should match visual baseline dark theme', async () => {
const report = await visualRunner.render('monaco.dark', template('dark'), { network: true, waitFor: readyCheck });
expect(report.maxDiffPercentage).toBeLessThanOrEqual(5);
});
});
45 changes: 1 addition & 44 deletions projects/monaco/src/index.test.visual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,11 @@

import { expect, test, describe } from 'vitest';
import { visualRunner } from '@internals/vite';
import { template as inputTemplate, readyCheck as inputReadyCheck } from './input/input.test.visual.js';
import { template as editorTemplate, readyCheck as editorReadyCheck } from './editor/editor.test.visual.js';
import {
template as diffEditorTemplate,
readyCheck as diffEditorReadyCheck
} from './diff-editor/diff-editor.test.visual.js';
import {
template as diffInputTemplate,
readyCheck as diffInputReadyCheck
} from './diff-input/diff-input.test.visual.js';
import { template as problemsTemplate, readyCheck as problemsReadyCheck } from './problems/problems.test.visual.js';
import { template, readyCheck } from './fixtures.test.visual.js';

describe('monaco input visual', () => {
test('input should match visual baseline', async () => {
const report = await visualRunner.render('monaco', template(''), { network: true, waitFor: readyCheck });
expect(report.maxDiffPercentage).toBeLessThanOrEqual(5);
});

test('input should match visual baseline dark theme', async () => {
const report = await visualRunner.render('monaco.dark', template('dark'), { network: true, waitFor: readyCheck });
expect(report.maxDiffPercentage).toBeLessThanOrEqual(5);
});
});

async function readyCheck(evaluate: (fn: () => boolean) => Promise<boolean>) {
await inputReadyCheck(evaluate);
await editorReadyCheck(evaluate);
await diffEditorReadyCheck(evaluate);
await diffInputReadyCheck(evaluate);
await problemsReadyCheck(evaluate);
}

function template(theme: '' | 'dark' = '') {
return /* html */ `
<script type="module">
import '@nvidia-elements/core/forms/define.js';
import '@nvidia-elements/monaco/input/define.js';
import '@nvidia-elements/monaco/editor/define.js';
import '@nvidia-elements/monaco/diff-editor/define.js';
import '@nvidia-elements/monaco/diff-input/define.js';
import '@nvidia-elements/monaco/problems/define.js';
document.documentElement.setAttribute('nve-theme', '${theme} reduced-motion');
</script>
<div nve-layout="column gap:lg align:stretch">
${inputTemplate()}
${editorTemplate()}
${diffEditorTemplate()}
${diffInputTemplate()}
${problemsTemplate()}
</div>`;
}
5 changes: 4 additions & 1 deletion projects/monaco/vitest.visual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { libraryVisualTestConfig } from '@internals/vite/configs/visual.js';
export default mergeConfig(libraryVisualTestConfig, {
root: import.meta.dirname,
test: {
include: ['src/index.test.visual.ts'],
fileParallelism: true,
maxWorkers: 2,
maxConcurrency: 1,
include: ['src/index*.test.visual.ts'],
outputFile: {
junit: './coverage/visual/junit.xml'
}
Expand Down
3 changes: 3 additions & 0 deletions projects/viewport/vitest.visual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { libraryVisualTestConfig } from '@internals/vite/configs/visual.js';
export default mergeConfig(libraryVisualTestConfig, {
root: import.meta.dirname,
test: {
fileParallelism: true,
maxWorkers: 2,
maxConcurrency: 1,
include: ['src/**/*.test.visual.ts'],
outputFile: {
junit: './coverage/visual/junit.xml'
Expand Down
Loading