@genesislcap/foundation-testing 14.68.2 → 14.68.3-alpha-43b6152.0

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.
@@ -0,0 +1,435 @@
1
+ import * as assert from 'uvu/assert';
2
+ import { Constructable } from '@microsoft/fast-element';
3
+ import { Container } from '@microsoft/fast-foundation';
4
+ import { DesignSystem } from '@microsoft/fast-foundation';
5
+ import { ExecutionContext } from '@microsoft/fast-element';
6
+ import type { FoundationElement } from '@microsoft/fast-foundation';
7
+ import { FoundationElementDefinition } from '@microsoft/fast-foundation';
8
+ import { FoundationElementRegistry } from '@microsoft/fast-foundation';
9
+ import { HTMLView } from '@microsoft/fast-element';
10
+ import { Logger } from '@genesislcap/foundation-utils';
11
+ import { Registration } from '@microsoft/fast-foundation';
12
+ import { default as sinon_2 } from 'sinon';
13
+ import { default as sinon_3 } from 'sinon';
14
+ import { suite } from 'uvu';
15
+ import { test } from 'uvu';
16
+ import { uvu } from 'uvu';
17
+ import { ViewTemplate } from '@microsoft/fast-element';
18
+
19
+ export { assert }
20
+
21
+ /**
22
+ * Component suite context interface
23
+ * @public
24
+ */
25
+ export declare interface ComponentContext<TElement = HTMLElement> extends Pick<Fixture<TElement>, 'element' | 'disconnect'> {
26
+ designSystem: DesignSystem;
27
+ container: Container;
28
+ runCases: RunCases;
29
+ }
30
+
31
+ /**
32
+ * Create component test suite.
33
+ *
34
+ * @example
35
+ * Simple suite using the tag name of the component.
36
+ * ```ts
37
+ * import { createComponentSuite } from '@genesislcap/foundation-testing';
38
+ * import { Component } from './component';
39
+ * Component; // < As we're using tag name in the Suite, we hold a reference to avoid tree shaking.
40
+ * const Suite = createComponentSuite<Component>('Component', 'my-component');
41
+ * // test cases...
42
+ * Suite.run();
43
+ * ```
44
+ *
45
+ * @example
46
+ * Mocking a DI dependency for a composable component.
47
+ * ```ts
48
+ * const connectMock = new ConnectMock();
49
+ * const mocks = [Registration.instance(Connect, connectMock)];
50
+ * const Suite = createComponentSuite<ConnectionIndicator>('ConnectionIndicator Component', () => connectionIndicator(), null, mocks);
51
+ * // test cases...
52
+ * Suite.run();
53
+ * ```
54
+ *
55
+ * @example
56
+ * An element will be required to test anything that directly or in-directly makes use of the DI container, for example,
57
+ * a service that can be injected into components, or has its own injected dependencies.
58
+ * ```ts
59
+ * import { Service } from './service';
60
+ * @customElement({
61
+ * name: 'test-element',
62
+ * template: html`<slot></slot>`,
63
+ * })
64
+ * class TestElement extends FASTElement {}
65
+ * const mocks = [...];
66
+ * const Suite = createComponentSuite<TestElement>('Service', 'test-element', null, mocks);
67
+ * // test cases...
68
+ * Suite.run();
69
+ * ```
70
+ *
71
+ * Importing the service should invoke the Service's DI registration, so in your test cases you can simply query the
72
+ * container to get a reference to your service.
73
+ * ```ts
74
+ * Suite('Service.x does something expected', async ({ container }) => {
75
+ * const myService = container.get(Service);
76
+ * // assert
77
+ * });
78
+ * ```
79
+ *
80
+ * You can optionally add the service to the test element for lookup convenience, but this is not required.
81
+ * ```ts
82
+ * class TestElement extends FASTElement {
83
+ * @Service service: Service;
84
+ * }
85
+ * Suite('Element has service injected', async ({ element }) => {
86
+ * assert.ok(element.service);
87
+ * });
88
+ * ```
89
+ *
90
+ * @typeParam TElement - The element interface.
91
+ * @param title - Title of the test suite
92
+ * @param elementNameOrGetter - Element tag name or getter which is used to create the element within the fixture
93
+ * @param context - Optional component context {@link ComponentContext}
94
+ * @param registrations - Optional array of DI container registrations
95
+ * @returns The test suite
96
+ * @public
97
+ * @remarks
98
+ * Used to test function output given certain input arguments.
99
+ */
100
+ export declare function createComponentSuite<TElement = HTMLElement>(title: string, elementNameOrGetter: string | ElementGetter, context?: ComponentContext<TElement>, registrations?: Registration<any>[]): uvu.Test<ComponentContext<TElement>>;
101
+
102
+ /**
103
+ * Create logic test suite.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * import { createLogicSuite } from '@genesislcap/foundation-testing';
108
+ * import { myFunction } from './logic';
109
+ * const Suite = createLogicSuite('myFunction');
110
+ * Suite('myFunction should provide expected results', ({ runCases }) => {
111
+ * runCases(myFunction, [
112
+ * [['1'], true],
113
+ * [[123], true],
114
+ * [['60%'], true],
115
+ * [['$60'], false],
116
+ * [['1.1'], false],
117
+ * [[''], false],
118
+ * [[true], false],
119
+ * [[null], false],
120
+ * [[undefined], false],
121
+ * ]);
122
+ * });
123
+ * Suite.run();
124
+ * ```
125
+ *
126
+ * @typeParam TContext - The context interface.
127
+ * @param title - Title of the test suite
128
+ * @param context - Optional context which extends {@link LogicContext}
129
+ * @returns The test suite
130
+ * @public
131
+ * @remarks
132
+ * Used to test function output given certain input arguments.
133
+ */
134
+ export declare function createLogicSuite<TContext = LogicContext>(title: string, context?: TContext): uvu.Test<TContext>;
135
+
136
+ /**
137
+ * Delayed resolve utility.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * test('delayed resolve', async () => {
142
+ * const mockAPI = delayedResolve({ foo: 'bar' }, 2_000);
143
+ * const result = await mockAPI();
144
+ * });
145
+ * ```
146
+ * @param result - The result of the promise.
147
+ * @param duration - An optional duration in milliseconds. Defaults to 500.
148
+ * @public
149
+ */
150
+ export declare const delayedResolve: (result: unknown, duration?: number) => () => Promise<unknown>;
151
+
152
+ /**
153
+ * @public
154
+ */
155
+ export declare type ElementGetter = () => FoundationElementRegistry<FoundationElementDefinition, FoundationElement>;
156
+
157
+ /**
158
+ * Expects that the type parameter X is equal to Y, returning true or false
159
+ * @public
160
+ */
161
+ export declare type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
162
+
163
+ /**
164
+ * Expects that the type parameter T resolves to true, or will be a typescript error
165
+ * @public
166
+ */
167
+ export declare type Expect<T extends true> = T;
168
+
169
+ /**
170
+ * Filters out key/value pairs from an object where they value does not match a condition
171
+ * @public
172
+ */
173
+ export declare type FilterConditionally<Source, Condition> = Pick<Source, {
174
+ [K in keyof Source]: Source[K] extends Condition ? K : never;
175
+ }[keyof Source]>;
176
+
177
+ /**
178
+ * Unit test fixture.
179
+ * @public
180
+ */
181
+ export declare interface Fixture<TElement = HTMLElement> {
182
+ /**
183
+ * The document the fixture is running in.
184
+ */
185
+ document: Document;
186
+ /**
187
+ * The template the fixture was created from.
188
+ */
189
+ template: ViewTemplate;
190
+ /**
191
+ * The view that was created from the fixture's template.
192
+ */
193
+ view: HTMLView;
194
+ /**
195
+ * The parent element that the view was appended to.
196
+ * @remarks
197
+ * This element will be appended to the DOM only
198
+ * after {@link Fixture.connect} has been called.
199
+ */
200
+ parent: HTMLElement;
201
+ /**
202
+ * The first element in the {@link Fixture.view}.
203
+ */
204
+ element: TElement;
205
+ /**
206
+ * Adds the {@link Fixture.parent} to the DOM, causing the
207
+ * connect lifecycle to begin.
208
+ * @remarks
209
+ * Yields control to the caller one Microtask later, in order to
210
+ * ensure that the DOM has settled.
211
+ */
212
+ connect(): Promise<void>;
213
+ /**
214
+ * Removes the {@link Fixture.parent} from the DOM, causing the
215
+ * disconnect lifecycle to begin.
216
+ * @remarks
217
+ * Yields control to the caller one Microtask later, in order to
218
+ * ensure that the DOM has settled.
219
+ */
220
+ disconnect(): Promise<void>;
221
+ }
222
+
223
+ /**
224
+ * Creates a test fixture suitable for testing custom elements, templates, and bindings.
225
+ * @param templateNameOrRegistry - An HTML template or single element name to create the fixture for.
226
+ * @param options - Enables customizing fixture creation behavior.
227
+ * @returns A promise of the test fixture {@link Fixture}.
228
+ *
229
+ * @public
230
+ * @remarks
231
+ * Yields control to the caller one Microtask later, in order to
232
+ * ensure that the DOM has settled. This has changed in the latest version of FAST!
233
+ */
234
+ export declare function fixture<TElement = HTMLElement>(templateNameOrRegistry: ViewTemplate | string | FoundationElementRegistry<FoundationElementDefinition, Constructable<TElement>> | [
235
+ FoundationElementRegistry<FoundationElementDefinition, Constructable<TElement>>,
236
+ ...FoundationElementRegistry<FoundationElementDefinition, Constructable>[]
237
+ ], options?: FixtureOptions): Promise<Fixture<TElement>>;
238
+
239
+ /**
240
+ * Options used to customize the creation of the unit test fixture.
241
+ * @public
242
+ */
243
+ export declare interface FixtureOptions {
244
+ /**
245
+ * The document to run the fixture in.
246
+ * @defaultValue `globalThis.document`
247
+ */
248
+ document?: Document;
249
+ /**
250
+ * The parent element to append the fixture to.
251
+ * @defaultValue An instance of `HTMLDivElement`.
252
+ */
253
+ parent?: HTMLElement;
254
+ /**
255
+ * The data source to bind the HTML to.
256
+ * @defaultValue An empty object.
257
+ */
258
+ source?: any;
259
+ /**
260
+ * The execution context to use during binding.
261
+ * @defaultValue {@link @microsoft/fast-element#defaultExecutionContext}
262
+ */
263
+ context?: ExecutionContext;
264
+ /**
265
+ * A pre-configured design system instance used in setting up the fixture.
266
+ */
267
+ designSystem?: DesignSystem;
268
+ }
269
+
270
+ /**
271
+ * Test logger
272
+ * @public
273
+ * @remarks
274
+ * Exported so you can set log levels differently across packages when needed.
275
+ */
276
+ export declare const logger: Logger;
277
+
278
+ /**
279
+ * Logic suite context interface
280
+ * @public
281
+ */
282
+ export declare interface LogicContext {
283
+ /**
284
+ * @privateRemarks
285
+ * Not hardening the typing on this to RunCases until get a feel for usages.
286
+ */
287
+ runCases?(fn: (value: any) => any, cases: [any, any, assert.Message?][]): void;
288
+ }
289
+
290
+ export { Registration }
291
+
292
+ /**
293
+ * Resets the history of the spied functions on the objects so previous running tests
294
+ * don't affect the current test.
295
+ * Must be called at the end of every test even if not asserting on the spies.
296
+ * @param wrapper - `WithTestHarness<T>` object to reset
297
+ * @public
298
+ */
299
+ export declare function resetTestHarness<T>(wrapper: WithTestHarness<T>): void;
300
+
301
+ /**
302
+ * Restores the spied functions back to the original functions without the spies.
303
+ * @param wrapper - `WithTestHarness<T>` object to restore
304
+ * @public
305
+ */
306
+ export declare function restoreTestHarness<T>(wrapper: WithTestHarness<T>): void;
307
+
308
+ /**
309
+ * @public
310
+ */
311
+ export declare type RunCases = (fn: (...args: unknown[]) => unknown, cases: [unknown[], unknown, string?][], assertion?: (...args: unknown[]) => void) => void;
312
+
313
+ /**
314
+ * Assert a set of test cases.
315
+ * @param fn - The function to test.
316
+ * @param cases - An array of test cases to run against the function.
317
+ * @param assertion - Optional uvu assertion method, defaults to assert.equal.
318
+ * @public
319
+ * @remarks
320
+ * This is available as part of the suite context, but can also be run standalone.
321
+ */
322
+ export declare function runCases(fn: (...args: unknown[]) => unknown, cases: [unknown[], unknown, string?][], assertion?: (...args: unknown[]) => void): void;
323
+
324
+ export { sinon_2 as sinon }
325
+
326
+ /**
327
+ * Crates an object with a key value pair for each spied function
328
+ * where the key is the function name and the value is the sinon wrapped
329
+ * spy function
330
+ * @public
331
+ * @typeParam T - object with keys where values are functions
332
+ */
333
+ export declare type SinonWrapper<T extends {
334
+ [K in keyof T]: (...args: any) => any;
335
+ }> = {
336
+ [K in keyof T]: sinon_3.SinonSpy<Parameters<T[K]>, ReturnType<T[K]>>;
337
+ };
338
+
339
+ /**
340
+ * Filters out all key/value pairs which are not functions, and omits the constructor
341
+ * @public
342
+ */
343
+ export declare type SpiedFunctions<T> = Omit<FilterConditionally<T, (...args: any[]) => any>, 'constructor'>;
344
+
345
+ export { suite }
346
+
347
+ /**
348
+ * Defines the Generic type for a test Suite `T` the callback function
349
+ * required to assert on type `T`.
350
+ * @typeParam T - the top level class we are testing
351
+ * @public
352
+ */
353
+ export declare type SuiteCallback<T> = uvu.Callback<ComponentContext<T>>;
354
+
355
+ export { test }
356
+
357
+ /**
358
+ * Decorator: Used on a test harness class based on a `FoundationElement` to give it extra functionality
359
+ * which can be used during testing. *Important* this is to be used on a parent element compared to
360
+ * the element under test.
361
+ *
362
+ *
363
+ * @remarks
364
+ * Access the test functionality using the type with {@link WithTestHarness}
365
+ *
366
+ * Reset after each test with {@link resetTestHarness}
367
+ *
368
+ * @example
369
+ *
370
+ * // Testing the first function call
371
+ * async (\{ element \}) =\> \{
372
+ *
373
+ assert.ok(
374
+ element.layout.addItemFromChild.calledWith(\{
375
+ type: 'component',
376
+ componentType: 'test',
377
+ title: `Item test`,
378
+ reorderEnabled: true,
379
+ isClosable: false,
380
+ size: undefined,
381
+ \})
382
+ );
383
+ *
384
+ * // Reset the tester at the end of every test even if you don't assert on any of it!
385
+ * resetTestHarness(element.layout);
386
+ * \}
387
+ *
388
+ * @public
389
+ */
390
+ export declare function testSpy(constructor: Function): void;
391
+
392
+ /**
393
+ * Timeout utility.
394
+ *
395
+ * @example A promise that never ends.
396
+ * ```ts
397
+ * test(
398
+ * 'should fail',
399
+ * timeout(async () => {
400
+ * await new Promise(() => {});
401
+ * assert.ok(true);
402
+ * }, 5_000),
403
+ * );
404
+ * ```
405
+ *
406
+ * @example A slow API.
407
+ * ```ts
408
+ * test(
409
+ * 'should fail',
410
+ * timeout(async () => {
411
+ * const slowMockAPI = delayedResolve({ foo: 'bar' }, 4_000);
412
+ * await slowMockAPI();
413
+ * assert.unreachable('should have timed out');
414
+ * }, 2_000),
415
+ * );
416
+ * ```
417
+ *
418
+ * @param callback - The async test function.
419
+ * @param duration - An optional duration in milliseconds. Defaults to 5_000.
420
+ * @public
421
+ */
422
+ export declare const timeout: (callback: (context: any) => Promise<any>, duration?: number) => (context: any) => Promise<any>;
423
+
424
+ export { uvu }
425
+
426
+ /**
427
+ * Type can be used to inform Typescript the object decorated with {@link testSpy} has function
428
+ * spies which can be interacted upon their normal function reference
429
+ * @public
430
+ */
431
+ export declare type WithTestHarness<T extends {
432
+ [k in keyof T]: any;
433
+ }> = T & SinonWrapper<SpiedFunctions<T>>;
434
+
435
+ export { }
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.34.9"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,24 @@
1
+ <!-- Do not edit this file. It is automatically generated by API Documenter. -->
2
+
3
+ [Home](./index.md) &gt; [@genesislcap/foundation-testing](./foundation-testing.md) &gt; [delayedResolve](./foundation-testing.delayedresolve.md)
4
+
5
+ ## delayedResolve variable
6
+
7
+ Delayed resolve utility.
8
+
9
+ **Signature:**
10
+
11
+ ```typescript
12
+ delayedResolve: (result: unknown, duration?: number) => () => Promise<unknown>
13
+ ```
14
+
15
+ ## Example
16
+
17
+
18
+ ```ts
19
+ test('delayed resolve', async () => {
20
+ const mockAPI = delayedResolve({ foo: 'bar' }, 2_000);
21
+ const result = await mockAPI();
22
+ });
23
+ ```
24
+
@@ -29,7 +29,9 @@
29
29
 
30
30
  | Variable | Description |
31
31
  | --- | --- |
32
+ | [delayedResolve](./foundation-testing.delayedresolve.md) | Delayed resolve utility. |
32
33
  | [logger](./foundation-testing.logger.md) | Test logger |
34
+ | [timeout](./foundation-testing.timeout.md) | Timeout utility. |
33
35
 
34
36
  ## Type Aliases
35
37
 
@@ -0,0 +1,43 @@
1
+ <!-- Do not edit this file. It is automatically generated by API Documenter. -->
2
+
3
+ [Home](./index.md) &gt; [@genesislcap/foundation-testing](./foundation-testing.md) &gt; [timeout](./foundation-testing.timeout.md)
4
+
5
+ ## timeout variable
6
+
7
+ Timeout utility.
8
+
9
+ **Signature:**
10
+
11
+ ```typescript
12
+ timeout: (callback: (context: any) => Promise<any>, duration?: number) => (context: any) => Promise<any>
13
+ ```
14
+
15
+ ## Example 1
16
+
17
+ A promise that never ends.
18
+
19
+ ```ts
20
+ test(
21
+ 'should fail',
22
+ timeout(async () => {
23
+ await new Promise(() => {});
24
+ assert.ok(true);
25
+ }, 5_000),
26
+ );
27
+ ```
28
+
29
+ ## Example 2
30
+
31
+ A slow API.
32
+
33
+ ```ts
34
+ test(
35
+ 'should fail',
36
+ timeout(async () => {
37
+ const slowMockAPI = delayedResolve({ foo: 'bar' }, 4_000);
38
+ await slowMockAPI();
39
+ assert.unreachable('should have timed out');
40
+ }, 2_000),
41
+ );
42
+ ```
43
+
@@ -16,6 +16,7 @@ import { HTMLView } from '@microsoft/fast-element';
16
16
  import { Logger } from '@genesislcap/foundation-utils';
17
17
  import { Registration } from '@microsoft/fast-foundation';
18
18
  import { default as sinon_2 } from 'sinon';
19
+ import { default as sinon_3 } from 'sinon';
19
20
  import { suite } from 'uvu';
20
21
  import { test } from 'uvu';
21
22
  import { uvu } from 'uvu';
@@ -39,6 +40,9 @@ export function createComponentSuite<TElement = HTMLElement>(title: string, elem
39
40
  // @public
40
41
  export function createLogicSuite<TContext = LogicContext>(title: string, context?: TContext): uvu.Test<TContext>;
41
42
 
43
+ // @public
44
+ export const delayedResolve: (result: unknown, duration?: number) => () => Promise<unknown>;
45
+
42
46
  // @public (undocumented)
43
47
  export type ElementGetter = () => FoundationElementRegistry<FoundationElementDefinition, FoundationElement>;
44
48
 
@@ -102,11 +106,13 @@ export type RunCases = (fn: (...args: unknown[]) => unknown, cases: [unknown[],
102
106
  // @public
103
107
  export function runCases(fn: (...args: unknown[]) => unknown, cases: [unknown[], unknown, string?][], assertion?: (...args: unknown[]) => void): void;
104
108
 
109
+ export { sinon_2 as sinon }
110
+
105
111
  // @public
106
112
  export type SinonWrapper<T extends {
107
113
  [K in keyof T]: (...args: any) => any;
108
114
  }> = {
109
- [K in keyof T]: sinon_2.SinonSpy<Parameters<T[K]>, ReturnType<T[K]>>;
115
+ [K in keyof T]: sinon_3.SinonSpy<Parameters<T[K]>, ReturnType<T[K]>>;
110
116
  };
111
117
 
112
118
  // @public
@@ -122,6 +128,9 @@ export { test }
122
128
  // @public
123
129
  export function testSpy(constructor: Function): void;
124
130
 
131
+ // @public
132
+ export const timeout: (callback: (context: any) => Promise<any>, duration?: number) => (context: any) => Promise<any>;
133
+
125
134
  export { uvu }
126
135
 
127
136
  // @public
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-testing",
3
3
  "description": "Genesis Foundation Testing",
4
- "version": "14.68.2",
4
+ "version": "14.68.3-alpha-43b6152.0",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -33,18 +33,23 @@
33
33
  "PORT": 3050
34
34
  },
35
35
  "scripts": {
36
- "build": "npm run clean && tsc -b ./tsconfig.json",
36
+ "api:extract": "api-extractor run",
37
+ "api:document": "api-documenter markdown -i dist -o docs/api",
38
+ "build": "npm run clean && tsc -b ./tsconfig.json && npm run api:extract && npm run api:document",
37
39
  "clean": "rimraf dist tsconfig.tsbuildinfo",
38
- "dev": "tsc -b ./tsconfig.json -w"
40
+ "dev": "tsc -b ./tsconfig.json -w",
41
+ "test": "uvu -r tsm --tsmconfig ./src/tsm/tsm.ts -r @esbuild-kit/cjs-loader -r ./src/jsdom/setup.ts . test.ts"
39
42
  },
40
43
  "devDependencies": {
44
+ "@microsoft/api-documenter": "^7.19.13",
45
+ "@microsoft/api-extractor": "7.34.9",
41
46
  "@types/node": "^20.3.1",
42
47
  "@types/sinon": "^10.0.13",
43
48
  "rimraf": "^3.0.2",
44
49
  "typescript": "^4.5.5"
45
50
  },
46
51
  "dependencies": {
47
- "@genesislcap/foundation-utils": "14.68.2",
52
+ "@genesislcap/foundation-utils": "14.68.3-alpha-43b6152.0",
48
53
  "@microsoft/fast-element": "^1.7.0",
49
54
  "@microsoft/fast-foundation": "^2.33.2",
50
55
  "@playwright/test": "^1.18.1",
@@ -65,5 +70,5 @@
65
70
  "publishConfig": {
66
71
  "access": "public"
67
72
  },
68
- "gitHead": "5366046018e36151b75f74ec6a088aed80b12686"
73
+ "gitHead": "86cd12b91d5e5abfc932bb89286f0ff71b85d72f"
69
74
  }