From 37ef9022cf5857596310d882eeb52fdc33b8e670 Mon Sep 17 00:00:00 2001 From: Arjun <14841132+arjunvegda@users.noreply.github.com> Date: Thu, 1 Aug 2024 20:22:02 -0400 Subject: [PATCH 1/5] chore: update jotai --- __tests__/devtools/AtomViewer.test.tsx | 10 +- __tests__/devtools/TimeTravel.test.tsx | 12 +- __tests__/devtools/basic.test.tsx | 12 +- __tests__/devtools/store1/AtomViewer.test.tsx | 489 -- __tests__/devtools/store1/TimeTravel.test.tsx | 653 --- .../__snapshots__/AtomViewer.test.tsx.snap | 4959 ----------------- .../__snapshots__/TimeTravel.test.tsx.snap | 2666 --------- .../store1/__snapshots__/basic.test.tsx.snap | 541 -- __tests__/devtools/store1/basic.test.tsx | 168 - .../utils/store1/useAtomDevtools.test.tsx | 335 -- .../utils/store1/useAtomsDevtools.test.tsx | 793 --- .../utils/store1/useAtomsSnapshot.test.tsx | 268 - .../store1/useGoToAtomsSnapshot.test.tsx | 249 - __tests__/utils/useAtomDevtools.test.tsx | 39 +- __tests__/utils/useAtomsDevtools.test.tsx | 51 +- __tests__/utils/useAtomsSnapshot.test.tsx | 28 +- __tests__/utils/useGoToAtomsSnapshot.test.tsx | 17 - package.json | 2 +- pnpm-lock.yaml | 20 +- src/stories/Default/Demos/demo-store.ts | 2 +- .../Default/Playground/Playground.stories.tsx | 12 +- src/types.ts | 5 +- 22 files changed, 31 insertions(+), 11300 deletions(-) delete mode 100644 __tests__/devtools/store1/AtomViewer.test.tsx delete mode 100644 __tests__/devtools/store1/TimeTravel.test.tsx delete mode 100644 __tests__/devtools/store1/__snapshots__/AtomViewer.test.tsx.snap delete mode 100644 __tests__/devtools/store1/__snapshots__/TimeTravel.test.tsx.snap delete mode 100644 __tests__/devtools/store1/__snapshots__/basic.test.tsx.snap delete mode 100644 __tests__/devtools/store1/basic.test.tsx delete mode 100644 __tests__/utils/store1/useAtomDevtools.test.tsx delete mode 100644 __tests__/utils/store1/useAtomsDevtools.test.tsx delete mode 100644 __tests__/utils/store1/useAtomsSnapshot.test.tsx delete mode 100644 __tests__/utils/store1/useGoToAtomsSnapshot.test.tsx diff --git a/__tests__/devtools/AtomViewer.test.tsx b/__tests__/devtools/AtomViewer.test.tsx index 2ddb6b1c..42d9c7b2 100644 --- a/__tests__/devtools/AtomViewer.test.tsx +++ b/__tests__/devtools/AtomViewer.test.tsx @@ -3,18 +3,10 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import * as stringifyModule from 'javascript-stringify'; import { Provider, useAtomValue } from 'jotai'; -import { getDefaultStore } from 'jotai/experimental'; import { atom } from 'jotai/vanilla'; import { DevTools } from 'jotai-devtools'; import { AnyAtom } from 'src/types'; -import { customRender as customTestRender } from '../custom-render'; - -const customRender = (ui: React.ReactElement) => - customTestRender(ui, { - wrapper: ({ children }) => ( - {children} - ), - }); +import { customRender } from '../custom-render'; const BasicAtomsWithDevTools = () => { // Create atoms inside the component so that they are recreated for each test diff --git a/__tests__/devtools/TimeTravel.test.tsx b/__tests__/devtools/TimeTravel.test.tsx index 7c55d7cf..ee0d979c 100644 --- a/__tests__/devtools/TimeTravel.test.tsx +++ b/__tests__/devtools/TimeTravel.test.tsx @@ -1,17 +1,9 @@ import React, { useMemo } from 'react'; import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; -import { Provider, atom, useAtomValue, useSetAtom } from 'jotai'; -import { getDefaultStore } from 'jotai/experimental'; +import { atom, useAtomValue, useSetAtom } from 'jotai'; import { DevTools, DevToolsProps } from 'jotai-devtools'; -import { customRender as customTestRender } from '../custom-render'; - -const customRender = (ui: React.ReactElement) => - customTestRender(ui, { - wrapper: ({ children }) => ( - {children} - ), - }); +import { customRender } from '../custom-render'; const BasicAtomsWithDevTools = (props: DevToolsProps) => { // Create atoms inside the component so that they are recreated for each test diff --git a/__tests__/devtools/basic.test.tsx b/__tests__/devtools/basic.test.tsx index 3ca18021..72d404fa 100644 --- a/__tests__/devtools/basic.test.tsx +++ b/__tests__/devtools/basic.test.tsx @@ -1,17 +1,9 @@ import React, { useMemo } from 'react'; import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; -import { Provider, atom, useAtom } from 'jotai'; -import { getDefaultStore } from 'jotai/experimental'; +import { atom, useAtom } from 'jotai'; import { DevTools } from 'jotai-devtools'; -import { customRender as customTestRender } from '../custom-render'; - -const customRender = (ui: React.ReactElement) => - customTestRender(ui, { - wrapper: ({ children }) => ( - {children} - ), - }); +import { customRender } from '../custom-render'; describe('DevTools - basic', () => { it('should render the trigger button', () => { diff --git a/__tests__/devtools/store1/AtomViewer.test.tsx b/__tests__/devtools/store1/AtomViewer.test.tsx deleted file mode 100644 index 6f022285..00000000 --- a/__tests__/devtools/store1/AtomViewer.test.tsx +++ /dev/null @@ -1,489 +0,0 @@ -import React, { useMemo } from 'react'; -import { act, render, screen, waitFor } from '@testing-library/react'; -import { userEvent } from '@testing-library/user-event'; -import * as stringifyModule from 'javascript-stringify'; -import { useAtomValue } from 'jotai'; -import { atom } from 'jotai/vanilla'; -import { DevTools } from 'jotai-devtools'; -import { AnyAtom } from 'src/types'; -import { customRender } from '../../custom-render'; - -const BasicAtomsWithDevTools = () => { - // Create atoms inside the component so that they are recreated for each test - const countAtom = useMemo(() => atom(0), []); - countAtom.debugLabel = 'countAtom'; - const doubleAtom = useMemo( - () => atom((get) => get(countAtom) * 2), - [countAtom], - ); - - doubleAtom.debugLabel = 'doubleCountAtom'; - - useAtomValue(countAtom); - useAtomValue(doubleAtom); - return ; -}; - -describe('DevTools - AtomViewer', () => { - describe('List of atoms', () => { - it('should render atom viewer without any errors if there are no atoms', async () => { - const { container } = customRender(); - await waitFor(() => - expect(screen.getByText('👻 Jōtai DevTools')).toBeInTheDocument(), - ); - expect(screen.getByText('Atom Viewer')).toBeInTheDocument(); - expect( - screen.getByTestId('atom-list-no-atoms-found-message'), - ).toHaveTextContent('No Atoms found!'); - expect(screen.getByLabelText('Search')).toBeInTheDocument(); - expect( - screen.getByText( - 'Select an atom from the left panel to view the details', - ), - ).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - - it('should render atom viewer with correct atoms without provider', async () => { - const { container } = customRender(); - expect(screen.getByText('countAtom')).toBeInTheDocument(); - // We did not add `debugLabel` to `doubleAtom` so it should be unlabeled - expect(screen.getByText('doubleCountAtom')).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - - describe('private atoms', () => { - const PrivateAtomsWithDevTools = ({ - markAtomPrivate = true, - shouldShowPrivateAtoms = false, - }: { - markAtomPrivate?: boolean; - shouldShowPrivateAtoms?: boolean; - }) => { - // Create atoms inside the component so that they are recreated for each test - const countAtom = useMemo(() => atom(0), []); - countAtom.debugLabel = 'countAtom'; - - const privateAtom = useMemo( - () => atom((get) => get(countAtom) * 0), - [countAtom], - ); - privateAtom.debugLabel = 'privateAtom'; - privateAtom.debugPrivate = markAtomPrivate; - - const doubleAtom = useMemo( - () => atom((get) => get(countAtom) * get(privateAtom)), - [countAtom, privateAtom], - ); - - doubleAtom.debugLabel = 'doubleCountAtom'; - - useAtomValue(countAtom); - useAtomValue(doubleAtom); - useAtomValue(privateAtom); - - return ( - - ); - }; - - it('should not render private atoms', async () => { - customRender(); - expect(screen.queryByText('privateAtom')).not.toBeInTheDocument(); - expect(screen.getByText('countAtom')).toBeInTheDocument(); - expect(screen.getByText('doubleCountAtom')).toBeInTheDocument(); - }); - - it('should render private atoms when shouldShowPrivateAtoms is marked as true', async () => { - customRender(); - expect(screen.getByText('privateAtom')).toBeInTheDocument(); - }); - - it('should hide private atoms from dependents list when shouldShowPrivateAtoms is marked as false', async () => { - const { container } = customRender(); - - await act(async () => { - await userEvent.click(screen.getByText('countAtom')); - }); - - expect(screen.getByText('Atom Details')).toBeInTheDocument(); - expect(screen.getByText('Meta')).toBeInTheDocument(); - expect(screen.getByText('Debug Label')).toBeInTheDocument(); - expect( - screen.getByTestId('meta-info-value-countAtom'), - ).toHaveTextContent('countAtom'); - expect(screen.getByText('Value type')).toBeInTheDocument(); - expect(screen.getByText('number')).toBeInTheDocument(); - expect(screen.queryByText('Private')).not.toBeInTheDocument(); - expect(screen.queryByText('Yes')).not.toBeInTheDocument(); - - expect(screen.getByText('Raw value')).toBeInTheDocument(); - expect(screen.getByTestId('atom-parsed-value')).toHaveTextContent('0'); - - expect(screen.getByText('Dependents')).toBeInTheDocument(); - expect( - screen.queryByTestId('dependents-list-item-doubleCountAtom-0'), - ).toBeInTheDocument(); - expect( - screen.queryByTestId('dependents-list-item-privateAtom-0'), - ).not.toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - - it('should mark private atoms in atom details', async () => { - const { container } = customRender( - , - ); - - await act(async () => { - await userEvent.click(screen.getByText('privateAtom')); - }); - - expect(screen.getByText('Atom Details')).toBeInTheDocument(); - expect(screen.getByText('Meta')).toBeInTheDocument(); - expect(screen.getByText('Debug Label')).toBeInTheDocument(); - expect( - screen.getByTestId('meta-info-value-privateAtom'), - ).toHaveTextContent('privateAtom'); - expect(screen.getByText('Value type')).toBeInTheDocument(); - expect(screen.getByText('number')).toBeInTheDocument(); - expect(screen.getByText('Private')).toBeInTheDocument(); - expect(screen.getByText('Yes')).toBeInTheDocument(); - - expect(screen.getByText('Raw value')).toBeInTheDocument(); - expect(screen.getByTestId('atom-parsed-value')).toHaveTextContent('0'); - - expect(screen.getByText('Dependents')).toBeInTheDocument(); - expect( - screen.getByTestId('dependents-list-item-doubleCountAtom-0'), - ).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - }); - - describe('Search', () => { - it('should search for atoms correctly', async () => { - const { container } = customRender(); - - await act(async () => { - await userEvent.type( - screen.getByLabelText('Search'), - 'doubleCountAtom', - ); - }); - - expect( - screen.queryByTestId('atom-list-no-atoms-found-message'), - ).not.toBeInTheDocument(); - expect(screen.queryByText('countAtom')).not.toBeInTheDocument(); - expect(screen.getByText('doubleCountAtom')).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - - it('should display an error if no atoms are found', async () => { - const { container } = customRender(); - - await act(async () => { - await userEvent.type(screen.getByLabelText('Search'), 'abc 123'); - }); - expect( - screen.getByTestId('atom-list-no-atoms-found-message'), - ).toHaveTextContent('No Atoms found!'); - expect(screen.queryByText('countAtom')).not.toBeInTheDocument(); - expect(screen.queryByText('doubleCountAtom')).not.toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - }); - - describe('auto unmount', () => { - it('should unselect the atom when an atom is unsubscribed', async () => { - const BasicAtoms = () => { - const countAtom = useMemo(() => atom(0), []); - countAtom.debugLabel = 'countAtom'; - const doubleCountAtom = useMemo( - () => atom((get) => get(countAtom) * 2), - [countAtom], - ); - doubleCountAtom.debugLabel = 'doubleCountAtom'; - useAtomValue(doubleCountAtom); - - return
; - }; - - const ToggleAbleAtomWithDevTools = () => { - const [shouldShow, setShouldShow] = React.useState(true); - - const handleOntoggle = React.useCallback(() => { - setShouldShow((s) => !s); - }, [setShouldShow]); - - return ( - <> - {shouldShow ? : null} - - - ); - }; - - const TestComponent = () => { - return ( - <> - - - - ); - }; - - customRender(); - await act(async () => { - await userEvent.click(screen.getByText('doubleCountAtom')); - }); - expect( - screen.getByTestId('meta-info-value-doubleCountAtom'), - ).toBeInTheDocument(); - - await act(async () => { - await userEvent.click(screen.getByText('Toggle')); - }); - - expect(screen.queryByText('Atom Details')).not.toBeInTheDocument(); - expect( - screen.queryByText('meta-info-value-doubleCountAtom'), - ).not.toBeInTheDocument(); - expect( - screen.getByTestId('atom-list-no-atoms-found-message'), - ).toHaveTextContent('No Atoms found!'); - expect(screen.getByLabelText('Search')).toBeInTheDocument(); - expect( - screen.getByText( - 'Select an atom from the left panel to view the details', - ), - ).toBeInTheDocument(); - }); - }); - }); - - describe('Atom details', () => { - describe('Raw value', () => { - it('should display an error when we are not able to parse the value', async () => { - const stringifySpy = jest - .spyOn(stringifyModule, 'stringify') - .mockImplementation(() => { - throw new Error('some-error'); - }); - - const { container } = customRender(); - - await act(async () => { - await userEvent.click(screen.getByText('countAtom')); - }); - - expect(screen.getByText('Atom Details')).toBeInTheDocument(); - expect(screen.getByText('Meta')).toBeInTheDocument(); - expect(screen.getByText('Debug Label')).toBeInTheDocument(); - - expect(screen.getByText('Raw value')).toBeInTheDocument(); - expect( - screen.getByText('Failed to parse the value of the atom'), - ).toBeInTheDocument(); - expect(screen.getByText('Dependents')).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - stringifySpy.mockRestore(); - }); - - it('should display atom details when an atom is selected', async () => { - const { container } = customRender(); - - await act(async () => { - await userEvent.click(screen.getByText('countAtom')); - }); - - expect(screen.getByText('Atom Details')).toBeInTheDocument(); - expect(screen.getByText('Meta')).toBeInTheDocument(); - expect(screen.getByText('Debug Label')).toBeInTheDocument(); - expect( - screen.getByTestId('meta-info-value-countAtom'), - ).toHaveTextContent('countAtom'); - expect(screen.getByText('Value type')).toBeInTheDocument(); - expect(screen.getByText('number')).toBeInTheDocument(); - - expect(screen.getByText('Raw value')).toBeInTheDocument(); - expect(screen.getByTestId('atom-parsed-value')).toHaveTextContent('0'); - - expect(screen.getByText('Dependents')).toBeInTheDocument(); - expect( - screen.getByTestId('dependents-list-item-doubleCountAtom-0'), - ).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - - it('should display the dependents of the atom correctly', async () => { - const { container } = render(); - - await act(async () => { - await userEvent.click(screen.getByText('doubleCountAtom')); - }); - - expect(screen.getByText('Atom Details')).toBeInTheDocument(); - - expect(screen.getByText('Dependents')).toBeInTheDocument(); - expect(screen.getByText('No dependents')).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - - describe('Supports most primitive value types', () => { - const AtomRenderer = ({ atom }: { atom: AnyAtom }) => { - useAtomValue(atom); - return ; - }; - - const circObj: any = { circObj: null }; - circObj.circObj = circObj; - - it.each` - type | value | expected - ${'string'} | ${'some-string'} | ${'some-string'} - ${'number'} | ${123} | ${123} - ${'boolean'} | ${true} | ${true} - ${'boolean'} | ${false} | ${false} - ${'null'} | ${null} | ${'null'} - ${'undefined'} | ${undefined} | ${'undefined'} - ${'bigint'} | ${BigInt(123)} | ${'123'} - ${'symbol'} | ${Symbol('some-symbol')} | ${'Symbol(some-symbol)'} - ${'function'} | ${() => () => 'hello'} | ${"()=>'hello'"} - ${'object'} | ${{ foo: 'bar' }} | ${'{ foo: "bar" }'} - ${'circular-object'} | ${circObj} | ${'{}'} - ${'array'} | ${[1, 2, 3]} | ${'[ 1, 2, 3 ]'} - `( - 'should parse "$type" value correctly', - async ({ value, expected }) => { - const valueAtom = atom(value); - valueAtom.debugLabel = 'valueAtom'; - - customRender(); - - await act(async () => { - await userEvent.click(screen.getByText('valueAtom')); - }); - - expect(screen.getByTestId('atom-parsed-value')).toHaveTextContent( - expected, - ); - }, - ); - }); - }); - describe('JSON value', () => { - const localStorageSetItemSpy = jest.spyOn( - window.localStorage.__proto__, - 'setItem', - ); - - const localStorageGetItemSpy = jest.spyOn( - window.localStorage.__proto__, - 'getItem', - ); - - beforeAll(() => { - localStorageSetItemSpy.mockImplementation(jest.fn()); - localStorageGetItemSpy.mockImplementation(jest.fn()); - }); - - afterAll(() => { - localStorageSetItemSpy.mockRestore(); - localStorageGetItemSpy.mockRestore(); - }); - - it('should display both the options when object is compatible with JSON tree view and defaults to raw value', async () => { - const ObjectAtomsWithDevTools = () => { - // Create atoms inside the component so that they are recreated for each test - const objectAtom = useMemo(() => atom({ x: 0, a: { b: 'c' } }), []); - objectAtom.debugLabel = 'objectAtom'; - - useAtomValue(objectAtom); - return ; - }; - - customRender(); - - await act(async () => { - await userEvent.click(screen.getByText('objectAtom')); - }); - - expect(screen.getByText('Raw value')).toBeInTheDocument(); - expect(screen.getByText('Tree view')).toBeInTheDocument(); - expect(screen.getByTestId('atom-parsed-value')).toBeVisible(); - expect(screen.getByTestId('json-tree-panel')).not.toBeVisible(); - }); - it('should display JSON tree view when user selects from the tab header', async () => { - const ObjectAtomsWithDevTools = () => { - // Create atoms inside the component so that they are recreated for each test - const objectAtom = useMemo(() => atom({ x: 0, a: { b: 'c' } }), []); - objectAtom.debugLabel = 'objectAtom'; - - useAtomValue(objectAtom); - return ; - }; - customRender(); - await act(async () => { - await userEvent.click(screen.getByText('objectAtom')); - }); - - expect(screen.getByTestId('json-tree-panel')).not.toBeVisible(); - - await act(async () => { - await userEvent.click(screen.getByText('Tree view')); - }); - - expect(screen.getByTestId('json-tree-panel')).toBeVisible(); - expect(screen.getByTestId('json-tree-panel')).toMatchSnapshot(); - expect( - screen.queryByTestId('atom-parsed-value'), - ).not.toBeInTheDocument(); - - expect(screen.getByTestId('json-tree-panel')).toHaveTextContent( - `x:0▶a:{ b: "c" }`, - ); - }); - - it('should display JSON tree view with all the values expanded', async () => { - const ObjectAtomsWithDevTools = () => { - // Create atoms inside the component so that they are recreated for each test - const objectAtom = useMemo(() => atom({ x: 0, a: { b: 'c' } }), []); - objectAtom.debugLabel = 'objectAtom'; - - useAtomValue(objectAtom); - return ( - - ); - }; - - customRender(); - await act(async () => { - await userEvent.click(screen.getByText('objectAtom')); - }); - - expect(screen.getByTestId('json-tree-panel')).not.toBeVisible(); - - await act(async () => { - await userEvent.click(screen.getByText('Tree view')); - }); - - expect(screen.getByTestId('json-tree-panel')).toMatchSnapshot(); - expect(screen.getByTestId('json-tree-panel')).toHaveTextContent( - `x:0▶a:{ b: "c" }b:"c"`, - ); - }); - }); - }); -}); diff --git a/__tests__/devtools/store1/TimeTravel.test.tsx b/__tests__/devtools/store1/TimeTravel.test.tsx deleted file mode 100644 index 692c357c..00000000 --- a/__tests__/devtools/store1/TimeTravel.test.tsx +++ /dev/null @@ -1,653 +0,0 @@ -import React, { useMemo } from 'react'; -import { act, fireEvent, screen, waitFor } from '@testing-library/react'; -import { userEvent } from '@testing-library/user-event'; -import { atom, useAtomValue, useSetAtom } from 'jotai'; -import { DevTools, DevToolsProps } from 'jotai-devtools'; -import { customRender } from '../../custom-render'; - -const BasicAtomsWithDevTools = (props: DevToolsProps) => { - // Create atoms inside the component so that they are recreated for each test - const countAtom = useMemo(() => atom(0), []); - countAtom.debugLabel = 'countAtom'; - const doubleAtom = useMemo( - () => atom((get) => get(countAtom) * 2), - [countAtom], - ); - - doubleAtom.debugLabel = 'doubleCountAtom'; - - const count = useAtomValue(countAtom); - const setCount = useSetAtom(countAtom); - useAtomValue(doubleAtom); - return ( -
- {count} - - -
- ); -}; -describe('DevTools - TimeTravel', () => { - const localStorageSetItemSpy = jest.spyOn( - window.localStorage.__proto__, - 'setItem', - ); - - const localStorageGetItemSpy = jest.spyOn( - window.localStorage.__proto__, - 'getItem', - ); - - beforeAll(() => { - localStorageSetItemSpy.mockImplementation(jest.fn()); - localStorageGetItemSpy.mockImplementation(jest.fn()); - }); - - afterAll(() => { - localStorageSetItemSpy.mockRestore(); - localStorageGetItemSpy.mockRestore(); - }); - describe('Snapshot list', () => { - it('should render time travel without any errors', () => { - customRender(); - expect(screen.getByText('👻 Jōtai DevTools')).toBeInTheDocument(), - expect(screen.getByText('Time travel')).toBeInTheDocument(); - - fireEvent.click(screen.getByText('Time travel')); - expect(screen.getByPlaceholderText('Search')).toBeInTheDocument(), - expect( - screen.getByText( - 'Select a snapshot from the left panel to view the details', - ), - ).toBeInTheDocument(); - expect(screen.getByTestId('jotai-devtools-shell')).toMatchSnapshot(); - }); - - it('should clear the list when user presses the trash icon and show the last snapshot on the right side', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - expect( - screen.getByTestId('jotai-devtools-snapshot-1'), - ).toBeInTheDocument(); - fireEvent.click(screen.getByTitle('Clear snapshot history')); - expect( - screen.getByTestId('jotai-devtools-snapshot-history-list'), - ).toBeEmptyDOMElement(); - expect(screen.getByText('Snapshot')).toBeInTheDocument(); - expect( - screen.queryByText( - 'Select a snapshot from the left panel to view the details', - ), - ).not.toBeInTheDocument(); - }); - - it('should limit the number of snapshot history based on props', () => { - customRender( - , - ); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - expect( - screen.getByTestId('jotai-devtools-snapshot-2'), - ).toBeInTheDocument(); - expect( - screen.getByTestId('jotai-devtools-snapshot-3'), - ).toBeInTheDocument(); - expect( - screen.getAllByTestId(/jotai-devtools-snapshot-[0-9]/), - ).toHaveLength(2); - }); - - describe('snapshot list navigation', () => { - it('should display next snapshot when user clicks on the next button', async () => { - customRender(); - - await userEvent.click(screen.getByText('Time travel')); - await userEvent.click(screen.getByLabelText('Record snapshot history')); - await userEvent.click(screen.getByText('Increment')); - await userEvent.click(screen.getByText('Increment')); - - await userEvent.click(screen.getByTestId('jotai-devtools-snapshot-1')); - - await userEvent.click(screen.getByTitle('Select next snapshot')); - - expect(screen.getByText('Snapshot 2')).toBeInTheDocument(); - }); - - it('should disable next snapshot button if user has selected the last snapshot', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-2')); - - expect(screen.getByTitle('Select next snapshot')).toBeDisabled(); - }); - - it('should display previous snapshot when user clicks on the previous button', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-2')); - - fireEvent.click(screen.getByTitle('Select previous snapshot')); - - expect(screen.getByText('Snapshot 1')).toBeInTheDocument(); - }); - - it('should disable previous snapshot button if user has selected the first snapshot', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-1')); - - expect(screen.getByTitle('Select previous snapshot')).toBeDisabled(); - }); - }); - - describe('Search', () => { - it('should search for atoms correctly', async () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - - await act(async () => { - await userEvent.type(screen.getByPlaceholderText('Search'), '2'); - }); - - expect( - screen.getByTestId('jotai-devtools-snapshot-2'), - ).toBeInTheDocument(); - - expect( - screen.queryByTestId('jotai-devtools-snapshot-1'), - ).not.toBeInTheDocument(); - - expect( - screen.queryByTestId('jotai-devtools-no-snapshot-found-message'), - ).not.toBeInTheDocument(); - - expect( - screen.getByTestId('jotai-devtools-time-travel-panel-left-content'), - ).toMatchSnapshot(); - }); - - it('should display an error if no snapshots are found', async () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - - await act(async () => { - await userEvent.type(screen.getByPlaceholderText('Search'), 'a'); - }); - expect( - screen.getByTestId('jotai-devtools-no-snapshot-found-message'), - ).toHaveTextContent('No snapshots found!'); - expect( - screen.queryByTestId('jotai-devtools-snapshot-1'), - ).not.toBeInTheDocument(); - expect( - screen.getByTestId('jotai-devtools-time-travel-panel-left-content'), - ).toMatchSnapshot(); - }); - }); - describe('Snapshot history with actions', () => { - const og = global.performance; - beforeEach(() => { - jest.useFakeTimers({ advanceTimers: true }); - jest.setSystemTime(new Date('2023-01-01T10:00:00.000Z')); - - const mockPerformance = { - timeOrigin: Date.now(), - now: jest.fn(() => 0), - }; - global.performance = mockPerformance as unknown as Performance; - }); - afterEach(() => { - global.performance = og; - jest.runOnlyPendingTimers(); - jest.useRealTimers(); - }); - it('should render empty snapshot list when there is action', () => { - customRender(); - fireEvent.click(screen.getByText('Time travel')); - expect( - screen.getByTestId('jotai-devtools-snapshot-history-list'), - ).toBeEmptyDOMElement(); - }); - - it('should render a snapshot list when there is action but recording is turned off', () => { - customRender(); - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByText('Increment')); - expect( - screen.queryByTestId('jotai-devtools-snapshot-1'), - ).not.toBeInTheDocument(); - }); - - it('should render a snapshot list with initial snapshot details on action with recording on', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - jest.clearAllTimers(); - fireEvent.click(screen.getByText('Increment')); - expect( - screen.getByTestId('jotai-devtools-snapshot-1'), - ).toBeInTheDocument(); - expect(screen.getByText('Snapshot')).toBeInTheDocument(); - expect(screen.getByText('Meta')).toBeInTheDocument(); - expect( - screen.getByTestId('meta-info-label-Timestamp'), - ).toBeInTheDocument(); - - expect(screen.getByText('10:00:00.000 AM')).toBeInTheDocument(); - expect(screen.getByText('Value')).toBeInTheDocument(); - expect(screen.getByText('State')).toBeInTheDocument(); - expect(screen.getByText('Diff')).toBeInTheDocument(); - expect(screen.getByText('Actions')).toBeInTheDocument(); - expect(screen.getByText('Restore')).toBeInTheDocument(); - expect( - screen.getByTestId('jotai-devtools-time-travel-recording-indicator'), - ).toBeInTheDocument(); - - expect(screen.getByTestId('jotai-devtools-shell')).toMatchSnapshot(); - }); - }); - }); - - describe('Snapshot details', () => { - it('should make the restore button disabled if if the states are equal', async () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-1')); - - await waitFor(() => - expect(screen.getByTitle('Restore this state')).toBeDisabled(), - ); - }); - it('should display the full state in json tree format when state is selected', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-1')); - fireEvent.click(screen.getByText('State')); - expect(screen.getByTestId('json-tree-view-container')).toMatchSnapshot(); - }); - - describe('diff', () => { - const AddMoveRemoveDiffTest = () => { - const countAtom = useMemo(() => atom(0), []); - countAtom.debugLabel = 'countAtom'; - // Create atoms inside the component so that they are recreated for each test - const objectAtom = useMemo( - () => - atom({ - add: { - arr: [1], - }, - move: { - arr: [1, 2], - }, - remove: { - arr: [1, 2, 3], - }, - }), - [], - ); - objectAtom.debugLabel = 'objectAtom'; - - useAtomValue(objectAtom); - const setObject = useSetAtom(objectAtom); - const add = () => { - setObject((prev) => ({ - ...prev, - add: { - arr: [...prev.add.arr, 2], - }, - })); - }; - - const move = () => { - setObject((prev) => ({ - ...prev, - move: { - arr: [2, 1], - }, - })); - }; - const remove = () => { - setObject((prev) => ({ - ...prev, - remove: { - arr: [1, 3], - }, - })); - }; - - const count = useAtomValue(countAtom); - const setCount = useSetAtom(countAtom); - return ( -
- - {count} - - - - -
- ); - }; - - it('should show "states are equal" msg when there is no diff', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-1')); - fireEvent.click(screen.getByText('Diff')); - expect(screen.getByText('(states are equal)')).toBeInTheDocument(); - }); - - it('should display highlighted diff', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-2')); - fireEvent.click(screen.getByText('Diff')); - expect( - screen.getByTestId('jotai-devtools-diff-panel'), - ).toMatchSnapshot(); - }); - - it.each` - operation - ${'Add'} - ${'Move'} - ${'Remove'} - `( - 'should display highlighted diff when performing "$operation" on arrays and objects', - ({ operation }) => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - - fireEvent.click(screen.getByText(operation)); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-2')); - fireEvent.click(screen.getByText('Diff')); - expect( - screen.getByTestId('jotai-devtools-diff-panel'), - ).toMatchSnapshot(); - }, - ); - }); - - it('should restore the snapshot when user clicks on "restore" button', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-2')); - - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('3'); - fireEvent.click(screen.getByTitle('Restore this state')); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('2'); - }); - - it('should not add another snapshot history entry when restoring', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTestId('jotai-devtools-snapshot-2')); - - expect( - screen.getAllByTestId(/jotai-devtools-snapshot-[0-9]/), - ).toHaveLength(3); - fireEvent.click(screen.getByTitle('Restore this state')); - expect( - screen.getAllByTestId(/jotai-devtools-snapshot-[0-9]/), - ).toHaveLength(3); - }); - }); - - describe('Time travel', () => { - const defaultTravelTime = 750; - it('should automatically play through the snapshots history when user clicks on "play" button', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('3'); - jest.useFakeTimers(); - fireEvent.click(screen.getByTitle('Start time travel')); - - act(() => { - jest.advanceTimersByTime(defaultTravelTime); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('1'); - - act(() => { - jest.advanceTimersByTime(defaultTravelTime); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('2'); - - act(() => { - jest.advanceTimersByTime(defaultTravelTime); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('3'); - jest.useRealTimers(); - }); - - it('should pause time travel when user clicks on "pause" button', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - jest.useFakeTimers(); - fireEvent.click(screen.getByTitle('Start time travel')); - - act(() => { - jest.advanceTimersByTime(defaultTravelTime); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('1'); - - fireEvent.click(screen.getByTitle('Pause time travel')); - act(() => { - jest.advanceTimersByTime(defaultTravelTime); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('1'); - jest.useRealTimers(); - }); - - it('should change the "play" button to "pause" button when time travelling', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - jest.useFakeTimers(); - fireEvent.click(screen.getByTitle('Start time travel')); - expect(screen.getByTitle('Pause time travel')).toBeInTheDocument(); - - act(() => { - jest.advanceTimersByTime(defaultTravelTime); - }); - - expect(screen.getByTitle('Start time travel')).toBeInTheDocument(); - jest.useRealTimers(); - }); - - it('should allow user to change the speed of time travel via props', async () => { - const timeTravelPlaybackInterval = 500; - customRender( - , - ); - - await userEvent.click(screen.getByText('Time travel')); - await userEvent.click(screen.getByLabelText('Record snapshot history')); - await userEvent.click(screen.getByText('Increment')); - await userEvent.click(screen.getByText('Increment')); - jest.useFakeTimers(); - const user = userEvent.setup({ delay: null }); - await user.click(screen.getByTitle('Start time travel')); - - act(() => { - jest.advanceTimersByTime(timeTravelPlaybackInterval); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('1'); - - act(() => { - jest.advanceTimersByTime(timeTravelPlaybackInterval); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('2'); - jest.useRealTimers(); - }); - // Interval is calculated using time + time * speed formula - it.each` - speed | interval - ${'0.5x'} | ${1500} - ${'1x'} | ${750} - ${'1.5x'} | ${500} - ${'1.75x'} | ${428.5} - ${'2x'} | ${375} - `( - 'should change the speed to "$interval" when user selects "$speed" from the dropdown', - async ({ speed, interval }) => { - customRender(); - - await userEvent.click(screen.getByText('Time travel')); - await userEvent.click(screen.getByLabelText('Record snapshot history')); - await userEvent.click(screen.getByText('Increment')); - await userEvent.click(screen.getByText('Increment')); - - jest.useFakeTimers(); - - const user = userEvent.setup({ delay: null }); - await user.click( - screen.getByTestId('jotai-devtools-playback-speed-dropdown'), - ); - await user.click(screen.getByText(speed)); - await user.click( - screen.getByTestId('jotai-devtools-playback-speed-dropdown'), - ); - await user.click(screen.getByTitle('Start time travel')); - - act(() => { - jest.advanceTimersByTime(interval); - }); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('1'); - - jest.useRealTimers(); - }, - ); - - describe('manual time travel', () => { - it('should restore next snapshot when user clicks on the next button', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - - fireEvent.click(screen.getByTitle('Restore next snapshot')); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('1'); - }); - - it('should disable next snapshot restore button if user has selected the last snapshot', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByTitle('Restore next snapshot')); - - expect(screen.getByTitle('Restore next snapshot')).toBeDisabled(); - }); - - it('should display previous snapshot when user clicks on the previous snapshot restore button', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - fireEvent.click(screen.getByText('Increment')); - - fireEvent.click(screen.getByTitle('Restore next snapshot')); - fireEvent.click(screen.getByTitle('Restore next snapshot')); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('2'); - - fireEvent.click(screen.getByTitle('Restore previous snapshot')); - expect(screen.getByTestId('count-atom-value')).toHaveTextContent('1'); - }); - - it('should disable previous snapshot restore button if user has selected the first snapshot', () => { - customRender(); - - fireEvent.click(screen.getByText('Time travel')); - fireEvent.click(screen.getByLabelText('Record snapshot history')); - fireEvent.click(screen.getByText('Increment')); - - expect(screen.getByTitle('Restore previous snapshot')).toBeDisabled(); - }); - }); - }); -}); diff --git a/__tests__/devtools/store1/__snapshots__/AtomViewer.test.tsx.snap b/__tests__/devtools/store1/__snapshots__/AtomViewer.test.tsx.snap deleted file mode 100644 index 0a1cbc95..00000000 --- a/__tests__/devtools/store1/__snapshots__/AtomViewer.test.tsx.snap +++ /dev/null @@ -1,4959 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DevTools - AtomViewer Atom details JSON value should display JSON tree view when user selects from the tab header 1`] = ` -
-
-
    -
  • -
      -
    • - - - 0 - -
    • -
    • -
      -
      - ▶ -
      -
      - - - { b: "c" } - -
        - -
      -
    • -
    -
-
-`; - -exports[`DevTools - AtomViewer Atom details JSON value should display JSON tree view with all the values expanded 1`] = ` -
-
-
    -
  • -
      -
    • - - - 0 - -
    • -
    • -
      -
      - ▶ -
      -
      - - - { b: "c" } - -
        -
      • - - - "c" - -
      • -
      -
    • -
    -
  • -
-
-
-`; - -exports[`DevTools - AtomViewer Atom details Raw value should display an error when we are not able to parse the value 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
- - -
- -
- - -
-`; - -exports[`DevTools - AtomViewer Atom details Raw value should display atom details when an atom is selected 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
- - -
- -
- - -
-`; - -exports[`DevTools - AtomViewer Atom details Raw value should display the dependents of the atom correctly 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
- - -
- -
- - -
-`; - -exports[`DevTools - AtomViewer List of atoms Search should display an error if no atoms are found 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
-
- - - - - -

- No Atoms found! -

-
-
-
- -
- - -
-`; - -exports[`DevTools - AtomViewer List of atoms Search should search for atoms correctly 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
- - -
-
- -
- - -
-`; - -exports[`DevTools - AtomViewer List of atoms private atoms should hide private atoms from dependents list when shouldShowPrivateAtoms is marked as false 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
- - -
- -
- - -
-`; - -exports[`DevTools - AtomViewer List of atoms private atoms should mark private atoms in atom details 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
- - - -
- - -
-`; - -exports[`DevTools - AtomViewer List of atoms should render atom viewer with correct atoms without provider 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
- - -
- -
- - -
-`; - -exports[`DevTools - AtomViewer List of atoms should render atom viewer without any errors if there are no atoms 1`] = ` -
- - -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
- -
-
-
-
- - - - - -

- No Atoms found! -

-
-
-
- -
- - -
-`; diff --git a/__tests__/devtools/store1/__snapshots__/TimeTravel.test.tsx.snap b/__tests__/devtools/store1/__snapshots__/TimeTravel.test.tsx.snap deleted file mode 100644 index 7fb44ba2..00000000 --- a/__tests__/devtools/store1/__snapshots__/TimeTravel.test.tsx.snap +++ /dev/null @@ -1,2666 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DevTools - TimeTravel Snapshot details diff should display highlighted diff 1`] = ` -
-
-
    -
  • -
      -
    • - - - - 1 - - - => - - - 2 - - -
    • -
    • - - - - 2 - - - => - - - 4 - - -
    • -
    -
  • -
-
-
-`; - -exports[`DevTools - TimeTravel Snapshot details diff should display highlighted diff when performing "Add" on arrays and objects 1`] = ` -
-
-
    -
  • -
      -
    • -
      -
      - ▶ -
      -
      - - - { add: {…} } - -
        -
      • -
        -
        - ▶ -
        -
        - - - { arr: {…} } - -
          -
        • -
          -
          - ▶ -
          -
          - - - { 1: 2 } - -
            -
          • - - - - 2 - - -
          • -
          -
        • -
        -
      • -
      -
    • -
    -
  • -
-
-
-`; - -exports[`DevTools - TimeTravel Snapshot details diff should display highlighted diff when performing "Move" on arrays and objects 1`] = ` -
-
-
    -
  • -
      -
    • -
      -
      - ▶ -
      -
      - - - { move: {…} } - -
        -
      • -
        -
        - ▶ -
        -
        - - - { arr: {…} } - -
          -
        • -
          -
          - ▶ -
          -
          - - - { 0: 2, 1: 2 } - -
            -
          • - - - - 2 - - -
          • -
          • - - - - 2 - - -
          • -
          -
        • -
        -
      • -
      -
    • -
    -
  • -
-
-
-`; - -exports[`DevTools - TimeTravel Snapshot details diff should display highlighted diff when performing "Remove" on arrays and objects 1`] = ` -
-
-
    -
  • -
      -
    • -
      -
      - ▶ -
      -
      - - - { remove: {…} } - -
        -
      • -
        -
        - ▶ -
        -
        - - - { arr: {…} } - -
          -
        • -
          -
          - ▶ -
          -
          - - - { 1: 2 } - -
            -
          • - - - - 2 - - -
          • -
          -
        • -
        -
      • -
      -
    • -
    -
  • -
-
-
-`; - -exports[`DevTools - TimeTravel Snapshot details should display the full state in json tree format when state is selected 1`] = ` -
-
    -
  • -
      -
    • - - - 1 - -
    • -
    • - - - 2 - -
    • -
    -
  • -
-
-`; - -exports[`DevTools - TimeTravel Snapshot list Search should display an error if no snapshots are found 1`] = ` -
-
-
-
- - -
-
- - -
-
-
-
- -
-
-
-
-
- - - - - -

- No snapshots found! -

-
-
-`; - -exports[`DevTools - TimeTravel Snapshot list Search should search for atoms correctly 1`] = ` -
-
-
-
- - -
-
- - -
-
-
-
- -
-
-
-
- - -
-`; - -exports[`DevTools - TimeTravel Snapshot list Snapshot history with actions should render a snapshot list with initial snapshot details on action with recording on 1`] = ` -
-
-
-
-
-

- 👻 Jōtai DevTools -

-
- - Alpha - -
-
-
- - -
-
-
-
- - -
-