88 lines
2.9 KiB
JavaScript
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`
|
|
);
|