@servicetitan/react-ioc 23.2.3 → 23.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fallbacks.d.ts +2 -3
- package/dist/fallbacks.d.ts.map +1 -1
- package/dist/fallbacks.js.map +1 -1
- package/dist/provide.d.ts +6 -4
- package/dist/provide.d.ts.map +1 -1
- package/dist/provide.js +12 -1
- package/dist/provide.js.map +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +8 -1
- package/dist/provider.js.map +1 -1
- package/package.json +4 -3
- package/src/__tests__/provide.test.tsx +71 -317
- package/src/__tests__/provider.test.tsx +352 -0
- package/src/__tests__/react-ioc.test.tsx +4 -2
- package/src/fallbacks.tsx +2 -2
- package/src/provide.tsx +25 -11
- package/src/provider.tsx +9 -0
package/dist/fallbacks.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const defaultErrorFallback: JSX.Element;
|
|
1
|
+
export declare const defaultLoadingFallback: import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const defaultErrorFallback: import("react/jsx-runtime").JSX.Element;
|
|
4
3
|
//# sourceMappingURL=fallbacks.d.ts.map
|
package/dist/fallbacks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallbacks.d.ts","sourceRoot":"","sources":["../src/fallbacks.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fallbacks.d.ts","sourceRoot":"","sources":["../src/fallbacks.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,yCAAsB,CAAC;AAC1D,eAAO,MAAM,oBAAoB,yCAAuC,CAAC"}
|
package/dist/fallbacks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallbacks.js","sourceRoot":"","sources":["../src/fallbacks.tsx"],"names":[],"mappings":";AAAA,MAAM,CAAC,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"fallbacks.js","sourceRoot":"","sources":["../src/fallbacks.tsx"],"names":[],"mappings":";AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,sCAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,2BAAI,SAAS,EAAC,WAAW,2BAAW,CAAC"}
|
package/dist/provide.d.ts
CHANGED
|
@@ -4,12 +4,14 @@ export interface WrappedComponent<P> {
|
|
|
4
4
|
wrappedComponent: ComponentType<P>;
|
|
5
5
|
wrappedInstance: ReactInstance | undefined;
|
|
6
6
|
}
|
|
7
|
+
interface FallbackOptions {
|
|
8
|
+
loadingFallback?: JSX.Element;
|
|
9
|
+
errorFallback?: JSX.Element;
|
|
10
|
+
}
|
|
7
11
|
export declare type ProvideDecorator = (stores: {
|
|
8
12
|
singletons?: ProviderConfigEntrySingletons<any>[];
|
|
9
13
|
instances?: ProviderConfigEntryInstances<any>[];
|
|
10
|
-
}, options?:
|
|
11
|
-
loadingFallback?: JSX.Element;
|
|
12
|
-
errorFallback?: JSX.Element;
|
|
13
|
-
}) => <T extends ComponentType>(target: T) => T & WrappedComponent<T>;
|
|
14
|
+
}, options?: JSX.Element | FallbackOptions) => <T extends ComponentType<any>>(target: T) => T & WrappedComponent<T>;
|
|
14
15
|
export declare const provide: ProvideDecorator;
|
|
16
|
+
export {};
|
|
15
17
|
//# sourceMappingURL=provide.d.ts.map
|
package/dist/provide.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provide.d.ts","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,EAAY,6BAA6B,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAEnG,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAC/B,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACnC,eAAe,EAAE,aAAa,GAAG,SAAS,CAAC;CAC9C;AAED,oBAAY,gBAAgB,GAAG,CAC3B,MAAM,EAAE;IACJ,UAAU,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,EAAE,CAAC;IAClD,SAAS,CAAC,EAAE,4BAA4B,CAAC,GAAG,CAAC,EAAE,CAAC;CACnD,EACD,OAAO,CAAC,EAAE
|
|
1
|
+
{"version":3,"file":"provide.d.ts","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,EAAY,6BAA6B,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAEnG,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAC/B,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACnC,eAAe,EAAE,aAAa,GAAG,SAAS,CAAC;CAC9C;AAED,UAAU,eAAe;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC9B,aAAa,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;CAC/B;AAED,oBAAY,gBAAgB,GAAG,CAC3B,MAAM,EAAE;IACJ,UAAU,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,EAAE,CAAC;IAClD,SAAS,CAAC,EAAE,4BAA4B,CAAC,GAAG,CAAC,EAAE,CAAC;CACnD,EACD,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,eAAe,KACtC,CAAC,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;AAE1E,eAAO,MAAM,OAAO,kBAKM,CAAC"}
|
package/dist/provide.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Provider } from './provider';
|
|
3
|
-
export const provide = ((stores,
|
|
3
|
+
export const provide = ((stores, elementOrOptions) => (Component) => props => (_jsx(Provider, Object.assign({}, stores, getOptions(elementOrOptions), { children: _jsx(Component, Object.assign({}, props)) }))));
|
|
4
|
+
function getOptions(arg) {
|
|
5
|
+
return arg && isOptionsObject(arg) ? arg : { loadingFallback: arg };
|
|
6
|
+
}
|
|
7
|
+
function isOptionsObject(arg) {
|
|
8
|
+
return (Object.hasOwn(arg, 'loadingFallback') ||
|
|
9
|
+
Object.hasOwn(arg, 'errorFallback') ||
|
|
10
|
+
isEmptyObject(arg));
|
|
11
|
+
}
|
|
12
|
+
function isEmptyObject(arg) {
|
|
13
|
+
return typeof arg === 'object' && Object.keys(arg).length === 0;
|
|
14
|
+
}
|
|
4
15
|
//# sourceMappingURL=provide.js.map
|
package/dist/provide.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provide.js","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAA+D,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"provide.js","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAA+D,MAAM,YAAY,CAAC;AAoBnG,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC,SAAwB,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CACxF,CACI,KAAC,QAAQ,oBAAK,MAAM,EAAM,UAAU,CAAC,gBAAgB,CAAC,cAClD,KAAC,SAAS,oBAAK,KAAK,EAAI,IACjB,CACd,CAAqB,CAAC;AAE3B,SAAS,UAAU,CAAC,GAAmC;IACnD,OAAO,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC;AACxE,CAAC;AAED,SAAS,eAAe,CAAC,GAAkC;IACvD,OAAO,CACH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,iBAAiB,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,eAAe,CAAC;QACnC,aAAa,CAAC,GAAG,CAAC,CACrB,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,GAAkC;IACrD,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACpE,CAAC"}
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EACH,EAAE,EACF,iBAAiB,EACjB,YAAY,
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EACH,EAAE,EACF,iBAAiB,EACjB,YAAY,EAMf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAa,UAAU,EAAE,MAAM,WAAW,CAAC;AAGlD,OAAO,EAAoB,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAIhE,oBAAY,6BAA6B,CAAC,CAAC,IACrC,wBAAwB,CAAC,CAAC,CAAC,GAC3B,wBAAwB,CAAC,CAAC,CAAC,CAAC;AAClC,oBAAY,4BAA4B,CAAC,CAAC,IAAI,iCAAiC,CAAC,CAAC,CAAC,CAAC;AACnF,oBAAY,mBAAmB,CAAC,CAAC,IAC3B,6BAA6B,CAAC,CAAC,CAAC,GAChC,4BAA4B,CAAC,CAAC,CAAC,CAAC;AAEtC,aAAK,wBAAwB,CAAC,CAAC,IACzB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GACrB,gCAAgC,CAAC,CAAC,CAAC,GACnC,6BAA6B,CAAC,CAAC,CAAC,CAAC;AAEvC,aAAK,iCAAiC,CAAC,CAAC,IAClC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GACrB,IAAI,CAAC,gCAAgC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAE3D,UAAU,gCAAgC,CAAC,CAAC;IACxC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAClB,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,UAAU,6BAA6B,CAAC,CAAC;IACrC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,UAAU,wBAAwB,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjD,QAAQ,EAAE,CAAC,CAAC;IACZ,OAAO,CAAC,EAAE,IAAI,CAAC;CAClB;AA8GD,oBAAY,aAAa,GAAG,iBAAiB,CAAC;IAC1C,UAAU,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,EAAE,CAAC;IAClD,SAAS,CAAC,EAAE,4BAA4B,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,eAAe,CAAC,EAAE,YAAY,CAAC;IAC/B,aAAa,CAAC,EAAE,YAAY,CAAC;CAChC,CAAC,CAAC;AAEH,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,aAAa,CA+FtC,CAAC"}
|
package/dist/provider.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useContext, useLayoutEffect, useRef, } from 'react';
|
|
2
|
+
import { useState, useContext, useEffect, useLayoutEffect, useRef, } from 'react';
|
|
3
3
|
import { Container } from 'inversify';
|
|
4
4
|
import { Await } from './await';
|
|
5
5
|
import { ContainerContext } from './common';
|
|
@@ -101,7 +101,14 @@ export const Provider = ({ singletons, instances, loadingFallback = defaultLoadi
|
|
|
101
101
|
}
|
|
102
102
|
containerRef.current = container;
|
|
103
103
|
setPromises(promises);
|
|
104
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
105
|
+
useEffect(() => {
|
|
104
106
|
return () => {
|
|
107
|
+
const container = containerRef.current;
|
|
108
|
+
if (!container) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const storeInstances = getStoreInstances(container, singletons, instances);
|
|
105
112
|
const unbind = unbindEntry(container);
|
|
106
113
|
singletons === null || singletons === void 0 ? void 0 : singletons.forEach(unbind);
|
|
107
114
|
instances === null || instances === void 0 ? void 0 : instances.forEach(unbind);
|
package/dist/provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/react-ioc",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.4.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://docs.st.dev/docs/frontend/react-ioc",
|
|
6
6
|
"repository": {
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@servicetitan/startup-jest": "^1.1.0",
|
|
23
|
-
"@testing-library/
|
|
23
|
+
"@testing-library/jest-dom": "^6.4.2",
|
|
24
|
+
"@testing-library/react": "^14.2.2",
|
|
24
25
|
"@types/react": "~18.2.55",
|
|
25
26
|
"@types/react-test-renderer": "~18.0.7",
|
|
26
27
|
"react": "~18.2.0",
|
|
@@ -35,5 +36,5 @@
|
|
|
35
36
|
"cli": {
|
|
36
37
|
"webpack": false
|
|
37
38
|
},
|
|
38
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "94612136ff4d645bc38ae6b13fffad2ab0358666"
|
|
39
40
|
}
|
|
@@ -1,343 +1,97 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import { FC, Fragment, PropsWithChildren } from 'react';
|
|
3
|
+
|
|
4
|
+
import { provide } from '../provide';
|
|
5
|
+
import { render, screen } from '@testing-library/react';
|
|
6
|
+
|
|
7
|
+
jest.mock('../provider', () => ({
|
|
8
|
+
Provider: ({ children, ...props }: PropsWithChildren<any>) => {
|
|
9
|
+
return (
|
|
10
|
+
<Fragment>
|
|
11
|
+
{`<Provider ${JSON.stringify(props)} />`}
|
|
12
|
+
{children}
|
|
13
|
+
</Fragment>
|
|
14
|
+
);
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('[react-ioc] provide', () => {
|
|
19
|
+
let stores: Parameters<typeof provide>[0];
|
|
20
|
+
let options: Parameters<typeof provide>[1];
|
|
21
|
+
let props: Record<string, any> | undefined;
|
|
22
|
+
|
|
23
|
+
const subject = () => {
|
|
24
|
+
const Component = provide(
|
|
25
|
+
stores,
|
|
26
|
+
options
|
|
27
|
+
)(renderProps => <div>{`<Component ${JSON.stringify(renderProps)} />`}</div>);
|
|
28
|
+
|
|
29
|
+
return render(<Component {...props} />);
|
|
16
30
|
};
|
|
17
31
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
this.counters[name]++;
|
|
24
|
-
|
|
25
|
-
const id = `${name}-${this.counters[name]}`;
|
|
26
|
-
this.queueCreate.push(id);
|
|
27
|
-
|
|
28
|
-
return id;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@injectable()
|
|
33
|
-
class StoreSimple {
|
|
34
|
-
readonly id;
|
|
35
|
-
|
|
36
|
-
constructor(@inject(StatStore) stat: StatStore) {
|
|
37
|
-
this.id = stat.register('StoreSimple');
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@injectable()
|
|
42
|
-
class StoreWithInitialize extends Store {
|
|
43
|
-
readonly id;
|
|
44
|
-
|
|
45
|
-
constructor(@inject(StatStore) private stat: StatStore) {
|
|
46
|
-
super();
|
|
47
|
-
this.id = stat.register('StoreWithInitialize');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
initialize() {
|
|
51
|
-
this.stat.testInitialize(this.id);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
type TestStore = typeof StoreSimple | typeof StoreWithInitialize | ReturnType<typeof symbolToken>;
|
|
56
|
-
|
|
57
|
-
const getTestComponent = (inst: TestStore) => {
|
|
58
|
-
return ({ children }: PropsWithChildren<{}>) => {
|
|
59
|
-
const [{ id }, { testRender }] = useDependencies<[any, StatStore]>(inst, StatStore);
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
testRender(id);
|
|
62
|
-
}, [testRender, id]);
|
|
63
|
-
return <div>{children}</div>;
|
|
64
|
-
};
|
|
65
|
-
};
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
stores = { singletons: [], instances: [] };
|
|
34
|
+
options = undefined;
|
|
35
|
+
props = { foo: 'bar' };
|
|
36
|
+
});
|
|
66
37
|
|
|
67
|
-
|
|
38
|
+
test('renders component with props', () => {
|
|
39
|
+
subject();
|
|
68
40
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const Component = provide({ singletons, instances })(getTestComponent(store));
|
|
72
|
-
return <Component>{children}</Component>;
|
|
73
|
-
}
|
|
41
|
+
expect(screen.getByText(`<Component ${JSON.stringify(props)} />`)).toBeInTheDocument();
|
|
42
|
+
});
|
|
74
43
|
|
|
75
|
-
|
|
76
|
-
|
|
44
|
+
test('wraps component within <Provider>', () => {
|
|
45
|
+
subject();
|
|
77
46
|
|
|
78
|
-
|
|
79
|
-
const [statStore] = useDependencies(StatStore);
|
|
80
|
-
stat = statStore;
|
|
81
|
-
return <div>{children}</div>;
|
|
47
|
+
expect(screen.getByText(`<Provider ${JSON.stringify(stores)} />`)).toBeInTheDocument();
|
|
82
48
|
});
|
|
83
49
|
|
|
84
|
-
|
|
50
|
+
describe('with fallback options', () => {
|
|
51
|
+
const LoadingFallback: FC = () => null;
|
|
52
|
+
const ErrorFallback: FC = () => null;
|
|
85
53
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
await waitFor(() => {
|
|
89
|
-
expect(stat.queueRender).toEqual(queueRender);
|
|
90
|
-
});
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
options = { loadingFallback: <LoadingFallback />, errorFallback: <ErrorFallback /> };
|
|
91
56
|
});
|
|
92
57
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
test('should reuse singletons for each container.get', async () => {
|
|
96
|
-
subject(
|
|
97
|
-
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
98
|
-
<TestProvider store={StoreSimple} />
|
|
99
|
-
</TestProvider>
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
expect(stat.queueCreate).toEqual(['StoreSimple-1']);
|
|
58
|
+
test('passes fallback options to <Provider>', () => {
|
|
59
|
+
subject();
|
|
103
60
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
test('should create separate singletons for nested tokens', async () => {
|
|
108
|
-
subject(
|
|
109
|
-
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
110
|
-
<TestProvider store={StoreSimple} singletons={[StoreSimple]} />
|
|
111
|
-
</TestProvider>
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
115
|
-
|
|
116
|
-
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-2']);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test('should reuse singleton when using provide inheritance and instance exists', async () => {
|
|
120
|
-
subject(
|
|
121
|
-
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
122
|
-
<TestProvider
|
|
123
|
-
store={StoreSimple}
|
|
124
|
-
singletons={[{ provide: StoreSimple, inherit: true }]}
|
|
125
|
-
/>
|
|
126
|
-
</TestProvider>
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
expect(stat.queueCreate).toEqual(['StoreSimple-1']);
|
|
130
|
-
|
|
131
|
-
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-1']);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test('should create singletons when using provide inheritance and instance not exists', () => {
|
|
135
|
-
subject(
|
|
136
|
-
<TestProvider
|
|
137
|
-
store={StoreSimple}
|
|
138
|
-
singletons={[{ provide: StoreSimple, inherit: true }]}
|
|
139
|
-
>
|
|
140
|
-
<TestProvider store={StoreSimple} singletons={[StoreSimple]} />
|
|
141
|
-
</TestProvider>
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
145
|
-
});
|
|
61
|
+
expect(
|
|
62
|
+
screen.getByText(`<Provider ${JSON.stringify({ ...stores, ...options })} />`)
|
|
63
|
+
).toBeInTheDocument();
|
|
146
64
|
});
|
|
147
65
|
|
|
148
|
-
describe('
|
|
149
|
-
|
|
150
|
-
subject(
|
|
151
|
-
<TestProvider store={StoreSimple} instances={[StoreSimple]}>
|
|
152
|
-
<TestProvider store={StoreSimple} />
|
|
153
|
-
</TestProvider>
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
66
|
+
describe('with empty fallback options', () => {
|
|
67
|
+
beforeEach(() => (options = {}));
|
|
157
68
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
test('should create separate instances for nested tokens', async () => {
|
|
162
|
-
subject(
|
|
163
|
-
<TestProvider store={StoreSimple} instances={[StoreSimple]}>
|
|
164
|
-
<TestProvider store={StoreSimple} instances={[StoreSimple]} />
|
|
165
|
-
</TestProvider>
|
|
166
|
-
);
|
|
69
|
+
test('does not pass options to <Provider>', () => {
|
|
70
|
+
subject();
|
|
167
71
|
|
|
168
|
-
expect(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('useValue', () => {
|
|
175
|
-
const valueToken = symbolToken('valueToken');
|
|
176
|
-
|
|
177
|
-
test('create new values', async () => {
|
|
178
|
-
subject(
|
|
179
|
-
<TestProvider
|
|
180
|
-
store={valueToken}
|
|
181
|
-
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
182
|
-
>
|
|
183
|
-
<TestProvider
|
|
184
|
-
store={valueToken}
|
|
185
|
-
singletons={[{ provide: valueToken, useValue: { id: 'value2' } }]}
|
|
186
|
-
/>
|
|
187
|
-
</TestProvider>
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
await expectQueueRenderToBe(['value1', 'value2']);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test('reuse nested values', async () => {
|
|
194
|
-
subject(
|
|
195
|
-
<TestProvider
|
|
196
|
-
store={valueToken}
|
|
197
|
-
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
198
|
-
>
|
|
199
|
-
<TestProvider
|
|
200
|
-
store={valueToken}
|
|
201
|
-
singletons={[
|
|
202
|
-
{ provide: valueToken, useValue: { id: 'value2' }, inherit: true },
|
|
203
|
-
]}
|
|
204
|
-
/>
|
|
205
|
-
</TestProvider>
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
await expectQueueRenderToBe(['value1', 'value1']);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
test('no reuse nested values for instances', async () => {
|
|
212
|
-
subject(
|
|
213
|
-
<TestProvider
|
|
214
|
-
store={valueToken}
|
|
215
|
-
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
216
|
-
>
|
|
217
|
-
<TestProvider
|
|
218
|
-
store={valueToken}
|
|
219
|
-
instances={[
|
|
220
|
-
{
|
|
221
|
-
provide: valueToken,
|
|
222
|
-
useValue: { id: 'value2' },
|
|
223
|
-
inherit: true,
|
|
224
|
-
} as any,
|
|
225
|
-
]}
|
|
226
|
-
/>
|
|
227
|
-
</TestProvider>
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
await expectQueueRenderToBe(['value1', 'value2']);
|
|
72
|
+
expect(
|
|
73
|
+
screen.getByText(`<Provider ${JSON.stringify({ ...stores })} />`)
|
|
74
|
+
).toBeInTheDocument();
|
|
231
75
|
});
|
|
232
76
|
});
|
|
233
77
|
});
|
|
234
78
|
|
|
235
|
-
describe('
|
|
236
|
-
|
|
237
|
-
subject(
|
|
238
|
-
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]} />
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
expect(stat.queueRender).toEqual([]);
|
|
242
|
-
|
|
243
|
-
await TestRenderer.act(async () => {
|
|
244
|
-
await waitFor(() => {
|
|
245
|
-
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
await expectQueueRenderToBe(['StoreWithInitialize-1']);
|
|
250
|
-
});
|
|
251
|
-
test('should wait for for nested initialization', async () => {
|
|
252
|
-
subject(
|
|
253
|
-
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]}>
|
|
254
|
-
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]} />
|
|
255
|
-
</TestProvider>
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
await TestRenderer.act(async () => {
|
|
259
|
-
await waitFor(() => {
|
|
260
|
-
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
261
|
-
});
|
|
79
|
+
describe('with legacy fallback element', () => {
|
|
80
|
+
const LoadingFallback: FC = () => null;
|
|
262
81
|
|
|
263
|
-
|
|
264
|
-
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-2');
|
|
265
|
-
});
|
|
266
|
-
});
|
|
82
|
+
beforeEach(() => (options = <LoadingFallback />));
|
|
267
83
|
|
|
268
|
-
|
|
84
|
+
test('passes {loadingFallback} option', () => {
|
|
85
|
+
subject();
|
|
269
86
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
/>
|
|
279
|
-
</TestProvider>
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
await TestRenderer.act(async () => {
|
|
283
|
-
await waitFor(() => {
|
|
284
|
-
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
expect(stat.queueCreate).toEqual(['StoreWithInitialize-1']);
|
|
289
|
-
await expectQueueRenderToBe(['StoreWithInitialize-1', 'StoreWithInitialize-1']);
|
|
290
|
-
expect(stat.testInitialize).toHaveBeenCalledTimes(1);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
describe('error initialization', () => {
|
|
294
|
-
@injectable()
|
|
295
|
-
class StoreWithErrorInitialize extends Store {
|
|
296
|
-
initialize() {
|
|
297
|
-
throw new Error('error');
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
@injectable()
|
|
301
|
-
class StoreWithAsyncErrorInitialize extends Store {
|
|
302
|
-
async initialize() {
|
|
303
|
-
await new Promise<void>(resolve => resolve());
|
|
304
|
-
throw new Error('error');
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const subject = async (
|
|
309
|
-
children: ReactElement
|
|
310
|
-
): Promise<ReturnType<typeof TestRenderer.create>> => {
|
|
311
|
-
let result: ReturnType<typeof TestRenderer.create> | undefined = undefined;
|
|
312
|
-
|
|
313
|
-
await TestRenderer.act(async () => {
|
|
314
|
-
// we need this function to be async because of TestRenderer warnings
|
|
315
|
-
await new Promise<void>(resolve => resolve());
|
|
316
|
-
result = TestRenderer.create(children);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
if (!result) {
|
|
320
|
-
throw new Error();
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return result;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
test('async error initialization should show error', async () => {
|
|
327
|
-
const { root } = await subject(
|
|
328
|
-
<Provider singletons={[StoreWithAsyncErrorInitialize]}>hello</Provider>
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
expect(root.findByType('h2')).toBeTruthy();
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
test('sync error initialization should show error', async () => {
|
|
335
|
-
const { root } = await subject(
|
|
336
|
-
<Provider singletons={[StoreWithErrorInitialize]}>hello</Provider>
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
expect(root.findByType('h2')).toBeTruthy();
|
|
340
|
-
});
|
|
87
|
+
expect(
|
|
88
|
+
screen.getByText(
|
|
89
|
+
`<Provider ${JSON.stringify({
|
|
90
|
+
...stores,
|
|
91
|
+
loadingFallback: <LoadingFallback />,
|
|
92
|
+
})} />`
|
|
93
|
+
)
|
|
94
|
+
).toBeInTheDocument();
|
|
341
95
|
});
|
|
342
96
|
});
|
|
343
97
|
});
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { PropsWithChildren, ReactElement, ReactNode, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
inject,
|
|
4
|
+
injectable,
|
|
5
|
+
useDependencies,
|
|
6
|
+
Store,
|
|
7
|
+
symbolToken,
|
|
8
|
+
Provider,
|
|
9
|
+
ProviderProps,
|
|
10
|
+
} from '..';
|
|
11
|
+
import TestRenderer from 'react-test-renderer';
|
|
12
|
+
import { waitFor } from '@testing-library/react';
|
|
13
|
+
|
|
14
|
+
@injectable()
|
|
15
|
+
class StatStore {
|
|
16
|
+
readonly id = 'stat';
|
|
17
|
+
counters: Record<string, number> = {};
|
|
18
|
+
queueCreate: string[] = [];
|
|
19
|
+
queueRender: string[] = [];
|
|
20
|
+
|
|
21
|
+
testInitialize: (id: string) => void = jest.fn();
|
|
22
|
+
testRender = (id: string) => {
|
|
23
|
+
this.queueRender.push(id);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
register(name: string) {
|
|
27
|
+
if (!this.counters[name]) {
|
|
28
|
+
this.counters[name] = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.counters[name]++;
|
|
32
|
+
|
|
33
|
+
const id = `${name}-${this.counters[name]}`;
|
|
34
|
+
this.queueCreate.push(id);
|
|
35
|
+
|
|
36
|
+
return id;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@injectable()
|
|
41
|
+
class StoreSimple {
|
|
42
|
+
readonly id;
|
|
43
|
+
|
|
44
|
+
constructor(@inject(StatStore) stat: StatStore) {
|
|
45
|
+
this.id = stat.register('StoreSimple');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@injectable()
|
|
50
|
+
class StoreWithInitialize extends Store {
|
|
51
|
+
readonly id;
|
|
52
|
+
|
|
53
|
+
constructor(@inject(StatStore) private stat: StatStore) {
|
|
54
|
+
super();
|
|
55
|
+
this.id = stat.register('StoreWithInitialize');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
initialize() {
|
|
59
|
+
this.stat.testInitialize(this.id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type TestStore = typeof StoreSimple | typeof StoreWithInitialize | ReturnType<typeof symbolToken>;
|
|
64
|
+
|
|
65
|
+
const getTestComponent = (inst: TestStore) => {
|
|
66
|
+
return ({ children }: PropsWithChildren<{}>) => {
|
|
67
|
+
const [{ id }, { testRender }] = useDependencies<[any, StatStore]>(inst, StatStore);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
testRender(id);
|
|
70
|
+
}, [testRender, id]);
|
|
71
|
+
return <div>{children}</div>;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function TestProvider(props: PropsWithChildren<ProviderProps & { store: TestStore }>) {
|
|
76
|
+
const { singletons, instances, children, store = StoreSimple } = props;
|
|
77
|
+
const Component = getTestComponent(store);
|
|
78
|
+
return (
|
|
79
|
+
<Provider singletons={singletons} instances={instances}>
|
|
80
|
+
<Component>{children}</Component>
|
|
81
|
+
</Provider>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe('[react-ioc] Provider', () => {
|
|
86
|
+
let stat: StatStore;
|
|
87
|
+
|
|
88
|
+
beforeEach(() => (stat = new StatStore()));
|
|
89
|
+
|
|
90
|
+
const subject = (children: ReactNode) =>
|
|
91
|
+
TestRenderer.create(
|
|
92
|
+
<Provider singletons={[{ provide: StatStore, useValue: stat }]}>{children}</Provider>
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const expectQueueRenderToBe = (queueRender: string[]) =>
|
|
96
|
+
TestRenderer.act(async () => {
|
|
97
|
+
await waitFor(() => {
|
|
98
|
+
expect(stat.queueRender).toEqual(queueRender);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('with singletons', () => {
|
|
103
|
+
test('reuses singletons for each container.get', async () => {
|
|
104
|
+
subject(
|
|
105
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
106
|
+
<TestProvider store={StoreSimple} />
|
|
107
|
+
</TestProvider>
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1']);
|
|
111
|
+
|
|
112
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-1']);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('creates separate singletons for nested tokens', async () => {
|
|
116
|
+
subject(
|
|
117
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
118
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]} />
|
|
119
|
+
</TestProvider>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
123
|
+
|
|
124
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-2']);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('reuses inheritable singleton', async () => {
|
|
128
|
+
subject(
|
|
129
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
130
|
+
<TestProvider
|
|
131
|
+
store={StoreSimple}
|
|
132
|
+
singletons={[{ provide: StoreSimple, inherit: true }]}
|
|
133
|
+
/>
|
|
134
|
+
</TestProvider>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1']);
|
|
138
|
+
|
|
139
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-1']);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('creates singletons when inheritable singleton does not exist', () => {
|
|
143
|
+
subject(
|
|
144
|
+
<TestProvider
|
|
145
|
+
store={StoreSimple}
|
|
146
|
+
singletons={[{ provide: StoreSimple, inherit: true }]}
|
|
147
|
+
>
|
|
148
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]} />
|
|
149
|
+
</TestProvider>
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('with instances', () => {
|
|
157
|
+
test('creates separate instances for each container.get', async () => {
|
|
158
|
+
subject(
|
|
159
|
+
<TestProvider store={StoreSimple} instances={[StoreSimple]}>
|
|
160
|
+
<TestProvider store={StoreSimple} />
|
|
161
|
+
</TestProvider>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
165
|
+
|
|
166
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-2']);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('creates separate instances for nested tokens', async () => {
|
|
170
|
+
subject(
|
|
171
|
+
<TestProvider store={StoreSimple} instances={[StoreSimple]}>
|
|
172
|
+
<TestProvider store={StoreSimple} instances={[StoreSimple]} />
|
|
173
|
+
</TestProvider>
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
177
|
+
|
|
178
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-2']);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('useValue', () => {
|
|
183
|
+
const valueToken = symbolToken('valueToken');
|
|
184
|
+
|
|
185
|
+
test('creates new values', async () => {
|
|
186
|
+
subject(
|
|
187
|
+
<TestProvider
|
|
188
|
+
store={valueToken}
|
|
189
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
190
|
+
>
|
|
191
|
+
<TestProvider
|
|
192
|
+
store={valueToken}
|
|
193
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value2' } }]}
|
|
194
|
+
/>
|
|
195
|
+
</TestProvider>
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
await expectQueueRenderToBe(['value1', 'value2']);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('reuses nested values', async () => {
|
|
202
|
+
subject(
|
|
203
|
+
<TestProvider
|
|
204
|
+
store={valueToken}
|
|
205
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
206
|
+
>
|
|
207
|
+
<TestProvider
|
|
208
|
+
store={valueToken}
|
|
209
|
+
singletons={[
|
|
210
|
+
{ provide: valueToken, useValue: { id: 'value2' }, inherit: true },
|
|
211
|
+
]}
|
|
212
|
+
/>
|
|
213
|
+
</TestProvider>
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
await expectQueueRenderToBe(['value1', 'value1']);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('does not reuse nested values for instances', async () => {
|
|
220
|
+
subject(
|
|
221
|
+
<TestProvider
|
|
222
|
+
store={valueToken}
|
|
223
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
224
|
+
>
|
|
225
|
+
<TestProvider
|
|
226
|
+
store={valueToken}
|
|
227
|
+
instances={[
|
|
228
|
+
{
|
|
229
|
+
provide: valueToken,
|
|
230
|
+
useValue: { id: 'value2' },
|
|
231
|
+
inherit: true,
|
|
232
|
+
} as any,
|
|
233
|
+
]}
|
|
234
|
+
/>
|
|
235
|
+
</TestProvider>
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
await expectQueueRenderToBe(['value1', 'value2']);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('check store initialization', () => {
|
|
243
|
+
test('waits for store initialization', async () => {
|
|
244
|
+
subject(
|
|
245
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]} />
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
expect(stat.queueRender).toEqual([]);
|
|
249
|
+
|
|
250
|
+
await TestRenderer.act(async () => {
|
|
251
|
+
await waitFor(() => {
|
|
252
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
await expectQueueRenderToBe(['StoreWithInitialize-1']);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('waits for nested initialization', async () => {
|
|
260
|
+
subject(
|
|
261
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]}>
|
|
262
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]} />
|
|
263
|
+
</TestProvider>
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
await TestRenderer.act(async () => {
|
|
267
|
+
await waitFor(() => {
|
|
268
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await waitFor(() => {
|
|
272
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-2');
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(stat.queueCreate).toEqual(['StoreWithInitialize-1', 'StoreWithInitialize-2']);
|
|
277
|
+
|
|
278
|
+
await expectQueueRenderToBe(['StoreWithInitialize-1', 'StoreWithInitialize-2']);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test('initializes only once when store is reused', async () => {
|
|
282
|
+
subject(
|
|
283
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]}>
|
|
284
|
+
<TestProvider
|
|
285
|
+
store={StoreWithInitialize}
|
|
286
|
+
singletons={[{ provide: StoreWithInitialize, inherit: true }]}
|
|
287
|
+
/>
|
|
288
|
+
</TestProvider>
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
await TestRenderer.act(async () => {
|
|
292
|
+
await waitFor(() => {
|
|
293
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
expect(stat.queueCreate).toEqual(['StoreWithInitialize-1']);
|
|
298
|
+
await expectQueueRenderToBe(['StoreWithInitialize-1', 'StoreWithInitialize-1']);
|
|
299
|
+
expect(stat.testInitialize).toHaveBeenCalledTimes(1);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('error initialization', () => {
|
|
303
|
+
@injectable()
|
|
304
|
+
class StoreWithErrorInitialize extends Store {
|
|
305
|
+
initialize() {
|
|
306
|
+
throw new Error('error');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
@injectable()
|
|
310
|
+
class StoreWithAsyncErrorInitialize extends Store {
|
|
311
|
+
async initialize() {
|
|
312
|
+
await new Promise<void>(resolve => resolve());
|
|
313
|
+
throw new Error('error');
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const subject = async (
|
|
318
|
+
children: ReactElement
|
|
319
|
+
): Promise<ReturnType<typeof TestRenderer.create>> => {
|
|
320
|
+
let result: ReturnType<typeof TestRenderer.create> | undefined = undefined;
|
|
321
|
+
|
|
322
|
+
await TestRenderer.act(async () => {
|
|
323
|
+
// we need this function to be async because of TestRenderer warnings
|
|
324
|
+
await new Promise<void>(resolve => resolve());
|
|
325
|
+
result = TestRenderer.create(children);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
if (!result) {
|
|
329
|
+
throw new Error();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return result;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
test('async error initialization shows error', async () => {
|
|
336
|
+
const { root } = await subject(
|
|
337
|
+
<Provider singletons={[StoreWithAsyncErrorInitialize]}>hello</Provider>
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
expect(root.findByType('h2')).toBeTruthy();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('sync error initialization shows error', async () => {
|
|
344
|
+
const { root } = await subject(
|
|
345
|
+
<Provider singletons={[StoreWithErrorInitialize]}>hello</Provider>
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
expect(root.findByType('h2')).toBeTruthy();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
});
|
|
@@ -44,7 +44,7 @@ const SampleConsumer: FC<
|
|
|
44
44
|
</ContainerContext.Consumer>
|
|
45
45
|
);
|
|
46
46
|
|
|
47
|
-
describe('[
|
|
47
|
+
describe('[react-ioc]', () => {
|
|
48
48
|
test('Context container should bind the provided store', () => {
|
|
49
49
|
let contextContainer!: Container;
|
|
50
50
|
|
|
@@ -101,7 +101,7 @@ describe('[IOC tests]', () => {
|
|
|
101
101
|
</Provider>
|
|
102
102
|
);
|
|
103
103
|
|
|
104
|
-
renderer.unmount();
|
|
104
|
+
TestRenderer.act(() => renderer.unmount());
|
|
105
105
|
|
|
106
106
|
expect(() => contextContainer.get(SampleStore)).toThrow();
|
|
107
107
|
expect(() => contextContainer.get(ANOTHER_STORE_TOKEN)).toThrow();
|
|
@@ -225,6 +225,8 @@ describe('[IOC tests]', () => {
|
|
|
225
225
|
});
|
|
226
226
|
|
|
227
227
|
test('store injection must throw an error if React context is not passed to constructor', () => {
|
|
228
|
+
jest.spyOn(console, 'error').mockImplementation(jest.fn()); // suppress "context doesn't exist" error
|
|
229
|
+
|
|
228
230
|
expect(() => {
|
|
229
231
|
class SampleComponent extends Component {
|
|
230
232
|
@injectDependency(SampleStore)
|
package/src/fallbacks.tsx
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const defaultLoadingFallback
|
|
2
|
-
export const defaultErrorFallback
|
|
1
|
+
export const defaultLoadingFallback = <h2>Loading...</h2>;
|
|
2
|
+
export const defaultErrorFallback = <h2 className="c-red-500">Error</h2>;
|
package/src/provide.tsx
CHANGED
|
@@ -7,24 +7,38 @@ export interface WrappedComponent<P> {
|
|
|
7
7
|
wrappedInstance: ReactInstance | undefined;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
interface FallbackOptions {
|
|
11
|
+
loadingFallback?: JSX.Element;
|
|
12
|
+
errorFallback?: JSX.Element;
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
export type ProvideDecorator = (
|
|
11
16
|
stores: {
|
|
12
17
|
singletons?: ProviderConfigEntrySingletons<any>[];
|
|
13
18
|
instances?: ProviderConfigEntryInstances<any>[];
|
|
14
19
|
},
|
|
15
|
-
options?:
|
|
16
|
-
|
|
17
|
-
errorFallback?: JSX.Element;
|
|
18
|
-
}
|
|
19
|
-
) => <T extends ComponentType>(target: T) => T & WrappedComponent<T>;
|
|
20
|
+
options?: JSX.Element | FallbackOptions
|
|
21
|
+
) => <T extends ComponentType<any>>(target: T) => T & WrappedComponent<T>;
|
|
20
22
|
|
|
21
|
-
export const provide = ((stores,
|
|
23
|
+
export const provide = ((stores, elementOrOptions) => (Component: ComponentType) => props =>
|
|
22
24
|
(
|
|
23
|
-
<Provider
|
|
24
|
-
{...stores}
|
|
25
|
-
loadingFallback={options?.loadingFallback}
|
|
26
|
-
errorFallback={options?.errorFallback}
|
|
27
|
-
>
|
|
25
|
+
<Provider {...stores} {...getOptions(elementOrOptions)}>
|
|
28
26
|
<Component {...props} />
|
|
29
27
|
</Provider>
|
|
30
28
|
)) as ProvideDecorator;
|
|
29
|
+
|
|
30
|
+
function getOptions(arg?: FallbackOptions | JSX.Element): FallbackOptions | undefined {
|
|
31
|
+
return arg && isOptionsObject(arg) ? arg : { loadingFallback: arg };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isOptionsObject(arg: FallbackOptions | JSX.Element): arg is FallbackOptions {
|
|
35
|
+
return (
|
|
36
|
+
Object.hasOwn(arg, 'loadingFallback') ||
|
|
37
|
+
Object.hasOwn(arg, 'errorFallback') ||
|
|
38
|
+
isEmptyObject(arg)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isEmptyObject(arg: FallbackOptions | JSX.Element) {
|
|
43
|
+
return typeof arg === 'object' && Object.keys(arg).length === 0;
|
|
44
|
+
}
|
package/src/provider.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
ReactElement,
|
|
5
5
|
useState,
|
|
6
6
|
useContext,
|
|
7
|
+
useEffect,
|
|
7
8
|
useLayoutEffect,
|
|
8
9
|
useRef,
|
|
9
10
|
} from 'react';
|
|
@@ -211,8 +212,16 @@ export const Provider: FC<ProviderProps> = ({
|
|
|
211
212
|
|
|
212
213
|
containerRef.current = container;
|
|
213
214
|
setPromises(promises);
|
|
215
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
214
216
|
|
|
217
|
+
useEffect(() => {
|
|
215
218
|
return () => {
|
|
219
|
+
const container = containerRef.current;
|
|
220
|
+
if (!container) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const storeInstances = getStoreInstances(container, singletons, instances);
|
|
216
225
|
const unbind = unbindEntry(container);
|
|
217
226
|
|
|
218
227
|
singletons?.forEach(unbind);
|