// eslint-env jest
import {
NetworkManager,
Manager,
Middleware,
Controller,
} from '@data-client/core';
import { act, render } from '@testing-library/react';
import { CoolerArticleResource } from '__tests__/new';
import nock from 'nock';
import React, { useContext, Suspense, StrictMode } from 'react';
import { ControllerContext, StateContext } from '../../context';
import { useController, useSuspense } from '../../hooks';
import { payload } from '../../test-fixtures';
import DataProvider from '../DataProvider';
import { getDefaultManagers } from '../getDefaultManagers';
describe('', () => {
let errorSpy: jest.SpyInstance;
beforeAll(() => {
errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterAll(() => {
errorSpy.mockRestore();
});
let warnspy: jest.Spied;
let debugspy: jest.Spied;
beforeEach(() => {
warnspy = jest.spyOn(global.console, 'warn').mockImplementation(() => {});
debugspy = jest.spyOn(global.console, 'info').mockImplementation(() => {});
});
afterEach(() => {
warnspy.mockRestore();
debugspy.mockRestore();
});
beforeAll(() => {
nock(/.*/)
.persist()
.defaultReplyHeaders({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Access-Token',
'Content-Type': 'application/json',
})
.options(/.*/)
.reply(200)
.get(`/article-cooler/${payload.id}`)
.reply(200, payload);
});
afterAll(() => {
nock.cleanAll();
});
it('should warn users about missing Suspense', () => {
const Component = () => {
const article = useSuspense(CoolerArticleResource.get, { id: 5 });
return {article.title}
;
};
const tree = (
);
const { getByText } = render(tree);
const msg = getByText('Uncaught Suspense.');
expect(msg).toBeDefined();
expect(warnspy).toHaveBeenCalled();
expect(warnspy.mock.calls).toMatchSnapshot();
});
it('should not warn if Suspense is provided', () => {
const Component = () => {
const article = useSuspense(CoolerArticleResource.get, { id: 5 });
return {article.title}
;
};
const tree = (
);
const { getByText } = render(tree);
const msg = getByText('loading');
expect(msg).toBeDefined();
expect(warnspy).not.toHaveBeenCalled();
});
it('should not change dispatch function on re-render', () => {
let dispatch;
let controller;
let count = 0;
function DispatchTester() {
controller = useController();
dispatch = useController().dispatch;
count++;
return null;
}
const chil = ;
const tree = {chil};
const { rerender } = render(tree);
expect(dispatch).toBeDefined();
let curDisp = dispatch;
let curController = controller;
rerender(tree);
expect(curDisp).toBe(dispatch);
expect(count).toBe(1);
rerender({chil});
expect(curDisp).toBe(dispatch);
expect(count).toBe(1);
const managers: any[] = [new NetworkManager()];
rerender({chil});
expect(count).toBe(1);
curDisp = dispatch;
curController = controller;
rerender({chil});
expect(curDisp).toBe(dispatch);
expect(count).toBe(1);
rerender(
Promise.resolve() })}
>
{chil}
,
);
expect(curController).not.toBe(controller);
expect(count).toBe(2);
});
it('should construct a Controller subclass with its own members', () => {
class MyController extends Controller {
doSomething = () => 'hi';
}
let ctrl: Controller | undefined;
function ContextTester() {
ctrl = useController();
return null;
}
render(
,
);
expect(ctrl).toBeInstanceOf(MyController);
expect((ctrl as MyController).doSomething()).toBe('hi');
});
it('should change state', () => {
jest.useFakeTimers({ now: 50 });
let ctrl: Controller | undefined = undefined;
let state;
let count = 0;
function ContextTester() {
ctrl = useController();
state = useContext(StateContext);
count++;
return null;
}
const chil = ;
const tree = {chil};
render(tree);
expect(ctrl).toBeDefined();
expect(state).toBeDefined();
act(() => {
ctrl?.setResponse(
CoolerArticleResource.get,
{ id: 5 },
{ id: 5, title: 'hi', content: 'more things here' },
);
});
expect(count).toBe(2);
expect(state).toMatchSnapshot();
jest.useRealTimers();
});
it('should ignore dispatches after unmount', async () => {
class InjectorManager implements Manager {
declare controller: Controller;
cleanup() {}
middleware: Middleware = controller => {
this.controller = controller;
return next => async action => {
await next(action);
};
};
}
const injector = new InjectorManager();
const managers = [injector, ...getDefaultManagers()];
let resolve: (r: any) => void = () => {};
const endpoint = CoolerArticleResource.get.extend({
fetch() {
return new Promise(res => {
resolve = res;
});
},
});
const Component = () => {
const article = useSuspense(endpoint, { id: 5 });
return {article.title}
;
};
const tree = (
);
const { getByText, unmount } = render(tree);
const msg = getByText('loading');
expect(msg).toBeDefined();
unmount();
expect(debugspy).not.toHaveBeenCalled();
await injector.controller.setResponse(
endpoint,
{ id: 5 },
{ id: 5, title: 'hi' },
);
expect(debugspy).toHaveBeenCalled();
expect(debugspy.mock.calls[0]).toMatchInlineSnapshot(`
[
"Action dispatched after unmount. This will be ignored.",
]
`);
});
});