Files
dsh-web/image/pdf-compat.test.mjs
T

48 lines
3.4 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { runInNewContext } from 'node:vm';
import { compat, patchPdf, workerTarget, marker } from './install-pdf-compat.mjs';
function run(code) {
return runInNewContext('delete Math.sumPrecise; delete Map.prototype.getOrInsert; delete Map.prototype.getOrInsertComputed;\n' + compat + '\n' + code);
}
test('precise summation handles cancellation, rounding, overflow, subnormals and special values', () => {
assert.equal(run('Math.sumPrecise([1e20, 1, -1e20])'), 1);
assert.equal(run('Math.sumPrecise([Number.MAX_VALUE, Number.MAX_VALUE, -Number.MAX_VALUE])'), Number.MAX_VALUE);
assert.equal(run('Math.sumPrecise([Number.MAX_VALUE, Number.MAX_VALUE])'), Infinity);
assert.equal(run('Math.sumPrecise([Number.MIN_VALUE, Number.MIN_VALUE])'), 2 * Number.MIN_VALUE);
assert.equal(run('Math.sumPrecise([1, 2 ** -53])'), 1);
assert.equal(run('Math.sumPrecise([1, 2 ** -53, 2 ** -54])'), 1 + Number.EPSILON);
assert.ok(Object.is(run('Math.sumPrecise([])'), -0));
assert.ok(Object.is(run('Math.sumPrecise([-0])'), -0));
assert.ok(Object.is(run('Math.sumPrecise([-1, 1])'), 0));
assert.ok(Number.isNaN(run('Math.sumPrecise([Infinity, -Infinity])')));
assert.throws(() => run('Math.sumPrecise([Infinity, "1"])'), /numbers/);
});
test('computed insertion caches undefined and promises without recomputation', () => {
assert.equal(run('const m = new Map(); let n = 0; m.getOrInsertComputed("x", () => { n++; }); m.getOrInsertComputed("x", () => { n++; }); n'), 1);
assert.equal(run('const m = new Map(); const p = Promise.resolve(1); m.getOrInsertComputed("x", () => p) === m.getOrInsertComputed("x", () => 2)'), true);
});
test('callback validation, errors, receiver, key normalization and reentrancy', () => {
assert.throws(() => run('new Map([[1, 2]]).getOrInsertComputed(1, null)'), /callable/);
assert.throws(() => run('Map.prototype.getOrInsertComputed.call({}, 1, () => 2)'), /incompatible receiver/);
assert.equal(run('new Map().getOrInsertComputed(-0, k => Object.is(k, 0))'), true);
assert.equal(run('const m = new Map(); m.getOrInsertComputed(1, k => { m.set(k, 2); return 3; }); m.get(1)'), 3);
assert.equal(run('const m = new Map(); try { m.getOrInsertComputed(1, () => { throw Error(); }); } catch {} m.size'), 0);
assert.equal(run('new Map([[NaN, 4]]).getOrInsertComputed(NaN, () => 5)'), 4);
});
test('plain insertion and non-enumerable, repeat-safe methods', () => {
assert.equal(run('const m = new Map([[1, undefined]]); m.getOrInsert(1, 2) === undefined && m.getOrInsert(2, 3) === 3'), true);
assert.equal(run('Object.getOwnPropertyDescriptor(Map.prototype, "getOrInsertComputed").enumerable'), false);
assert.equal(runInNewContext('Map.prototype.getOrInsertComputed = () => 42;\n' + compat + '\nnew Map().getOrInsertComputed()'), 42);
});
test('patch guards and worker injection', () => {
const source = 'this.#methodPromises.getOrInsertComputed(name, f); ' + workerTarget + ' tail]);';
const patched = patchPdf(source, '0.2.1-rc.2', '0.2.1-rc.2');
assert.ok(patched.startsWith(marker + '\n' + compat));
assert.ok(patched.includes('new Blob([' + JSON.stringify(compat) + ', _dsh_pdf_worker_default,'));
assert.throws(() => patchPdf(patched, '0.2.1-rc.2', '0.2.1-rc.2'), /already/);
assert.throws(() => patchPdf(source, 'other', '0.2.1-rc.2'), /version/);
for (const bad of ['', source + workerTarget]) assert.throws(() => patchPdf(bad, '0.2.1-rc.2', '0.2.1-rc.2'), /unexpected/);
});