1import { map, tap, pipe, fromValue, toArray, toPromise } from 'wonka';
2import { vi, expect, describe, it } from 'vitest';
3
4import { Client } from '../client';
5import { queryResponse, queryOperation } from '../test-utils';
6import { Operation } from '../types';
7import { mapExchange } from './map';
8
9import {
10 makeOperation,
11 makeErrorResult,
12 makeResult,
13 CombinedError,
14} from '../utils';
15
16describe('onOperation', () => {
17 it('triggers and maps on operations', () => {
18 const mockOperation = makeOperation('query', queryOperation, {
19 ...queryOperation.context,
20 mock: true,
21 });
22
23 const onOperation = vi.fn().mockReturnValue(mockOperation);
24 const onExchangeResult = vi.fn();
25
26 const exchangeArgs = {
27 forward: op$ =>
28 pipe(
29 op$,
30 tap(onExchangeResult),
31 map((operation: Operation) => makeResult(operation, { data: null }))
32 ),
33 client: {} as Client,
34 dispatchDebug: () => null,
35 };
36
37 pipe(
38 fromValue(queryOperation),
39 mapExchange({ onOperation })(exchangeArgs),
40 toArray
41 );
42
43 expect(onOperation).toBeCalledTimes(1);
44 expect(onOperation).toBeCalledWith(queryOperation);
45 expect(onExchangeResult).toBeCalledTimes(1);
46 expect(onExchangeResult).toBeCalledWith(mockOperation);
47 });
48
49 it('triggers and forwards identity when returning undefined', () => {
50 const onOperation = vi.fn().mockReturnValue(undefined);
51 const onExchangeResult = vi.fn();
52
53 const exchangeArgs = {
54 forward: op$ =>
55 pipe(
56 op$,
57 tap(onExchangeResult),
58 map((operation: Operation) => makeResult(operation, { data: null }))
59 ),
60 client: {} as Client,
61 dispatchDebug: () => null,
62 };
63
64 pipe(
65 fromValue(queryOperation),
66 mapExchange({ onOperation })(exchangeArgs),
67 toArray
68 );
69
70 expect(onOperation).toBeCalledTimes(1);
71 expect(onOperation).toBeCalledWith(queryOperation);
72 expect(onExchangeResult).toBeCalledTimes(1);
73 expect(onExchangeResult).toBeCalledWith(queryOperation);
74 });
75
76 it('awaits returned promises as needed', async () => {
77 const mockOperation = makeOperation('query', queryOperation, {
78 ...queryOperation.context,
79 mock: true,
80 });
81
82 const onOperation = vi.fn().mockResolvedValue(mockOperation);
83 const onExchangeResult = vi.fn();
84
85 const exchangeArgs = {
86 forward: op$ =>
87 pipe(
88 op$,
89 tap(onExchangeResult),
90 map((operation: Operation) => makeResult(operation, { data: null }))
91 ),
92 client: {} as Client,
93 dispatchDebug: () => null,
94 };
95
96 await pipe(
97 fromValue(queryOperation),
98 mapExchange({ onOperation })(exchangeArgs),
99 toPromise
100 );
101
102 expect(onOperation).toBeCalledTimes(1);
103 expect(onOperation).toBeCalledWith(queryOperation);
104 expect(onExchangeResult).toBeCalledTimes(1);
105 expect(onExchangeResult).toBeCalledWith(mockOperation);
106 });
107});
108
109describe('onResult', () => {
110 it('triggers and maps on results', async () => {
111 const mockOperation = makeOperation('query', queryOperation, {
112 ...queryOperation.context,
113 mock: true,
114 });
115
116 const mockResult = makeErrorResult(mockOperation, new Error('Mock'));
117 const onResult = vi.fn().mockReturnValue(mockResult);
118 const onExchangeResult = vi.fn();
119
120 const exchangeArgs = {
121 forward: op$ =>
122 pipe(
123 op$,
124 map((operation: Operation) => makeResult(operation, { data: null }))
125 ),
126 client: {} as Client,
127 dispatchDebug: () => null,
128 };
129
130 pipe(
131 fromValue(queryOperation),
132 mapExchange({ onResult })(exchangeArgs),
133 tap(onExchangeResult),
134 toArray
135 );
136
137 expect(onResult).toBeCalledTimes(1);
138 expect(onResult).toBeCalledWith(makeResult(queryOperation, { data: null }));
139 expect(onExchangeResult).toBeCalledTimes(1);
140 expect(onExchangeResult).toBeCalledWith(mockResult);
141 });
142
143 it('triggers and forwards identity when returning undefined', async () => {
144 const onResult = vi.fn().mockReturnValue(undefined);
145 const onExchangeResult = vi.fn();
146
147 const exchangeArgs = {
148 forward: op$ =>
149 pipe(
150 op$,
151 map((operation: Operation) => makeResult(operation, { data: null }))
152 ),
153 client: {} as Client,
154 dispatchDebug: () => null,
155 };
156
157 pipe(
158 fromValue(queryOperation),
159 mapExchange({ onResult })(exchangeArgs),
160 tap(onExchangeResult),
161 toArray
162 );
163
164 const result = makeResult(queryOperation, { data: null });
165 expect(onResult).toBeCalledTimes(1);
166 expect(onResult).toBeCalledWith(result);
167 expect(onExchangeResult).toBeCalledTimes(1);
168 expect(onExchangeResult).toBeCalledWith(result);
169 });
170
171 it('awaits returned promises as needed', async () => {
172 const mockOperation = makeOperation('query', queryOperation, {
173 ...queryOperation.context,
174 mock: true,
175 });
176
177 const mockResult = makeErrorResult(mockOperation, new Error('Mock'));
178 const onResult = vi.fn().mockResolvedValue(mockResult);
179 const onExchangeResult = vi.fn();
180
181 const exchangeArgs = {
182 forward: op$ =>
183 pipe(
184 op$,
185 map((operation: Operation) => makeResult(operation, { data: null }))
186 ),
187 client: {} as Client,
188 dispatchDebug: () => null,
189 };
190
191 await pipe(
192 fromValue(queryOperation),
193 mapExchange({ onResult })(exchangeArgs),
194 tap(onExchangeResult),
195 toPromise
196 );
197
198 expect(onResult).toBeCalledTimes(1);
199 expect(onResult).toBeCalledWith(makeResult(queryOperation, { data: null }));
200 expect(onExchangeResult).toBeCalledTimes(1);
201 expect(onExchangeResult).toBeCalledWith(mockResult);
202 });
203});
204
205describe('onError', () => {
206 it('does not trigger when there are no errors', async () => {
207 const onError = vi.fn();
208
209 const exchangeArgs = {
210 forward: op$ =>
211 pipe(
212 op$,
213 map((operation: Operation) => ({ ...queryResponse, operation }))
214 ),
215 client: {} as Client,
216 dispatchDebug: () => null,
217 };
218
219 pipe(
220 fromValue(queryOperation),
221 mapExchange({ onError })(exchangeArgs),
222 toArray
223 );
224
225 expect(onError).toBeCalledTimes(0);
226 });
227
228 it('triggers correctly when the operations has an error', async () => {
229 const onError = vi.fn();
230 const error = new Error('Sad times');
231
232 const exchangeArgs = {
233 forward: op$ =>
234 pipe(
235 op$,
236 map((operation: Operation) => makeErrorResult(operation, error))
237 ),
238 client: {} as Client,
239 dispatchDebug: () => null,
240 };
241
242 pipe(
243 fromValue(queryOperation),
244 mapExchange({ onError })(exchangeArgs),
245 toArray
246 );
247
248 expect(onError).toBeCalledTimes(1);
249 expect(onError).toBeCalledWith(
250 new CombinedError({ networkError: error }),
251 queryOperation
252 );
253 });
254});