@luvio/adapter-test-library 0.142.2 → 0.142.4
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/{es/es2018/test-library.js → test-library.js} +10 -4
- package/dist/{umd/es5/types → types}/adapterRoundtrip.d.ts +1 -0
- package/package.json +13 -6
- package/babel.config.js +0 -1
- package/dist/es/es2018/types/adapterRoundtrip.d.ts +0 -35
- package/dist/umd/es2018/test-library.js +0 -600
- package/dist/umd/es2018/types/MockDurableStore.d.ts +0 -16
- package/dist/umd/es2018/types/adapterRoundtrip.d.ts +0 -35
- package/dist/umd/es2018/types/customMatchers.d.ts +0 -10
- package/dist/umd/es2018/types/durableStorePersistence.d.ts +0 -13
- package/dist/umd/es2018/types/jest.setup.d.ts +0 -1
- package/dist/umd/es2018/types/main.d.ts +0 -9
- package/dist/umd/es2018/types/mocks.d.ts +0 -17
- package/dist/umd/es2018/types/network.d.ts +0 -38
- package/dist/umd/es2018/types/utils.d.ts +0 -39
- package/dist/umd/es2018/types/verification.d.ts +0 -2
- package/dist/umd/es5/test-library.js +0 -818
- package/dist/umd/es5/types/MockDurableStore.d.ts +0 -16
- package/dist/umd/es5/types/customMatchers.d.ts +0 -10
- package/dist/umd/es5/types/durableStorePersistence.d.ts +0 -13
- package/dist/umd/es5/types/jest.setup.d.ts +0 -1
- package/dist/umd/es5/types/main.d.ts +0 -9
- package/dist/umd/es5/types/mocks.d.ts +0 -17
- package/dist/umd/es5/types/network.d.ts +0 -38
- package/dist/umd/es5/types/utils.d.ts +0 -39
- package/dist/umd/es5/types/verification.d.ts +0 -2
- package/jest.config.js +0 -16
- package/matcher-types.d.ts +0 -10
- package/project.json +0 -8
- package/rollup.config.js +0 -33
- package/src/MockDurableStore.ts +0 -145
- package/src/__tests__/MockDurableStore.spec.ts +0 -53
- package/src/adapterRoundtrip.ts +0 -101
- package/src/customMatchers.ts +0 -17
- package/src/durableStorePersistence.ts +0 -34
- package/src/jest.setup.ts +0 -4
- package/src/main.ts +0 -26
- package/src/mocks.ts +0 -31
- package/src/network.ts +0 -240
- package/src/utils.ts +0 -207
- package/src/verification.ts +0 -60
- package/tsconfig.json +0 -4
- /package/dist/{es/es2018/types → types}/MockDurableStore.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/customMatchers.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/durableStorePersistence.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/jest.setup.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/main.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/mocks.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/network.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/utils.d.ts +0 -0
- /package/dist/{es/es2018/types → types}/verification.d.ts +0 -0
package/src/network.ts
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import type { NetworkAdapter, ResourceRequest, FetchResponse, Headers } from '@luvio/engine';
|
|
2
|
-
import sinon from 'sinon';
|
|
3
|
-
|
|
4
|
-
import { clone, flushPromises } from './utils';
|
|
5
|
-
|
|
6
|
-
const networkConnectivityStateMap = new WeakMap<sinon.SinonStub, ConnectivityState>();
|
|
7
|
-
|
|
8
|
-
export enum ConnectivityState {
|
|
9
|
-
Online,
|
|
10
|
-
Offline,
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface MockPayload {
|
|
14
|
-
// the args for the endpoint that will result in this payload
|
|
15
|
-
networkArgs: Partial<ResourceRequest>;
|
|
16
|
-
|
|
17
|
-
// mock response to be returned from calling the network adapter
|
|
18
|
-
response: FetchResponse<any>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const callCountMap = new WeakMap<sinon.SinonStub, number>();
|
|
22
|
-
const mockPayloadsMap = new WeakMap<sinon.SinonStub, MockPayload[]>();
|
|
23
|
-
|
|
24
|
-
function buildOfflineError() {
|
|
25
|
-
return new Error('Failed to fetch, request timeout');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function buildMockSetupError() {
|
|
29
|
-
return new Error('A mock network response was not setup for this request');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* This function sorts two payloads based on number of parameters. It sorts in
|
|
34
|
-
* ascending order, meaning a MockPayload with fewer parameters in the networkArgs
|
|
35
|
-
* will come before a MockPayload with more parameters in the networkArgs.
|
|
36
|
-
*
|
|
37
|
-
* Sorting really only matters when a two networkArg objects have all the same
|
|
38
|
-
* fields except for uri/query/headers, so this sorting algorithm is simplistic.
|
|
39
|
-
* If two networkArg objects have different baseUri or basePath properties, for
|
|
40
|
-
* example, then the sinon matcher won't have a collision so sorting doesn't
|
|
41
|
-
* matter in that situation.
|
|
42
|
-
*
|
|
43
|
-
* @param {MockPayload} a
|
|
44
|
-
* @param {MockPayload} b
|
|
45
|
-
* @returns {number}
|
|
46
|
-
*/
|
|
47
|
-
function sortPayloads(a: MockPayload, b: MockPayload) {
|
|
48
|
-
const {
|
|
49
|
-
networkArgs: { urlParams: bUrlParams, queryParams: bQueryParams, headers: bHeaders },
|
|
50
|
-
} = b;
|
|
51
|
-
const {
|
|
52
|
-
networkArgs: { urlParams: aUrlParams, queryParams: aQueryParams, headers: aHeaders },
|
|
53
|
-
} = a;
|
|
54
|
-
|
|
55
|
-
const aParams =
|
|
56
|
-
Object.keys(aUrlParams ?? {}).length +
|
|
57
|
-
Object.keys(aQueryParams ?? {}).length +
|
|
58
|
-
Object.keys(aHeaders ?? {}).length;
|
|
59
|
-
const bParams =
|
|
60
|
-
Object.keys(bUrlParams ?? {}).length +
|
|
61
|
-
Object.keys(bQueryParams ?? {}).length +
|
|
62
|
-
Object.keys(bHeaders ?? {}).length;
|
|
63
|
-
|
|
64
|
-
return aParams - bParams;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function isOkResponse(status: number) {
|
|
68
|
-
return status >= 200 && status <= 299;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Flushes any pending network requests. Useful for tests that need to ensure all
|
|
73
|
-
* un-awaited background refreshes are complete
|
|
74
|
-
*
|
|
75
|
-
* @param _mockNetworkAdapter {NetworkAdapter} The network adapter instance to flush
|
|
76
|
-
*/
|
|
77
|
-
export async function flushPendingNetworkRequests(
|
|
78
|
-
_mockNetworkAdapter: NetworkAdapter
|
|
79
|
-
): Promise<void> {
|
|
80
|
-
// since the network mock is actually synchronous (just returns things wrapped
|
|
81
|
-
// in Promise.resolve/reject) the only thing necessary to flush any pending
|
|
82
|
-
// network activity is to flush the pending microtask queue
|
|
83
|
-
await flushPromises();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function buildMockNetworkAdapter(mockPayloads: MockPayload[]) {
|
|
87
|
-
// any endpoints not setup with a fake will return a rejected promise
|
|
88
|
-
const networkAdapter = sinon.stub().rejects(buildMockSetupError());
|
|
89
|
-
callCountMap.set(networkAdapter, 0);
|
|
90
|
-
mockPayloadsMap.set(networkAdapter, mockPayloads);
|
|
91
|
-
|
|
92
|
-
setMockNetworkPayloads(networkAdapter, mockPayloads);
|
|
93
|
-
|
|
94
|
-
return networkAdapter as NetworkAdapter;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function setNetworkConnectivity(
|
|
98
|
-
mockNetworkAdapter: NetworkAdapter,
|
|
99
|
-
connectivityState: ConnectivityState
|
|
100
|
-
) {
|
|
101
|
-
const stub = mockNetworkAdapter as sinon.SinonStub;
|
|
102
|
-
networkConnectivityStateMap.set(stub, connectivityState);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function onNetworkStubCalled(stub: sinon.SinonStub, response: FetchResponse<any>) {
|
|
106
|
-
const { body, status, statusText, ok, headers } = response;
|
|
107
|
-
|
|
108
|
-
const currentCallCount = callCountMap.get(stub);
|
|
109
|
-
callCountMap.set(stub, currentCallCount ? currentCallCount + 1 : 1);
|
|
110
|
-
|
|
111
|
-
const connectivityState = networkConnectivityStateMap.get(stub) ?? ConnectivityState.Online;
|
|
112
|
-
|
|
113
|
-
if (connectivityState === ConnectivityState.Offline) {
|
|
114
|
-
return Promise.reject(buildOfflineError());
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return Promise.resolve({
|
|
118
|
-
status,
|
|
119
|
-
statusText,
|
|
120
|
-
ok,
|
|
121
|
-
body: clone(body),
|
|
122
|
-
headers: clone(headers),
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function setMockNetworkPayloads(
|
|
127
|
-
mockNetworkAdapter: NetworkAdapter,
|
|
128
|
-
mockPayloads: MockPayload[]
|
|
129
|
-
) {
|
|
130
|
-
const stub = mockNetworkAdapter as sinon.SinonStub;
|
|
131
|
-
|
|
132
|
-
// sort mock payloads so least-specific network args are registered first
|
|
133
|
-
mockPayloads.sort(sortPayloads).forEach((mockPayload) => {
|
|
134
|
-
const { networkArgs, response } = mockPayload;
|
|
135
|
-
const args = sinon.match(networkArgs);
|
|
136
|
-
|
|
137
|
-
stub.withArgs(args).callsFake(() => {
|
|
138
|
-
return onNetworkStubCalled(stub, response);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Overrides the mock network adapter responses with the given "responses". Each request to
|
|
145
|
-
* the network adapter will "pop" a response from the response array. If more requests
|
|
146
|
-
* to the network adapter are made than responses in the given array then an error
|
|
147
|
-
* will be returned.
|
|
148
|
-
*
|
|
149
|
-
* This differs from setMockNetworkPayloads because it does not look at the incoming
|
|
150
|
-
* network request args to determine what the response should be, it just returns
|
|
151
|
-
* the response based on the request order.
|
|
152
|
-
*
|
|
153
|
-
* NOTE: this overrides any previously setup MockPayloads for the given network adapter
|
|
154
|
-
* until resetMockNetworkAdapter is called.
|
|
155
|
-
*/
|
|
156
|
-
export function overrideMockNetworkResponses(
|
|
157
|
-
mockNetworkAdapter: NetworkAdapter,
|
|
158
|
-
responses: FetchResponse<any>[]
|
|
159
|
-
) {
|
|
160
|
-
const stub = mockNetworkAdapter as sinon.SinonStub;
|
|
161
|
-
let index = 0;
|
|
162
|
-
|
|
163
|
-
stub.withArgs(sinon.match.any).callsFake(() => {
|
|
164
|
-
const response = responses[index++];
|
|
165
|
-
if (response === undefined) {
|
|
166
|
-
// if they have more requests than expected
|
|
167
|
-
return Promise.reject(buildMockSetupError());
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return onNetworkStubCalled(stub, response);
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function resetMockNetworkAdapter(
|
|
175
|
-
mockNetworkAdapter: NetworkAdapter,
|
|
176
|
-
newPayloads?: MockPayload[]
|
|
177
|
-
): void {
|
|
178
|
-
const stub = mockNetworkAdapter as sinon.SinonStub;
|
|
179
|
-
|
|
180
|
-
// reset stub in case a test modified payload responses
|
|
181
|
-
stub.reset();
|
|
182
|
-
|
|
183
|
-
// reset call count
|
|
184
|
-
callCountMap.set(stub, 0);
|
|
185
|
-
|
|
186
|
-
// set mock payloads up - Allow for the test to provide a different set of payloads
|
|
187
|
-
// Default behavior is to reset to the original payloads.
|
|
188
|
-
const payloads = newPayloads ? newPayloads : mockPayloadsMap.get(stub);
|
|
189
|
-
if (payloads) {
|
|
190
|
-
setMockNetworkPayloads(stub, payloads);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function getMockNetworkAdapterCallCount(
|
|
195
|
-
mockNetworkAdapter: NetworkAdapter
|
|
196
|
-
): number | undefined {
|
|
197
|
-
const stub = mockNetworkAdapter as sinon.SinonStub;
|
|
198
|
-
return callCountMap.get(stub);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function buildFetchResponse<T>(
|
|
202
|
-
body: T,
|
|
203
|
-
status = 200,
|
|
204
|
-
statusText = 'Ok',
|
|
205
|
-
headers: Headers = {}
|
|
206
|
-
): FetchResponse<T> {
|
|
207
|
-
return {
|
|
208
|
-
headers,
|
|
209
|
-
body,
|
|
210
|
-
status,
|
|
211
|
-
statusText,
|
|
212
|
-
ok: isOkResponse(status),
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export function buildSuccessMockPayload(
|
|
217
|
-
networkArgs: Partial<ResourceRequest>,
|
|
218
|
-
body: any,
|
|
219
|
-
headers: Headers = {}
|
|
220
|
-
): MockPayload {
|
|
221
|
-
const mockPayload: MockPayload = {
|
|
222
|
-
networkArgs,
|
|
223
|
-
response: buildFetchResponse(body, 200, 'Ok', headers),
|
|
224
|
-
};
|
|
225
|
-
return mockPayload;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function buildErrorMockPayload(
|
|
229
|
-
networkArgs: Partial<ResourceRequest>,
|
|
230
|
-
body: any,
|
|
231
|
-
errorStatusCode: number,
|
|
232
|
-
errorStatusText: string,
|
|
233
|
-
headers: Headers = {}
|
|
234
|
-
): MockPayload {
|
|
235
|
-
const mockPayload: MockPayload = {
|
|
236
|
-
networkArgs,
|
|
237
|
-
response: buildFetchResponse(body, errorStatusCode, errorStatusText, headers),
|
|
238
|
-
};
|
|
239
|
-
return mockPayload;
|
|
240
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ErrorSnapshot,
|
|
3
|
-
FetchResponse,
|
|
4
|
-
FulfilledSnapshot,
|
|
5
|
-
NetworkAdapter,
|
|
6
|
-
Snapshot,
|
|
7
|
-
StaleSnapshot,
|
|
8
|
-
} from '@luvio/engine';
|
|
9
|
-
import { getMockNetworkAdapterCallCount } from './network';
|
|
10
|
-
import { isImmutable } from './verification';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Clone an object
|
|
14
|
-
*
|
|
15
|
-
* @param {object} data The object to clone
|
|
16
|
-
* @returns {object} The cloned object
|
|
17
|
-
*/
|
|
18
|
-
export function clone<T>(data: T): T {
|
|
19
|
-
if (data === undefined) {
|
|
20
|
-
return undefined as T;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return JSON.parse(JSON.stringify(data));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Strips the given properties from an object. Useful for comparing data payloads
|
|
28
|
-
* where the given props may not be present in the data returned from an adapter.
|
|
29
|
-
*
|
|
30
|
-
* Does not mutate the passed in object.
|
|
31
|
-
*
|
|
32
|
-
* @param {object} obj The object from which to strip properties
|
|
33
|
-
* @param {string[]} props The properties to be removed
|
|
34
|
-
* @returns {object} The updated object with the given props stripped out
|
|
35
|
-
*/
|
|
36
|
-
export function stripProperties(obj: { [s: string]: any }, props: string[]) {
|
|
37
|
-
// don't mutate the original object
|
|
38
|
-
const cloned = clone(obj);
|
|
39
|
-
stripPropertiesMutating(cloned, props);
|
|
40
|
-
return cloned;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// private version of strip properties that mutates
|
|
44
|
-
function stripPropertiesMutating(obj: { [s: string]: any }, props: string[]) {
|
|
45
|
-
props.forEach((prop) => {
|
|
46
|
-
delete obj[prop];
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
Object.keys(obj).forEach((key) => {
|
|
50
|
-
const value = obj[key];
|
|
51
|
-
if (typeof value === 'object' && value !== null) {
|
|
52
|
-
stripPropertiesMutating(value, props);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function doesThrow(predicate: () => void) {
|
|
58
|
-
try {
|
|
59
|
-
predicate();
|
|
60
|
-
} catch (e) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function flushPromises() {
|
|
67
|
-
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
type MatcherResult = { pass: boolean; message: () => string };
|
|
71
|
-
// Copied from engine to avoid build time dependency
|
|
72
|
-
// BEGIN OF COPY BLOCK
|
|
73
|
-
enum SnapshotState {
|
|
74
|
-
Fulfilled = 'Fulfilled',
|
|
75
|
-
Unfulfilled = 'Unfulfilled',
|
|
76
|
-
Error = 'Error',
|
|
77
|
-
Pending = 'Pending',
|
|
78
|
-
Stale = 'Stale',
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function isErrorSnapshot(snapshot: Snapshot<unknown, unknown>): snapshot is ErrorSnapshot {
|
|
82
|
-
return snapshot.state === SnapshotState.Error;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function isFulfilledSnapshot(
|
|
86
|
-
snapshot: Snapshot<unknown, unknown>
|
|
87
|
-
): snapshot is FulfilledSnapshot<unknown, unknown> {
|
|
88
|
-
return snapshot.state === SnapshotState.Fulfilled;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function isStaleSnapshot(
|
|
92
|
-
snapshot: Snapshot<unknown, unknown>
|
|
93
|
-
): snapshot is StaleSnapshot<unknown, unknown> {
|
|
94
|
-
return snapshot.state === SnapshotState.Stale;
|
|
95
|
-
}
|
|
96
|
-
// END OF COPY BLOCK
|
|
97
|
-
|
|
98
|
-
export const customMatchers = {
|
|
99
|
-
toEqualFulfilledSnapshotWithData: (
|
|
100
|
-
snapshot: Snapshot<unknown, unknown>,
|
|
101
|
-
expected: any,
|
|
102
|
-
privateProperties?: string[]
|
|
103
|
-
): MatcherResult => {
|
|
104
|
-
if (isFulfilledSnapshot(snapshot)) {
|
|
105
|
-
const expectedWithoutPrivateProperties = stripProperties(
|
|
106
|
-
expected,
|
|
107
|
-
privateProperties || []
|
|
108
|
-
);
|
|
109
|
-
expect(snapshot.data).toEqual(expectedWithoutPrivateProperties);
|
|
110
|
-
expect(isImmutable(snapshot.data)).toBe(true);
|
|
111
|
-
return { pass: true, message: () => 'Snapshot is a FulfilledSnapshot' };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
pass: false,
|
|
116
|
-
message: () => 'Actual Snapshot is not a FulfilledSnapshot.',
|
|
117
|
-
};
|
|
118
|
-
},
|
|
119
|
-
toEqualStaleSnapshotWithData: (
|
|
120
|
-
snapshot: Snapshot<unknown, unknown>,
|
|
121
|
-
expected: any,
|
|
122
|
-
privateProperties?: string[]
|
|
123
|
-
): MatcherResult => {
|
|
124
|
-
if (isStaleSnapshot(snapshot)) {
|
|
125
|
-
const expectedWithoutPrivateProperties = stripProperties(
|
|
126
|
-
expected,
|
|
127
|
-
privateProperties || []
|
|
128
|
-
);
|
|
129
|
-
expect(snapshot.data).toEqual(expectedWithoutPrivateProperties);
|
|
130
|
-
expect(isImmutable(snapshot.data)).toBe(true);
|
|
131
|
-
return { pass: true, message: () => 'Snapshot is a StaleSnapshot' };
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
pass: false,
|
|
136
|
-
message: () => 'Actual Snapshot is not a StaleSnapshot.',
|
|
137
|
-
};
|
|
138
|
-
},
|
|
139
|
-
toEqualErrorSnapshot: (
|
|
140
|
-
actual: Snapshot<unknown, unknown>,
|
|
141
|
-
expectedStatus?: number
|
|
142
|
-
): MatcherResult => {
|
|
143
|
-
if (isErrorSnapshot(actual)) {
|
|
144
|
-
expect(isImmutable(actual.error)).toBe(true);
|
|
145
|
-
expect(actual.data).toBeUndefined();
|
|
146
|
-
if (expectedStatus !== undefined) {
|
|
147
|
-
expect((actual.error as FetchResponse<unknown>).status).toBe(expectedStatus);
|
|
148
|
-
}
|
|
149
|
-
return { pass: true, message: () => 'Snapshot is an ErrorSnapshot' };
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
pass: false,
|
|
154
|
-
message: () => 'Actual Snapshot is not a ErrorSnapshot.',
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
toEqualOfflineErrorSnapshot: (actual: Snapshot<unknown, unknown>): MatcherResult => {
|
|
158
|
-
if (isErrorSnapshot(actual)) {
|
|
159
|
-
expect(isImmutable(actual.error)).toBe(true);
|
|
160
|
-
expect(actual.data).toBeUndefined();
|
|
161
|
-
expect(actual.error).toBeInstanceOf(Error);
|
|
162
|
-
expect(actual.error.errorType).toBe('networkAdapterError');
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
pass: true,
|
|
166
|
-
message: () => 'Snapshot is an ErrorSnapshot',
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
pass: false,
|
|
172
|
-
message: () => 'Actual Snapshot is not a ErrorSnapshot.',
|
|
173
|
-
};
|
|
174
|
-
},
|
|
175
|
-
toHaveBeenHitTimes: (mockNetworkAdapter: NetworkAdapter, expected: Number): MatcherResult => {
|
|
176
|
-
const callCount = getMockNetworkAdapterCallCount(mockNetworkAdapter);
|
|
177
|
-
if (callCount !== expected) {
|
|
178
|
-
return {
|
|
179
|
-
pass: false,
|
|
180
|
-
message: () =>
|
|
181
|
-
`Number of network calls made are different. Actual ${callCount}, Expected ${expected}.`,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return { pass: true, message: () => `Network calls were made ${expected} times.` };
|
|
186
|
-
},
|
|
187
|
-
toHaveBeenHitOnce: (mockNetworkAdapter: NetworkAdapter): MatcherResult => {
|
|
188
|
-
const callCount = getMockNetworkAdapterCallCount(mockNetworkAdapter);
|
|
189
|
-
if (callCount !== 1) {
|
|
190
|
-
return {
|
|
191
|
-
pass: false,
|
|
192
|
-
message: () => `Number of network calls was not 1. Actual ${callCount}.`,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return { pass: true, message: () => 'Number of network calls was 1' };
|
|
197
|
-
},
|
|
198
|
-
toEqualFetchResponse: (
|
|
199
|
-
actual: FetchResponse<unknown>,
|
|
200
|
-
expected: FetchResponse<unknown>
|
|
201
|
-
): MatcherResult => {
|
|
202
|
-
expect(actual).toEqual({ ...expected, errorType: 'fetchResponse' });
|
|
203
|
-
expect(isImmutable(actual)).toBe(true);
|
|
204
|
-
|
|
205
|
-
return { pass: true, message: () => 'Actual response equals expected response' };
|
|
206
|
-
},
|
|
207
|
-
};
|
package/src/verification.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { doesThrow } from './utils';
|
|
2
|
-
|
|
3
|
-
export function isImmutable(value: any): boolean {
|
|
4
|
-
return !doesThrow(() => {
|
|
5
|
-
// if verifyImmutable does not throw then the object is immutable
|
|
6
|
-
verifyImmutable(value, '$');
|
|
7
|
-
});
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function verifyImmutable(value: any, path: string): void {
|
|
11
|
-
if (typeof value !== 'object' || value === null) {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
if (Array.isArray(value)) {
|
|
15
|
-
if (
|
|
16
|
-
doesThrow(() => {
|
|
17
|
-
const len = value.length;
|
|
18
|
-
value.push('__test');
|
|
19
|
-
if (len === value.length) {
|
|
20
|
-
throw new Error('IE11 does not throw when mutating a frozen object');
|
|
21
|
-
}
|
|
22
|
-
}) === false
|
|
23
|
-
) {
|
|
24
|
-
throw new Error(`Unexpected mutable property found at ${path}: Array is extensible!`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
value.forEach((item, index) => {
|
|
28
|
-
verifyImmutable(item, `${path}.${index}`);
|
|
29
|
-
});
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
doesThrow(() => {
|
|
35
|
-
value['__test_____'] = true;
|
|
36
|
-
if (value['__test_____'] !== true) {
|
|
37
|
-
throw new Error('IE11 does not throw when mutating a frozen object');
|
|
38
|
-
}
|
|
39
|
-
}) === false
|
|
40
|
-
) {
|
|
41
|
-
throw new Error(`Unexpected mutable property found at ${path}: Object is extensible!`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
Object.keys(value).forEach((key) => {
|
|
45
|
-
if (
|
|
46
|
-
doesThrow(() => {
|
|
47
|
-
const old = value[key];
|
|
48
|
-
value[key] = '_______foo';
|
|
49
|
-
if (value[key] === old) {
|
|
50
|
-
throw new Error('IE11 does not throw when mutating a frozen object');
|
|
51
|
-
}
|
|
52
|
-
}) === false
|
|
53
|
-
) {
|
|
54
|
-
throw new Error(
|
|
55
|
-
`Unexpected mutable property found at ${path}: "${path}.${key}" is mutable!`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
verifyImmutable(value[key], `${path}.${key}`);
|
|
59
|
-
});
|
|
60
|
-
}
|
package/tsconfig.json
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|