Prepare production smart scale Android app

This commit is contained in:
FinleyHsu
2026-07-28 16:44:40 +08:00
parent af50188332
commit eab3ca651a
70 changed files with 4286 additions and 459 deletions
+19 -57
View File
@@ -1,13 +1,8 @@
import assert from "node:assert/strict";
import { access, readFile, readdir } from "node:fs/promises";
import { readFile } from "node:fs/promises";
import test from "node:test";
const developmentPreviewMeta =
/<meta(?=[^>]*\bname=["']codex-preview["'])(?=[^>]*\bcontent=["']development["'])[^>]*>/i;
const templateRoot = new URL("../", import.meta.url);
const previewRoot = new URL("../app/_sites-preview/", import.meta.url);
async function render() {
async function renderHome() {
const workerUrl = new URL("../dist/server/index.js", import.meta.url);
workerUrl.searchParams.set("test", `${process.pid}-${Date.now()}`);
const { default: worker } = await import(workerUrl.href);
@@ -28,64 +23,31 @@ async function render() {
);
}
test("server-renders the starter loading skeleton", async () => {
const response = await render();
test("首页可正常服务端渲染正式称重界面", async () => {
const response = await renderHome();
assert.equal(response.status, 200);
assert.match(response.headers.get("content-type") ?? "", /^text\/html\b/i);
const html = await response.text();
assert.match(html, developmentPreviewMeta);
assert.match(html, /<title>Your site is taking shape<\/title>/i);
assert.match(html, /Building your site/);
assert.match(html, /Your site is taking shape/);
assert.match(
html,
/Your first version will appear here automatically when it’s ready\./,
);
assert.doesNotMatch(html, /Codex/);
assert.match(html, /react-loading-skeleton/);
assert.match(html, /role="status"/);
assert.match(html, /<title>智能称重终端|沃尔核材<\/title>/);
assert.match(html, /选择废料类型/);
assert.match(html, /管理员登录/);
assert.match(html, /TYPE ID · (?:<!-- -->)?3/);
assert.match(html, /TYPE ID · (?:<!-- -->)?6/);
});
test("keeps the loading skeleton scoped and disposable", async () => {
const [preview, css, page, layout, packageJson, files] = await Promise.all([
readFile(new URL("SkeletonPreview.tsx", previewRoot), "utf8"),
readFile(new URL("preview.css", previewRoot), "utf8"),
test("正式代码不包含历史人员、机台或重量模拟数据", async () => {
const [page, utilities] = await Promise.all([
readFile(new URL("../app/page.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/layout.tsx", import.meta.url), "utf8"),
readFile(new URL("../package.json", import.meta.url), "utf8"),
readdir(previewRoot),
readFile(new URL("../app/scale-utils.ts", import.meta.url), "utf8"),
]);
assert.deepEqual(files.sort(), ["SkeletonPreview.tsx", "preview.css"]);
assert.match(preview, /from "react-loading-skeleton"/);
assert.match(preview, /baseColor="#eceae7"/);
assert.match(preview, /highlightColor="#f9f8f6"/);
assert.match(preview, /duration=\{2\.8\}/);
assert.match(preview, /sites-skeleton-search-placeholder/);
assert.match(packageJson, /"react-loading-skeleton": "3\.5\.0"/);
for (const legacySample of ["张伟", "E-0826", "A01 绕线机", "2.486"]) {
assert.doesNotMatch(page, new RegExp(legacySample));
}
const shellIndex = preview.indexOf('className="sites-skeleton-shell"');
const statusIndex = preview.indexOf('className="sites-skeleton-status"');
assert.ok(shellIndex >= 0 && statusIndex > shellIndex);
assert.match(css, /position:\s*fixed/);
assert.match(css, /inset:\s*0/);
assert.match(css, /opacity:\s*0\.52/);
assert.match(css, /prefers-reduced-motion:\s*reduce/);
assert.doesNotMatch(css, /#020617|canvas|pets|progress/i);
assert.doesNotMatch(
preview,
/loading-spinner|status-mark|status-progress|canvas|cookie|random/i,
);
assert.match(page, /export const metadata:\s*Metadata/);
assert.match(page, /"codex-preview": "development"/);
assert.match(page, /<SkeletonPreview \/>/);
assert.match(layout, /title:\s*"Starter Project"/);
assert.doesNotMatch(layout, /codex-preview|_sites-preview|themeColor|\bViewport\b/);
assert.doesNotMatch(css, /(^|\s)(html|body)\s*\{/m);
await assert.rejects(
access(new URL("public/_sites-preview", templateRoot)),
);
assert.match(page, /new WebSocket\(appSettings\.weight\.trim\(\)\)/);
assert.match(page, /if \(!appSettings\.upload\.trim\(\)\) throw/);
assert.match(page, /evaluateStability\(stableSamples\.current\)/);
assert.match(utilities, /返回值统一为千克/);
});
+44
View File
@@ -0,0 +1,44 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
createFileTimestamp,
evaluateStability,
formatWeight,
parseScaleMessage,
} from "../app/scale-utils.ts";
test("电子秤消息统一转换为千克", () => {
assert.equal(parseScaleMessage("2.49"), 2.49);
assert.equal(parseScaleMessage('{"weight":2490,"unit":"g"}'), 2.49);
assert.equal(parseScaleMessage('{"weightKg":2.49}'), 2.49);
assert.ok(Math.abs(parseScaleMessage('{"value":2.20462262,"unit":"lb"}') - 1) < 1e-9);
});
test("电子秤消息拒绝非法数据", () => {
assert.equal(parseScaleMessage(""), null);
assert.equal(parseScaleMessage("-1"), null);
assert.equal(parseScaleMessage('{"weight":"abc"}'), null);
assert.equal(parseScaleMessage('{"weight":1,"unit":"oz"}'), null);
});
test("连续相同读数可完成稳定判断", () => {
assert.deepEqual(evaluateStability([2.49, 2.49, 2.49, 2.49, 2.49]), {
stable: false,
progress: 70,
});
assert.deepEqual(evaluateStability([2.49, 2.49, 2.49, 2.49, 2.49, 2.49]), {
stable: true,
progress: 100,
});
assert.equal(evaluateStability([2.4, 2.5, 2.42, 2.51, 2.43, 2.5]).stable, false);
});
test("重量格式和文件时间戳符合终端规范", () => {
assert.equal(formatWeight(2.486, "kg"), "2.49");
assert.equal(formatWeight(2.486, "g"), "2486.00");
assert.equal(formatWeight(Number.NaN, "kg"), "0.00");
const localTime = new Date(2026, 6, 28, 16, 9, 5).toISOString();
assert.equal(createFileTimestamp(localTime), "20260728160905");
});