onboarding.test.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import { afterEach, beforeEach, describe, expect, it } from "vitest";
  2. import {
  3. resetAllOnboarding,
  4. resetOnboarding,
  5. setOnboardingCompleted,
  6. shouldShowOnboarding,
  7. } from "@/lib/onboarding";
  8. function createMemoryLocalStorage() {
  9. const store = new Map<string, string>();
  10. return {
  11. getItem: (key: string) => store.get(key) ?? null,
  12. setItem: (key: string, value: string) => {
  13. store.set(key, value);
  14. },
  15. removeItem: (key: string) => {
  16. store.delete(key);
  17. },
  18. };
  19. }
  20. afterEach(() => {
  21. // 清理测试中注入的全局对象,避免污染其他测试
  22. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  23. delete (globalThis as any).window;
  24. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  25. delete (globalThis as any).localStorage;
  26. });
  27. describe("onboarding", () => {
  28. beforeEach(() => {
  29. // 在某些测试环境(例如 DOM 仿真环境)下,window/localStorage 可能默认存在
  30. // 为了让“SSR 环境”用例稳定,这里在每个用例开始前都强制清理一次
  31. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  32. delete (globalThis as any).window;
  33. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  34. delete (globalThis as any).localStorage;
  35. });
  36. it("SSR 环境下不显示引导", () => {
  37. expect(shouldShowOnboarding("webhookMigration")).toBe(false);
  38. });
  39. it("正常流程:未完成时显示,完成后不再显示,可重置", () => {
  40. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  41. (globalThis as any).window = {};
  42. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  43. (globalThis as any).localStorage = createMemoryLocalStorage();
  44. expect(shouldShowOnboarding("webhookMigration")).toBe(true);
  45. setOnboardingCompleted("webhookMigration");
  46. expect(shouldShowOnboarding("webhookMigration")).toBe(false);
  47. resetOnboarding("webhookMigration");
  48. expect(shouldShowOnboarding("webhookMigration")).toBe(true);
  49. });
  50. it("resetAllOnboarding 会清空所有类型的状态", () => {
  51. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  52. (globalThis as any).window = {};
  53. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  54. (globalThis as any).localStorage = createMemoryLocalStorage();
  55. setOnboardingCompleted("userManagement");
  56. setOnboardingCompleted("webhookMigration");
  57. setOnboardingCompleted("providerSetup");
  58. setOnboardingCompleted("quotaManagement");
  59. expect(shouldShowOnboarding("userManagement")).toBe(false);
  60. expect(shouldShowOnboarding("webhookMigration")).toBe(false);
  61. expect(shouldShowOnboarding("providerSetup")).toBe(false);
  62. expect(shouldShowOnboarding("quotaManagement")).toBe(false);
  63. resetAllOnboarding();
  64. expect(shouldShowOnboarding("userManagement")).toBe(true);
  65. expect(shouldShowOnboarding("webhookMigration")).toBe(true);
  66. expect(shouldShowOnboarding("providerSetup")).toBe(true);
  67. expect(shouldShowOnboarding("quotaManagement")).toBe(true);
  68. });
  69. it("localStorage 抛错时应静默失败且不崩溃", () => {
  70. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  71. (globalThis as any).window = {};
  72. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  73. (globalThis as any).localStorage = {
  74. getItem: () => {
  75. throw new Error("getItem failed");
  76. },
  77. setItem: () => {
  78. throw new Error("setItem failed");
  79. },
  80. removeItem: () => {
  81. throw new Error("removeItem failed");
  82. },
  83. };
  84. expect(shouldShowOnboarding("webhookMigration")).toBe(false);
  85. expect(() => setOnboardingCompleted("webhookMigration")).not.toThrow();
  86. expect(() => resetOnboarding("webhookMigration")).not.toThrow();
  87. expect(() => resetAllOnboarding()).not.toThrow();
  88. });
  89. });