visibility.test.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import { describe, it, expect, vi } from "vitest";
  2. import { defineComponent, h, type Component, type ComputedRef } from "vue";
  3. import { mount } from "@vue/test-utils";
  4. import { StateProvider, useStateStore } from "./state";
  5. import { VisibilityProvider, useVisibility, useIsVisible } from "./visibility";
  6. /** Mount StateProvider → VisibilityProvider with a child that captures context. */
  7. function withProviders<T>(
  8. composable: () => T,
  9. initialState: Record<string, unknown> = {},
  10. ): { result: T } {
  11. let result!: T;
  12. const Child = defineComponent({
  13. setup() {
  14. result = composable();
  15. return () => h("div");
  16. },
  17. });
  18. mount(StateProvider as Component, {
  19. props: { initialState } as any,
  20. slots: {
  21. default: () =>
  22. h(VisibilityProvider as Component, null, {
  23. default: () => h(Child),
  24. }),
  25. },
  26. });
  27. return { result };
  28. }
  29. describe("VisibilityProvider — provide/inject", () => {
  30. it("useVisibility() throws outside a VisibilityProvider", () => {
  31. const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
  32. expect(() => useVisibility()).toThrow(
  33. "useVisibility must be used within a VisibilityProvider",
  34. );
  35. warn.mockRestore();
  36. });
  37. it("useVisibility() returns a context with isVisible and ctx", () => {
  38. const { result } = withProviders(() => useVisibility());
  39. expect(typeof result.isVisible).toBe("function");
  40. expect(result.ctx).toBeDefined();
  41. expect(result.ctx.value).toBeDefined();
  42. });
  43. });
  44. describe("useIsVisible — state integration", () => {
  45. it("undefined condition returns true", () => {
  46. const { result } = withProviders(() => useVisibility());
  47. expect(result.isVisible(undefined)).toBe(true);
  48. });
  49. it("{ $state: '/flag' } returns true when state flag is truthy", () => {
  50. const { result } = withProviders(() => useVisibility(), { flag: true });
  51. expect(result.isVisible({ $state: "/flag" })).toBe(true);
  52. });
  53. it("{ $state: '/flag' } returns false when state flag is falsy", () => {
  54. const { result } = withProviders(() => useVisibility(), { flag: false });
  55. expect(result.isVisible({ $state: "/flag" })).toBe(false);
  56. });
  57. it("ctx is a ComputedRef whose .value reflects current state", () => {
  58. const { result } = withProviders(() => useVisibility(), { count: 3 });
  59. expect(result.ctx.value.stateModel).toEqual({ count: 3 });
  60. });
  61. });
  62. describe("useIsVisible — reactivity", () => {
  63. it("returns a ComputedRef<boolean> that updates when state changes", () => {
  64. let storeCtx!: ReturnType<typeof useStateStore>;
  65. let isVisible!: ComputedRef<boolean>;
  66. const Child = defineComponent({
  67. setup() {
  68. storeCtx = useStateStore();
  69. isVisible = useIsVisible({ $state: "/flag" });
  70. return () => h("div");
  71. },
  72. });
  73. mount(StateProvider as Component, {
  74. props: { initialState: { flag: false } } as any,
  75. slots: {
  76. default: () =>
  77. h(VisibilityProvider as Component, null, {
  78. default: () => h(Child),
  79. }),
  80. },
  81. });
  82. expect(isVisible.value).toBe(false);
  83. storeCtx.set("/flag", true);
  84. expect(isVisible.value).toBe(true);
  85. });
  86. });