useServerDraft.test.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { StrictMode } from 'react';
  2. import { act, renderHook } from '@testing-library/react';
  3. import { describe, expect, it } from 'vitest';
  4. import { useServerDraft } from '@/hooks/useServerDraft';
  5. describe('useServerDraft', () => {
  6. it('keeps an edited draft when the server refetches', () => {
  7. const { result, rerender } = renderHook(
  8. ({ server }) =>
  9. useServerDraft(
  10. server,
  11. (value) => ({ ...value }),
  12. (left, right) => left.value === right.value,
  13. ),
  14. { initialProps: { server: { value: 'one' } } },
  15. );
  16. act(() => result.current.setDraft({ value: 'edited' }));
  17. rerender({ server: { value: 'two' } });
  18. expect(result.current.draft).toEqual({ value: 'edited' });
  19. expect(result.current.isDirty).toBe(true);
  20. });
  21. it('accepts a refetch that matches the saved draft', () => {
  22. const { result, rerender } = renderHook(
  23. ({ server }) =>
  24. useServerDraft(
  25. server,
  26. (value) => ({ ...value }),
  27. (left, right) => left.value === right.value,
  28. ),
  29. { initialProps: { server: { value: 'one' } } },
  30. );
  31. act(() => result.current.setDraft({ value: 'saved' }));
  32. rerender({ server: { value: 'saved' } });
  33. expect(result.current.draft).toEqual({ value: 'saved' });
  34. expect(result.current.isDirty).toBe(false);
  35. });
  36. it('compares a preserved draft with the latest server value', () => {
  37. const { result, rerender } = renderHook(
  38. ({ server }) =>
  39. useServerDraft(
  40. server,
  41. (value) => ({ ...value }),
  42. (left, right) => left.value === right.value,
  43. ),
  44. { initialProps: { server: { value: 'one' } } },
  45. );
  46. act(() => result.current.setDraft({ value: 'later' }));
  47. rerender({ server: { value: 'saved' } });
  48. act(() => result.current.setDraft({ value: 'one' }));
  49. expect(result.current.isDirty).toBe(true);
  50. });
  51. it('hydrates clean drafts under StrictMode', () => {
  52. const { result, rerender } = renderHook(
  53. ({ server }) =>
  54. useServerDraft(
  55. server,
  56. (value) => ({ ...value }),
  57. (left, right) => left.value === right.value,
  58. ),
  59. {
  60. initialProps: { server: { value: 'one' } },
  61. wrapper: StrictMode,
  62. },
  63. );
  64. rerender({ server: { value: 'two' } });
  65. expect(result.current.draft).toEqual({ value: 'two' });
  66. expect(result.current.isDirty).toBe(false);
  67. });
  68. it('preserves an edit made before the first server response', () => {
  69. const { result, rerender } = renderHook(
  70. ({ server }) =>
  71. useServerDraft(
  72. server,
  73. (value) => ({ ...value }),
  74. (left, right) => left.value === right.value,
  75. ),
  76. { initialProps: { server: undefined as { value: string } | undefined } },
  77. );
  78. act(() => result.current.setDraft({ value: 'edited' }));
  79. rerender({ server: { value: 'one' } });
  80. expect(result.current.draft).toEqual({ value: 'edited' });
  81. expect(result.current.isDirty).toBe(true);
  82. });
  83. it('marks a sent draft clean before its refetch arrives', () => {
  84. const { result } = renderHook(
  85. ({ server }) =>
  86. useServerDraft(
  87. server,
  88. (value) => ({ ...value }),
  89. (left, right) => left.value === right.value,
  90. ),
  91. { initialProps: { server: { value: 'one' } } },
  92. );
  93. act(() => result.current.setDraft({ value: 'saved' }));
  94. act(() => result.current.markSaved({ value: 'saved' }));
  95. expect(result.current.isDirty).toBe(false);
  96. });
  97. });