Files

137 lines
3.8 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
applyMaskToImageData,
convertImageData,
loadImage,
prepareImageForCanvas,
tensorToImageData,
validateImageData,
} from '../js/media/image_utils.js';
import { ErrorTypes } from '../js/shared/error_handler.js';
test('image validation accepts tensor-like payloads and normalizes array data', () => {
const payload = [{
shape: [1, 1, 1, 3],
data: [0.1, 0.2, 0.3],
}];
assert.equal(validateImageData(payload), true);
assert.ok(payload[0].data instanceof Float32Array);
assert.equal(validateImageData(null), false);
assert.equal(validateImageData({ shape: [1, 1, 1, 3] }), false);
});
test('image conversion maps RGB values and mask values to RGBA bytes', () => {
const image = convertImageData({
shape: [1, 1, 2, 3],
data: new Float32Array([0, 0.5, 1, 1, 0, 0]),
});
assert.equal(image.width, 2);
assert.equal(image.height, 1);
assert.deepEqual([...image.data], [0, 128, 255, 255, 255, 0, 0, 255]);
const masked = applyMaskToImageData(image, {
shape: [1, 1, 2, 1],
data: new Float32Array([0, 0.5]),
});
assert.deepEqual([...masked.data], [0, 128, 255, 0, 255, 0, 0, 128]);
});
test('prepareImageForCanvas returns pixels and reports validation errors', async () => {
const result = await prepareImageForCanvas({
shape: [1, 1, 1, 3],
data: new Float32Array([0.25, 0.5, 0.75]),
});
assert.deepEqual([...result.data], [64, 128, 191, 255]);
await assert.rejects(
() => prepareImageForCanvas(null),
error => error.type === ErrorTypes.VALIDATION && error.message === 'Invalid input image format'
);
});
test('convertImageData and prepareImageForCanvas preserve identical RGB conversion semantics', async () => {
const tensor = {
shape: [1, 1, 2, 3],
data: new Float32Array([-0.1, 0.5, 1.2, 0.25, 0.5, 0.75]),
};
const converted = convertImageData(tensor);
const prepared = await prepareImageForCanvas([tensor]);
assert.equal(converted.width, prepared.width);
assert.equal(converted.height, prepared.height);
assert.deepEqual([...converted.data], [...prepared.data]);
});
test('tensorToImageData supports grayscale masks without a browser canvas', () => {
const originalImageData = globalThis.ImageData;
globalThis.ImageData = class {
constructor(width, height) {
this.width = width;
this.height = height;
this.data = new Uint8ClampedArray(width * height * 4);
}
};
try {
const imageData = tensorToImageData({
shape: [1, 1, 2, 1],
data: new Float32Array([0, 1]),
}, 'grayscale');
assert.deepEqual([...imageData.data], [0, 0, 0, 255, 255, 255, 255, 255]);
} finally {
if (originalImageData === undefined) {
delete globalThis.ImageData;
} else {
globalThis.ImageData = originalImageData;
}
}
});
test('image loading resolves loaded images and preserves load errors', async () => {
const originalImage = globalThis.Image;
globalThis.Image = class {
constructor() {
this.onload = null;
this.onerror = null;
this.width = 4;
this.height = 3;
}
set src(value) {
this.source = value;
queueMicrotask(() => {
if (value === 'bad-image') {
this.onerror?.(new Error('image decode failed'));
} else {
this.onload?.();
}
});
}
};
try {
const image = await loadImage('good-image');
assert.equal(image.source, 'good-image');
assert.equal(image.width, 4);
assert.equal(image.height, 3);
await assert.rejects(
() => loadImage('bad-image'),
error => error.message === 'image decode failed'
);
} finally {
if (originalImage === undefined) {
delete globalThis.Image;
} else {
globalThis.Image = originalImage;
}
}
});