Skip to content

Commit 9c27322

Browse files
committed
[tests] New API
1 parent 4bbaf83 commit 9c27322

File tree

2 files changed

+131
-98
lines changed

2 files changed

+131
-98
lines changed

src/@types/ui-react/docs.js

Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,98 @@
1010
*/
1111
/// ui-react
1212

13+
/**
14+
* The useCreateManager hook is used to create a Manager within a React
15+
* application with convenient memoization.
16+
*
17+
* It is possible to create a Manager outside of the React app with the regular
18+
* createManager function and pass it in, but you may prefer to create it within
19+
* the app, perhaps inside the top-level component. To prevent a new Manager
20+
* being created every time the app renders or re-renders, the useCreateManager
21+
* hook wraps the creation in a memoization.
22+
*
23+
* The useCreateManager hook is a very thin wrapper around the React `useMemo`
24+
* hook, defaulting to an empty array for its dependencies, so that by default,
25+
* the creation only occurs once.
26+
*
27+
* If your `create` function contains other dependencies, the changing of which
28+
* should cause the Manager to be recreated, you can provide them in an array in
29+
* the optional second parameter, just as you would for any React hook with
30+
* dependencies.
31+
* @param create A function for performing the creation of the Manager, plus any
32+
* additional steps such as starting it, and returning it.
33+
* @param createDeps An optional array of dependencies for the `create`
34+
* function, which, if any change, result in its rerun. This parameter defaults
35+
* to an empty array.
36+
* @returns A reference to the Manager.
37+
* @example
38+
* This example creates an empty Manager at the top level of a React
39+
* application. Even though the App component is rendered twice, the Manager
40+
* creation only occurs once by default.
41+
*
42+
* ```jsx
43+
* import React from 'react';
44+
* import {createRoot} from 'react-dom/client';
45+
* import {createManager} from 'tinytick';
46+
* import {useCreateManager} from 'tinytick/ui-react';
47+
*
48+
* const App = () => {
49+
* const manager = useCreateManager(() => {
50+
* console.log('Manager created');
51+
* return createManager().start();
52+
* });
53+
* return <span>{manager.getStatus()}</span>;
54+
* };
55+
*
56+
* const app = document.createElement('div');
57+
* const root = createRoot(app);
58+
* root.render(<App />); // !act
59+
* // -> 'Manager created'
60+
*
61+
* root.render(<App />); // !act
62+
* // No second Manager creation
63+
*
64+
* console.log(app.innerHTML);
65+
* // -> '<span>1</span>'
66+
* ```
67+
* @example
68+
* This example creates an empty Manager at the top level of a React
69+
* application. The App component is rendered twice, each with a different
70+
* top-level prop. The useCreateManager hook takes the `fidoSpecies` prop as a
71+
* dependency, and so the Manager is created again on the second render.
72+
*
73+
* ```jsx
74+
* import React from 'react';
75+
* import {createRoot} from 'react-dom/client';
76+
* import {createManager} from 'tinytick';
77+
* import {useCreateManager} from 'tinytick/ui-react';
78+
*
79+
* const App = ({managerStarted}) => {
80+
* const manager = useCreateManager(() => {
81+
* console.log(`Manager created, ${managerStarted?'started':'stopped'}`);
82+
* return createManager()[managerStarted?'start':'stop']();});
83+
* }, [managerStarted]);
84+
* return <span>{manager.getStatus()}</span>;
85+
* };
86+
*
87+
* const app = document.createElement('div');
88+
* const root = createRoot(app);
89+
* root.render(<App managerStarted={true} />); // !act
90+
* // -> 'Manager created, started'
91+
*
92+
* console.log(app.innerHTML);
93+
* // -> '<span>dog</span>'
94+
*
95+
* root.render(<App managerStarted={false} />); // !act
96+
* // -> 'Manager created, not started'
97+
*
98+
* console.log(app.innerHTML);
99+
* // -> '<span>cat</span>'
100+
* ```
101+
* @category Manager hooks
102+
* @since v1.1.0
103+
*/
104+
/// useCreateManager
13105
/**
14106
* The useManager hook returns the Manager provided by the a Provider component.
15107
* @returns The current Manager, or `undefined` if called from outside an active
@@ -869,19 +961,12 @@
869961
/// ProviderProps
870962
{
871963
/**
872-
* Whether the Manager should be started on first render, defaulting to
873-
* `true`.
874-
* @category Prop
875-
* @since v1.1.0
876-
*/
877-
/// ProviderProps.started
878-
/**
879-
* Whether the Manager should be force-stopped when the context is unmounted,
880-
* defaulting to `true`.
964+
* A default single Manager object that will be available within the Provider
965+
* context.
881966
* @category Prop
882967
* @since v1.1.0
883968
*/
884-
/// ProviderProps.forceStop
969+
/// ProviderProps.manager
885970
}
886971
/**
887972
* The Provider component is used to wrap part of an application in a context
Lines changed: 36 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,76 @@
11
import {render} from '@testing-library/react';
22
import React, {act, StrictMode} from 'react';
3-
import {ManagerProvider, useManager} from 'tinytick/ui-react';
3+
import {createManager, type Manager} from 'tinytick';
4+
import {Provider, useCreateManager, useManager} from 'tinytick/ui-react';
45
import {pause} from '../../common.ts';
56

7+
let manager: Manager;
68
let didRender: jest.Mock;
79
beforeEach(() => {
10+
manager = createManager();
811
didRender = jest.fn((rendered) => rendered);
912
});
1013

14+
test('useCreateManager', () => {
15+
const initManager = jest.fn((tickInterval) =>
16+
createManager().setManagerConfig({tickInterval}),
17+
);
18+
const Test = ({tickInterval}: {tickInterval: number}) => {
19+
const manager = useCreateManager(() => initManager(tickInterval));
20+
return didRender(
21+
<>
22+
{tickInterval},{manager.getManagerConfig(true).tickInterval}
23+
</>,
24+
);
25+
};
26+
27+
const {container, rerender, unmount} = render(<Test tickInterval={200} />);
28+
expect(container.textContent).toEqual('200,200');
29+
30+
rerender(<Test tickInterval={300} />);
31+
expect(container.textContent).toEqual('300,200');
32+
33+
expect(didRender).toHaveBeenCalledTimes(2);
34+
expect(initManager).toHaveBeenCalledTimes(1);
35+
36+
unmount();
37+
});
38+
1139
describe('Context Hooks', () => {
1240
describe('useManager', () => {
1341
test('basic', () => {
1442
const Test = () =>
1543
didRender(useManager()?.getManagerConfig(true).tickInterval);
1644

1745
const {container, unmount} = render(
18-
<ManagerProvider>
46+
<Provider manager={manager}>
1947
<Test />
20-
</ManagerProvider>,
48+
</Provider>,
2149
);
22-
expect(didRender).toHaveBeenCalledTimes(2);
50+
expect(didRender).toHaveBeenCalledTimes(1);
2351
expect(container.textContent).toEqual('100');
2452

2553
unmount();
2654
});
2755

28-
test('started', async () => {
29-
let count = 0;
30-
const Test = () => {
31-
const manager = useManager();
32-
manager?.setTask('count', async () => count++);
33-
manager?.scheduleTaskRun('count', '', 0);
34-
return didRender(
35-
<>
36-
{manager?.getStatus()},{manager?.getScheduledTaskRunIds().length}
37-
</>,
38-
);
39-
};
40-
41-
const {container, unmount} = render(
42-
<ManagerProvider>
43-
<Test />
44-
</ManagerProvider>,
45-
);
46-
expect(didRender).toHaveBeenCalledTimes(2);
47-
expect(container.textContent).toEqual('1,1');
48-
expect(count).toEqual(0);
49-
50-
await act(async () => await pause(100));
51-
expect(count).toEqual(1);
52-
53-
unmount();
54-
});
55-
56-
test('stopped', async () => {
57-
let count = 0;
58-
const Test = () => {
59-
const manager = useManager();
60-
manager?.setTask('count', async () => count++);
61-
manager?.scheduleTaskRun('count', '', 0);
62-
return didRender(
63-
<>
64-
{manager?.getStatus()},{manager?.getScheduledTaskRunIds().length}
65-
</>,
66-
);
67-
};
68-
69-
const {container, unmount} = render(
70-
<ManagerProvider started={false}>
71-
<Test />
72-
</ManagerProvider>,
73-
);
74-
expect(didRender).toHaveBeenCalledTimes(2);
75-
expect(container.textContent).toEqual('0,1');
76-
expect(count).toEqual(0);
77-
78-
await act(async () => await pause(100));
79-
expect(count).toEqual(0);
80-
81-
unmount();
82-
});
83-
8456
test('strict mode', async () => {
8557
let count = 0;
8658
const Test = () => {
87-
const manager = useManager();
59+
const manager = useManager()?.start();
8860
manager?.setTask('count', async () => count++);
8961
manager?.scheduleTaskRun('count', '', 0);
9062
return didRender(manager?.getScheduledTaskRunIds().length);
9163
};
9264

9365
const {container, unmount} = render(
9466
<StrictMode>
95-
<ManagerProvider>
67+
<Provider manager={manager}>
9668
<Test />
97-
</ManagerProvider>
69+
</Provider>
9870
</StrictMode>,
9971
);
10072

101-
expect(didRender).toHaveBeenCalledTimes(4);
73+
expect(didRender).toHaveBeenCalledTimes(2);
10274
expect(container.textContent).toEqual('2');
10375
expect(count).toEqual(0);
10476

@@ -107,29 +79,5 @@ describe('Context Hooks', () => {
10779

10880
unmount();
10981
});
110-
111-
test('unmount', async () => {
112-
let count = 0;
113-
const Test = () => {
114-
const manager = useManager();
115-
manager?.setTask('count', async () => count++);
116-
manager?.scheduleTaskRun('count', '', 0);
117-
return didRender(manager?.getScheduledTaskRunIds().length);
118-
};
119-
120-
const {container, unmount} = render(
121-
<ManagerProvider>
122-
<Test />
123-
</ManagerProvider>,
124-
);
125-
126-
expect(didRender).toHaveBeenCalledTimes(2);
127-
expect(container.textContent).toEqual('1');
128-
expect(count).toEqual(0);
129-
130-
unmount();
131-
await act(async () => await pause(100));
132-
expect(count).toEqual(0);
133-
});
13482
});
13583
});

0 commit comments

Comments
 (0)