diff --git a/package.json b/package.json index 1e9572b..7678e5b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "jest": { "preset": "jest-expo", + "testTimeout": 30000, "setupFilesAfterEnv": [ "/jest.setup.js" ], diff --git a/src/components/report/DelaysSectionSheet.test.tsx b/src/components/report/DelaysSectionSheet.test.tsx index 8650fc0..5e05452 100644 --- a/src/components/report/DelaysSectionSheet.test.tsx +++ b/src/components/report/DelaysSectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,15 +10,30 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { DelaysSectionSheet } from './DelaysSectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; +// Fake timers, not `waitFor`: this single test also pays the suite's one-time +// cost of lazily requiring the RN/Expo component stack during its first +// `render`. Waiting the 400ms debounce on the wall clock puts both inside one +// 5s jest budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('Add delay writes an entry', async () => { - const { getByLabelText } = render( - - - , - ); - fireEvent.press(getByLabelText('Add delay')); - await waitFor(() => - expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'delays', expect.anything(), false), - ); + jest.useFakeTimers(); + try { + const { getByLabelText } = render( + + + , + ); + fireEvent.press(getByLabelText('Add delay')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); + expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'delays', expect.anything(), false); + } finally { + jest.useRealTimers(); + } }); diff --git a/src/components/report/DeliveriesSectionSheet.test.tsx b/src/components/report/DeliveriesSectionSheet.test.tsx index c23ec27..3a1a153 100644 --- a/src/components/report/DeliveriesSectionSheet.test.tsx +++ b/src/components/report/DeliveriesSectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,15 +10,35 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { DeliveriesSectionSheet } from './DeliveriesSectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; +// Fake timers, not `waitFor`: this test also pays the suite's one-time cost of +// lazily requiring the RN/Expo component stack during its first `render`. +// Waiting the 400ms debounce on the wall clock puts both inside one jest test +// budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('Add delivery writes an entry', async () => { - const { getByLabelText } = render( - - - , - ); - fireEvent.press(getByLabelText('Add delivery')); - await waitFor(() => - expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'deliveries', expect.anything(), false), - ); + jest.useFakeTimers(); + try { + const { getByLabelText } = render( + + + , + ); + fireEvent.press(getByLabelText('Add delivery')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); + expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'deliveries', expect.anything(), false); + } finally { + jest.useRealTimers(); + } }); diff --git a/src/components/report/EquipmentSectionSheet.test.tsx b/src/components/report/EquipmentSectionSheet.test.tsx index f697951..ec3fba8 100644 --- a/src/components/report/EquipmentSectionSheet.test.tsx +++ b/src/components/report/EquipmentSectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,16 +10,31 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { EquipmentSectionSheet } from './EquipmentSectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; +// Fake timers, not `waitFor`: this test also pays the suite's one-time cost of +// lazily requiring the RN/Expo component stack during its first `render`. +// Waiting the 400ms debounce on the wall clock puts both inside one jest test +// budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('Add equipment writes a row', async () => { - const { getByPlaceholderText, getByLabelText } = render( - - - , - ); - fireEvent.changeText(getByPlaceholderText('Equipment name'), 'Excavator'); - fireEvent.press(getByLabelText('Add equipment')); - await waitFor(() => - expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'equipment', expect.anything(), false), - ); + jest.useFakeTimers(); + try { + const { getByPlaceholderText, getByLabelText } = render( + + + , + ); + fireEvent.changeText(getByPlaceholderText('Equipment name'), 'Excavator'); + fireEvent.press(getByLabelText('Add equipment')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); + expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'equipment', expect.anything(), false); + } finally { + jest.useRealTimers(); + } }); diff --git a/src/components/report/InspectionsSectionSheet.test.tsx b/src/components/report/InspectionsSectionSheet.test.tsx index 146eff1..79b78a2 100644 --- a/src/components/report/InspectionsSectionSheet.test.tsx +++ b/src/components/report/InspectionsSectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,20 +10,35 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { InspectionsSectionSheet } from './InspectionsSectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; +// Fake timers, not `waitFor`: this test also pays the suite's one-time cost of +// lazily requiring the RN/Expo component stack during its first `render`. +// Waiting the 400ms debounce on the wall clock puts both inside one jest test +// budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('Add inspection writes an entry', async () => { - const { getByLabelText } = render( - - - , - ); - fireEvent.press(getByLabelText('Add inspection')); - await waitFor(() => - expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'inspections', expect.anything(), false), - ); + jest.useFakeTimers(); + try { + const { getByLabelText } = render( + + + , + ); + fireEvent.press(getByLabelText('Add inspection')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); + expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'inspections', expect.anything(), false); + } finally { + jest.useRealTimers(); + } }); diff --git a/src/components/report/RfisSectionSheet.test.tsx b/src/components/report/RfisSectionSheet.test.tsx index 9a057df..c619a42 100644 --- a/src/components/report/RfisSectionSheet.test.tsx +++ b/src/components/report/RfisSectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,15 +10,30 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { RfisSectionSheet } from './RfisSectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; +// Fake timers, not `waitFor`: this test also pays the suite's one-time cost of +// lazily requiring the RN/Expo component stack during its first `render`. +// Waiting the 400ms debounce on the wall clock puts both inside one jest test +// budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('Add item writes an entry', async () => { - const { getByLabelText } = render( - - - , - ); - fireEvent.press(getByLabelText('Add item')); - await waitFor(() => - expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'rfis', expect.anything(), false), - ); + jest.useFakeTimers(); + try { + const { getByLabelText } = render( + + + , + ); + fireEvent.press(getByLabelText('Add item')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); + expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'rfis', expect.anything(), false); + } finally { + jest.useRealTimers(); + } }); diff --git a/src/components/report/SafetySectionSheet.test.tsx b/src/components/report/SafetySectionSheet.test.tsx index d57ace8..41761c6 100644 --- a/src/components/report/SafetySectionSheet.test.tsx +++ b/src/components/report/SafetySectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,21 +10,36 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { SafetySectionSheet } from './SafetySectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; beforeEach(() => { mockUpdateSection.mockClear(); }); +// Fake timers, not `waitFor`: this test also pays the suite's one-time cost of +// lazily requiring the RN/Expo component stack during its first `render`. +// Waiting the 400ms debounce on the wall clock puts both inside one jest test +// budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('Nothing to report marks the section complete', async () => { - const { getByLabelText } = render( - - - , - ); - fireEvent.press(getByLabelText('Nothing to report')); - await waitFor(() => - expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'safety', expect.anything(), true), - ); + jest.useFakeTimers(); + try { + const { getByLabelText } = render( + + + , + ); + fireEvent.press(getByLabelText('Nothing to report')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); + expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'safety', expect.anything(), true); + } finally { + jest.useRealTimers(); + } }); test('readOnly renders content but hides None today and never writes', async () => { diff --git a/src/components/report/VisitorsSectionSheet.test.tsx b/src/components/report/VisitorsSectionSheet.test.tsx index 248ba75..f67fbd4 100644 --- a/src/components/report/VisitorsSectionSheet.test.tsx +++ b/src/components/report/VisitorsSectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,15 +10,30 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { VisitorsSectionSheet } from './VisitorsSectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; +// Fake timers, not `waitFor`: this test also pays the suite's one-time cost of +// lazily requiring the RN/Expo component stack during its first `render`. +// Waiting the 400ms debounce on the wall clock puts both inside one jest test +// budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('Add visitor writes an entry', async () => { - const { getByLabelText } = render( - - - , - ); - fireEvent.press(getByLabelText('Add visitor')); - await waitFor(() => - expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'visitors', expect.anything(), false), - ); + jest.useFakeTimers(); + try { + const { getByLabelText } = render( + + + , + ); + fireEvent.press(getByLabelText('Add visitor')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); + expect(mockUpdateSection).toHaveBeenCalledWith('r1', 'visitors', expect.anything(), false); + } finally { + jest.useRealTimers(); + } }); diff --git a/src/components/report/WeatherSectionSheet.test.tsx b/src/components/report/WeatherSectionSheet.test.tsx index dc3d7ef..a30e9cf 100644 --- a/src/components/report/WeatherSectionSheet.test.tsx +++ b/src/components/report/WeatherSectionSheet.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor } from '@testing-library/react-native'; +import { fireEvent, render } from '@testing-library/react-native'; import { ThemeProvider } from '../../theme'; @@ -10,20 +10,35 @@ jest.mock('../../data', () => ({ // eslint-disable-next-line import/first import { WeatherSectionSheet } from './WeatherSectionSheet'; +// eslint-disable-next-line import/first +import { SECTION_DRAFT_DEBOUNCE_MS } from './useSectionDraft'; +// Fake timers, not `waitFor`: this test also pays the suite's one-time cost of +// lazily requiring the RN/Expo component stack during its first `render`. +// Waiting the 400ms debounce on the wall clock puts both inside one jest test +// budget, which a cold transform cache blows through. Repo precedent: +// CrewWorkSheet.test.tsx and useSectionDraft.test.tsx. test('choosing a condition writes the weather override', async () => { - const { getByLabelText } = render( - - - , - ); - fireEvent.press(getByLabelText('Rain')); - await waitFor(() => + jest.useFakeTimers(); + try { + const { getByLabelText } = render( + + + , + ); + fireEvent.press(getByLabelText('Rain')); + jest.advanceTimersByTime(SECTION_DRAFT_DEBOUNCE_MS); + // Flush the microtask chain the debounced callback kicks off (issueWrite + // chains through a Promise) — fake timers don't do this for us. + await Promise.resolve(); + await Promise.resolve(); expect(mockUpdateSection).toHaveBeenCalledWith( 'r1', 'weather', expect.objectContaining({ condition: 'rain' }), false, - ), - ); + ); + } finally { + jest.useRealTimers(); + } });