Files
ark-stronghold-protocol/test/ui/assetUrls.test.js
T
sganggs 01a44cd288 卫戍协议:盟约 web remake — initial import
Online co-op browser remake of Arknights' Stronghold Protocol: Alliance
(client-simulated combat, official waves/bonds/items/strategies, 3D board,
facing wheel, operator loadout, official emotes, deployment scripts).
2026-09-29 16:21:31 +08:00

78 lines
4.0 KiB
JavaScript

// Asset URL helpers (public/js/ui/assetUrls.js) against the real manifest: every visible chess resolves its
// avatar and portrait (normal + golden), bonds / bands / items / enemies / factions / titles resolve, and
// nothing outside the manifest is ever returned.
import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
uiUrl, chessAvatarUrl, chessPortraitUrl, skillIconUrl, profIconUrl, subProfIconUrl, bondIconUrl, bandIconUrl, itemIconUrl,
enemyIconUrl, tokenAvatarUrl, factionIconUrl, titleIconUrl, effectIconUrl,
} from '../../public/js/ui/assetUrls.js';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const load = (f) => JSON.parse(readFileSync(path.join(ROOT, 'data', f), 'utf8'));
const m = load('assets.json');
const chess = load('chess.json');
function manifestUrls(obj, out = new Set()) {
if (typeof obj === 'string' && obj.startsWith('/assets/')) out.add(obj);
else if (obj && typeof obj === 'object') for (const v of Object.values(obj)) manifestUrls(v, out);
return out;
}
const ALL = manifestUrls(m);
const inManifest = (u) => u == null || ALL.has(u);
describe('assetUrls', () => {
test('every visible chess (normal + golden) has avatar, portrait and skill icon', () => {
let n = 0;
for (const c of Object.values(chess)) {
if (!c.charId || c.isDiy) continue;
const a = chessAvatarUrl(m, c);
const p = chessPortraitUrl(m, c);
assert.ok(a && inManifest(a), `avatar ${c.chessId}`);
assert.ok(p && inManifest(p), `portrait ${c.chessId}`);
assert.ok(inManifest(skillIconUrl(m, c)), `skill ${c.chessId}`);
assert.ok(inManifest(profIconUrl(m, c.profession)));
assert.ok(inManifest(subProfIconUrl(m, c)));
n++;
}
assert.ok(n >= 200);
const golden = Object.values(chess).find((c) => c.isGolden && m.chars[c.charId]?.avatarE2);
assert.equal(chessAvatarUrl(m, golden), m.chars[golden.charId].avatarE2, 'golden uses E2 art');
});
test('bonds, bands, items, enemies, tokens, factions, titles, effects', () => {
for (const id of Object.keys(load('bonds.json'))) assert.ok(bondIconUrl(m, id), id);
for (const id of Object.keys(load('bands.json'))) assert.ok(bandIconUrl(m, id), id);
for (const it of Object.values(load('items.json'))) assert.ok(inManifest(itemIconUrl(m, it)), it.id);
assert.ok(itemIconUrl(m, Object.values(load('items.json'))[0]));
const enemies = load('enemies.json');
let found = 0;
for (const k of Object.keys(enemies)) { const u = enemyIconUrl(m, k); assert.ok(inManifest(u), k); if (u) found++; }
assert.ok(found > Object.keys(enemies).length * 0.9);
for (const t of Object.values(load('factions.json').types)) assert.ok(factionIconUrl(m, t.icon), t.type);
for (const t of load('config.json').titles) assert.ok(titleIconUrl(m, t.picId), t.id);
assert.ok(tokenAvatarUrl(m, 'token_10028_vigil_wolf'));
assert.ok(effectIconUrl(m, { iconKind: 'team' }));
assert.ok(effectIconUrl(m, { iconKind: 'choice' }));
assert.ok(effectIconUrl(m, { iconKind: 'band', iconId: 'band_bldsk' }));
assert.ok(effectIconUrl(m, { iconKind: 'garrison' }));
assert.equal(effectIconUrl(m, { iconKind: 'x' }), null);
assert.ok(uiUrl(m, 'hudPanel/icon_hp'));
});
test('missing manifest / ids ⇒ null, never throws', () => {
for (const fn of [chessAvatarUrl, chessPortraitUrl, skillIconUrl, subProfIconUrl, itemIconUrl]) {
assert.equal(fn(null, null), null);
assert.equal(fn(m, { assets: {} }) ?? null, fn === skillIconUrl ? uiUrl(m, 'skillIcon/empty') : null);
}
assert.equal(bondIconUrl(m, 'nope'), null);
assert.equal(bandIconUrl(null, 'band_bldsk'), null);
assert.equal(enemyIconUrl(m, 'enemy_nope'), null);
assert.equal(profIconUrl(m, 'NOPE'), null);
assert.equal(uiUrl(undefined, 'x'), null);
assert.equal(tokenAvatarUrl(m, null), null);
});
});