Skip to content

Commit 6b0c9b5

Browse files
committed
test: add tests for with-view-model HOC
1 parent 25c9792 commit 6b0c9b5

File tree

7 files changed

+103
-54
lines changed

7 files changed

+103
-54
lines changed

.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = {
88
'error',
99
{ ignore: Object.keys(packageJson.peerDependencies) },
1010
],
11+
'unicorn/prevent-abbreviations': 'off'
1112
},
1213
overrides: [
1314
{

src/hoc/with-view-model.test.tsx

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,87 @@
1-
import { render, screen } from '@testing-library/react';
1+
import { act, render, screen, } from '@testing-library/react';
2+
import { observer } from 'mobx-react-lite';
3+
import { ReactNode } from 'react';
24
import { describe, expect, test } from 'vitest';
35

4-
import { ViewModelsContext } from '../contexts';
6+
import { ViewModelsProvider } from '..';
57
import { TestViewModelStoreImpl } from '../view-model/abstract-view-model.store.test';
68
import { TestViewModelImpl } from '../view-model/view-model.impl.test';
79

810
import { ViewModelProps, withViewModel } from './with-view-model';
11+
import { createCounter } from '../utils';
12+
13+
const createIdGenerator = (prefix?: string) => {
14+
const counter = createCounter();
15+
return () => (prefix || '') + counter().toString()
16+
}
917

1018
describe('withViewModel', () => {
1119
test('renders', () => {
1220
class VM extends TestViewModelImpl { }
1321
const View = ({ model }: ViewModelProps<VM>) => {
1422
return <div>{`hello ${model.id}`}</div>;
1523
};
16-
const Component = withViewModel(VM)(View);
24+
const Component = withViewModel(VM, { generateId: createIdGenerator() })(View);
1725

1826
render(<Component />);
19-
expect(screen.getByText('hello VM_0_00000')).toBeDefined();
27+
expect(screen.getByText('hello VM_0')).toBeDefined();
28+
});
29+
30+
test('renders nesting', () => {
31+
class VM1 extends TestViewModelImpl { }
32+
const View1 = ({
33+
children,
34+
}: ViewModelProps<VM1> & { children?: ReactNode }) => {
35+
return (
36+
<div data-testid="parent-container">
37+
<div>parent</div>
38+
<div>{children}</div>
39+
</div>
40+
);
41+
};
42+
const Component1 = withViewModel(VM1, { generateId: createIdGenerator() })(View1);
43+
44+
const Component2 = withViewModel(class VM2 extends TestViewModelImpl { }, { generateId: createIdGenerator() })(
45+
() => {
46+
return <div>child</div>;
47+
},
48+
49+
);
50+
51+
render(
52+
<Component1>
53+
<Component2 />
54+
</Component1>,
55+
);
56+
expect(screen.getByTestId('parent-container')).toBe(`<div
57+
data-testid="parent-container"
58+
>
59+
<div>
60+
parent
61+
</div>
62+
<div>
63+
<div>
64+
child
65+
</div>
66+
</div>
67+
</div>`);
2068
});
2169

2270
test('renders twice', async () => {
2371
class VM extends TestViewModelImpl { }
2472
const View = ({ model }: ViewModelProps<VM>) => {
2573
return <div>{`hello ${model.id}`}</div>;
2674
};
27-
const Component = withViewModel(VM)(View);
75+
const Component = withViewModel(VM, { generateId: createIdGenerator() })(View);
2876

2977
render(
3078
<>
3179
<Component />
3280
<Component />
3381
</>,
3482
);
35-
expect(screen.getByText('hello VM_1_00000')).toBeDefined();
36-
expect(screen.getByText('hello VM_1_00001')).toBeDefined();
83+
expect(screen.getByText('hello VM_0')).toBeDefined();
84+
expect(screen.getByText('hello VM_1')).toBeDefined();
3785
});
3886

3987
test('renders with fixed id', () => {
@@ -63,32 +111,32 @@ describe('withViewModel', () => {
63111
expect(await screen.findAllByText('hello my-test')).toHaveLength(2);
64112
});
65113

66-
test('renders with view model store', () => {
114+
test('renders with view model store', async () => {
67115
class VM extends TestViewModelImpl { }
68-
const View = ({ model }: ViewModelProps<VM>) => {
69-
return <div>{`hello ${model.id}`}</div>;
70-
};
116+
const View = observer(({ model }: ViewModelProps<VM>) => {
117+
return (
118+
<div>
119+
<div>{`hello my friend. Model has id ? ${!!model.id}`}</div>
120+
</div>
121+
);
122+
});
71123
const Component = withViewModel(VM)(View);
72124
const vmStore = new TestViewModelStoreImpl();
73125

74-
render(
75-
<div>
76-
2
77-
<Component />
78-
1
79-
</div>,
80-
{
81-
wrapper: ({ children }) => {
82-
return (
83-
<ViewModelsContext.Provider value={vmStore}>
84-
{children}
85-
</ViewModelsContext.Provider>
86-
);
87-
},
88-
},
126+
const Wrapper = ({ children }: { children?: ReactNode }) => {
127+
return (
128+
<ViewModelsProvider value={vmStore}>{children}</ViewModelsProvider>
129+
);
130+
};
131+
132+
await act(async () =>
133+
render(<Component />, {
134+
wrapper: Wrapper,
135+
}),
89136
);
90137

91-
screen.debug();
92-
expect(screen.getByText('hello VM_0_00000')).toBeDefined();
138+
expect(
139+
screen.getByText('hello my friend. Model has id ? true'),
140+
).toBeDefined();
93141
});
94142
});

src/hoc/with-view-model.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,22 @@ export type ViewModelHocConfig<VM extends AnyViewModel> = {
2828
* Уникальный идентификатор вьюшки
2929
*/
3030
id?: Maybe<string>;
31+
32+
/**
33+
* Функция генератор идентификатор для вью модели
34+
*/
35+
generateId?: (ctx: AnyObject) => string;
36+
37+
/**
38+
* Компонент, который будет отрисован в случае если инциализация вью модели происходит слишком долго
39+
*/
3140
fallback?: ComponentType;
41+
3242
/**
3343
* Доп. данные, которые могут быть полезны при создании VM
3444
*/
3545
ctx?: AnyObject;
46+
3647
/**
3748
* Функция, в которой можно вызывать доп. реакт хуки в результирующем компоненте
3849
*/
@@ -68,9 +79,10 @@ export function withViewModel(
6879
Model: Class<any>,
6980
config?: ViewModelHocConfig<any>,
7081
) {
71-
const context: AnyObject = config?.ctx ?? {};
82+
const ctx: AnyObject = config?.ctx ?? {};
7283

73-
context.VM = Model;
84+
ctx.VM = Model;
85+
ctx.generateId = config?.generateId;
7486

7587
return (Component: ComponentType<any>) => {
7688
const instances = new Map<string, AnyViewModel>();
@@ -89,15 +101,15 @@ export function withViewModel(
89101
if (!idRef.current) {
90102
idRef.current =
91103
viewModels?.generateViewModelId({
92-
ctx: context,
93-
id: config?.id,
104+
ctx: ctx,
105+
id: config?.id,
94106
VM: Model,
95107
parentViewModelId,
96108
fallback: config?.fallback,
97109
instances,
98110
}) ??
99111
config?.id ??
100-
generateVMId(context);
112+
generateVMId(ctx);
101113
}
102114

103115
const id = idRef.current;
@@ -112,7 +124,7 @@ export function withViewModel(
112124
(parentViewModelId && instances.get(parentViewModelId)) || null,
113125
fallback: config?.fallback,
114126
instances,
115-
ctx: context,
127+
ctx: ctx,
116128
};
117129

118130
const instance =

src/utils/counter.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const createCounter = () => {
2+
let counter = 0;
3+
return () => counter++;
4+
};
5+
6+
export const internalCounter = createCounter();

src/utils/create-vm-id-generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { internalCounter, createCounter } from './id';
1+
import { internalCounter, createCounter } from './counter';
22
import { AnyObject } from './types';
33

44
declare const process: { env: { NODE_ENV?: string } };

src/utils/id.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/utils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * from './id';
1+
export * from './counter';
22
export * from './create-vm-id-generator';

0 commit comments

Comments
 (0)