问题:frameFileName 只校验下界(frameIndex > 0),未校验上界(frameIndex <= frameCount)。
结果:会安静产出指向不存在文件的名字(如 frameFileName('00014.png', 999, 12) → '00014_999.png'),
一路流进中间描述,直到 Cocos 加载资源才报错。
这是「把读不出来当成没问题」这一形态的第四次出现:
- Task 2: readPngSize 返回 null 被当成校验通过
- 前期: ??: 缺顶层键当成空集合
- 前轮: frameIndex 任何 falsy 当成帧 1
- 本轮: frameFileName 超界当成合法产出垃圾名字
修法:加上界检查 `normalized > frameCount`,错误信息对齐 frameRect 格式。
新增 1 条测试用例:frameFileName('00014.png', 13, 12) 应抛错。
顺手补齐 frameFileName 的 undefined/NaN 测试的消息断言(与 frameRect 对齐)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
96 lines
4.1 KiB
JavaScript
96 lines
4.1 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { frameRect, padWidth, frameFileName, frameNames } from '../ui-migration/frames.mjs';
|
|
|
|
// 实测样例:00014.png 450x280,网格 3x4,单帧 150x70
|
|
const IMG_00014 = { id: 14, w_all: 450, h_all: 280, w: 3, h: 4, frame_all: 12, bmp: '00014.png', w1: 150, h1: 70 };
|
|
// 13 列 1 行
|
|
const IMG_00019 = { id: 19, w_all: 221, h_all: 28, w: 13, h: 1, frame_all: 13, bmp: '00019.png', w1: 17, h1: 28 };
|
|
const IMG_SINGLE = { id: 1, w_all: 1280, h_all: 720, w: 1, h: 1, frame_all: 1, bmp: '00001.png', w1: 1280, h1: 720 };
|
|
|
|
test('frameRect 帧 1 是左上角(1 基)', () => {
|
|
assert.deepEqual(frameRect(IMG_00014, 1), { x: 0, y: 0, width: 150, height: 70 });
|
|
});
|
|
|
|
test('frameRect 行优先:帧 4 换到第二行首列', () => {
|
|
// idx=3 → col = 3%3 = 0, row = 1
|
|
assert.deepEqual(frameRect(IMG_00014, 4), { x: 0, y: 70, width: 150, height: 70 });
|
|
});
|
|
|
|
test('frameRect 实证样例:「通知」FrameIndex=10 → 第 3 行第 0 列', () => {
|
|
// 已由读图验证:00014.png 第 3 行第 0 列即「通知」
|
|
assert.deepEqual(frameRect(IMG_00014, 10), { x: 0, y: 210, width: 150, height: 70 });
|
|
});
|
|
|
|
test('frameRect 实证样例:「设置」FrameIndex=11 → 第 3 行第 1 列', () => {
|
|
assert.deepEqual(frameRect(IMG_00014, 11), { x: 150, y: 210, width: 150, height: 70 });
|
|
});
|
|
|
|
test('frameRect 实证样例:「战绩」FrameIndex=4 → 第 1 行第 0 列', () => {
|
|
assert.deepEqual(frameRect(IMG_00014, 4), { x: 0, y: 70, width: 150, height: 70 });
|
|
});
|
|
|
|
test('frameRect 单行网格逐列推进', () => {
|
|
assert.deepEqual(frameRect(IMG_00019, 1), { x: 0, y: 0, width: 17, height: 28 });
|
|
assert.deepEqual(frameRect(IMG_00019, 13), { x: 204, y: 0, width: 17, height: 28 });
|
|
});
|
|
|
|
test('frameRect FrameIndex=0 按帧 1 处理', () => {
|
|
assert.deepEqual(frameRect(IMG_00014, 0), frameRect(IMG_00014, 1));
|
|
});
|
|
|
|
test('frameRect 越界抛 RangeError 且带图名', () => {
|
|
assert.throws(() => frameRect(IMG_00014, 13), RangeError);
|
|
assert.throws(() => frameRect(IMG_00014, 13), /00014\.png/);
|
|
});
|
|
|
|
test('frameRect undefined 作为 frameIndex 抛 RangeError', () => {
|
|
assert.throws(() => frameRect(IMG_00014, undefined), RangeError);
|
|
assert.throws(() => frameRect(IMG_00014, undefined), /00014\.png/);
|
|
});
|
|
|
|
test('frameRect NaN 作为 frameIndex 抛 RangeError', () => {
|
|
assert.throws(() => frameRect(IMG_00014, NaN), RangeError);
|
|
assert.throws(() => frameRect(IMG_00014, NaN), /00014\.png/);
|
|
});
|
|
|
|
test('padWidth 按帧总数决定补零位数', () => {
|
|
assert.equal(padWidth(9), 1);
|
|
assert.equal(padWidth(12), 2);
|
|
assert.equal(padWidth(100), 3);
|
|
});
|
|
|
|
test('frameFileName 1 基且补零,保证字典序等于帧序', () => {
|
|
assert.equal(frameFileName('00014.png', 1, 12), '00014_01.png');
|
|
assert.equal(frameFileName('00014.png', 10, 12), '00014_10.png');
|
|
const names = [frameFileName('00014.png', 2, 12), frameFileName('00014.png', 10, 12)];
|
|
assert.deepEqual([...names].sort(), ['00014_02.png', '00014_10.png'], '字典序必须等于帧序');
|
|
});
|
|
|
|
test('frameFileName 单帧图原样返回', () => {
|
|
assert.equal(frameFileName('00001.png', 1, 1), '00001.png');
|
|
});
|
|
|
|
test('frameFileName undefined 作为 frameIndex 抛 RangeError', () => {
|
|
assert.throws(() => frameFileName('00014.png', undefined, 12), RangeError);
|
|
assert.throws(() => frameFileName('00014.png', undefined, 12), /00014\.png/);
|
|
});
|
|
|
|
test('frameFileName NaN 作为 frameIndex 抛 RangeError', () => {
|
|
assert.throws(() => frameFileName('00014.png', NaN, 12), RangeError);
|
|
assert.throws(() => frameFileName('00014.png', NaN, 12), /00014\.png/);
|
|
});
|
|
|
|
test('frameFileName 越界抛 RangeError 且带图名', () => {
|
|
assert.throws(() => frameFileName('00014.png', 13, 12), RangeError);
|
|
assert.throws(() => frameFileName('00014.png', 13, 12), /00014\.png/);
|
|
});
|
|
|
|
test('frameNames 返回全部帧,下标 0 对应帧 1', () => {
|
|
const names = frameNames(IMG_00014);
|
|
assert.equal(names.length, 12);
|
|
assert.equal(names[0], '00014_01.png');
|
|
assert.equal(names[11], '00014_12.png');
|
|
assert.deepEqual(frameNames(IMG_SINGLE), ['00001.png']);
|
|
});
|