2023-12-04 15:47:53 +01:00
|
|
|
import { render } from 'utils/testRenderer';
|
|
|
|
import { screen, waitFor } from '@testing-library/react';
|
|
|
|
import { usePersistentTableState } from './usePersistentTableState';
|
|
|
|
import { Route, Routes } from 'react-router-dom';
|
|
|
|
import { createLocalStorage } from '../utils/createLocalStorage';
|
2024-01-19 09:27:47 +01:00
|
|
|
import { ArrayParam, NumberParam, StringParam } from 'use-query-params';
|
2023-12-05 17:31:23 +01:00
|
|
|
import { FilterItemParam } from '../utils/serializeQueryParams';
|
2023-12-04 15:47:53 +01:00
|
|
|
|
|
|
|
type TestComponentProps = {
|
|
|
|
keyName: string;
|
|
|
|
queryParamsDefinition: Record<string, any>;
|
2024-08-29 15:43:16 +02:00
|
|
|
nonPersistentParams?: string[];
|
2023-12-04 15:47:53 +01:00
|
|
|
};
|
|
|
|
|
2024-08-29 15:43:16 +02:00
|
|
|
function TestComponent({
|
|
|
|
keyName,
|
|
|
|
queryParamsDefinition,
|
|
|
|
nonPersistentParams,
|
|
|
|
}: TestComponentProps) {
|
2023-12-04 15:47:53 +01:00
|
|
|
const [tableState, setTableState] = usePersistentTableState(
|
|
|
|
keyName,
|
|
|
|
queryParamsDefinition,
|
2024-08-29 15:43:16 +02:00
|
|
|
nonPersistentParams,
|
2023-12-04 15:47:53 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Routes>
|
|
|
|
<Route
|
|
|
|
path={'/my-url'}
|
|
|
|
element={
|
|
|
|
<div>
|
|
|
|
<span data-testid='state-value'>
|
|
|
|
{tableState.query}
|
|
|
|
</span>
|
2023-12-19 15:35:39 +01:00
|
|
|
<span data-testid='state-keys'>
|
|
|
|
{Object.keys(tableState).join(',')}
|
|
|
|
</span>
|
2023-12-04 15:47:53 +01:00
|
|
|
<button
|
|
|
|
type='button'
|
|
|
|
onClick={() => setTableState({ query: 'after' })}
|
|
|
|
>
|
|
|
|
Update State
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
type='button'
|
|
|
|
onClick={() => setTableState({ offset: 20 })}
|
|
|
|
>
|
|
|
|
Update Offset
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</Routes>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
describe('usePersistentTableState', () => {
|
|
|
|
it('initializes correctly from URL', async () => {
|
|
|
|
createLocalStorage('testKey', {});
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{ query: StringParam }}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url?query=initialUrl' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(screen.getByTestId('state-value').textContent).toBe(
|
|
|
|
'initialUrl',
|
|
|
|
);
|
|
|
|
expect(window.location.href).toContain('my-url?query=initialUrl');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('initializes correctly from localStorage', async () => {
|
|
|
|
createLocalStorage('testKey', {}).setValue({ query: 'initialStorage' });
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{ query: StringParam }}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(screen.getByTestId('state-value').textContent).toBe(
|
|
|
|
'initialStorage',
|
|
|
|
);
|
|
|
|
expect(window.location.href).toContain('my-url?query=initialStorage');
|
|
|
|
});
|
|
|
|
|
2023-12-05 17:31:23 +01:00
|
|
|
it('initializes correctly from localStorage with complex decoder', async () => {
|
|
|
|
createLocalStorage('testKey', {}).setValue({
|
|
|
|
query: 'initialStorage',
|
2024-06-11 12:59:52 +02:00
|
|
|
filterItem: {
|
|
|
|
operator: 'IS',
|
|
|
|
values: ['default'],
|
|
|
|
},
|
2024-01-19 09:27:47 +01:00
|
|
|
columns: ['a', 'b'],
|
2023-12-05 17:31:23 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{
|
|
|
|
query: StringParam,
|
|
|
|
filterItem: FilterItemParam,
|
2024-01-19 09:27:47 +01:00
|
|
|
columns: ArrayParam,
|
2023-12-05 17:31:23 +01:00
|
|
|
}}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(screen.getByTestId('state-value').textContent).toBe(
|
|
|
|
'initialStorage',
|
|
|
|
);
|
|
|
|
expect(window.location.href).toContain(
|
2024-01-19 09:27:47 +01:00
|
|
|
'my-url?query=initialStorage&filterItem=IS%3Adefault&columns=a&columns=b',
|
2023-12-05 17:31:23 +01:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2023-12-04 15:47:53 +01:00
|
|
|
it('initializes correctly from localStorage and URL', async () => {
|
|
|
|
createLocalStorage('testKey', {}).setValue({ query: 'initialStorage' });
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{ query: StringParam }}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url?query=initialUrl' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(screen.getByTestId('state-value').textContent).toBe(
|
|
|
|
'initialUrl',
|
|
|
|
);
|
|
|
|
expect(window.location.href).toContain('my-url?query=initialUrl');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('partially updates the state on button click', async () => {
|
|
|
|
createLocalStorage('testKey', {}).setValue({
|
|
|
|
query: 'before',
|
|
|
|
other: 'other',
|
|
|
|
});
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{
|
|
|
|
query: StringParam,
|
|
|
|
other: StringParam,
|
|
|
|
}}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(screen.getByTestId('state-value').textContent).toBe('before');
|
|
|
|
|
2024-06-11 12:59:52 +02:00
|
|
|
(await screen.findByText('Update State')).click();
|
2023-12-04 15:47:53 +01:00
|
|
|
|
2024-06-11 12:59:52 +02:00
|
|
|
expect((await screen.findByTestId('state-value')).textContent).toBe(
|
|
|
|
'after',
|
|
|
|
);
|
2023-12-04 15:47:53 +01:00
|
|
|
expect(window.location.href).toContain(
|
|
|
|
'my-url?query=after&other=other',
|
|
|
|
);
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
const { value } = createLocalStorage('testKey', {});
|
2024-06-11 12:59:52 +02:00
|
|
|
expect(value).toStrictEqual({
|
|
|
|
query: 'after',
|
|
|
|
other: 'other',
|
|
|
|
});
|
2023-12-04 15:47:53 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('omits offset in local storage', async () => {
|
|
|
|
createLocalStorage('testKey', {}).setValue({ query: 'before' });
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{
|
|
|
|
query: StringParam,
|
|
|
|
offset: NumberParam,
|
|
|
|
}}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url' },
|
|
|
|
);
|
|
|
|
|
|
|
|
screen.getByText('Update Offset').click();
|
|
|
|
screen.getByText('Update State').click();
|
|
|
|
|
2023-12-15 10:20:55 +01:00
|
|
|
expect(window.location.href).toContain('my-url?query=after&offset=0');
|
2023-12-04 15:47:53 +01:00
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
const { value } = createLocalStorage('testKey', {});
|
|
|
|
expect(value).toStrictEqual({ query: 'after' });
|
|
|
|
});
|
|
|
|
});
|
2023-12-15 10:20:55 +01:00
|
|
|
|
|
|
|
it('resets offset to 0 on state update', async () => {
|
|
|
|
createLocalStorage('testKey', {}).setValue({ query: 'before' });
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{
|
|
|
|
query: StringParam,
|
|
|
|
offset: NumberParam,
|
|
|
|
}}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url?query=before&offset=10' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(window.location.href).toContain('my-url?query=before&offset=10');
|
|
|
|
|
|
|
|
screen.getByText('Update State').click();
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
expect(window.location.href).toContain(
|
|
|
|
'my-url?query=after&offset=0',
|
|
|
|
);
|
|
|
|
expect(window.location.href).not.toContain('offset=10');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not reset offset to 0 without offset decoder', async () => {
|
|
|
|
createLocalStorage('testKey', {}).setValue({ query: 'before' });
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{
|
|
|
|
query: StringParam,
|
|
|
|
}}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url?query=before&offset=10' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(window.location.href).toContain('my-url?query=before&offset=10');
|
|
|
|
|
|
|
|
screen.getByText('Update State').click();
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
expect(window.location.href).toContain(
|
|
|
|
'my-url?query=after&offset=10',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2023-12-19 15:35:39 +01:00
|
|
|
|
|
|
|
it('maintains key order', async () => {
|
|
|
|
createLocalStorage('testKey', {});
|
|
|
|
|
|
|
|
render(
|
|
|
|
<TestComponent
|
|
|
|
keyName='testKey'
|
|
|
|
queryParamsDefinition={{
|
|
|
|
query: StringParam,
|
|
|
|
another: StringParam,
|
|
|
|
ignore: StringParam,
|
|
|
|
}}
|
|
|
|
/>,
|
|
|
|
{ route: '/my-url?another=another&query=initialUrl' },
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(screen.getByTestId('state-keys').textContent).toBe(
|
|
|
|
'another,query,ignore',
|
|
|
|
);
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
const { value } = createLocalStorage('testKey', {});
|
|
|
|
expect(Object.keys(value)).toStrictEqual(['another', 'query']);
|
|
|
|
});
|
|
|
|
});
|
2023-12-04 15:47:53 +01:00
|
|
|
});
|