@dhis2/app-service-data 3.2.7 → 3.2.8

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.
@@ -10,8 +10,8 @@ function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return
10
10
 
11
11
  function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
12
12
 
13
- describe('Testing custom data provider and useQuery hook', () => {
14
- it('Should render without failing', async () => {
13
+ describe('<DataQuery />', () => {
14
+ it('should render without failing', async () => {
15
15
  const data = {
16
16
  answer: 42
17
17
  };
@@ -22,18 +22,8 @@ describe('Testing custom data provider and useQuery hook', () => {
22
22
  data: data
23
23
  }, children);
24
24
 
25
- const renderFunction = jest.fn(({
26
- loading,
27
- error,
28
- data
29
- }) => {
30
- if (loading) return 'loading';
31
- if (error) return /*#__PURE__*/React.createElement("div", null, "error: ", error.message);
32
- return /*#__PURE__*/React.createElement("div", null, "data: ", data && data.answer);
33
- });
34
- const {
35
- getByText
36
- } = (0, _react.render)( /*#__PURE__*/React.createElement(_react3.DataQuery, {
25
+ const renderFunction = jest.fn(() => null);
26
+ (0, _react.render)( /*#__PURE__*/React.createElement(_react3.DataQuery, {
37
27
  query: {
38
28
  answer: {
39
29
  resource: 'answer'
@@ -42,24 +32,21 @@ describe('Testing custom data provider and useQuery hook', () => {
42
32
  }, renderFunction), {
43
33
  wrapper
44
34
  });
45
- expect(getByText(/loading/i)).not.toBeUndefined();
46
35
  expect(renderFunction).toHaveBeenCalledTimes(1);
47
36
  expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
48
37
  called: true,
49
38
  loading: true
50
39
  }));
51
40
  await (0, _react.waitFor)(() => {
52
- getByText(/data: /i);
41
+ expect(renderFunction).toHaveBeenCalledTimes(2);
42
+ expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
43
+ called: true,
44
+ loading: false,
45
+ data
46
+ }));
53
47
  });
54
- expect(getByText(/data: /i)).toHaveTextContent("data: ".concat(data.answer));
55
- expect(renderFunction).toHaveBeenCalledTimes(2);
56
- expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
57
- called: true,
58
- loading: false,
59
- data
60
- }));
61
48
  });
62
- it('Should render an error', async () => {
49
+ it('should render an error', async () => {
63
50
  const expectedError = new Error('Something went wrong');
64
51
  const data = {
65
52
  test: () => {
@@ -73,18 +60,8 @@ describe('Testing custom data provider and useQuery hook', () => {
73
60
  data: data
74
61
  }, children);
75
62
 
76
- const renderFunction = jest.fn(({
77
- loading,
78
- error,
79
- data
80
- }) => {
81
- if (loading) return 'loading';
82
- if (error) return /*#__PURE__*/React.createElement("div", null, "error: ", error.message);
83
- return /*#__PURE__*/React.createElement("div", null, "data: ", data && data.test);
84
- });
85
- const {
86
- getByText
87
- } = (0, _react.render)( /*#__PURE__*/React.createElement(_react3.DataQuery, {
63
+ const renderFunction = jest.fn(() => null);
64
+ (0, _react.render)( /*#__PURE__*/React.createElement(_react3.DataQuery, {
88
65
  query: {
89
66
  test: {
90
67
  resource: 'test'
@@ -93,21 +70,18 @@ describe('Testing custom data provider and useQuery hook', () => {
93
70
  }, renderFunction), {
94
71
  wrapper
95
72
  });
96
- expect(getByText(/loading/i)).not.toBeUndefined();
97
73
  expect(renderFunction).toHaveBeenCalledTimes(1);
98
74
  expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
99
75
  called: true,
100
76
  loading: true
101
77
  }));
102
78
  await (0, _react.waitFor)(() => {
103
- getByText(/error: /i);
79
+ expect(renderFunction).toHaveBeenCalledTimes(2);
80
+ expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
81
+ called: true,
82
+ loading: false,
83
+ error: expectedError
84
+ }));
104
85
  });
105
- expect(renderFunction).toHaveBeenCalledTimes(2);
106
- expect(getByText(/error: /i)).toHaveTextContent("error: ".concat(expectedError.message));
107
- expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
108
- called: true,
109
- loading: false,
110
- error: expectedError
111
- }));
112
86
  });
113
87
  });
@@ -2,82 +2,73 @@
2
2
 
3
3
  var _react = require("@testing-library/react");
4
4
 
5
- var _react2 = _interopRequireDefault(require("react"));
5
+ var React = _interopRequireWildcard(require("react"));
6
6
 
7
7
  var _react3 = require("../react");
8
8
 
9
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
10
10
 
11
- const mockBackend = {
12
- target: jest.fn((type, query) => {
13
- expect(query.resource).toBe('target');
14
- expect(type).toBe('create');
15
- expect(query.data).toMatchObject({
16
- question: '?'
17
- });
18
- return Promise.resolve({
19
- answer: 42
20
- });
21
- })
22
- };
23
- describe('Test mutations', () => {
24
- it('Should call the mock callback', async () => {
25
- let doMutation;
26
- const renderFunction = jest.fn(([mutate, {
27
- called,
28
- loading,
29
- error,
30
- data
31
- }]) => {
32
- doMutation = mutate;
33
- if (!called) return 'uncalled';
34
- if (loading) return 'loading';
35
- if (error) return /*#__PURE__*/_react2.default.createElement("div", null, "error: ", error.message);
36
- if (data) return /*#__PURE__*/_react2.default.createElement("div", null, "data: ", data.answer);
37
- });
38
- const testMutation = {
39
- resource: 'target',
11
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
12
+
13
+ describe('<DataMutation />', () => {
14
+ it('should render without failing', async () => {
15
+ const endpointSpy = jest.fn(() => Promise.resolve(42));
16
+ const mutation = {
17
+ resource: 'answer',
40
18
  type: 'create',
41
19
  data: {
42
20
  question: '?'
43
21
  }
44
22
  };
45
- const {
46
- getByText
47
- } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_react3.CustomDataProvider, {
48
- data: mockBackend
49
- }, /*#__PURE__*/_react2.default.createElement(_react3.DataMutation, {
50
- mutation: testMutation
51
- }, renderFunction)));
52
- expect(getByText(/uncalled/i)).not.toBeUndefined();
53
- expect(renderFunction).toHaveBeenCalledTimes(1);
54
- expect(mockBackend.target).not.toHaveBeenCalled();
55
- expect(renderFunction).toHaveBeenLastCalledWith([expect.any(Function), {
23
+ const data = {
24
+ answer: endpointSpy
25
+ };
26
+
27
+ const wrapper = ({
28
+ children
29
+ }) => /*#__PURE__*/React.createElement(_react3.CustomDataProvider, {
30
+ data: data
31
+ }, children);
32
+
33
+ const renderSpy = jest.fn(() => null);
34
+ (0, _react.render)( /*#__PURE__*/React.createElement(_react3.DataMutation, {
35
+ mutation: mutation
36
+ }, renderSpy), {
37
+ wrapper
38
+ });
39
+ expect(endpointSpy).toHaveBeenCalledTimes(0);
40
+ expect(renderSpy).toHaveBeenCalledTimes(1);
41
+ expect(renderSpy).toHaveBeenLastCalledWith([expect.any(Function), expect.objectContaining({
56
42
  called: false,
57
43
  loading: false,
58
44
  engine: expect.any(Object)
59
- }]);
60
- expect(doMutation).not.toBeUndefined();
61
- (0, _react.act)(() => {
62
- doMutation && doMutation();
45
+ })]);
46
+ await (0, _react.act)(async () => {
47
+ const firstRenderSpyCall = renderSpy.mock.calls[0];
48
+ const firstRenderSpyArgument = firstRenderSpyCall[0];
49
+ const [mutate] = firstRenderSpyArgument;
50
+ await mutate();
51
+ });
52
+ (0, _react.waitFor)(() => {
53
+ expect(endpointSpy).toHaveBeenCalledTimes(1);
54
+ expect(renderSpy).toHaveBeenCalledTimes(2);
55
+ expect(renderSpy).toHaveBeenLastCalledWith([expect.any(Function), expect.objectContaining({
56
+ called: true,
57
+ loading: true,
58
+ engine: expect.any(Object)
59
+ })]);
60
+ });
61
+ (0, _react.waitFor)(() => {
62
+ expect(endpointSpy).toHaveBeenCalledTimes(1);
63
+ expect(renderSpy).toHaveBeenCalledTimes(3);
64
+ expect(renderSpy).toHaveBeenLastCalledWith([expect.any(Function), expect.objectContaining({
65
+ called: true,
66
+ loading: false,
67
+ data: {
68
+ answer: 42
69
+ },
70
+ engine: expect.any(Object)
71
+ })]);
63
72
  });
64
- expect(renderFunction).toHaveBeenCalledTimes(2);
65
- expect(renderFunction).toHaveBeenLastCalledWith([doMutation, {
66
- called: true,
67
- loading: true,
68
- engine: expect.any(Object)
69
- }]);
70
- expect(mockBackend.target).toHaveBeenCalledTimes(1);
71
- await (0, _react.waitFor)(() => getByText(/data: /i));
72
- expect(renderFunction).toHaveBeenCalledTimes(3);
73
- expect(renderFunction).toHaveBeenLastCalledWith([doMutation, {
74
- called: true,
75
- loading: false,
76
- data: {
77
- answer: 42
78
- },
79
- engine: expect.any(Object)
80
- }]);
81
- expect(getByText(/data: /i)).toHaveTextContent("data: 42");
82
73
  });
83
74
  });
@@ -36,7 +36,7 @@ exports.isCreateFeedbackMessage = isCreateFeedbackMessage;
36
36
  const isCreateInterpretation = (type, {
37
37
  resource
38
38
  }) => {
39
- const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
39
+ const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventVisualization|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
40
40
  return type === 'create' && pattern.test(resource);
41
41
  }; // PUT to `interpretations/${id}` (update an interpretation)
42
42
 
@@ -2,79 +2,347 @@
2
2
 
3
3
  var _reactHooks = require("@testing-library/react-hooks");
4
4
 
5
- var _react = _interopRequireDefault(require("react"));
5
+ var React = _interopRequireWildcard(require("react"));
6
6
 
7
7
  var _CustomDataProvider = require("../components/CustomDataProvider");
8
8
 
9
+ var _useDataEngine = require("./useDataEngine");
10
+
9
11
  var _useDataMutation = require("./useDataMutation");
10
12
 
11
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
-
13
- const customData = {
14
- answer: 42
15
- };
16
-
17
- const wrapper = ({
18
- children
19
- }) => /*#__PURE__*/_react.default.createElement(_CustomDataProvider.CustomDataProvider, {
20
- data: customData
21
- }, children);
22
-
23
- const mutation = {
24
- type: 'create',
25
- resource: 'answer',
26
- data: {
27
- answer: 42
28
- }
29
- };
30
- describe('useDataMustation', () => {
31
- const originalError = console.error;
32
- afterEach(() => {
33
- console.error = originalError;
13
+ function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
14
+
15
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
16
+
17
+ describe('useDataMutation', () => {
18
+ it('should render without failing', async () => {
19
+ const mutation = {
20
+ type: 'create',
21
+ resource: 'answer',
22
+ data: {
23
+ answer: '?'
24
+ }
25
+ };
26
+ const data = {
27
+ answer: 42
28
+ };
29
+
30
+ const wrapper = ({
31
+ children
32
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
33
+ data: data
34
+ }, children);
35
+
36
+ const {
37
+ result,
38
+ waitFor
39
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation), {
40
+ wrapper
41
+ });
42
+ const [mutate, beforeMutation] = result.current;
43
+ expect(beforeMutation).toMatchObject({
44
+ loading: false,
45
+ called: false
46
+ });
47
+ (0, _reactHooks.act)(() => {
48
+ mutate();
49
+ });
50
+ await waitFor(() => {
51
+ const [, duringMutation] = result.current;
52
+ expect(duringMutation).toMatchObject({
53
+ loading: true,
54
+ called: true
55
+ });
56
+ });
57
+ await waitFor(() => {
58
+ const [, afterMutation] = result.current;
59
+ expect(afterMutation).toMatchObject({
60
+ loading: false,
61
+ called: true,
62
+ data: 42
63
+ });
64
+ });
34
65
  });
35
- it('Should render without failing', async () => {
36
- let hookState;
37
- console.error = jest.fn();
66
+ it('should run immediately with lazy: false', async () => {
67
+ const mutation = {
68
+ type: 'create',
69
+ resource: 'answer',
70
+ data: {
71
+ answer: '?'
72
+ }
73
+ };
74
+ const data = {
75
+ answer: 42
76
+ };
77
+
78
+ const wrapper = ({
79
+ children
80
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
81
+ data: data
82
+ }, children);
83
+
84
+ const {
85
+ result,
86
+ waitFor
87
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation, {
88
+ lazy: false
89
+ }), {
90
+ wrapper
91
+ });
92
+ const [, duringMutation] = result.current;
93
+ expect(duringMutation).toMatchObject({
94
+ loading: true,
95
+ called: true
96
+ });
97
+ await waitFor(() => {
98
+ const [, afterMutation] = result.current;
99
+ expect(afterMutation).toMatchObject({
100
+ loading: false,
101
+ called: true,
102
+ data: 42
103
+ });
104
+ });
105
+ });
106
+ it('should call onComplete on success', async () => {
107
+ const onComplete = jest.fn();
108
+ const mutation = {
109
+ type: 'create',
110
+ resource: 'answer',
111
+ data: {
112
+ answer: '?'
113
+ }
114
+ };
115
+ const data = {
116
+ answer: 42
117
+ };
118
+
119
+ const wrapper = ({
120
+ children
121
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
122
+ data: data
123
+ }, children);
124
+
125
+ const {
126
+ result,
127
+ waitFor
128
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation, {
129
+ onComplete
130
+ }), {
131
+ wrapper
132
+ });
133
+ expect(onComplete).toHaveBeenCalledTimes(0);
134
+ const [mutate] = result.current;
38
135
  (0, _reactHooks.act)(() => {
39
- hookState = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation), {
40
- wrapper
136
+ mutate();
137
+ });
138
+ await waitFor(() => {
139
+ const [, state] = result.current;
140
+ expect(state).toMatchObject({
141
+ loading: false,
142
+ called: true,
143
+ data: 42
41
144
  });
145
+ expect(onComplete).toHaveBeenCalledTimes(1);
146
+ expect(onComplete).toHaveBeenLastCalledWith(42);
42
147
  });
43
- let [mutate, state] = hookState.result.current;
44
- expect(state).toMatchObject({
45
- called: false,
46
- loading: false
148
+ });
149
+ it('should call onError on error', async () => {
150
+ const error = new Error('Something went wrong');
151
+ const onError = jest.fn();
152
+ const mutation = {
153
+ type: 'create',
154
+ resource: 'answer',
155
+ data: {
156
+ answer: 42
157
+ }
158
+ };
159
+ const data = {
160
+ answer: () => {
161
+ throw error;
162
+ }
163
+ };
164
+
165
+ const wrapper = ({
166
+ children
167
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
168
+ data: data
169
+ }, children);
170
+
171
+ const {
172
+ result,
173
+ waitFor
174
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation, {
175
+ onError
176
+ }), {
177
+ wrapper
47
178
  });
179
+ expect(onError).toHaveBeenCalledTimes(0);
180
+ const [mutate] = result.current;
48
181
  (0, _reactHooks.act)(() => {
49
182
  mutate();
50
183
  });
51
- mutate = hookState.result.current[0];
52
- state = hookState.result.current[1];
53
- expect(state).toMatchObject({
54
- called: true,
55
- loading: true
184
+ await waitFor(() => {
185
+ const [, state] = result.current;
186
+ expect(state).toMatchObject({
187
+ loading: false,
188
+ called: true,
189
+ error
190
+ });
56
191
  });
192
+ expect(onError).toHaveBeenCalledTimes(1);
193
+ expect(onError).toHaveBeenLastCalledWith(error);
57
194
  });
58
- it('Should run immediately with lazy: false', async () => {
59
- let hookState;
60
- console.error = jest.fn();
195
+ it('should resolve variables', async () => {
196
+ const mutation = {
197
+ type: 'update',
198
+ resource: 'answer',
199
+ id: ({
200
+ id
201
+ }) => id,
202
+ data: {
203
+ answer: '?'
204
+ }
205
+ };
206
+ const answerSpy = jest.fn(() => 42);
207
+ const data = {
208
+ answer: answerSpy
209
+ };
210
+
211
+ const wrapper = ({
212
+ children
213
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
214
+ data: data
215
+ }, children);
216
+
217
+ const {
218
+ result,
219
+ waitFor
220
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation, {
221
+ lazy: false,
222
+ variables: {
223
+ id: '1'
224
+ }
225
+ }), {
226
+ wrapper
227
+ });
228
+ await waitFor(() => {
229
+ expect(answerSpy).toHaveBeenLastCalledWith(expect.any(String), expect.objectContaining({
230
+ id: '1'
231
+ }), expect.any(Object));
232
+ });
233
+ const [mutate] = result.current;
61
234
  (0, _reactHooks.act)(() => {
62
- hookState = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation, {
63
- lazy: false
64
- }), {
65
- wrapper
235
+ mutate({
236
+ id: '2'
66
237
  });
67
238
  });
68
- let [, state] = hookState.result.current;
69
- expect(state).toMatchObject({
70
- called: true,
71
- loading: true
239
+ await waitFor(() => {
240
+ expect(answerSpy).toHaveBeenLastCalledWith(expect.any(String), expect.objectContaining({
241
+ id: '2'
242
+ }), expect.any(Object));
72
243
  });
73
- await hookState.waitForNextUpdate();
74
- state = hookState.result.current[1];
244
+ });
245
+ it('should return a reference to the engine', async () => {
246
+ const mutation = {
247
+ type: 'create',
248
+ resource: 'answer',
249
+ data: {
250
+ answer: '?'
251
+ }
252
+ };
253
+
254
+ const wrapper = ({
255
+ children
256
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
257
+ data: {}
258
+ }, children);
259
+
260
+ const engineHook = (0, _reactHooks.renderHook)(() => (0, _useDataEngine.useDataEngine)(), {
261
+ wrapper
262
+ });
263
+ const mutationHook = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation), {
264
+ wrapper
265
+ });
266
+ /**
267
+ * Ideally we'd check referential equality here with .toBe, but since
268
+ * both hooks run in a different context that doesn't work.
269
+ */
270
+
271
+ expect(mutationHook.result.current[1].engine).toStrictEqual(engineHook.result.current);
272
+ });
273
+ it('should return a stable mutate function', async () => {
274
+ const mutation = {
275
+ type: 'create',
276
+ resource: 'answer',
277
+ data: {
278
+ answer: '?'
279
+ }
280
+ };
281
+ const data = {
282
+ answer: 42
283
+ };
284
+
285
+ const wrapper = ({
286
+ children
287
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
288
+ data: data
289
+ }, children);
290
+
291
+ const {
292
+ result
293
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation), {
294
+ wrapper
295
+ });
296
+ const [firstMutate] = result.current;
297
+ await (0, _reactHooks.act)(async () => {
298
+ await firstMutate({
299
+ variable: 'variable'
300
+ });
301
+ });
302
+ const [secondMutate, state] = result.current;
75
303
  expect(state).toMatchObject({
76
304
  loading: false,
77
- data: 42
305
+ called: true
306
+ });
307
+ expect(firstMutate).toBe(secondMutate);
308
+ });
309
+ it('should resolve with the data from mutate on success', async () => {
310
+ const mutation = {
311
+ type: 'create',
312
+ resource: 'answer',
313
+ data: {
314
+ answer: '?'
315
+ }
316
+ };
317
+ const data = {
318
+ answer: 42
319
+ };
320
+
321
+ const wrapper = ({
322
+ children
323
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
324
+ data: data
325
+ }, children);
326
+
327
+ const {
328
+ result,
329
+ waitFor
330
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataMutation.useDataMutation)(mutation), {
331
+ wrapper
332
+ });
333
+ let mutatePromise;
334
+ const [mutate] = result.current;
335
+ (0, _reactHooks.act)(() => {
336
+ mutatePromise = mutate();
337
+ });
338
+ await waitFor(() => {
339
+ const [, state] = result.current;
340
+ expect(state).toMatchObject({
341
+ loading: false,
342
+ called: true,
343
+ data: 42
344
+ });
345
+ expect(mutatePromise).resolves.toBe(42);
78
346
  });
79
347
  });
80
348
  });
@@ -1,8 +1,8 @@
1
1
  import { render, waitFor } from '@testing-library/react';
2
2
  import * as React from 'react';
3
3
  import { CustomDataProvider, DataQuery } from '../react';
4
- describe('Testing custom data provider and useQuery hook', () => {
5
- it('Should render without failing', async () => {
4
+ describe('<DataQuery />', () => {
5
+ it('should render without failing', async () => {
6
6
  const data = {
7
7
  answer: 42
8
8
  };
@@ -13,18 +13,8 @@ describe('Testing custom data provider and useQuery hook', () => {
13
13
  data: data
14
14
  }, children);
15
15
 
16
- const renderFunction = jest.fn(({
17
- loading,
18
- error,
19
- data
20
- }) => {
21
- if (loading) return 'loading';
22
- if (error) return /*#__PURE__*/React.createElement("div", null, "error: ", error.message);
23
- return /*#__PURE__*/React.createElement("div", null, "data: ", data && data.answer);
24
- });
25
- const {
26
- getByText
27
- } = render( /*#__PURE__*/React.createElement(DataQuery, {
16
+ const renderFunction = jest.fn(() => null);
17
+ render( /*#__PURE__*/React.createElement(DataQuery, {
28
18
  query: {
29
19
  answer: {
30
20
  resource: 'answer'
@@ -33,24 +23,21 @@ describe('Testing custom data provider and useQuery hook', () => {
33
23
  }, renderFunction), {
34
24
  wrapper
35
25
  });
36
- expect(getByText(/loading/i)).not.toBeUndefined();
37
26
  expect(renderFunction).toHaveBeenCalledTimes(1);
38
27
  expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
39
28
  called: true,
40
29
  loading: true
41
30
  }));
42
31
  await waitFor(() => {
43
- getByText(/data: /i);
32
+ expect(renderFunction).toHaveBeenCalledTimes(2);
33
+ expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
34
+ called: true,
35
+ loading: false,
36
+ data
37
+ }));
44
38
  });
45
- expect(getByText(/data: /i)).toHaveTextContent("data: ".concat(data.answer));
46
- expect(renderFunction).toHaveBeenCalledTimes(2);
47
- expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
48
- called: true,
49
- loading: false,
50
- data
51
- }));
52
39
  });
53
- it('Should render an error', async () => {
40
+ it('should render an error', async () => {
54
41
  const expectedError = new Error('Something went wrong');
55
42
  const data = {
56
43
  test: () => {
@@ -64,18 +51,8 @@ describe('Testing custom data provider and useQuery hook', () => {
64
51
  data: data
65
52
  }, children);
66
53
 
67
- const renderFunction = jest.fn(({
68
- loading,
69
- error,
70
- data
71
- }) => {
72
- if (loading) return 'loading';
73
- if (error) return /*#__PURE__*/React.createElement("div", null, "error: ", error.message);
74
- return /*#__PURE__*/React.createElement("div", null, "data: ", data && data.test);
75
- });
76
- const {
77
- getByText
78
- } = render( /*#__PURE__*/React.createElement(DataQuery, {
54
+ const renderFunction = jest.fn(() => null);
55
+ render( /*#__PURE__*/React.createElement(DataQuery, {
79
56
  query: {
80
57
  test: {
81
58
  resource: 'test'
@@ -84,21 +61,18 @@ describe('Testing custom data provider and useQuery hook', () => {
84
61
  }, renderFunction), {
85
62
  wrapper
86
63
  });
87
- expect(getByText(/loading/i)).not.toBeUndefined();
88
64
  expect(renderFunction).toHaveBeenCalledTimes(1);
89
65
  expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
90
66
  called: true,
91
67
  loading: true
92
68
  }));
93
69
  await waitFor(() => {
94
- getByText(/error: /i);
70
+ expect(renderFunction).toHaveBeenCalledTimes(2);
71
+ expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
72
+ called: true,
73
+ loading: false,
74
+ error: expectedError
75
+ }));
95
76
  });
96
- expect(renderFunction).toHaveBeenCalledTimes(2);
97
- expect(getByText(/error: /i)).toHaveTextContent("error: ".concat(expectedError.message));
98
- expect(renderFunction).toHaveBeenLastCalledWith(expect.objectContaining({
99
- called: true,
100
- loading: false,
101
- error: expectedError
102
- }));
103
77
  });
104
78
  });
@@ -1,76 +1,65 @@
1
- import { render, act, waitFor } from '@testing-library/react';
2
- import React from 'react';
1
+ import { render, waitFor, act } from '@testing-library/react';
2
+ import * as React from 'react';
3
3
  import { CustomDataProvider, DataMutation } from '../react';
4
- const mockBackend = {
5
- target: jest.fn((type, query) => {
6
- expect(query.resource).toBe('target');
7
- expect(type).toBe('create');
8
- expect(query.data).toMatchObject({
9
- question: '?'
10
- });
11
- return Promise.resolve({
12
- answer: 42
13
- });
14
- })
15
- };
16
- describe('Test mutations', () => {
17
- it('Should call the mock callback', async () => {
18
- let doMutation;
19
- const renderFunction = jest.fn(([mutate, {
20
- called,
21
- loading,
22
- error,
23
- data
24
- }]) => {
25
- doMutation = mutate;
26
- if (!called) return 'uncalled';
27
- if (loading) return 'loading';
28
- if (error) return /*#__PURE__*/React.createElement("div", null, "error: ", error.message);
29
- if (data) return /*#__PURE__*/React.createElement("div", null, "data: ", data.answer);
30
- });
31
- const testMutation = {
32
- resource: 'target',
4
+ describe('<DataMutation />', () => {
5
+ it('should render without failing', async () => {
6
+ const endpointSpy = jest.fn(() => Promise.resolve(42));
7
+ const mutation = {
8
+ resource: 'answer',
33
9
  type: 'create',
34
10
  data: {
35
11
  question: '?'
36
12
  }
37
13
  };
38
- const {
39
- getByText
40
- } = render( /*#__PURE__*/React.createElement(CustomDataProvider, {
41
- data: mockBackend
42
- }, /*#__PURE__*/React.createElement(DataMutation, {
43
- mutation: testMutation
44
- }, renderFunction)));
45
- expect(getByText(/uncalled/i)).not.toBeUndefined();
46
- expect(renderFunction).toHaveBeenCalledTimes(1);
47
- expect(mockBackend.target).not.toHaveBeenCalled();
48
- expect(renderFunction).toHaveBeenLastCalledWith([expect.any(Function), {
14
+ const data = {
15
+ answer: endpointSpy
16
+ };
17
+
18
+ const wrapper = ({
19
+ children
20
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
21
+ data: data
22
+ }, children);
23
+
24
+ const renderSpy = jest.fn(() => null);
25
+ render( /*#__PURE__*/React.createElement(DataMutation, {
26
+ mutation: mutation
27
+ }, renderSpy), {
28
+ wrapper
29
+ });
30
+ expect(endpointSpy).toHaveBeenCalledTimes(0);
31
+ expect(renderSpy).toHaveBeenCalledTimes(1);
32
+ expect(renderSpy).toHaveBeenLastCalledWith([expect.any(Function), expect.objectContaining({
49
33
  called: false,
50
34
  loading: false,
51
35
  engine: expect.any(Object)
52
- }]);
53
- expect(doMutation).not.toBeUndefined();
54
- act(() => {
55
- doMutation && doMutation();
36
+ })]);
37
+ await act(async () => {
38
+ const firstRenderSpyCall = renderSpy.mock.calls[0];
39
+ const firstRenderSpyArgument = firstRenderSpyCall[0];
40
+ const [mutate] = firstRenderSpyArgument;
41
+ await mutate();
42
+ });
43
+ waitFor(() => {
44
+ expect(endpointSpy).toHaveBeenCalledTimes(1);
45
+ expect(renderSpy).toHaveBeenCalledTimes(2);
46
+ expect(renderSpy).toHaveBeenLastCalledWith([expect.any(Function), expect.objectContaining({
47
+ called: true,
48
+ loading: true,
49
+ engine: expect.any(Object)
50
+ })]);
51
+ });
52
+ waitFor(() => {
53
+ expect(endpointSpy).toHaveBeenCalledTimes(1);
54
+ expect(renderSpy).toHaveBeenCalledTimes(3);
55
+ expect(renderSpy).toHaveBeenLastCalledWith([expect.any(Function), expect.objectContaining({
56
+ called: true,
57
+ loading: false,
58
+ data: {
59
+ answer: 42
60
+ },
61
+ engine: expect.any(Object)
62
+ })]);
56
63
  });
57
- expect(renderFunction).toHaveBeenCalledTimes(2);
58
- expect(renderFunction).toHaveBeenLastCalledWith([doMutation, {
59
- called: true,
60
- loading: true,
61
- engine: expect.any(Object)
62
- }]);
63
- expect(mockBackend.target).toHaveBeenCalledTimes(1);
64
- await waitFor(() => getByText(/data: /i));
65
- expect(renderFunction).toHaveBeenCalledTimes(3);
66
- expect(renderFunction).toHaveBeenLastCalledWith([doMutation, {
67
- called: true,
68
- loading: false,
69
- data: {
70
- answer: 42
71
- },
72
- engine: expect.any(Object)
73
- }]);
74
- expect(getByText(/data: /i)).toHaveTextContent("data: 42");
75
64
  });
76
65
  });
@@ -23,7 +23,7 @@ export const isCreateFeedbackMessage = (type, {
23
23
  export const isCreateInterpretation = (type, {
24
24
  resource
25
25
  }) => {
26
- const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
26
+ const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventVisualization|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
27
27
  return type === 'create' && pattern.test(resource);
28
28
  }; // PUT to `interpretations/${id}` (update an interpretation)
29
29
 
@@ -1,72 +1,337 @@
1
1
  import { renderHook, act } from '@testing-library/react-hooks';
2
- import React from 'react';
2
+ import * as React from 'react';
3
3
  import { CustomDataProvider } from '../components/CustomDataProvider';
4
+ import { useDataEngine } from './useDataEngine';
4
5
  import { useDataMutation } from './useDataMutation';
5
- const customData = {
6
- answer: 42
7
- };
8
-
9
- const wrapper = ({
10
- children
11
- }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
12
- data: customData
13
- }, children);
14
-
15
- const mutation = {
16
- type: 'create',
17
- resource: 'answer',
18
- data: {
19
- answer: 42
20
- }
21
- };
22
- describe('useDataMustation', () => {
23
- const originalError = console.error;
24
- afterEach(() => {
25
- console.error = originalError;
6
+ describe('useDataMutation', () => {
7
+ it('should render without failing', async () => {
8
+ const mutation = {
9
+ type: 'create',
10
+ resource: 'answer',
11
+ data: {
12
+ answer: '?'
13
+ }
14
+ };
15
+ const data = {
16
+ answer: 42
17
+ };
18
+
19
+ const wrapper = ({
20
+ children
21
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
22
+ data: data
23
+ }, children);
24
+
25
+ const {
26
+ result,
27
+ waitFor
28
+ } = renderHook(() => useDataMutation(mutation), {
29
+ wrapper
30
+ });
31
+ const [mutate, beforeMutation] = result.current;
32
+ expect(beforeMutation).toMatchObject({
33
+ loading: false,
34
+ called: false
35
+ });
36
+ act(() => {
37
+ mutate();
38
+ });
39
+ await waitFor(() => {
40
+ const [, duringMutation] = result.current;
41
+ expect(duringMutation).toMatchObject({
42
+ loading: true,
43
+ called: true
44
+ });
45
+ });
46
+ await waitFor(() => {
47
+ const [, afterMutation] = result.current;
48
+ expect(afterMutation).toMatchObject({
49
+ loading: false,
50
+ called: true,
51
+ data: 42
52
+ });
53
+ });
54
+ });
55
+ it('should run immediately with lazy: false', async () => {
56
+ const mutation = {
57
+ type: 'create',
58
+ resource: 'answer',
59
+ data: {
60
+ answer: '?'
61
+ }
62
+ };
63
+ const data = {
64
+ answer: 42
65
+ };
66
+
67
+ const wrapper = ({
68
+ children
69
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
70
+ data: data
71
+ }, children);
72
+
73
+ const {
74
+ result,
75
+ waitFor
76
+ } = renderHook(() => useDataMutation(mutation, {
77
+ lazy: false
78
+ }), {
79
+ wrapper
80
+ });
81
+ const [, duringMutation] = result.current;
82
+ expect(duringMutation).toMatchObject({
83
+ loading: true,
84
+ called: true
85
+ });
86
+ await waitFor(() => {
87
+ const [, afterMutation] = result.current;
88
+ expect(afterMutation).toMatchObject({
89
+ loading: false,
90
+ called: true,
91
+ data: 42
92
+ });
93
+ });
26
94
  });
27
- it('Should render without failing', async () => {
28
- let hookState;
29
- console.error = jest.fn();
95
+ it('should call onComplete on success', async () => {
96
+ const onComplete = jest.fn();
97
+ const mutation = {
98
+ type: 'create',
99
+ resource: 'answer',
100
+ data: {
101
+ answer: '?'
102
+ }
103
+ };
104
+ const data = {
105
+ answer: 42
106
+ };
107
+
108
+ const wrapper = ({
109
+ children
110
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
111
+ data: data
112
+ }, children);
113
+
114
+ const {
115
+ result,
116
+ waitFor
117
+ } = renderHook(() => useDataMutation(mutation, {
118
+ onComplete
119
+ }), {
120
+ wrapper
121
+ });
122
+ expect(onComplete).toHaveBeenCalledTimes(0);
123
+ const [mutate] = result.current;
30
124
  act(() => {
31
- hookState = renderHook(() => useDataMutation(mutation), {
32
- wrapper
125
+ mutate();
126
+ });
127
+ await waitFor(() => {
128
+ const [, state] = result.current;
129
+ expect(state).toMatchObject({
130
+ loading: false,
131
+ called: true,
132
+ data: 42
33
133
  });
134
+ expect(onComplete).toHaveBeenCalledTimes(1);
135
+ expect(onComplete).toHaveBeenLastCalledWith(42);
34
136
  });
35
- let [mutate, state] = hookState.result.current;
36
- expect(state).toMatchObject({
37
- called: false,
38
- loading: false
137
+ });
138
+ it('should call onError on error', async () => {
139
+ const error = new Error('Something went wrong');
140
+ const onError = jest.fn();
141
+ const mutation = {
142
+ type: 'create',
143
+ resource: 'answer',
144
+ data: {
145
+ answer: 42
146
+ }
147
+ };
148
+ const data = {
149
+ answer: () => {
150
+ throw error;
151
+ }
152
+ };
153
+
154
+ const wrapper = ({
155
+ children
156
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
157
+ data: data
158
+ }, children);
159
+
160
+ const {
161
+ result,
162
+ waitFor
163
+ } = renderHook(() => useDataMutation(mutation, {
164
+ onError
165
+ }), {
166
+ wrapper
39
167
  });
168
+ expect(onError).toHaveBeenCalledTimes(0);
169
+ const [mutate] = result.current;
40
170
  act(() => {
41
171
  mutate();
42
172
  });
43
- mutate = hookState.result.current[0];
44
- state = hookState.result.current[1];
45
- expect(state).toMatchObject({
46
- called: true,
47
- loading: true
173
+ await waitFor(() => {
174
+ const [, state] = result.current;
175
+ expect(state).toMatchObject({
176
+ loading: false,
177
+ called: true,
178
+ error
179
+ });
48
180
  });
181
+ expect(onError).toHaveBeenCalledTimes(1);
182
+ expect(onError).toHaveBeenLastCalledWith(error);
49
183
  });
50
- it('Should run immediately with lazy: false', async () => {
51
- let hookState;
52
- console.error = jest.fn();
184
+ it('should resolve variables', async () => {
185
+ const mutation = {
186
+ type: 'update',
187
+ resource: 'answer',
188
+ id: ({
189
+ id
190
+ }) => id,
191
+ data: {
192
+ answer: '?'
193
+ }
194
+ };
195
+ const answerSpy = jest.fn(() => 42);
196
+ const data = {
197
+ answer: answerSpy
198
+ };
199
+
200
+ const wrapper = ({
201
+ children
202
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
203
+ data: data
204
+ }, children);
205
+
206
+ const {
207
+ result,
208
+ waitFor
209
+ } = renderHook(() => useDataMutation(mutation, {
210
+ lazy: false,
211
+ variables: {
212
+ id: '1'
213
+ }
214
+ }), {
215
+ wrapper
216
+ });
217
+ await waitFor(() => {
218
+ expect(answerSpy).toHaveBeenLastCalledWith(expect.any(String), expect.objectContaining({
219
+ id: '1'
220
+ }), expect.any(Object));
221
+ });
222
+ const [mutate] = result.current;
53
223
  act(() => {
54
- hookState = renderHook(() => useDataMutation(mutation, {
55
- lazy: false
56
- }), {
57
- wrapper
224
+ mutate({
225
+ id: '2'
58
226
  });
59
227
  });
60
- let [, state] = hookState.result.current;
61
- expect(state).toMatchObject({
62
- called: true,
63
- loading: true
228
+ await waitFor(() => {
229
+ expect(answerSpy).toHaveBeenLastCalledWith(expect.any(String), expect.objectContaining({
230
+ id: '2'
231
+ }), expect.any(Object));
64
232
  });
65
- await hookState.waitForNextUpdate();
66
- state = hookState.result.current[1];
233
+ });
234
+ it('should return a reference to the engine', async () => {
235
+ const mutation = {
236
+ type: 'create',
237
+ resource: 'answer',
238
+ data: {
239
+ answer: '?'
240
+ }
241
+ };
242
+
243
+ const wrapper = ({
244
+ children
245
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
246
+ data: {}
247
+ }, children);
248
+
249
+ const engineHook = renderHook(() => useDataEngine(), {
250
+ wrapper
251
+ });
252
+ const mutationHook = renderHook(() => useDataMutation(mutation), {
253
+ wrapper
254
+ });
255
+ /**
256
+ * Ideally we'd check referential equality here with .toBe, but since
257
+ * both hooks run in a different context that doesn't work.
258
+ */
259
+
260
+ expect(mutationHook.result.current[1].engine).toStrictEqual(engineHook.result.current);
261
+ });
262
+ it('should return a stable mutate function', async () => {
263
+ const mutation = {
264
+ type: 'create',
265
+ resource: 'answer',
266
+ data: {
267
+ answer: '?'
268
+ }
269
+ };
270
+ const data = {
271
+ answer: 42
272
+ };
273
+
274
+ const wrapper = ({
275
+ children
276
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
277
+ data: data
278
+ }, children);
279
+
280
+ const {
281
+ result
282
+ } = renderHook(() => useDataMutation(mutation), {
283
+ wrapper
284
+ });
285
+ const [firstMutate] = result.current;
286
+ await act(async () => {
287
+ await firstMutate({
288
+ variable: 'variable'
289
+ });
290
+ });
291
+ const [secondMutate, state] = result.current;
67
292
  expect(state).toMatchObject({
68
293
  loading: false,
69
- data: 42
294
+ called: true
295
+ });
296
+ expect(firstMutate).toBe(secondMutate);
297
+ });
298
+ it('should resolve with the data from mutate on success', async () => {
299
+ const mutation = {
300
+ type: 'create',
301
+ resource: 'answer',
302
+ data: {
303
+ answer: '?'
304
+ }
305
+ };
306
+ const data = {
307
+ answer: 42
308
+ };
309
+
310
+ const wrapper = ({
311
+ children
312
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
313
+ data: data
314
+ }, children);
315
+
316
+ const {
317
+ result,
318
+ waitFor
319
+ } = renderHook(() => useDataMutation(mutation), {
320
+ wrapper
321
+ });
322
+ let mutatePromise;
323
+ const [mutate] = result.current;
324
+ act(() => {
325
+ mutatePromise = mutate();
326
+ });
327
+ await waitFor(() => {
328
+ const [, state] = result.current;
329
+ expect(state).toMatchObject({
330
+ loading: false,
331
+ called: true,
332
+ data: 42
333
+ });
334
+ expect(mutatePromise).resolves.toBe(42);
70
335
  });
71
336
  });
72
337
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/app-service-data",
3
- "version": "3.2.7",
3
+ "version": "3.2.8",
4
4
  "main": "./build/cjs/index.js",
5
5
  "module": "./build/es/index.js",
6
6
  "types": "build/types/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "build/**"
23
23
  ],
24
24
  "peerDependencies": {
25
- "@dhis2/app-service-config": "3.2.7",
25
+ "@dhis2/app-service-config": "3.2.8",
26
26
  "@dhis2/cli-app-scripts": "^7.1.1",
27
27
  "prop-types": "^15.7.2",
28
28
  "react": "^16.8",