fix snapshot for the umpteenth time, now with custom line widgets
This commit is contained in:
136
frontend/tests/workflowCapture.test.mjs
Normal file
136
frontend/tests/workflowCapture.test.mjs
Normal file
@@ -0,0 +1,136 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { OVERLAY_CAPTURE_SELECTORS, captureViewportBlob } from '../src/workflowCapture.js';
|
||||
|
||||
function makeElement(name, { tagName = 'DIV', width = 160, height = 90, src = '' } = {}) {
|
||||
return {
|
||||
name,
|
||||
tagName,
|
||||
src,
|
||||
currentSrc: src,
|
||||
complete: true,
|
||||
naturalWidth: 1,
|
||||
clientWidth: width,
|
||||
clientHeight: height,
|
||||
parentNode: null,
|
||||
getBoundingClientRect() {
|
||||
return { width, height };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function makeParent(initialChild) {
|
||||
return {
|
||||
currentChild: initialChild,
|
||||
replaceChild(nextChild, prevChild) {
|
||||
assert.equal(this.currentChild, prevChild);
|
||||
this.currentChild = nextChild;
|
||||
nextChild.parentNode = this;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function makeViewport({ overlay, image, canvas }) {
|
||||
return {
|
||||
querySelectorAll(selector) {
|
||||
if (selector === '.lineplot-overlay') {
|
||||
return overlay.parentNode?.currentChild === overlay ? [overlay] : [];
|
||||
}
|
||||
if (selector === '.cs-overlay' || selector === '.crop-overlay') {
|
||||
return [];
|
||||
}
|
||||
if (selector === 'img') {
|
||||
return image.parentNode?.currentChild === image ? [image] : [];
|
||||
}
|
||||
if (selector === 'canvas') {
|
||||
return canvas.parentNode?.currentChild === canvas ? [canvas] : [];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('captureViewportBlob rasterizes custom overlays before the final workflow snapshot', async () => {
|
||||
const overlay = makeElement('overlay', { width: 320, height: 220 });
|
||||
const image = makeElement('image', { tagName: 'IMG', width: 180, height: 120, src: 'data:image/png;base64,abc' });
|
||||
const canvas = makeElement('canvas', { tagName: 'CANVAS', width: 128, height: 128 });
|
||||
canvas.toDataURL = () => 'data:image/png;base64,canvas';
|
||||
|
||||
const overlayParent = makeParent(overlay);
|
||||
const imageParent = makeParent(image);
|
||||
const canvasParent = makeParent(canvas);
|
||||
overlay.parentNode = overlayParent;
|
||||
image.parentNode = imageParent;
|
||||
canvas.parentNode = canvasParent;
|
||||
|
||||
const selectorsSeen = [];
|
||||
const viewport = makeViewport({ overlay, image, canvas });
|
||||
const blob = await captureViewportBlob(viewport, { width: 800, height: 600 }, {
|
||||
queryAll(root, selector) {
|
||||
selectorsSeen.push(selector);
|
||||
return root.querySelectorAll(selector);
|
||||
},
|
||||
waitForImageElement: async () => {},
|
||||
renderOverlayToDataUrl: async (el) => `data:image/png;base64,overlay-${el.name}`,
|
||||
renderImageToDataUrl: async (el) => `data:image/png;base64,image-${el.name}`,
|
||||
renderCanvasToDataUrl: async (el) => `data:image/png;base64,canvas-${el.name}`,
|
||||
createPlaceholder: (el, dataUrl) => ({ placeholderFor: el.name, dataUrl, parentNode: null }),
|
||||
nextFrame: async () => {},
|
||||
toBlobImpl: async (target) => {
|
||||
if (target === viewport) {
|
||||
assert.notEqual(overlayParent.currentChild, overlay);
|
||||
assert.notEqual(imageParent.currentChild, image);
|
||||
assert.notEqual(canvasParent.currentChild, canvas);
|
||||
return new Blob(['viewport'], { type: 'image/png' });
|
||||
}
|
||||
throw new Error('Unexpected element capture path');
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(await blob.text(), 'viewport');
|
||||
assert.equal(overlayParent.currentChild, overlay);
|
||||
assert.equal(imageParent.currentChild, image);
|
||||
assert.equal(canvasParent.currentChild, canvas);
|
||||
assert.deepEqual(
|
||||
selectorsSeen.filter((selector) => OVERLAY_CAPTURE_SELECTORS.includes(selector)),
|
||||
OVERLAY_CAPTURE_SELECTORS,
|
||||
);
|
||||
});
|
||||
|
||||
test('captureViewportBlob restores live elements when final capture fails', async () => {
|
||||
const overlay = makeElement('overlay', { width: 320, height: 220 });
|
||||
const image = makeElement('image', { tagName: 'IMG', width: 180, height: 120, src: 'data:image/png;base64,abc' });
|
||||
const canvas = makeElement('canvas', { tagName: 'CANVAS', width: 128, height: 128 });
|
||||
canvas.toDataURL = () => 'data:image/png;base64,canvas';
|
||||
|
||||
const overlayParent = makeParent(overlay);
|
||||
const imageParent = makeParent(image);
|
||||
const canvasParent = makeParent(canvas);
|
||||
overlay.parentNode = overlayParent;
|
||||
image.parentNode = imageParent;
|
||||
canvas.parentNode = canvasParent;
|
||||
|
||||
const viewport = makeViewport({ overlay, image, canvas });
|
||||
|
||||
await assert.rejects(
|
||||
captureViewportBlob(viewport, { width: 800, height: 600 }, {
|
||||
queryAll: (root, selector) => root.querySelectorAll(selector),
|
||||
waitForImageElement: async () => {},
|
||||
renderOverlayToDataUrl: async () => 'data:image/png;base64,overlay',
|
||||
renderImageToDataUrl: async () => 'data:image/png;base64,image',
|
||||
renderCanvasToDataUrl: async () => 'data:image/png;base64,canvas',
|
||||
createPlaceholder: (el) => ({ placeholderFor: el.name, parentNode: null }),
|
||||
nextFrame: async () => {},
|
||||
toBlobImpl: async (target) => {
|
||||
if (target === viewport) throw new Error('capture failed');
|
||||
return new Blob(['unexpected'], { type: 'image/png' });
|
||||
},
|
||||
}),
|
||||
/capture failed/,
|
||||
);
|
||||
|
||||
assert.equal(overlayParent.currentChild, overlay);
|
||||
assert.equal(imageParent.currentChild, image);
|
||||
assert.equal(canvasParent.currentChild, canvas);
|
||||
});
|
||||
Reference in New Issue
Block a user