Files
juzi/taobao/test/camera-image-export.test.cjs
T
xsl 774376fa16 feat: 完善淘宝小程序拍照、报告与登录流程
接入本地实时识别和自动三连拍,调整报告裁切、商品跳转、分享标题及居中页眉;完善游客会话、后台渠道隔离、部署脚本与回归测试。
2026-11-04 15:07:51 +08:00

88 lines
5.2 KiB
JavaScript

'use strict';
const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs'), path = require('node:path'), vm = require('node:vm');
// Synthetic JPEG marker bytes only; no real photograph or service is involved.
const jpeg = '/9j/2Q==', dataURL = 'data:image/jpeg;base64,' + jpeg;
function setup(result, options = {}) {
const writes = [], draws = [], calls = [];
const platform = {
getImageInfo(options) { options.success({ width: 2, height: 1 }); },
createCanvasContext(id) {
assert.equal(id, 'compress-canvas');
const context = { clearRect() {}, drawImage(src) { draws.push(src); }, draw(reserve, complete) { complete(); },
toDataURL(value) {
calls.push(value);
if (options.nativePromiseFailure) { value.fail({ errorMessage: '原生 JPEG 导出失败' }); return Promise.reject(Error('原生 JPEG 导出失败')); }
if (options.promiseOnly) return Promise.resolve(result);
value.success(result); if (options.dualPromise) return Promise.resolve(result);
} };
if (options.fileExport) context.toTempFilePath = value => { calls.push(value); value.success({ apFilePath: 'PRIVATE_TEMP_JPEG_PATH' }); };
return context;
},
getFileSystemManager: () => ({ readFile(value) { if (options.readFailure) value.fail({ errorMessage: '读取失败' }); else value.success({ data: result }); } }),
canvasPutImageData(options) { writes.push(options); options.success(); }
};
const module = { exports: {} };
vm.runInNewContext(fs.readFileSync(path.resolve(__dirname, '../utils/image.js'), 'utf8'), {
module, my: platform, Promise, setTimeout, clearTimeout, ArrayBuffer, Uint8Array, Uint8ClampedArray
});
const page = { alive: true, setData(value, complete) { complete(); } };
return { image: module.exports, page, writes, draws, calls };
}
test('legacy/native Canvas file export validates actual JPEG bytes rather than trusting a requested file type', async () => {
const fixture = setup(jpeg, { fileExport: true });
assert.equal(await fixture.image.compressToBase64('native-takePhoto-path', fixture.page), dataURL);
assert.deepEqual(fixture.draws, ['native-takePhoto-path']); assert.equal(fixture.calls[0].fileType, 'jpg');
for (const value of ['iVBORw0KGgo=', '/9j/invalid', 'data:image/png;base64,' + jpeg]) {
const bad = setup(value, { fileExport: true });
await assert.rejects(bad.image.compressToBase64('photo', bad.page), /JPEG 转换失败/);
}
const empty = setup('', { fileExport: true }); await assert.rejects(empty.image.compressToBase64('photo', empty.page), /内容为空/);
const readFailure = setup(jpeg, { fileExport: true, readFailure: true }); await assert.rejects(readFailure.image.compressToBase64('photo', readFailure.page), /读取失败/);
});
function exportImage(fixture, kind) {
if (kind === 'compression') return fixture.image.compressToBase64('synthetic-photo', fixture.page);
return fixture.image.captureFrameToBase64({ width: 2, height: 1, pixelFormat: 'RGBA',
capturedImage: new Uint8Array([10, 20, 30, 255, 40, 50, 60, 255]).buffer }, fixture.page);
}
for (const kind of ['compression', 'raw AR frame']) {
test(kind + ' handles native legacy Canvas Promise and callback exports without duplicate completion or unhandled rejection', async () => {
for (const options of [{ promiseOnly: true }, { dualPromise: true }]) {
const fixture = setup(dataURL, options); assert.equal(await exportImage(fixture, kind), dataURL); assert.equal(fixture.calls.length, 1);
}
const failure = setup(null, { nativePromiseFailure: true });
await assert.rejects(exportImage(failure, kind), /原生 JPEG 导出失败/);
await new Promise(resolve => setImmediate(resolve));
});
test(kind + ' accepts Taobao string and documented native wrapper shapes', async () => {
const results = [dataURL, jpeg, { dataURL }, { dataUrl: dataURL }, { base64: dataURL }, { base64: jpeg }];
for (const result of results) {
const fixture = setup(result);
assert.equal(await exportImage(fixture, kind), dataURL);
assert.equal(fixture.calls.length, 1);
assert.equal(fixture.calls[0].fileType, 'jpg');
assert.equal(fixture.calls[0].quality, .88);
if (kind === 'compression') assert.deepEqual(fixture.draws, ['synthetic-photo']);
else {
assert.equal(fixture.writes.length, 1);
assert.deepEqual(Array.from(fixture.writes[0].data), [10, 20, 30, 255, 40, 50, 60, 255]);
}
}
});
test(kind + ' rejects empty, malformed, unknown and non-JPEG export responses', async () => {
const invalid = [null, undefined, '', {}, { data: dataURL }, { dataURL: jpeg }, { dataUrl: jpeg },
{ base64: '' }, 'data:image/jpeg;base64,', { dataURL: 'data:image/jpeg;base64,' },
'data:image/png;base64,/9j/2Q==', { base64: 'data:image/png;base64,/9j/2Q==' },
'iVBORw0KGgo=', { base64: 'iVBORw0KGgo=' },
'data:image/jpeg;base64,iVBORw0KGgo=', '/9j/frame', '/9j/2Q=', '/9j/2Q== ', '/9j/2_==',
{ dataURL: 'https://example.invalid/photo.jpg' }];
for (const result of invalid) {
const fixture = setup(result);
await assert.rejects(exportImage(fixture, kind), /JPEG 转换失败/, JSON.stringify(result));
assert.equal(fixture.calls.length, 1);
}
});
}