@dhis2/app-service-data 3.2.5 → 3.2.9
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/build/cjs/__tests__/integration.test.js +19 -45
- package/build/cjs/__tests__/mutations.test.js +55 -64
- package/build/cjs/links/CustomDataLink.js +1 -1
- package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +18 -8
- package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +38 -10
- package/build/cjs/react/hooks/useDataMutation.test.js +320 -52
- package/build/cjs/react/hooks/useDataQuery.js +10 -6
- package/build/cjs/react/hooks/useDataQuery.test.js +105 -0
- package/build/es/__tests__/integration.test.js +19 -45
- package/build/es/__tests__/mutations.test.js +53 -64
- package/build/es/links/CustomDataLink.js +1 -1
- package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +13 -6
- package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +39 -11
- package/build/es/react/hooks/useDataMutation.test.js +315 -50
- package/build/es/react/hooks/useDataQuery.js +10 -6
- package/build/es/react/hooks/useDataQuery.test.js +105 -0
- package/build/types/links/RestAPILink/queryToRequestOptions/textPlainMatchers.d.ts +2 -1
- package/package.json +2 -2
|
@@ -1,76 +1,65 @@
|
|
|
1
|
-
import { render,
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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 class CustomDataLink {
|
|
|
23
23
|
|
|
24
24
|
const customResource = this.data[query.resource];
|
|
25
25
|
|
|
26
|
-
if (
|
|
26
|
+
if (customResource === undefined) {
|
|
27
27
|
if (this.failOnMiss) {
|
|
28
28
|
throw new Error("No data provided for resource type ".concat(query.resource, "!"));
|
|
29
29
|
}
|
|
@@ -18,25 +18,32 @@ export const isReplyToMessageConversation = (type, {
|
|
|
18
18
|
|
|
19
19
|
export const isCreateFeedbackMessage = (type, {
|
|
20
20
|
resource
|
|
21
|
-
}) => type === 'create' && resource === 'messageConversations/feedback'; // POST
|
|
21
|
+
}) => type === 'create' && resource === 'messageConversations/feedback'; // POST `interpretations/${objectType}/${id}` (add an interpretation to a visualization)
|
|
22
22
|
|
|
23
|
-
export const
|
|
23
|
+
export const isCreateInterpretation = (type, {
|
|
24
|
+
resource
|
|
25
|
+
}) => {
|
|
26
|
+
const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventVisualization|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
|
|
27
|
+
return type === 'create' && pattern.test(resource);
|
|
28
|
+
}; // PUT to `interpretations/${id}` (update an interpretation)
|
|
29
|
+
|
|
30
|
+
export const isUpdateInterpretation = (type, {
|
|
24
31
|
resource,
|
|
25
32
|
id
|
|
26
33
|
}) => {
|
|
27
|
-
if (type !== '
|
|
34
|
+
if (type !== 'replace') {
|
|
28
35
|
return false;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
let resourcePattern;
|
|
32
39
|
|
|
33
|
-
if (
|
|
34
|
-
resourcePattern = /^interpretations
|
|
40
|
+
if (id) {
|
|
41
|
+
resourcePattern = /^interpretations$/;
|
|
35
42
|
const idPattern = /^[a-zA-Z0-9]{11}$/;
|
|
36
43
|
return resourcePattern.test(resource) && idPattern.test(id);
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
resourcePattern = /^interpretations\/
|
|
46
|
+
resourcePattern = /^interpretations\/[a-zA-Z0-9]{11}$/;
|
|
40
47
|
return resourcePattern.test(resource);
|
|
41
48
|
}; // POST to `interpretations/${id}/comments` (comment on an interpretation)
|
|
42
49
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isReplyToMessageConversation, isCreateFeedbackMessage,
|
|
1
|
+
import { isReplyToMessageConversation, isCreateFeedbackMessage, isCreateInterpretation, isUpdateInterpretation, isCommentOnInterpretation, isInterpretationCommentUpdate, isAddOrUpdateSystemOrUserSetting, addOrUpdateConfigurationProperty, isMetadataPackageInstallation } from './textPlainMatchers';
|
|
2
2
|
describe('isReplyToMessageConversation', () => {
|
|
3
3
|
it('retuns true for POST to `messageConversations/${id}`', () => {
|
|
4
4
|
expect(isReplyToMessageConversation('create', {
|
|
@@ -23,30 +23,58 @@ describe('isCreateFeedbackMessage', () => {
|
|
|
23
23
|
})).toEqual(false);
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
|
-
describe('
|
|
26
|
+
describe('isCreateInterpretation', () => {
|
|
27
27
|
it('returns true for a POST to "interpretations/chart/${id}"', () => {
|
|
28
|
-
expect(
|
|
28
|
+
expect(isCreateInterpretation('create', {
|
|
29
29
|
resource: 'interpretations/chart/oXD88WWSQpR'
|
|
30
30
|
})).toEqual(true);
|
|
31
31
|
});
|
|
32
|
-
it('returns
|
|
33
|
-
expect(
|
|
32
|
+
it('returns false for a PUT to "interpretations/chart/${id}"', () => {
|
|
33
|
+
expect(isCreateInterpretation('replace', {
|
|
34
34
|
resource: 'interpretations/chart/oXD88WWSQpR'
|
|
35
|
+
})).toEqual(false);
|
|
36
|
+
});
|
|
37
|
+
it('retuns false for PATCH requests with a valid query', () => {
|
|
38
|
+
expect(isCreateInterpretation('update', {
|
|
39
|
+
resource: 'interpretations/chart/oXD88WWSQpR'
|
|
40
|
+
})).toEqual(false);
|
|
41
|
+
});
|
|
42
|
+
it('returns false for a request to a different resource', () => {
|
|
43
|
+
expect(isCreateInterpretation('create', {
|
|
44
|
+
resource: 'interpretations/dummy/oXD88WWSQpR'
|
|
45
|
+
})).toEqual(false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('isUpdateInterpretation', () => {
|
|
49
|
+
it('returns true for a PUT to "interpretations/${id}"', () => {
|
|
50
|
+
expect(isUpdateInterpretation('replace', {
|
|
51
|
+
resource: 'interpretations/oXD88WWSQpR'
|
|
35
52
|
})).toEqual(true);
|
|
36
53
|
});
|
|
37
54
|
it('returns true for PUT with populated query.id', () => {
|
|
38
|
-
expect(
|
|
39
|
-
resource: 'interpretations
|
|
55
|
+
expect(isUpdateInterpretation('replace', {
|
|
56
|
+
resource: 'interpretations',
|
|
40
57
|
id: 'oXD88WWSQpR'
|
|
41
58
|
})).toEqual(true);
|
|
42
59
|
});
|
|
43
|
-
it('
|
|
44
|
-
expect(
|
|
45
|
-
resource: 'interpretations/
|
|
60
|
+
it('returns false for a POST to "interpretations/${id}"', () => {
|
|
61
|
+
expect(isUpdateInterpretation('create', {
|
|
62
|
+
resource: 'interpretations/oXD88WWSQpR'
|
|
63
|
+
})).toEqual(false);
|
|
64
|
+
});
|
|
65
|
+
it('returns false for a PATCH to "interpretations/${id}"', () => {
|
|
66
|
+
expect(isUpdateInterpretation('update', {
|
|
67
|
+
resource: 'interpretations/oXD88WWSQpR'
|
|
68
|
+
})).toEqual(false);
|
|
69
|
+
});
|
|
70
|
+
it('returns false for PATCH with populated query.id', () => {
|
|
71
|
+
expect(isUpdateInterpretation('update', {
|
|
72
|
+
resource: 'interpretations',
|
|
73
|
+
id: 'oXD88WWSQpR'
|
|
46
74
|
})).toEqual(false);
|
|
47
75
|
});
|
|
48
76
|
it('returns false for a request to a different resource', () => {
|
|
49
|
-
expect(
|
|
77
|
+
expect(isUpdateInterpretation('create', {
|
|
50
78
|
resource: 'interpretations/dummy/oXD88WWSQpR'
|
|
51
79
|
})).toEqual(false);
|
|
52
80
|
});
|
|
@@ -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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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('
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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('
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
}), {
|
|
57
|
-
wrapper
|
|
224
|
+
mutate({
|
|
225
|
+
id: '2'
|
|
58
226
|
});
|
|
59
227
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
228
|
+
await waitFor(() => {
|
|
229
|
+
expect(answerSpy).toHaveBeenLastCalledWith(expect.any(String), expect.objectContaining({
|
|
230
|
+
id: '2'
|
|
231
|
+
}), expect.any(Object));
|
|
64
232
|
});
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
});
|
|
@@ -104,10 +104,6 @@ export const useDataQuery = (query, {
|
|
|
104
104
|
}) => data);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
if (!enabled) {
|
|
108
|
-
setEnabled(true);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
107
|
if (newVariables) {
|
|
112
108
|
// Use cached hash if it exists
|
|
113
109
|
const currentHash = variablesHash.current || stableVariablesHash(variables);
|
|
@@ -117,8 +113,11 @@ export const useDataQuery = (query, {
|
|
|
117
113
|
const mergedHash = stableVariablesHash(mergedVariables);
|
|
118
114
|
const identical = currentHash === mergedHash;
|
|
119
115
|
|
|
120
|
-
if (identical) {
|
|
121
|
-
|
|
116
|
+
if (identical && enabled) {
|
|
117
|
+
/**
|
|
118
|
+
* If the variables are identical and the query is enabled
|
|
119
|
+
* we'll need to trigger the refetch manually
|
|
120
|
+
*/
|
|
122
121
|
return queryRefetch({
|
|
123
122
|
cancelRefetch: true,
|
|
124
123
|
throwOnError: false
|
|
@@ -129,6 +128,11 @@ export const useDataQuery = (query, {
|
|
|
129
128
|
variablesHash.current = mergedHash;
|
|
130
129
|
setVariables(mergedVariables);
|
|
131
130
|
}
|
|
131
|
+
} // Enable the query after the variables have been set to prevent extra request
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if (!enabled) {
|
|
135
|
+
setEnabled(true);
|
|
132
136
|
} // This promise does not currently reject on errors
|
|
133
137
|
|
|
134
138
|
|