import * as React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import expect from 'expect';
import { CoreAdminContext } from '../core';
import { RaRecord } from '../types';
import { useUpdate } from './useUpdate';
import {
ErrorCase as ErrorCasePessimistic,
SuccessCase as SuccessCasePessimistic,
} from './useUpdate.pessimistic.stories';
import {
ErrorCase as ErrorCaseOptimistic,
SuccessCase as SuccessCaseOptimistic,
} from './useUpdate.optimistic.stories';
import {
ErrorCase as ErrorCaseUndoable,
SuccessCase as SuccessCaseUndoable,
} from './useUpdate.undoable.stories';
import { QueryClient } from 'react-query';
describe('useUpdate', () => {
describe('mutate', () => {
it('returns a callback that can be used with update arguments', async () => {
const dataProvider = {
update: jest.fn(() =>
Promise.resolve({ data: { id: 1 } } as any)
),
} as any;
let localUpdate;
const Dummy = () => {
const [update] = useUpdate();
localUpdate = update;
return ;
};
render(
);
localUpdate('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
await waitFor(() => {
expect(dataProvider.update).toHaveBeenCalledWith('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
});
});
it('returns a callback that can be used with no arguments', async () => {
const dataProvider = {
update: jest.fn(() =>
Promise.resolve({ data: { id: 1 } } as any)
),
} as any;
let localUpdate;
const Dummy = () => {
const [update] = useUpdate('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
localUpdate = update;
return ;
};
render(
);
localUpdate();
await waitFor(() => {
expect(dataProvider.update).toHaveBeenCalledWith('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
});
});
it('replaces hook call time params by and callback time params', async () => {
const dataProvider = {
update: jest.fn(() =>
Promise.resolve({ data: { id: 1 } } as any)
),
} as any;
let localUpdate;
const Dummy = () => {
const [update] = useUpdate('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
localUpdate = update;
return ;
};
render(
);
localUpdate(undefined, { data: { foo: 456 } });
await waitFor(() => {
expect(dataProvider.update).toHaveBeenCalledWith('foo', {
id: 1,
data: { foo: 456 },
previousData: { id: 1, bar: 'bar' },
});
});
});
it('accepts a meta parameter', async () => {
const dataProvider = {
update: jest.fn(() =>
Promise.resolve({ data: { id: 1 } } as any)
),
} as any;
let localUpdate;
const Dummy = () => {
const [update] = useUpdate();
localUpdate = update;
return ;
};
render(
);
localUpdate('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
meta: { hello: 'world' },
});
await waitFor(() => {
expect(dataProvider.update).toHaveBeenCalledWith('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
meta: { hello: 'world' },
});
});
});
});
describe('data', () => {
it('returns a data typed based on the parametric type', async () => {
interface Product extends RaRecord {
sku: string;
}
const dataProvider = {
update: jest.fn(() =>
Promise.resolve({ data: { id: 1, sku: 'abc' } } as any)
),
} as any;
let localUpdate;
let sku;
const Dummy = () => {
const [update, { data }] = useUpdate();
localUpdate = update;
sku = data && data.sku;
return ;
};
render(
);
expect(sku).toBeUndefined();
localUpdate('products', {
id: 1,
data: { sku: 'abc' },
previousData: { id: 1, sku: 'bcd' },
});
await waitFor(() => {
expect(sku).toEqual('abc');
});
});
});
describe('mutationMode', () => {
it('when pessimistic, displays result and success side effects when dataProvider promise resolves', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
render();
screen.getByText('Update title').click();
await waitFor(() => {
expect(screen.queryByText('success')).toBeNull();
expect(screen.queryByText('Hello World')).toBeNull();
expect(screen.queryByText('mutating')).not.toBeNull();
});
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
});
it('when pessimistic, displays error and error side effects when dataProvider promise rejects', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
render();
screen.getByText('Update title').click();
await waitFor(() => {
expect(screen.queryByText('success')).toBeNull();
expect(screen.queryByText('something went wrong')).toBeNull();
expect(screen.queryByText('Hello World')).toBeNull();
expect(screen.queryByText('mutating')).not.toBeNull();
});
await waitFor(() => {
expect(screen.queryByText('success')).toBeNull();
expect(
screen.queryByText('something went wrong')
).not.toBeNull();
expect(screen.queryByText('Hello World')).toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
});
it('when optimistic, displays result and success side effects right away', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
render();
screen.getByText('Update title').click();
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).not.toBeNull();
});
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
});
it('when optimistic, displays error and error side effects when dataProvider promise rejects', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
render();
screen.getByText('Update title').click();
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).not.toBeNull();
});
await waitFor(() => {
expect(screen.queryByText('success')).toBeNull();
expect(
screen.queryByText('something went wrong')
).not.toBeNull();
expect(screen.queryByText('Hello World')).toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
});
it('when undoable, displays result and success side effects right away and fetched on confirm', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
render();
screen.getByText('Update title').click();
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
screen.getByText('Confirm').click();
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).not.toBeNull();
});
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
});
it('when undoable, displays result and success side effects right away and reverts on cancel', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
render();
screen.getByText('Update title').click();
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
screen.getByText('Cancel').click();
await waitFor(() => {
expect(screen.queryByText('Hello World')).toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
});
it('when undoable, displays result and success side effects right away and reverts on error', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
render();
screen.getByText('Update title').click();
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
screen.getByText('Confirm').click();
await waitFor(() => {
expect(screen.queryByText('success')).not.toBeNull();
expect(screen.queryByText('Hello World')).not.toBeNull();
expect(screen.queryByText('mutating')).not.toBeNull();
});
await waitFor(() => {
expect(screen.queryByText('success')).toBeNull();
expect(screen.queryByText('Hello World')).toBeNull();
expect(screen.queryByText('mutating')).toBeNull();
});
});
});
describe('query cache', () => {
it('updates getList query cache when dataProvider promise resolves', async () => {
const queryClient = new QueryClient();
queryClient.setQueryData(['foo', 'getList'], {
data: [{ id: 1, bar: 'bar' }],
total: 1,
});
const dataProvider = {
update: jest.fn(() =>
Promise.resolve({ data: { id: 1, bar: 'baz' } } as any)
),
} as any;
let localUpdate;
const Dummy = () => {
const [update] = useUpdate();
localUpdate = update;
return ;
};
render(
);
localUpdate('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
await waitFor(() => {
expect(dataProvider.update).toHaveBeenCalledWith('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
});
await waitFor(() => {
expect(queryClient.getQueryData(['foo', 'getList'])).toEqual({
data: [{ id: 1, bar: 'baz' }],
total: 1,
});
});
});
it('updates getList query cache with pageInfo when dataProvider promise resolves', async () => {
const queryClient = new QueryClient();
queryClient.setQueryData(['foo', 'getList'], {
data: [{ id: 1, bar: 'bar' }],
pageInfo: {
hasPreviousPage: false,
hasNextPage: true,
},
});
const dataProvider = {
update: jest.fn(() =>
Promise.resolve({ data: { id: 1, bar: 'baz' } } as any)
),
} as any;
let localUpdate;
const Dummy = () => {
const [update] = useUpdate();
localUpdate = update;
return ;
};
render(
);
localUpdate('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
await waitFor(() => {
expect(dataProvider.update).toHaveBeenCalledWith('foo', {
id: 1,
data: { bar: 'baz' },
previousData: { id: 1, bar: 'bar' },
});
});
await waitFor(() => {
expect(queryClient.getQueryData(['foo', 'getList'])).toEqual({
data: [{ id: 1, bar: 'baz' }],
pageInfo: {
hasPreviousPage: false,
hasNextPage: true,
},
});
});
});
});
});
afterEach(() => {
jest.restoreAllMocks();
});