@baselift/blocks-testing 0.0.1

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.
Files changed (41) hide show
  1. package/README.md +177 -0
  2. package/dist/cjs/error_utils.js +56 -0
  3. package/dist/cjs/index.js +24 -0
  4. package/dist/cjs/inject_mock_airtable_interface.js +6 -0
  5. package/dist/cjs/mock_airtable_interface.js +851 -0
  6. package/dist/cjs/private_utils.js +78 -0
  7. package/dist/cjs/test_driver.js +658 -0
  8. package/dist/cjs/test_mutations.js +37 -0
  9. package/dist/cjs/vacant_airtable_interface.js +367 -0
  10. package/dist/types/src/error_utils.d.ts +11 -0
  11. package/dist/types/src/error_utils.d.ts.map +1 -0
  12. package/dist/types/src/index.d.ts +4 -0
  13. package/dist/types/src/index.d.ts.map +1 -0
  14. package/dist/types/src/inject_mock_airtable_interface.d.ts +2 -0
  15. package/dist/types/src/inject_mock_airtable_interface.d.ts.map +1 -0
  16. package/dist/types/src/mock_airtable_interface.d.ts +237 -0
  17. package/dist/types/src/mock_airtable_interface.d.ts.map +1 -0
  18. package/dist/types/src/private_utils.d.ts +33 -0
  19. package/dist/types/src/private_utils.d.ts.map +1 -0
  20. package/dist/types/src/test_driver.d.ts +450 -0
  21. package/dist/types/src/test_driver.d.ts.map +1 -0
  22. package/dist/types/src/test_mutations.d.ts +43 -0
  23. package/dist/types/src/test_mutations.d.ts.map +1 -0
  24. package/dist/types/src/vacant_airtable_interface.d.ts +2 -0
  25. package/dist/types/src/vacant_airtable_interface.d.ts.map +1 -0
  26. package/dist/types/test/index_compatible.test.d.ts +8 -0
  27. package/dist/types/test/index_compatible.test.d.ts.map +1 -0
  28. package/dist/types/test/index_incompatible.test.d.ts +2 -0
  29. package/dist/types/test/index_incompatible.test.d.ts.map +1 -0
  30. package/dist/types/test/mock_airtable_interface.test.d.ts +2 -0
  31. package/dist/types/test/mock_airtable_interface.test.d.ts.map +1 -0
  32. package/dist/types/test/mutation_types.test.d.ts +2 -0
  33. package/dist/types/test/mutation_types.test.d.ts.map +1 -0
  34. package/dist/types/test/package_json.test.d.ts +2 -0
  35. package/dist/types/test/package_json.test.d.ts.map +1 -0
  36. package/dist/types/test/test_driver.test.d.ts +2 -0
  37. package/dist/types/test/test_driver.test.d.ts.map +1 -0
  38. package/dist/types/test/untestable_bindings.test.d.ts +2 -0
  39. package/dist/types/test/untestable_bindings.test.d.ts.map +1 -0
  40. package/package.json +120 -0
  41. package/types/globals.d.ts +6 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock_airtable_interface.d.ts","sourceRoot":"","sources":["../../../src/mock_airtable_interface.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Allows creating an object map type with a dynamic key type.
3
+ *
4
+ * TypeScript only allows `string` for `K` in `{[key: K]: V}` so we need a utility to bridge
5
+ * the gap.
6
+ *
7
+ * This is an alias for TypeScript’s `Record` type, but the name “record” is confusing given our
8
+ * Airtable domain model.
9
+ *
10
+ * @hidden
11
+ */
12
+ export type ObjectMap<K extends keyof any, V> = {
13
+ [P in K]: V;
14
+ };
15
+ /**
16
+ * @hidden
17
+ */
18
+ export declare function cloneDeep<T extends unknown>(obj: T): T;
19
+ /**
20
+ * @hidden
21
+ */
22
+ export declare function has<T extends object>(obj: T, key: keyof any): key is keyof T;
23
+ /**
24
+ * @hidden
25
+ */
26
+ export declare function keyBy<Item, Key extends string>(array: ReadonlyArray<Item>, getKey: (arg1: Item) => Key): ObjectMap<Key, Item>;
27
+ /**
28
+ * @hidden
29
+ */
30
+ export declare function getId({ id }: {
31
+ id: string;
32
+ }): string;
33
+ //# sourceMappingURL=private_utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"private_utils.d.ts","sourceRoot":"","sources":["../../../src/private_utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,EAAE,CAAC,IAAI;KAAE,CAAC,IAAI,CAAC,GAAG,CAAC;CAAC,CAAC;AAE9D;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAMtD;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,MAAM,CAAC,CAE5E;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,GAAG,SAAS,MAAM,EAC1C,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,EAC1B,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG,GAC5B,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAMtB;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAC,EAAE,EAAC,EAAE;IAAC,EAAE,EAAE,MAAM,CAAA;CAAC,UAEvC"}
@@ -0,0 +1,450 @@
1
+ import React from 'react';
2
+ import { FieldId, RecordId, TableId, ViewId } from '@baselift/blocks/types';
3
+ import { Mutation } from '@baselift/blocks/unstable_testing_utils';
4
+ import { FixtureData, PickRecord, WatchableKeysAndArgs } from './mock_airtable_interface';
5
+ /**
6
+ * An object describing a {@link Table}, a {@link View}, or both.
7
+ */
8
+ type TableAndOrView = {
9
+ table: TableId | string;
10
+ view?: ViewId | string;
11
+ } | {
12
+ table?: TableId | string;
13
+ view: ViewId | string;
14
+ };
15
+ /**
16
+ * A class designed to facilitate the automated testing of Airtable Extensions
17
+ * outside of a production Extensions environment. Each instance creates a simulated
18
+ * {@link Base} which is distinct from any other Base created in this way.
19
+ * Custom Extensions can be instantiated using an instance of this class; see {@link
20
+ * Container|the `Container` method}.
21
+ *
22
+ * The example code for this class's methods is written in terms of a
23
+ * non-existent Airtable Extension called `MyCustomExtension`. Each example includes a
24
+ * description of the presumed behavior for that Extension. Consumers of this library
25
+ * will work with their own Extensions whose behavior differs from these examples, so
26
+ * their tests will be distinct in this regard.
27
+ *
28
+ * @docsPath testing/TestDriver
29
+ */
30
+ export default class TestDriver {
31
+ /**
32
+ * Create an instance of the test driver, initializing a simulated Airtable
33
+ * Base to take the state described by the provided fixture data.
34
+ */
35
+ constructor(fixtureData: FixtureData);
36
+ /**
37
+ * The simulated {@link Base} associated with this instance.
38
+ */
39
+ get base(): import("@baselift/blocks/models").Base;
40
+ /**
41
+ * The {@link Cursor} instance associated with this instance's Base.
42
+ */
43
+ get cursor(): import("@baselift/blocks/models").Cursor;
44
+ /**
45
+ * A {@link Session} instance. This will correspond to the first
46
+ * collaborator in your fixture data.
47
+ */
48
+ get session(): import("@baselift/blocks/models").Session;
49
+ /**
50
+ * A simulated {@link GlobalConfig} instance. This always starts empty.
51
+ */
52
+ get globalConfig(): import("@baselift/blocks/types").GlobalConfig;
53
+ /**
54
+ * A React Component which may be used to wrap Extension Components, enabling
55
+ * them to run outside of a production Extensions environment.
56
+ *
57
+ * @example
58
+ * ```js
59
+ * import TestDriver from '@baselift/blocks-testing';
60
+ * // Given MyCustomExtension, an Airtable Extension which defines a React Component...
61
+ * const MyCustomExtension = require('../src/my_custom_extension');
62
+ * // And given myFixtureData, a data structure describing the initial
63
+ * // state of a simulated Airtable Base...
64
+ * const myFixtureData = require('./my_fixture_data');
65
+ *
66
+ * const testDriver = new TestDriver(myFixtureData);
67
+ *
68
+ * render(
69
+ * <testDriver.Container>
70
+ * <MyCustomExtension />
71
+ * </testDriver.Container>
72
+ * );
73
+ * ```
74
+ */
75
+ Container({ children }: {
76
+ children: React.ReactNode;
77
+ }): React.JSX.Element;
78
+ /**
79
+ * Destroy a {@link Field} in the simulated {@link Base}.
80
+ *
81
+ * @example
82
+ * ```js
83
+ * import TestDriver from '@baselift/blocks-testing';
84
+ * // Given MyCustomExtension, an Airtable Extension which displays the names of all
85
+ * // the Fields in the active Table...
86
+ * import MyCustomExtension from '../src/my_custom_extension';
87
+ * // And given myFixtureData, a data structure describing an Airtable
88
+ * // Base which contains a Table named "Table One" with three Fields...
89
+ * import myFixtureData from './my_fixture_data';
90
+ * import {render, screen} from '@testing-library/react';
91
+ *
92
+ * const testDriver = new TestDriver(myFixtureData);
93
+ * let items, itemTexts;
94
+ *
95
+ * render(
96
+ * <testDriver.Container>
97
+ * <MyCustomExtension />
98
+ * </testDriver.Container>
99
+ * );
100
+ *
101
+ * // Verify that MyExtension initially displays all three Fields
102
+ * items = screen.getAllByRole('listitem');
103
+ * itemTexts = items.map((el) => el.textContent);
104
+ * expect(itemTexts).toEqual(['1st field', '2nd field', '3rd field']);
105
+ *
106
+ * // Simulate the destruction of the Field named "2nd field"
107
+ * await testDriver.deleteFieldAsync('Table One', '2nd field');
108
+ *
109
+ * // Verify that MyExtension correctly updates to describe the two remaining
110
+ * // Fields
111
+ * items = screen.getAllByRole('listitem');
112
+ * itemTexts = items.map((el) => el.textContent);
113
+ * expect(itemTexts).toEqual(['1st field', '3rd field']);
114
+ * ```
115
+ */
116
+ deleteFieldAsync(tableIdOrName: TableId | string, fieldIdOrName: FieldId | string): Promise<void>;
117
+ /**
118
+ * Destroy a {@link Table} in the simulated {@link Base}.
119
+ *
120
+ * @example
121
+ * ```js
122
+ * import TestDriver from '@baselift/blocks-testing';
123
+ * // Given MyCustomExtension, an Airtable Extension which displays the names of all
124
+ * // the Tables in the Base...
125
+ * import MyCustomExtension from '../src/my_custom_extension';
126
+ * // And given myFixtureData, a data structure describing an Airtable
127
+ * // Base which contains three Tables...
128
+ * import myFixtureData from './my_fixture_data';
129
+ * import {render, screen} from '@testing-library/react';
130
+ *
131
+ * const testDriver = new TestDriver(myFixtureData);
132
+ * let items, itemTexts;
133
+ *
134
+ * render(
135
+ * <testDriver.Container>
136
+ * <MyCustomExtension />
137
+ * </testDriver.Container>
138
+ * );
139
+ *
140
+ * // Verify that MyExtension initially displays all three Tables
141
+ * items = screen.getAllByRole('listitem');
142
+ * itemTexts = items.map((el) => el.textContent);
143
+ * expect(itemTexts).toEqual(['1st table', '2nd table', '3rd table']);
144
+ *
145
+ * // Simulate the destruction of the Table named "2nd table"
146
+ * testDriver.deleteTable('2nd table');
147
+ *
148
+ * // Verify that MyExtension correctly updates to describe the two remaining
149
+ * // Table
150
+ * items = screen.getAllByRole('listitem');
151
+ * itemTexts = items.map((el) => el.textContent);
152
+ * expect(itemTexts).toEqual(['1st table', '3rd table']);
153
+ * ```
154
+ */
155
+ deleteTable(tableIdOrName: TableId | string): void;
156
+ /**
157
+ * Destroy a {@link View} in the simulated {@link Base}.
158
+ *
159
+ * @example
160
+ * ```js
161
+ * import TestDriver from '@baselift/blocks-testing';
162
+ * // Given MyCustomExtension, an Airtable Extension which displays the names of all
163
+ * // the Views in the active Table...
164
+ * import MyCustomExtension from '../src/my_custom_extension';
165
+ * // And given myFixtureData, a data structure describing an Airtable
166
+ * // Base which contains a Table named "Table One" with three Views...
167
+ * import myFixtureData from './my_fixture_data';
168
+ * import {render, screen} from '@testing-library/react';
169
+ *
170
+ * const testDriver = new TestDriver(myFixtureData);
171
+ * let items, itemTexts;
172
+ *
173
+ * render(
174
+ * <testDriver.Container>
175
+ * <MyCustomExtension />
176
+ * </testDriver.Container>
177
+ * );
178
+ *
179
+ * // Verify that MyExtension initially displays all three Views
180
+ * items = screen.getAllByRole('listitem');
181
+ * itemTexts = items.map((el) => el.textContent);
182
+ * expect(itemTexts).toEqual(['1st view', '2nd view', '3rd view']);
183
+ *
184
+ * // Simulate the destruction of the Field named "2nd view"
185
+ * await testDriver.deleteViewAsync('Table One', '2nd view');
186
+ *
187
+ * // Verify that MyExtension correctly updates to describe the two remaining
188
+ * // Views
189
+ * items = screen.getAllByRole('listitem');
190
+ * itemTexts = items.map((el) => el.textContent);
191
+ * expect(itemTexts).toEqual(['1st view', '3rd view']);
192
+ * ```
193
+ */
194
+ deleteViewAsync(tableIdOrName: TableId | string, viewIdOrName: ViewId | string): Promise<void>;
195
+ /**
196
+ * Update the active {@link Table} and/or the active {@link View} of the
197
+ * Extension's {@link Cursor}. Either `table` or `view` must be specified.
198
+ *
199
+ * @example
200
+ * ```js
201
+ * testDriver.setActiveCursorModels({view: 'My grid view'});
202
+ * ```
203
+ *
204
+ * @example
205
+ * ```js
206
+ * import TestDriver from '@baselift/blocks-testing';
207
+ * // Given MyCustomExtension, an Airtable Extension which displays the names of the
208
+ * // active Table...
209
+ * import MyCustomExtension from '../src/my_custom_extension';
210
+ * // And given myFixtureData, a data structure describing an Airtable
211
+ * // Base which contains two Tables...
212
+ * import myFixtureData from './my_fixture_data';
213
+ * import {render, screen} from '@testing-library/react';
214
+ *
215
+ * const testDriver = new TestDriver(myFixtureData);
216
+ * let heading;
217
+ *
218
+ * render(
219
+ * <testDriver.Container>
220
+ * <MyCustomExtension />
221
+ * </testDriver.Container>
222
+ * );
223
+ *
224
+ * // Verify that MyExtension initially displays the first Table
225
+ * heading = screen.getByRole('heading');
226
+ * expect(heading.textContent).toBe('First table');
227
+ *
228
+ * // Simulate the end user selecting the second Table from the Airtable
229
+ * // user interface
230
+ * testDriver.setActiveCursorModels(({table: 'Second table'});
231
+ *
232
+ * // Verify that MyExtension correctly updates to describe the newly-selected
233
+ * // Table
234
+ * heading = screen.getByRole('heading');
235
+ * expect(heading.textContent).toBe('Second table');
236
+ * ```
237
+ */
238
+ setActiveCursorModels(tableAndOrView: TableAndOrView): void;
239
+ /**
240
+ * Specify the outcome of internal permission checks. This influences the
241
+ * behavior of not only explicit permission checks from Extensions code but also
242
+ * the outcome of model operations such as {@link createRecordsAsync}.
243
+ *
244
+ * @example
245
+ * ```js
246
+ * import TestDriver from '@baselift/blocks-testing';
247
+ * // Given MyCustomExtension, an Airtable Extension which displays a button labeled
248
+ * // "Add" and which disables that button for users who lack "write"
249
+ * // permissions to the Base...
250
+ * import MyCustomExtension from '../src/my_custom_extension';
251
+ * // And given myFixtureData, a data structure describing the initial
252
+ * // state of a simulated Airtable Base...
253
+ * import myFixtureData from './my_fixture_data';
254
+ * import {render, screen} from '@testing-library/react';
255
+ *
256
+ * const testDriver = new TestDriver(myFixtureData);
257
+ *
258
+ * // Configure the test driver to reject all "create record" mutations.
259
+ * testDriver.simulatePermissionCheck((mutation) => {
260
+ * return mutation.type !== 'createMultipleRecords';
261
+ * });
262
+ *
263
+ * render(
264
+ * <testDriver.Container>
265
+ * <MyCustomExtension />
266
+ * </testDriver.Container>
267
+ * );
268
+ *
269
+ * // Verify that MyCustomExtension recognizes that the current user may not
270
+ * // create Records and that disables the corresponding aspect of the user
271
+ * // interface.
272
+ * const button = screen.getByRole('button', {name: 'Add'});
273
+ * expect(button.disabled).toBe(true);
274
+ * ```
275
+ */
276
+ simulatePermissionCheck(check: (mutation: Mutation) => boolean): void;
277
+ /**
278
+ * Specify the outcome of a request for the user to select a record in the
279
+ * UI created by {@link expandRecordPickerAsync}.
280
+ *
281
+ * @example
282
+ * ```js
283
+ * import TestDriver from '@baselift/blocks-testing';
284
+ * // Given MyCustomExtension, an Airtable Extension which prompts the end user to
285
+ * // select a Record and displays the name of the Record they selected...
286
+ * import MyCustomExtension from '../src/my_custom_extension';
287
+ * // And given myFixtureData, a data structure describing the initial
288
+ * // state of a simulated Airtable Base...
289
+ * import myFixtureData from './my_fixture_data';
290
+ * import {render, screen} from '@testing-library/react';
291
+ * import userEvent from '@testing-library/user-event';
292
+ *
293
+ * const testDriver = new TestDriver(myFixtureData);
294
+ *
295
+ * testDriver.simulateExpandedRecordSelection((tableId, recordIds) => {
296
+ * return recordIds[1];
297
+ * });
298
+ *
299
+ * render(
300
+ * <testDriver.Container>
301
+ * <MyCustomExtension />
302
+ * </testDriver.Container>
303
+ * );
304
+ *
305
+ * // Simulate a user clicking on a button in MyCustomExtension labeled with the
306
+ * // text "Choose record". If MyCustomExtension reacts to this event by invoking
307
+ * // the SDK's `expandRecordPickerAsync`, then it will receive the second
308
+ * // available record due to the function that is provided to
309
+ * // `simulateExpandedRecordSelection` above.
310
+ * const button = screen.getByRole('button', {name: 'Choose record'});
311
+ * userEvent.click(button);
312
+ *
313
+ * // Verify that MyCustomExtension correctly responds to the simulated user's
314
+ * // input
315
+ * const heading = await waitFor(() => screen.getByRole('heading'));
316
+ * expect(heading.textContent)
317
+ * .toBe('You selected the record named "Number Two"');
318
+ * ```
319
+ */
320
+ simulateExpandedRecordSelection(pickRecord: PickRecord): void;
321
+ /**
322
+ * Simulate a user visually selecting a set of {@link Record|Records} in
323
+ * the active {@link Table}. This operation is unrelated to an Extension's
324
+ * programmatic "selection" of records via, e.g. {@link
325
+ * Table.selectRecords}. To deselect all records, invoke this method with
326
+ * an empty array.
327
+ *
328
+ * @example
329
+ * ```js
330
+ * import TestDriver from '@baselift/blocks-testing';
331
+ * // Given MyCustomExtension, an Airtable Extension which displays the number of
332
+ * // Records that an end user has selected in the active Table...
333
+ * import MyCustomExtension from '../src/my_custom_extension';
334
+ * // And given myFixtureData, a data structure describing the initial
335
+ * // state of a simulated Airtable Base...
336
+ * import myFixtureData from './my_fixture_data';
337
+ * import {render, screen} from '@testing-library/react';
338
+ *
339
+ * const testDriver = new TestDriver(myFixtureData);
340
+ *
341
+ * render(
342
+ * <testDriver.Container>
343
+ * <MyCustomExtension />
344
+ * </testDriver.Container>
345
+ * );
346
+ *
347
+ * // Retrieve all the Records present in the first Table in the Base
348
+ * const records = await testDriver.base.tables[0].selectRecordsAsync();
349
+ *
350
+ * // Simulate an end-user selecting the second and fourth Record
351
+ * testDriver.userSelectRecords([records[1].id, records[3].id]);
352
+ *
353
+ * // Verify that MyCustomExtension correctly responds to the simulated user's
354
+ * // input
355
+ * const heading = await waitFor(() => screen.getByRole('heading'));
356
+ * expect(heading.textContent).toBe('2 records selected');
357
+ * ```
358
+ */
359
+ userSelectRecords(recordIds: Array<RecordId>): void;
360
+ /**
361
+ * Register a function to be invoked in response to a given internal event.
362
+ * See {@link WatchableKeysAndArgs} for the available keys and the values
363
+ * which are included when they are emitted.
364
+ *
365
+ * @example
366
+ * ```js
367
+ * import TestDriver from '@baselift/blocks-testing';
368
+ * // Given MyCustomExtension, an Airtable Extension which presents the user with one
369
+ * // button for each available Record, and which responds to button clicks
370
+ * // by expanding the Record in the Airtable user interface...
371
+ * import MyCustomExtension from '../src/my_custom_extension';
372
+ * // And given myFixtureData, a data structure describing an Airtable
373
+ * // Base which contains a Table with three records...
374
+ * import myFixtureData from './my_fixture_data';
375
+ * import {render, screen} from '@testing-library/react';
376
+ * import userEvent from '@testing-library/user-event';
377
+ *
378
+ * const testDriver = new TestDriver(myFixtureData);
379
+ *
380
+ * // Keep track of every time MyCustomExtension attempts to expand a Record in
381
+ * // the Airtable user interface
382
+ * let expandedRecordIds = [];
383
+ * testDriver.watch('expandRecord', ({recordId}) => {
384
+ * expendedRecordIds.push(recordId);
385
+ * });
386
+ *
387
+ * render(
388
+ * <testDriver.Container>
389
+ * <MyCustomExtension />
390
+ * </testDriver.Container>
391
+ * );
392
+ *
393
+ * // Verify that MyCustomExtension does not expand any Records prior to user
394
+ * // interaction
395
+ * expect(expandedRecords).toEqual([]);
396
+ *
397
+ * // Simulate a user clicking on the second button in MyCustomExtension, which
398
+ * // is expected to correspond to the second Record in the simulated Base
399
+ * const buttons = screen.getAllByRole('button');
400
+ * userEvent.click(buttons[1]);
401
+ *
402
+ * // Verify that MyCustomExtension correctly expanded the second Record in the
403
+ * // Airtable user interface
404
+ * expect(expandedRecords).toEqual(['rec2']);
405
+ * ```
406
+ */
407
+ watch<Key extends keyof WatchableKeysAndArgs>(key: Key, fn: (data: WatchableKeysAndArgs[Key]) => void): void;
408
+ /**
409
+ * De-register a function which was previously registered with {@link
410
+ * watch}. See {@link WatchableKeysAndArgs} for the available keys.
411
+ *
412
+ * @example
413
+ * ```js
414
+ * import TestDriver from '@baselift/blocks-testing';
415
+ * // Given MyCustomExtension, an Airtable Extension which enters "full screen" mode
416
+ * // in response to certain interactions...
417
+ * const MyCustomExtension = require('../src/my_custom_extension');
418
+ * // And given myFixtureData, a data structure describing the initial
419
+ * // state of a simulated Airtable Base...
420
+ * const myFixtureData = require('./my_fixture_data');
421
+ *
422
+ * let testDriver;
423
+ * let enterCount;
424
+ * let increment = () => {
425
+ * enterCount += 1;
426
+ * });
427
+ *
428
+ * // Configure the test runner to create a TestDriver instance before
429
+ * // every test and to listen for requests to enter "full screen" mode
430
+ * beforeEach(() => {
431
+ * testDriver = new TestDriver(myFixtureData);
432
+ * enterCount = 0;
433
+ * testDriver.watch('enterFullscreen', increment);
434
+ * });
435
+ *
436
+ * // Configure the test runner to remove the event listener after every
437
+ * // test. (The next test will have a new instance of TestDriver with its
438
+ * // own event handler, so this one is no longer necessary.)
439
+ * afterEach(() => {
440
+ * testDriver.unwatch('enterFullscreen', increment);
441
+ * });
442
+ *
443
+ * // (include tests using the `testDriver` and `enterCount` variables
444
+ * // here)
445
+ * ```
446
+ */
447
+ unwatch<Key extends keyof WatchableKeysAndArgs>(key: Key, fn: (data: WatchableKeysAndArgs[Key]) => void): void;
448
+ }
449
+ export {};
450
+ //# sourceMappingURL=test_driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test_driver.d.ts","sourceRoot":"","sources":["../../../src/test_driver.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAC,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAc,QAAQ,EAAM,MAAM,yCAAyC,CAAC;AAGnF,OAAO,EACH,WAAW,EAEX,UAAU,EACV,oBAAoB,EACvB,MAAM,2BAA2B,CAAC;AAGnC;;GAEG;AACH,KAAK,cAAc,GACb;IAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,GACjD;IAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,CAAC;AAExD;;;;;;;;;;;;;;GAcG;AAEH,MAAM,CAAC,OAAO,OAAO,UAAU;IAM3B;;;OAGG;gBACS,WAAW,EAAE,WAAW;IAMpC;;OAEG;IACH,IAAI,IAAI,2CAEP;IAED;;OAEG;IACH,IAAI,MAAM,6CAET;IAED;;;OAGG;IACH,IAAI,OAAO,8CAEV;IAED;;OAEG;IACH,IAAI,YAAY,kDAEf;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,SAAS,CAAC,EAAC,QAAQ,EAAC,EAAE;QAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAC;IASjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACG,gBAAgB,CAAC,aAAa,EAAE,OAAO,GAAG,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,MAAM;IAWvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,WAAW,CAAC,aAAa,EAAE,OAAO,GAAG,MAAM;IAsB3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACG,eAAe,CAAC,aAAa,EAAE,OAAO,GAAG,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAWpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,qBAAqB,CAAC,cAAc,EAAE,cAAc;IAsCpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;IACH,uBAAuB,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO;IAI9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,+BAA+B,CAAC,UAAU,EAAE,UAAU;IAItD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,iBAAiB,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC;IA4B5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8CG;IACH,KAAK,CAAC,GAAG,SAAS,MAAM,oBAAoB,EACxC,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC,GAAG,CAAC,KAAK,IAAI;IAKjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsCG;IACH,OAAO,CAAC,GAAG,SAAS,MAAM,oBAAoB,EAC1C,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC,GAAG,CAAC,KAAK,IAAI;CAIpD"}
@@ -0,0 +1,43 @@
1
+ import { Mutation } from '@baselift/blocks/unstable_testing_utils';
2
+ import { FieldId, TableId, ViewId } from '@baselift/blocks/types';
3
+ /**
4
+ * Get the type of all the values of an object.
5
+ *
6
+ * Same as the legacy Flow `$Values<T>` type.
7
+ *
8
+ * @hidden
9
+ */
10
+ export type ObjectValues<T extends object> = T[keyof T];
11
+ /** @hidden */
12
+ export declare const TestMutationTypes: Readonly<{
13
+ DELETE_SINGLE_FIELD: "deleteSingleField";
14
+ DELETE_SINGLE_VIEW: "deleteSingleView";
15
+ SET_MULTIPLE_RECORDS_CELL_VALUES: "setMultipleRecordsCellValues";
16
+ DELETE_MULTIPLE_RECORDS: "deleteMultipleRecords";
17
+ CREATE_MULTIPLE_RECORDS: "createMultipleRecords";
18
+ SET_MULTIPLE_GLOBAL_CONFIG_PATHS: "setMultipleGlobalConfigPaths";
19
+ CREATE_SINGLE_FIELD: "createSingleField";
20
+ UPDATE_SINGLE_FIELD_CONFIG: "updateSingleFieldConfig";
21
+ UPDATE_SINGLE_FIELD_DESCRIPTION: "updateSingleFieldDescription";
22
+ UPDATE_SINGLE_FIELD_NAME: "updateSingleFieldName";
23
+ CREATE_SINGLE_TABLE: "createSingleTable";
24
+ UPDATE_VIEW_METADATA: "updateViewMetadata";
25
+ }>;
26
+ /** @hidden */
27
+ export type TestMutationType = ObjectValues<typeof TestMutationTypes>;
28
+ /** @hidden */
29
+ interface TestDeleteSingleFieldMutation {
30
+ readonly type: typeof TestMutationTypes.DELETE_SINGLE_FIELD;
31
+ readonly tableId: TableId;
32
+ readonly id: FieldId;
33
+ }
34
+ /** @hidden */
35
+ interface TestDeleteSingleViewMutation {
36
+ readonly type: typeof TestMutationTypes.DELETE_SINGLE_VIEW;
37
+ readonly tableId: TableId;
38
+ readonly id: ViewId;
39
+ }
40
+ /** @hidden */
41
+ export type TestMutation = Mutation | TestDeleteSingleFieldMutation | TestDeleteSingleViewMutation;
42
+ export {};
43
+ //# sourceMappingURL=test_mutations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test_mutations.d.ts","sourceRoot":"","sources":["../../../src/test_mutations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAgB,MAAM,yCAAyC,CAAC;AAChF,OAAO,EAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAC,MAAM,wBAAwB,CAAC;AAEhE;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAExD,cAAc;AACd,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;EAI5B,CAAC;AAEH,cAAc;AACd,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEtE,cAAc;AACd,UAAU,6BAA6B;IACnC,QAAQ,CAAC,IAAI,EAAE,OAAO,iBAAiB,CAAC,mBAAmB,CAAC;IAC5D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACxB;AAED,cAAc;AACd,UAAU,4BAA4B;IAClC,QAAQ,CAAC,IAAI,EAAE,OAAO,iBAAiB,CAAC,kBAAkB,CAAC;IAC3D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACvB;AAED,cAAc;AACd,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,6BAA6B,GAAG,4BAA4B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=vacant_airtable_interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vacant_airtable_interface.d.ts","sourceRoot":"","sources":["../../../src/vacant_airtable_interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Dynamically-imported modules are evaluated just once, and the result is
3
+ * shared by all occurrences of the "dynamic import" expression. In order to
4
+ * verify the module's behavior when loaded under different conditions, each
5
+ * test must be declared in a dedicated file (this subverts the cache because
6
+ * the Jest test runner does not share a module cache between files).
7
+ */
8
+ //# sourceMappingURL=index_compatible.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index_compatible.test.d.ts","sourceRoot":"","sources":["../../../test/index_compatible.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index_incompatible.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index_incompatible.test.d.ts","sourceRoot":"","sources":["../../../test/index_incompatible.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mock_airtable_interface.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock_airtable_interface.test.d.ts","sourceRoot":"","sources":["../../../test/mock_airtable_interface.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mutation_types.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation_types.test.d.ts","sourceRoot":"","sources":["../../../test/mutation_types.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=package_json.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package_json.test.d.ts","sourceRoot":"","sources":["../../../test/package_json.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=test_driver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test_driver.test.d.ts","sourceRoot":"","sources":["../../../test/test_driver.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import '../src';
2
+ //# sourceMappingURL=untestable_bindings.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"untestable_bindings.test.d.ts","sourceRoot":"","sources":["../../../test/untestable_bindings.test.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,CAAC"}