refine heart breath preparation and contraction

This commit is contained in:
leefer
2026-07-23 20:49:17 +08:00
parent fc3e7499d2
commit d520749252
4 changed files with 58 additions and 28 deletions
+21 -6
View File
@@ -340,7 +340,7 @@ test("saved daily fortune opens in the reading dialog without regenerating", asy
await expect(page.locator("#heavenReadingHistoryList .heaven-reading-history-item")).toHaveCount(1);
});
test("heart breathing uses five silent nine-second ripple cycles", async ({ page }) => {
test("heart breathing prepares once then contracts on each exhale", async ({ page }) => {
await mockApplication(page, session("user", true));
await page.goto("/index.html");
await page.locator('[data-view="heavenView"]').first().click();
@@ -350,26 +350,41 @@ test("heart breathing uses five silent nine-second ripple cycles", async ({ page
const timing = await page.evaluate(() => ({
remaining: state.heartBreathingEndsAt - Date.now(),
incenseDuration: getComputedStyle(document.querySelector("#heartIncenseEmber")).animationDuration,
incenseDelay: getComputedStyle(document.querySelector("#heartIncenseEmber")).animationDelay,
rippleDuration: getComputedStyle(document.querySelector(".heart-breath-ripple span")).animationDuration,
rippleDelay: getComputedStyle(document.querySelector(".heart-breath-ripple span")).animationDelay,
rippleAnimation: getComputedStyle(document.querySelector(".heart-breath-ripple span")).animationName,
rippleKeyframes: document.querySelector(".heart-breath-ripple span").getAnimations()[0].effect.getKeyframes(),
}));
expect(timing.remaining).toBeGreaterThan(44_000);
expect(timing.remaining).toBeLessThanOrEqual(45_000);
expect(timing.remaining).toBeGreaterThan(45_000);
expect(timing.remaining).toBeLessThanOrEqual(46_000);
expect(timing.incenseDuration).toBe("45s");
expect(timing.incenseDelay).toBe("1s");
expect(timing.rippleDuration).toBe("9s");
expect(timing.rippleDelay).toBe("1s");
expect(timing.rippleAnimation).toContain("heart-breath-ripple");
await expect(page.locator("#breathingPhase")).toHaveText("吸气");
expect(timing.rippleKeyframes.at(-1).transform).toContain("0.62");
await expect(page.locator("#breathingScene")).toHaveAttribute("data-phase", "prepare");
await expect(page.locator("#breathingPhase")).toHaveText("静");
await expect(page.locator("#breathingSeconds, #breathingProgress, .breathing-orbit")).toHaveCount(0);
await page.evaluate(() => {
clearInterval(state.heartTimer);
state.heartTimer = null;
state.heartSeconds = 45;
state.heartBreathingEndsAt = Date.now() + 44_500;
updateBreathingDisplay();
});
await expect(page.locator("#breathingScene")).toHaveAttribute("data-phase", "inhale");
await expect(page.locator("#breathingPhase")).toHaveText("吸");
await page.evaluate(() => {
state.heartSeconds = 42;
state.heartBreathingEndsAt = Date.now() + 41_500;
updateBreathingDisplay();
});
await expect(page.locator("#breathingScene")).toHaveAttribute("data-phase", "hold");
await expect(page.locator("#breathingPhase")).toHaveText("停驻");
await expect(page.locator("#breathingPhase")).toHaveText("");
await page.evaluate(() => {
state.heartSeconds = 39;
@@ -377,7 +392,7 @@ test("heart breathing uses five silent nine-second ripple cycles", async ({ page
updateBreathingDisplay();
});
await expect(page.locator("#breathingScene")).toHaveAttribute("data-phase", "exhale");
await expect(page.locator("#breathingPhase")).toHaveText("呼");
await expect(page.locator("#breathingPhase")).toHaveText("呼");
});
test("mobile shell stays within the viewport", async ({ page }) => {