hyapp-h5/scripts/check-activity-recharge-reward-fami-layer-assets.mjs
2026-07-17 11:54:43 +08:00

88 lines
2.9 KiB
JavaScript

import assert from 'node:assert/strict';
import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const productDir = path.join(root, 'activity/recharge-reward/fami');
const manifest = JSON.parse(
fs.readFileSync(path.join(productDir, 'manifest.json'), 'utf8')
);
const html = fs.readFileSync(
path.join(root, 'activity/recharge-reward/fami.html'),
'utf8'
);
const css = fs.readFileSync(path.join(productDir, 'fami.css'), 'utf8');
const js = fs.readFileSync(path.join(productDir, 'fami.js'), 'utf8');
function sha256(buffer) {
return crypto.createHash('sha256').update(buffer).digest('hex');
}
function pngSize(buffer) {
assert.equal(buffer.toString('ascii', 1, 4), 'PNG', 'asset must be a PNG');
return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
}
assert.equal(manifest.figma.rootNode, '822:2928');
assert.equal(manifest.rules.rootFrameUsedAsAsset, false);
assert.equal(manifest.rules.dynamicTextRenderedByDOM, true);
assert.equal(manifest.rules.productionBusinessMediaFromAPI, true);
for (const asset of manifest.assets) {
const file = path.join(productDir, asset.path);
const buffer = fs.readFileSync(file);
assert.deepEqual(
pngSize(buffer),
{ width: asset.width, height: asset.height },
asset.path
);
assert.equal(sha256(buffer), asset.sha256, `${asset.path} hash drifted`);
}
for (const font of manifest.fonts) {
const buffer = fs.readFileSync(path.join(productDir, font.path));
assert.equal(sha256(buffer), font.sha256, `${font.path} hash drifted`);
}
const implementation = `${html}\n${css}\n${js}`;
assert.doesNotMatch(
implementation,
/figma-822-2928\.png|\.waylog\//,
'reference screenshots must not ship'
);
assert.match(html, /data-node-id="822:2928"/);
assert.match(html, /id="profileAvatar"[\s\S]*?hidden/);
assert.doesNotMatch(
html,
/id="profileAvatar"[^>]*\ssrc=/,
'production avatar must be populated from API data'
);
assert.match(js, /value === '1' \|\| value === 'true'/);
assert.match(js, /preview_url/);
assert.match(js, /\/api\/v1\/activities\/cumulative-recharge-reward/);
assert.doesNotMatch(
js,
/HyAppAPI\.post|\/claim['"`]/,
'cumulative recharge has no claim RPC'
);
assert.doesNotMatch(
js,
/catch\([^)]*\)[\s\S]{0,160}mockData/,
'production failures must not become mock success'
);
const mockAssets = manifest.assets
.filter((asset) => asset.mockOnly)
.map((asset) => asset.path);
for (const mockAsset of mockAssets) {
const basename = mockAsset.split('/').pop().replaceAll('.', '\\.');
assert.match(js, new RegExp(basename));
assert.doesNotMatch(html, new RegExp(basename));
}
console.log(
`recharge reward Fami layer audit passed: ${manifest.assets.length} traced assets, root frame excluded`
);