Files
sundynix-pets/tests/miniprogram-startup.test.cjs
Blizzard 3175936266
ci-cd / verify (push) Successful in 2m18s
ci-cd / deploy (push) Skipped
feat: support shared pet care tasks and smooth daily page return
2026-10-05 20:20:05 +08:00

233 lines
7.1 KiB
JavaScript

const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const vm = require("node:vm");
const tick = () => new Promise((resolve) => setImmediate(resolve));
function runtime(cachedToken = "") {
let app;
const storage = new Map(cachedToken ? [["pet_token", cachedToken]] : []);
const requests = [],
logins = [];
const wx = {
getStorageSync: (key) => storage.get(key),
setStorageSync: (key, value) => storage.set(key, value),
removeStorageSync: (key) => storage.delete(key),
login: (options) => logins.push(options),
request: (options) => {
requests.push(options);
if (options.url.endsWith("/auth/login"))
options.success({
statusCode: 200,
data: {
code: 0,
data: { token: "fresh-token", user: { nickname: "毛孩家长9527" } },
},
});
else if (options.header.Authorization === "Bearer fresh-token")
options.success({ statusCode: 200, data: { code: 0, data: "ok" } });
},
};
const module = { exports: {} };
vm.runInNewContext(fs.readFileSync("pets-fe/utils/api.js", "utf8"), {
wx,
getApp: () => app,
require: () => ({ baseURL: "https://test.local/api" }),
module,
});
vm.runInNewContext(fs.readFileSync("pets-fe/app.js", "utf8"), {
wx,
App: (value) => {
app = value;
},
require: () => module.exports,
});
return { app, api: module.exports, requests, logins, storage };
}
test("启动即登录,多个首页请求共用登录任务并携带 token", async () => {
const r = runtime();
r.app.onLaunch();
const pending = [
r.api.secure("/pets"),
r.api.secure("/tasks"),
r.api.secure("/me"),
];
assert.equal(r.logins.length, 1);
assert.equal(r.requests.length, 0);
r.logins[0].success({ code: "wechat-code" });
assert.deepEqual(await Promise.all(pending), ["ok", "ok", "ok"]);
assert.equal(
r.requests.filter((req) => req.url.endsWith("/auth/login")).length,
1,
);
assert.ok(
r.requests
.slice(1)
.every((req) => req.header.Authorization === "Bearer fresh-token"),
);
assert.equal(r.storage.get("pet_token"), "fresh-token");
});
test("缓存 token 过期时只重新登录一次,延迟的旧 401 不清除新 token", async () => {
const r = runtime("expired");
r.app.onLaunch();
const first = r.api.secure("/pets"),
second = r.api.secure("/tasks");
await tick();
const old = r.requests.slice();
assert.equal(old.length, 2);
old[0].success({ statusCode: 401, data: { message: "expired" } });
await tick();
assert.equal(r.logins.length, 1);
r.logins[0].success({ code: "new-code" });
await first;
old[1].success({ statusCode: 401, data: { message: "expired" } });
await second;
assert.equal(r.app.globalData.token, "fresh-token");
assert.equal(r.logins.length, 1);
});
function home(login, secure, app = { login }) {
let definition;
const wx = { getStorageSync: () => [], navigateTo: () => {} };
vm.runInNewContext(fs.readFileSync("pets-fe/pages/home/home.js", "utf8"), {
wx,
Date,
getApp: () => app,
Page: (value) => {
definition = value;
},
require: (name) =>
name.includes("environment")
? { cached: () => null }
: name.includes("date")
? { localDate: () => "2026-10-05" }
: { secure, toast: () => {} },
});
const page = {
...definition,
data: { ...definition.data },
setData(value) {
Object.assign(this.data, value);
},
};
page.refreshEnvironment = () => new Promise(() => {});
return page;
}
function data(path) {
return Promise.resolve(
path === "/pets" || path === "/walks"
? []
: path === "/tasks"
? { items: [] }
: { nickname: "毛孩家长9527" },
);
}
test("登录和数据都准备好才显示首页,天气未完成也能进入", async () => {
let finishLogin,
calls = 0;
const login = new Promise((resolve) => {
finishLogin = resolve;
});
const page = home(
() => login,
(path) => {
calls++;
return data(path);
},
);
const loading = page.load();
page.load();
assert.equal(page.data.phase, "auth");
assert.equal(page.data.ready, false);
assert.equal(calls, 0);
finishLogin("token");
await loading;
assert.equal(calls, 4);
assert.equal(page.data.ready, true);
assert.equal(page.data.loading, false);
assert.equal(page.data.phase, "ready");
assert.equal(page.data.pets.length, 0);
});
test("首次登录失败可重试,失败期间不请求业务数据", async () => {
let attempts = 0,
calls = 0;
const page = home(
async () => {
if (++attempts === 1) throw new Error("无法连接");
return "token";
},
(path) => {
calls++;
return data(path);
},
);
await page.load();
assert.equal(page.data.phase, "auth-error");
assert.equal(page.data.ready, false);
assert.equal(calls, 0);
await page.load();
assert.equal(page.data.ready, true);
assert.equal(page.data.error, "");
});
test("首页请求失败不显示空数据状态,刷新失败保留已有记录", async () => {
let fail = true;
const page = home(
async () => "token",
(path) => (fail ? Promise.reject(new Error("网络异常")) : data(path)),
);
await page.load();
assert.equal(page.data.phase, "data-error");
assert.equal(page.data.ready, false);
fail = false;
await page.load();
assert.equal(page.data.ready, true);
page.setData({ pets: [{ id: "existing" }] });
fail = true;
await page.load();
assert.equal(page.data.ready, true);
assert.equal(page.data.pets[0].id, "existing");
assert.equal(page.data.error, "网络异常");
});
test("从其他页面回到日常立即显示已有数据,后台刷新不清空内容", async () => {
const app = { login: async () => "token", globalData: { token: "token", user: { id: "user-1" } } };
const first = home(app.login, data, app);
await first.load();
first.setData({ pets: [{ id: "pet-1" }], tasks: [{ id: "task-1", completed: true }], undoneCount: 0 });
first.onUnload();
let finish;
const pending = new Promise((resolve) => { finish = resolve; });
const next = home(app.login, async (route) => { await pending; return data(route); }, app);
next.onLoad();
assert.equal(next.data.ready, true);
assert.equal(next.data.phase, "ready");
assert.equal(next.data.pets[0].id, "pet-1");
assert.equal(next.data.tasks[0].completed, true);
const refreshing = next.load();
await tick();
assert.equal(next.data.ready, true);
assert.equal(next.data.pets[0].id, "pet-1");
finish();
await refreshing;
assert.equal(next.data.loading, false);
});
test("日常缓存不跨账号、不显示上一天的任务", async () => {
const app = { login: async () => "token", globalData: { user: { id: "new-user" } }, homeSnapshot: {
account: "old-user", date: "2026-10-05", data: { pets: [{ id: "private" }] },
} };
const page = home(app.login, data, app);
page.onLoad();
assert.equal(page.data.ready, false);
assert.equal(page.data.pets.length, 0);
app.homeSnapshot.account = "new-user";
app.homeSnapshot.date = "2026-10-04";
page.onLoad();
assert.equal(page.data.ready, false);
});