@quake2ts/test-utils 0.0.807 → 0.0.808
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +180 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -15
- package/dist/index.d.ts +37 -15
- package/dist/index.js +182 -143
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/engine/helpers/webgl-playwright.ts +76 -8
package/dist/index.cjs
CHANGED
|
@@ -296,6 +296,7 @@ __export(index_exports, {
|
|
|
296
296
|
teardownNodeEnvironment: () => teardownNodeEnvironment,
|
|
297
297
|
testComputeShader: () => testComputeShader,
|
|
298
298
|
testPipelineRendering: () => testPipelineRendering,
|
|
299
|
+
testWebGLAnimation: () => testWebGLAnimation,
|
|
299
300
|
testWebGLRenderer: () => testWebGLRenderer,
|
|
300
301
|
throttleBandwidth: () => throttleBandwidth,
|
|
301
302
|
verifySmoothing: () => verifySmoothing,
|
|
@@ -4547,9 +4548,153 @@ async function renderAndExpectSnapshot(setup, renderFn, options) {
|
|
|
4547
4548
|
});
|
|
4548
4549
|
}
|
|
4549
4550
|
|
|
4551
|
+
// src/visual/animation-snapshots.ts
|
|
4552
|
+
var import_upng_js = __toESM(require("upng-js"), 1);
|
|
4553
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
4554
|
+
var import_fs2 = require("fs");
|
|
4555
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
4556
|
+
async function loadAPNG(filepath) {
|
|
4557
|
+
const buffer = await import_promises2.default.readFile(filepath);
|
|
4558
|
+
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
4559
|
+
const img = import_upng_js.default.decode(arrayBuffer);
|
|
4560
|
+
const framesRGBA = import_upng_js.default.toRGBA8(img);
|
|
4561
|
+
const frames = framesRGBA.map((buffer2) => new Uint8ClampedArray(buffer2));
|
|
4562
|
+
return {
|
|
4563
|
+
width: img.width,
|
|
4564
|
+
height: img.height,
|
|
4565
|
+
frames
|
|
4566
|
+
};
|
|
4567
|
+
}
|
|
4568
|
+
async function saveAPNG(filepath, frames, width, height, delayMs) {
|
|
4569
|
+
const buffers = frames.map((f) => {
|
|
4570
|
+
const dst = new ArrayBuffer(f.byteLength);
|
|
4571
|
+
new Uint8ClampedArray(dst).set(f);
|
|
4572
|
+
return dst;
|
|
4573
|
+
});
|
|
4574
|
+
const delays = new Array(frames.length).fill(delayMs);
|
|
4575
|
+
const pngBuffer = import_upng_js.default.encode(buffers, width, height, 0, delays);
|
|
4576
|
+
await import_promises2.default.mkdir(import_path2.default.dirname(filepath), { recursive: true });
|
|
4577
|
+
await import_promises2.default.writeFile(filepath, Buffer.from(pngBuffer));
|
|
4578
|
+
}
|
|
4579
|
+
async function expectAnimationSnapshot(renderAndCaptureFrame, options) {
|
|
4580
|
+
const {
|
|
4581
|
+
name,
|
|
4582
|
+
width,
|
|
4583
|
+
height,
|
|
4584
|
+
frameCount,
|
|
4585
|
+
fps = 10,
|
|
4586
|
+
updateBaseline = false,
|
|
4587
|
+
snapshotDir = import_path2.default.join(process.cwd(), "tests", "__snapshots__"),
|
|
4588
|
+
threshold = 0.1,
|
|
4589
|
+
maxDifferencePercent = 0.1
|
|
4590
|
+
} = options;
|
|
4591
|
+
if (!width || !height) {
|
|
4592
|
+
throw new Error("Width and height are required for expectAnimationSnapshot");
|
|
4593
|
+
}
|
|
4594
|
+
const baselinePath = getSnapshotPath(name, "baseline", snapshotDir);
|
|
4595
|
+
const actualPath = getSnapshotPath(name, "actual", snapshotDir);
|
|
4596
|
+
const diffPath = getSnapshotPath(name, "diff", snapshotDir);
|
|
4597
|
+
const alwaysSave = process.env.ALWAYS_SAVE_SNAPSHOTS === "1";
|
|
4598
|
+
const actualFrames = [];
|
|
4599
|
+
for (let i = 0; i < frameCount; i++) {
|
|
4600
|
+
const frameData = await renderAndCaptureFrame(i);
|
|
4601
|
+
if (frameData.length !== width * height * 4) {
|
|
4602
|
+
throw new Error(`Frame ${i} dimension mismatch: expected length ${width * height * 4}, got ${frameData.length}`);
|
|
4603
|
+
}
|
|
4604
|
+
actualFrames.push(frameData);
|
|
4605
|
+
}
|
|
4606
|
+
const delayMs = 1e3 / fps;
|
|
4607
|
+
let baselineFrames = null;
|
|
4608
|
+
let shouldUpdateBaseline = updateBaseline || !(0, import_fs2.existsSync)(baselinePath);
|
|
4609
|
+
if (!shouldUpdateBaseline) {
|
|
4610
|
+
try {
|
|
4611
|
+
const baseline = await loadAPNG(baselinePath);
|
|
4612
|
+
if (baseline.width !== width || baseline.height !== height) {
|
|
4613
|
+
console.warn(`Baseline dimensions mismatch (${baseline.width}x${baseline.height} vs ${width}x${height}). Forcing update.`);
|
|
4614
|
+
shouldUpdateBaseline = true;
|
|
4615
|
+
} else if (baseline.frames.length !== frameCount) {
|
|
4616
|
+
console.warn(`Baseline frame count mismatch (${baseline.frames.length} vs ${frameCount}). Forcing update.`);
|
|
4617
|
+
shouldUpdateBaseline = true;
|
|
4618
|
+
} else {
|
|
4619
|
+
baselineFrames = baseline.frames;
|
|
4620
|
+
}
|
|
4621
|
+
} catch (e) {
|
|
4622
|
+
console.warn(`Failed to load baseline APNG: ${e}. Forcing update.`);
|
|
4623
|
+
shouldUpdateBaseline = true;
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
if (shouldUpdateBaseline) {
|
|
4627
|
+
console.log(`Creating/Updating baseline for ${name} at ${baselinePath}`);
|
|
4628
|
+
await saveAPNG(baselinePath, actualFrames, width, height, delayMs);
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
if (!baselineFrames) {
|
|
4632
|
+
throw new Error("Baseline frames missing despite checks.");
|
|
4633
|
+
}
|
|
4634
|
+
const frameStats = [];
|
|
4635
|
+
const diffFrames = [];
|
|
4636
|
+
let totalDiffPixels = 0;
|
|
4637
|
+
let totalPixels = 0;
|
|
4638
|
+
for (let i = 0; i < frameCount; i++) {
|
|
4639
|
+
const result2 = await compareSnapshots(actualFrames[i], baselineFrames[i], width, height, options);
|
|
4640
|
+
frameStats.push(result2);
|
|
4641
|
+
if (result2.diffImage) {
|
|
4642
|
+
diffFrames.push(result2.diffImage);
|
|
4643
|
+
} else {
|
|
4644
|
+
diffFrames.push(new Uint8ClampedArray(width * height * 4));
|
|
4645
|
+
}
|
|
4646
|
+
totalDiffPixels += result2.pixelsDifferent;
|
|
4647
|
+
totalPixels += width * height;
|
|
4648
|
+
}
|
|
4649
|
+
const avgPercentDifferent = totalDiffPixels / totalPixels * 100;
|
|
4650
|
+
const passed = avgPercentDifferent <= (maxDifferencePercent || 0);
|
|
4651
|
+
const result = {
|
|
4652
|
+
passed,
|
|
4653
|
+
totalPixels,
|
|
4654
|
+
totalDiffPixels,
|
|
4655
|
+
percentDifferent: avgPercentDifferent,
|
|
4656
|
+
frameStats
|
|
4657
|
+
};
|
|
4658
|
+
const statsPath = import_path2.default.join(snapshotDir, "stats", `${name}.json`);
|
|
4659
|
+
await import_promises2.default.mkdir(import_path2.default.dirname(statsPath), { recursive: true });
|
|
4660
|
+
await import_promises2.default.writeFile(statsPath, JSON.stringify({
|
|
4661
|
+
passed: result.passed,
|
|
4662
|
+
percentDifferent: result.percentDifferent,
|
|
4663
|
+
pixelsDifferent: result.totalDiffPixels,
|
|
4664
|
+
totalPixels: result.totalPixels,
|
|
4665
|
+
threshold: options.threshold ?? 0.1,
|
|
4666
|
+
maxDifferencePercent: options.maxDifferencePercent ?? 0.1,
|
|
4667
|
+
frameCount
|
|
4668
|
+
}, null, 2));
|
|
4669
|
+
if (!passed || alwaysSave) {
|
|
4670
|
+
await saveAPNG(actualPath, actualFrames, width, height, delayMs);
|
|
4671
|
+
await saveAPNG(diffPath, diffFrames, width, height, delayMs);
|
|
4672
|
+
}
|
|
4673
|
+
if (!passed) {
|
|
4674
|
+
const failThreshold = 10;
|
|
4675
|
+
const errorMessage = `Animation snapshot comparison failed for ${name}: ${result.percentDifferent.toFixed(2)}% different (${result.totalDiffPixels} pixels total). See ${diffPath} for details.`;
|
|
4676
|
+
if (result.percentDifferent <= failThreshold) {
|
|
4677
|
+
console.warn(`[WARNING] ${errorMessage} (Marked as failed in report but passing test execution due to <${failThreshold}% difference)`);
|
|
4678
|
+
} else {
|
|
4679
|
+
throw new Error(errorMessage);
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
|
|
4550
4684
|
// src/engine/helpers/webgl-playwright.ts
|
|
4551
4685
|
var import_http = require("http");
|
|
4552
|
-
var
|
|
4686
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
4687
|
+
var import_fs3 = __toESM(require("fs"), 1);
|
|
4688
|
+
function findWorkspaceRoot(startDir) {
|
|
4689
|
+
let currentDir = startDir;
|
|
4690
|
+
while (currentDir !== import_path3.default.parse(currentDir).root) {
|
|
4691
|
+
if (import_fs3.default.existsSync(import_path3.default.join(currentDir, "pnpm-workspace.yaml"))) {
|
|
4692
|
+
return currentDir;
|
|
4693
|
+
}
|
|
4694
|
+
currentDir = import_path3.default.dirname(currentDir);
|
|
4695
|
+
}
|
|
4696
|
+
return process.cwd();
|
|
4697
|
+
}
|
|
4553
4698
|
async function createWebGLPlaywrightSetup(options = {}) {
|
|
4554
4699
|
const width = options.width ?? 256;
|
|
4555
4700
|
const height = options.height ?? 256;
|
|
@@ -4568,7 +4713,7 @@ async function createWebGLPlaywrightSetup(options = {}) {
|
|
|
4568
4713
|
} catch (e) {
|
|
4569
4714
|
throw new Error('Failed to load "serve-handler" package. Please ensure it is installed.');
|
|
4570
4715
|
}
|
|
4571
|
-
const repoRoot =
|
|
4716
|
+
const repoRoot = findWorkspaceRoot(__dirname);
|
|
4572
4717
|
const staticServer = (0, import_http.createServer)((request, response) => {
|
|
4573
4718
|
return handler(request, response, {
|
|
4574
4719
|
public: repoRoot,
|
|
@@ -4632,9 +4777,9 @@ async function createWebGLPlaywrightSetup(options = {}) {
|
|
|
4632
4777
|
cleanup
|
|
4633
4778
|
};
|
|
4634
4779
|
}
|
|
4635
|
-
async function renderAndCaptureWebGLPlaywright(page, renderFn, width, height) {
|
|
4780
|
+
async function renderAndCaptureWebGLPlaywright(page, renderFn, width, height, frameIndex = 0) {
|
|
4636
4781
|
try {
|
|
4637
|
-
const pixelData = await page.evaluate(({ code, width: width2, height: height2 }) => {
|
|
4782
|
+
const pixelData = await page.evaluate(({ code, width: width2, height: height2, frameIndex: frameIndex2 }) => {
|
|
4638
4783
|
const renderer = window.testRenderer;
|
|
4639
4784
|
const gl = window.testGl;
|
|
4640
4785
|
const canvas = window.testCanvas;
|
|
@@ -4647,8 +4792,8 @@ async function renderAndCaptureWebGLPlaywright(page, renderFn, width, height) {
|
|
|
4647
4792
|
gl.viewport(0, 0, width2, height2);
|
|
4648
4793
|
}
|
|
4649
4794
|
try {
|
|
4650
|
-
const fn = new Function("renderer", "gl", code);
|
|
4651
|
-
fn(renderer, gl);
|
|
4795
|
+
const fn = new Function("renderer", "gl", "frameIndex", code);
|
|
4796
|
+
fn(renderer, gl, frameIndex2);
|
|
4652
4797
|
} catch (err) {
|
|
4653
4798
|
throw new Error(`Renderer Execution Error: ${err.message}
|
|
4654
4799
|
Code:
|
|
@@ -4656,7 +4801,7 @@ ${code}`);
|
|
|
4656
4801
|
}
|
|
4657
4802
|
gl.finish();
|
|
4658
4803
|
return window.captureCanvas();
|
|
4659
|
-
}, { code: renderFn, width, height });
|
|
4804
|
+
}, { code: renderFn, width, height, frameIndex });
|
|
4660
4805
|
return new Uint8ClampedArray(pixelData);
|
|
4661
4806
|
} catch (err) {
|
|
4662
4807
|
throw new Error(`Browser Test Error: ${err.message}`);
|
|
@@ -4683,6 +4828,33 @@ async function testWebGLRenderer(renderCode, options) {
|
|
|
4683
4828
|
await setup.cleanup();
|
|
4684
4829
|
}
|
|
4685
4830
|
}
|
|
4831
|
+
async function testWebGLAnimation(renderCode, options) {
|
|
4832
|
+
const setup = await createWebGLPlaywrightSetup(options);
|
|
4833
|
+
try {
|
|
4834
|
+
await expectAnimationSnapshot(async (frameIndex) => {
|
|
4835
|
+
return renderAndCaptureWebGLPlaywright(
|
|
4836
|
+
setup.page,
|
|
4837
|
+
renderCode,
|
|
4838
|
+
options.width,
|
|
4839
|
+
options.height,
|
|
4840
|
+
frameIndex
|
|
4841
|
+
);
|
|
4842
|
+
}, {
|
|
4843
|
+
name: options.name,
|
|
4844
|
+
description: options.description,
|
|
4845
|
+
width: setup.width,
|
|
4846
|
+
height: setup.height,
|
|
4847
|
+
frameCount: options.frameCount,
|
|
4848
|
+
fps: options.fps,
|
|
4849
|
+
updateBaseline: options.updateBaseline,
|
|
4850
|
+
snapshotDir: options.snapshotDir,
|
|
4851
|
+
threshold: options.threshold,
|
|
4852
|
+
maxDifferencePercent: options.maxDifferencePercent
|
|
4853
|
+
});
|
|
4854
|
+
} finally {
|
|
4855
|
+
await setup.cleanup();
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4686
4858
|
|
|
4687
4859
|
// src/engine/helpers/pipeline-test-template.ts
|
|
4688
4860
|
var import_vitest18 = require("vitest");
|
|
@@ -5307,139 +5479,6 @@ var verifySmoothing = (states) => {
|
|
|
5307
5479
|
};
|
|
5308
5480
|
};
|
|
5309
5481
|
|
|
5310
|
-
// src/visual/animation-snapshots.ts
|
|
5311
|
-
var import_upng_js = __toESM(require("upng-js"), 1);
|
|
5312
|
-
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
5313
|
-
var import_fs2 = require("fs");
|
|
5314
|
-
var import_path3 = __toESM(require("path"), 1);
|
|
5315
|
-
async function loadAPNG(filepath) {
|
|
5316
|
-
const buffer = await import_promises2.default.readFile(filepath);
|
|
5317
|
-
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
5318
|
-
const img = import_upng_js.default.decode(arrayBuffer);
|
|
5319
|
-
const framesRGBA = import_upng_js.default.toRGBA8(img);
|
|
5320
|
-
const frames = framesRGBA.map((buffer2) => new Uint8ClampedArray(buffer2));
|
|
5321
|
-
return {
|
|
5322
|
-
width: img.width,
|
|
5323
|
-
height: img.height,
|
|
5324
|
-
frames
|
|
5325
|
-
};
|
|
5326
|
-
}
|
|
5327
|
-
async function saveAPNG(filepath, frames, width, height, delayMs) {
|
|
5328
|
-
const buffers = frames.map((f) => {
|
|
5329
|
-
const dst = new ArrayBuffer(f.byteLength);
|
|
5330
|
-
new Uint8ClampedArray(dst).set(f);
|
|
5331
|
-
return dst;
|
|
5332
|
-
});
|
|
5333
|
-
const delays = new Array(frames.length).fill(delayMs);
|
|
5334
|
-
const pngBuffer = import_upng_js.default.encode(buffers, width, height, 0, delays);
|
|
5335
|
-
await import_promises2.default.mkdir(import_path3.default.dirname(filepath), { recursive: true });
|
|
5336
|
-
await import_promises2.default.writeFile(filepath, Buffer.from(pngBuffer));
|
|
5337
|
-
}
|
|
5338
|
-
async function expectAnimationSnapshot(renderAndCaptureFrame, options) {
|
|
5339
|
-
const {
|
|
5340
|
-
name,
|
|
5341
|
-
width,
|
|
5342
|
-
height,
|
|
5343
|
-
frameCount,
|
|
5344
|
-
fps = 10,
|
|
5345
|
-
updateBaseline = false,
|
|
5346
|
-
snapshotDir = import_path3.default.join(process.cwd(), "tests", "__snapshots__"),
|
|
5347
|
-
threshold = 0.1,
|
|
5348
|
-
maxDifferencePercent = 0.1
|
|
5349
|
-
} = options;
|
|
5350
|
-
if (!width || !height) {
|
|
5351
|
-
throw new Error("Width and height are required for expectAnimationSnapshot");
|
|
5352
|
-
}
|
|
5353
|
-
const baselinePath = getSnapshotPath(name, "baseline", snapshotDir);
|
|
5354
|
-
const actualPath = getSnapshotPath(name, "actual", snapshotDir);
|
|
5355
|
-
const diffPath = getSnapshotPath(name, "diff", snapshotDir);
|
|
5356
|
-
const alwaysSave = process.env.ALWAYS_SAVE_SNAPSHOTS === "1";
|
|
5357
|
-
const actualFrames = [];
|
|
5358
|
-
for (let i = 0; i < frameCount; i++) {
|
|
5359
|
-
const frameData = await renderAndCaptureFrame(i);
|
|
5360
|
-
if (frameData.length !== width * height * 4) {
|
|
5361
|
-
throw new Error(`Frame ${i} dimension mismatch: expected length ${width * height * 4}, got ${frameData.length}`);
|
|
5362
|
-
}
|
|
5363
|
-
actualFrames.push(frameData);
|
|
5364
|
-
}
|
|
5365
|
-
const delayMs = 1e3 / fps;
|
|
5366
|
-
let baselineFrames = null;
|
|
5367
|
-
let shouldUpdateBaseline = updateBaseline || !(0, import_fs2.existsSync)(baselinePath);
|
|
5368
|
-
if (!shouldUpdateBaseline) {
|
|
5369
|
-
try {
|
|
5370
|
-
const baseline = await loadAPNG(baselinePath);
|
|
5371
|
-
if (baseline.width !== width || baseline.height !== height) {
|
|
5372
|
-
console.warn(`Baseline dimensions mismatch (${baseline.width}x${baseline.height} vs ${width}x${height}). Forcing update.`);
|
|
5373
|
-
shouldUpdateBaseline = true;
|
|
5374
|
-
} else if (baseline.frames.length !== frameCount) {
|
|
5375
|
-
console.warn(`Baseline frame count mismatch (${baseline.frames.length} vs ${frameCount}). Forcing update.`);
|
|
5376
|
-
shouldUpdateBaseline = true;
|
|
5377
|
-
} else {
|
|
5378
|
-
baselineFrames = baseline.frames;
|
|
5379
|
-
}
|
|
5380
|
-
} catch (e) {
|
|
5381
|
-
console.warn(`Failed to load baseline APNG: ${e}. Forcing update.`);
|
|
5382
|
-
shouldUpdateBaseline = true;
|
|
5383
|
-
}
|
|
5384
|
-
}
|
|
5385
|
-
if (shouldUpdateBaseline) {
|
|
5386
|
-
console.log(`Creating/Updating baseline for ${name} at ${baselinePath}`);
|
|
5387
|
-
await saveAPNG(baselinePath, actualFrames, width, height, delayMs);
|
|
5388
|
-
return;
|
|
5389
|
-
}
|
|
5390
|
-
if (!baselineFrames) {
|
|
5391
|
-
throw new Error("Baseline frames missing despite checks.");
|
|
5392
|
-
}
|
|
5393
|
-
const frameStats = [];
|
|
5394
|
-
const diffFrames = [];
|
|
5395
|
-
let totalDiffPixels = 0;
|
|
5396
|
-
let totalPixels = 0;
|
|
5397
|
-
for (let i = 0; i < frameCount; i++) {
|
|
5398
|
-
const result2 = await compareSnapshots(actualFrames[i], baselineFrames[i], width, height, options);
|
|
5399
|
-
frameStats.push(result2);
|
|
5400
|
-
if (result2.diffImage) {
|
|
5401
|
-
diffFrames.push(result2.diffImage);
|
|
5402
|
-
} else {
|
|
5403
|
-
diffFrames.push(new Uint8ClampedArray(width * height * 4));
|
|
5404
|
-
}
|
|
5405
|
-
totalDiffPixels += result2.pixelsDifferent;
|
|
5406
|
-
totalPixels += width * height;
|
|
5407
|
-
}
|
|
5408
|
-
const avgPercentDifferent = totalDiffPixels / totalPixels * 100;
|
|
5409
|
-
const passed = avgPercentDifferent <= (maxDifferencePercent || 0);
|
|
5410
|
-
const result = {
|
|
5411
|
-
passed,
|
|
5412
|
-
totalPixels,
|
|
5413
|
-
totalDiffPixels,
|
|
5414
|
-
percentDifferent: avgPercentDifferent,
|
|
5415
|
-
frameStats
|
|
5416
|
-
};
|
|
5417
|
-
const statsPath = import_path3.default.join(snapshotDir, "stats", `${name}.json`);
|
|
5418
|
-
await import_promises2.default.mkdir(import_path3.default.dirname(statsPath), { recursive: true });
|
|
5419
|
-
await import_promises2.default.writeFile(statsPath, JSON.stringify({
|
|
5420
|
-
passed: result.passed,
|
|
5421
|
-
percentDifferent: result.percentDifferent,
|
|
5422
|
-
pixelsDifferent: result.totalDiffPixels,
|
|
5423
|
-
totalPixels: result.totalPixels,
|
|
5424
|
-
threshold: options.threshold ?? 0.1,
|
|
5425
|
-
maxDifferencePercent: options.maxDifferencePercent ?? 0.1,
|
|
5426
|
-
frameCount
|
|
5427
|
-
}, null, 2));
|
|
5428
|
-
if (!passed || alwaysSave) {
|
|
5429
|
-
await saveAPNG(actualPath, actualFrames, width, height, delayMs);
|
|
5430
|
-
await saveAPNG(diffPath, diffFrames, width, height, delayMs);
|
|
5431
|
-
}
|
|
5432
|
-
if (!passed) {
|
|
5433
|
-
const failThreshold = 10;
|
|
5434
|
-
const errorMessage = `Animation snapshot comparison failed for ${name}: ${result.percentDifferent.toFixed(2)}% different (${result.totalDiffPixels} pixels total). See ${diffPath} for details.`;
|
|
5435
|
-
if (result.percentDifferent <= failThreshold) {
|
|
5436
|
-
console.warn(`[WARNING] ${errorMessage} (Marked as failed in report but passing test execution due to <${failThreshold}% difference)`);
|
|
5437
|
-
} else {
|
|
5438
|
-
throw new Error(errorMessage);
|
|
5439
|
-
}
|
|
5440
|
-
}
|
|
5441
|
-
}
|
|
5442
|
-
|
|
5443
5482
|
// src/e2e/playwright.ts
|
|
5444
5483
|
async function createPlaywrightTestClient(options = {}) {
|
|
5445
5484
|
let playwright;
|
|
@@ -5853,6 +5892,7 @@ function createVisualTestScenario(sceneName) {
|
|
|
5853
5892
|
teardownNodeEnvironment,
|
|
5854
5893
|
testComputeShader,
|
|
5855
5894
|
testPipelineRendering,
|
|
5895
|
+
testWebGLAnimation,
|
|
5856
5896
|
testWebGLRenderer,
|
|
5857
5897
|
throttleBandwidth,
|
|
5858
5898
|
verifySmoothing,
|