@genesislcap/foundation-testing 14.137.0 → 14.137.1-alpha-51939fc.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,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fixture = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fast_element_1 = require("@microsoft/fast-element");
6
+ const fast_foundation_1 = require("@microsoft/fast-foundation");
7
+ function findElement(view) {
8
+ let current = view.firstChild;
9
+ while (current !== null && current.nodeType !== 1) {
10
+ current = current.nextSibling;
11
+ }
12
+ return current;
13
+ }
14
+ function isElementRegistry(obj) {
15
+ return typeof obj.register === 'function';
16
+ }
17
+ /**
18
+ * Creates a test fixture suitable for testing custom elements, templates, and bindings.
19
+ * @param templateNameOrRegistry - An HTML template or single element name to create the fixture for.
20
+ * @param options - Enables customizing fixture creation behavior.
21
+ * @returns A promise of the test fixture {@link Fixture}.
22
+ *
23
+ * @public
24
+ * @remarks
25
+ * Yields control to the caller one Microtask later, in order to
26
+ * ensure that the DOM has settled. This has changed in the latest version of FAST!
27
+ */
28
+ function fixture(templateNameOrRegistry, options = {}) {
29
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
30
+ const document = options.document || globalThis.document;
31
+ const parent = options.parent || document.createElement('div');
32
+ const source = options.source || {};
33
+ const context = options.context || fast_element_1.defaultExecutionContext;
34
+ if (typeof templateNameOrRegistry === 'string') {
35
+ const html = `<${templateNameOrRegistry}></${templateNameOrRegistry}>`;
36
+ templateNameOrRegistry = new fast_element_1.ViewTemplate(html, []);
37
+ }
38
+ else if (isElementRegistry(templateNameOrRegistry)) {
39
+ templateNameOrRegistry = [templateNameOrRegistry];
40
+ }
41
+ if (Array.isArray(templateNameOrRegistry)) {
42
+ const first = templateNameOrRegistry[0];
43
+ const ds = options.designSystem || fast_foundation_1.DesignSystem.getOrCreate(parent);
44
+ let prefix = '';
45
+ ds.register(templateNameOrRegistry, {
46
+ register(container, dsContext) {
47
+ prefix = dsContext.elementPrefix;
48
+ },
49
+ });
50
+ const elementName = `${prefix}-${first.definition.baseName}`;
51
+ const html = `<${elementName}></${elementName}>`;
52
+ templateNameOrRegistry = new fast_element_1.ViewTemplate(html, []);
53
+ }
54
+ const view = templateNameOrRegistry.create();
55
+ const element = findElement(view);
56
+ let isConnected = false;
57
+ view.bind(source, context);
58
+ view.appendTo(parent);
59
+ customElements.upgrade(parent);
60
+ // Hook into the Microtask Queue to ensure the DOM is settled
61
+ // before yielding control to the caller.
62
+ yield Promise.resolve();
63
+ const connect = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
64
+ if (isConnected) {
65
+ return;
66
+ }
67
+ isConnected = true;
68
+ document.body.appendChild(parent);
69
+ yield Promise.resolve();
70
+ });
71
+ const disconnect = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
72
+ if (!isConnected) {
73
+ return;
74
+ }
75
+ isConnected = false;
76
+ document.body.removeChild(parent);
77
+ yield Promise.resolve();
78
+ });
79
+ return {
80
+ document,
81
+ template: templateNameOrRegistry,
82
+ view,
83
+ parent,
84
+ element,
85
+ connect,
86
+ disconnect,
87
+ };
88
+ });
89
+ }
90
+ exports.fixture = fixture;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./fixture"), exports);
@@ -0,0 +1,24 @@
1
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
2
+ if (k2 === undefined) k2 = k;
3
+ var desc = Object.getOwnPropertyDescriptor(m, k);
4
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
5
+ desc = { enumerable: true, get: function() { return m[k]; } };
6
+ }
7
+ Object.defineProperty(o, k2, desc);
8
+ }) : (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ o[k2] = m[k];
11
+ }));
12
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
13
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
14
+ }) : function(o, v) {
15
+ o["default"] = v;
16
+ });
17
+ var __importStar = (this && this.__importStar) || function (mod) {
18
+ if (mod && mod.__esModule) return mod;
19
+ var result = {};
20
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
21
+ __setModuleDefault(result, mod);
22
+ return result;
23
+ };
24
+ Promise.resolve().then(() => __importStar(require('./index')));
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Registration = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Implemented by objects that wish to register dependencies in the container
7
+ * by creating resolvers.
8
+ * @public
9
+ */
10
+ var fast_foundation_1 = require("@microsoft/fast-foundation");
11
+ Object.defineProperty(exports, "Registration", { enumerable: true, get: function () { return fast_foundation_1.Registration; } });
12
+ tslib_1.__exportStar(require("./component"), exports);
13
+ tslib_1.__exportStar(require("./suite"), exports);
14
+ tslib_1.__exportStar(require("./utils"), exports);
15
+ tslib_1.__exportStar(require("./uvu"), exports);
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.teardown = exports.setup = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const jsdom_1 = require("jsdom");
6
+ /**
7
+ * Jsdom setup.
8
+ * @public
9
+ */
10
+ const setup = () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
11
+ const { window } = new jsdom_1.JSDOM('<!DOCTYPE html><html lang="en"><head></head><body></body></html>', {
12
+ pretendToBeVisual: true,
13
+ url: 'https://localhost',
14
+ });
15
+ window.matchMedia = () => ({
16
+ matches: false,
17
+ addListener: () => { },
18
+ removeListener: () => { },
19
+ });
20
+ /**
21
+ * There may have been a change to how the `CSSOM.parse(rule)` works, and how invalid rules are treated. Here we'll be
22
+ * more torrential of such issues given the limitations of JSDOM.
23
+ */
24
+ const cssInsertRule = window.CSSStyleSheet.prototype.insertRule;
25
+ window.CSSStyleSheet.prototype.insertRule = function (rule, index) {
26
+ try {
27
+ return cssInsertRule.call(this, rule, index);
28
+ }
29
+ catch (e) {
30
+ if (rule.includes('@import')) {
31
+ /**
32
+ * Longstanding issue it seems, https://github.com/jsdom/jsdom/issues/2124
33
+ */
34
+ console.log('Please note JSDOM does not support @import, skipping insertion.');
35
+ }
36
+ else {
37
+ /**
38
+ * Unknown malformation.
39
+ */
40
+ console.log('The CSS rule you are inserting is likely malformed', rule);
41
+ }
42
+ /**
43
+ * Return index as if were successful.
44
+ */
45
+ return index;
46
+ }
47
+ };
48
+ window.ResizeObserver = class ResizeObserver {
49
+ constructor(callback) {
50
+ this.callback = callback;
51
+ }
52
+ observe() { }
53
+ disconnect() { }
54
+ triggerResize(width, height) {
55
+ this.callback([{ contentRect: { width, height } }]);
56
+ }
57
+ };
58
+ globalThis.window = window;
59
+ globalThis.customElements = window.customElements;
60
+ globalThis.CSSImportRule = window.CSSImportRule;
61
+ globalThis.CSSMediaRule = window.CSSMediaRule;
62
+ globalThis.CSSRule = window.CSSRule;
63
+ globalThis.CSSStyleDeclaration = window.CSSStyleDeclaration;
64
+ globalThis.CSSStyleRule = window.CSSStyleRule;
65
+ globalThis.CSSStyleSheet = window.CSSStyleSheet;
66
+ globalThis.CustomEvent = window.CustomEvent;
67
+ globalThis.document = window.document;
68
+ globalThis.Document = window.Document;
69
+ globalThis.DocumentFragment = window.DocumentFragment;
70
+ globalThis.HTMLDialogElement = window.HTMLDialogElement;
71
+ globalThis.HTMLElement = window.HTMLElement;
72
+ globalThis.HTMLInputElement = window.HTMLInputElement;
73
+ globalThis.HTMLOptionElement = window.HTMLOptionElement;
74
+ globalThis.HTMLSelectElement = window.HTMLSelectElement;
75
+ globalThis.HTMLStyleElement = window.HTMLStyleElement;
76
+ globalThis.KeyboardEvent = window.KeyboardEvent;
77
+ globalThis.localStorage = window.localStorage;
78
+ globalThis.sessionStorage = window.sessionStorage;
79
+ globalThis.MutationObserver = window.MutationObserver;
80
+ globalThis.navigator = window.navigator;
81
+ globalThis.location = window.location;
82
+ globalThis.Node = window.Node;
83
+ globalThis.Option = window.Option;
84
+ globalThis.requestAnimationFrame = window.requestAnimationFrame;
85
+ globalThis.ShadowRoot = window.ShadowRoot;
86
+ // Env Variables
87
+ globalThis.FORCE_HTTP = false;
88
+ globalThis.HTTP_CONFIG = '';
89
+ });
90
+ exports.setup = setup;
91
+ /**
92
+ * Jsdom teardown / cleanup.
93
+ * @public
94
+ * @remarks
95
+ * This strictly is not required, but may be useful for certain test runs. If used, be mindful that you will need to
96
+ * call setup again before the next test run.
97
+ */
98
+ const teardown = () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
99
+ delete window.matchMedia;
100
+ delete globalThis.window;
101
+ delete globalThis.customElements;
102
+ delete globalThis.CSSImportRule;
103
+ delete globalThis.CSSMediaRule;
104
+ delete globalThis.CSSRule;
105
+ delete globalThis.CSSStyleDeclaration;
106
+ delete globalThis.CSSStyleRule;
107
+ delete globalThis.CSSStyleSheet;
108
+ delete globalThis.CustomEvent;
109
+ delete globalThis.document;
110
+ delete globalThis.Document;
111
+ delete globalThis.DocumentFragment;
112
+ delete globalThis.HTMLDialogElement;
113
+ delete globalThis.HTMLElement;
114
+ delete globalThis.HTMLInputElement;
115
+ delete globalThis.HTMLOptionElement;
116
+ delete globalThis.HTMLSelectElement;
117
+ delete globalThis.HTMLStyleElement;
118
+ delete globalThis.KeyboardEvent;
119
+ delete globalThis.localStorage;
120
+ delete globalThis.MutationObserver;
121
+ delete globalThis.navigator;
122
+ delete globalThis.location;
123
+ delete globalThis.Node;
124
+ delete globalThis.Option;
125
+ delete globalThis.requestAnimationFrame;
126
+ delete globalThis.ShadowRoot;
127
+ // Env Variables
128
+ delete globalThis.FORCE_HTTP;
129
+ delete globalThis.HTTP_CONFIG;
130
+ /**
131
+ * Cancel all timers etc.
132
+ */
133
+ window.close();
134
+ });
135
+ exports.teardown = teardown;
136
+ /**
137
+ * Run setup on import by default.
138
+ */
139
+ (0, exports.setup)();
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configDefaults = void 0;
4
+ /**
5
+ * Default Global Configs, to be re-used (and possibly overridden) by each app
6
+ * @public
7
+ */
8
+ exports.configDefaults = {
9
+ globalSetup: '@genesislcap/foundation-testing/playwright',
10
+ projects: [
11
+ {
12
+ name: 'Chrome Stable',
13
+ use: {
14
+ browserName: 'chromium',
15
+ channel: 'chrome',
16
+ ignoreHTTPSErrors: true,
17
+ /**
18
+ * We should be able to use these 'use' blocks at the App level to provide config data to our micro frontends.
19
+ * This might include what backend to run against, what username password combo to use etc.
20
+ *
21
+ * config: AppLevelPkgConfig;
22
+ */
23
+ // config: {
24
+ // API_HOST: 'wss://dev-foundation1/gwf/',
25
+ // DEFAULT_USER: process.env.DEFAULT_USER,
26
+ // DEFAULT_PASSWORD: process.env.DEFAULT_PASSWORD,
27
+ // PORT: 5030,
28
+ // },
29
+ },
30
+ },
31
+ ],
32
+ testMatch: '**/*.e2e.ts',
33
+ timeout: 60000,
34
+ webServer: {
35
+ command: 'npm run dev:no-open',
36
+ url: `http://localhost:${process.env.PORT}`,
37
+ timeout: 120000,
38
+ reuseExistingServer: !process.env.CI,
39
+ },
40
+ use: {
41
+ baseURL: `http://localhost:${process.env.PORT}`,
42
+ },
43
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ /**
5
+ * Do not export setup
6
+ */
7
+ tslib_1.__exportStar(require("./config"), exports);
8
+ tslib_1.__exportStar(require("./test"), exports);
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ /**
5
+ * @param config - A playwright configuration object.
6
+ * @returns A teardown function.
7
+ *
8
+ * @public
9
+ * @example
10
+ * An example demonstrating use of environment variables and temporary folder:
11
+ * ```
12
+ * import rimraf from 'rimraf';
13
+ *
14
+ * async function setup(config: FullConfig) {
15
+ * process.env.FOO = 'some data';
16
+ * process.env.BAR = JSON.stringify({ some: 'data' });
17
+ * return async function teardown() {
18
+ * delete process.env.FOO;
19
+ * delete process.env.BAR;
20
+ * const tmpDirPath = path.join(os.tmpdir(), 'tmp-folder');
21
+ * rimraf(tmpDirPath, console.log);
22
+ * };
23
+ * }
24
+ * ```
25
+ */
26
+ function setup(config) {
27
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
28
+ return function teardown() {
29
+ return tslib_1.__awaiter(this, void 0, void 0, function* () { });
30
+ };
31
+ });
32
+ }
33
+ exports.default = setup;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.test = exports.defaultAuditThresholds = exports.playAudit = exports.base = exports.expect = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const test_1 = require("@playwright/test");
6
+ const portfinder_sync_1 = require("portfinder-sync");
7
+ var test_2 = require("@playwright/test");
8
+ Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_2.expect; } });
9
+ var test_3 = require("@playwright/test");
10
+ Object.defineProperty(exports, "base", { enumerable: true, get: function () { return test_3.test; } });
11
+ var playwright_lighthouse_1 = require("playwright-lighthouse");
12
+ Object.defineProperty(exports, "playAudit", { enumerable: true, get: function () { return playwright_lighthouse_1.playAudit; } });
13
+ const DEFAULT_PORT = 5030;
14
+ const defaultPackageConfig = {
15
+ API_HOST: process.env.API_HOST,
16
+ DEFAULT_USER: process.env.DEFAULT_USER,
17
+ DEFAULT_PASSWORD: process.env.DEFAULT_PASSWORD,
18
+ PORT: Number(process.env.PORT || DEFAULT_PORT),
19
+ };
20
+ exports.defaultAuditThresholds = {
21
+ performance: 20,
22
+ accessibility: 20,
23
+ 'best-practices': 20,
24
+ seo: 20,
25
+ pwa: 20, // 30,
26
+ };
27
+ /**
28
+ * Playwright test
29
+ * @public
30
+ * @remarks
31
+ * This is just an example playwright test fixture stub. In reality, you will likely create a custom
32
+ * fixture for your e2e tests depending on the needs of your app or micro frontend.
33
+ */
34
+ exports.test = test_1.test.extend({
35
+ config: [defaultPackageConfig, { option: true, scope: 'worker', timeout: 60000 }],
36
+ port: [
37
+ ({ config }, use) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
38
+ const port = (0, portfinder_sync_1.getPort)((config === null || config === void 0 ? void 0 : config.PORT) || process.env.PORT || defaultPackageConfig.PORT);
39
+ yield use(port);
40
+ }),
41
+ { scope: 'worker' }, // option: true
42
+ ],
43
+ browser: [
44
+ ({ port }, use) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
45
+ const browser = yield test_1.chromium.launch({
46
+ args: [`--remote-debugging-port=${port}`],
47
+ });
48
+ yield use(browser);
49
+ }),
50
+ { scope: 'worker' },
51
+ ],
52
+ audit: [exports.defaultAuditThresholds, { option: true, scope: 'worker' }], // TODO: These are not being used from other test setups extending this.
53
+ });
54
+ // Perhaps we keen extending the test up the package chain... spread the mf tests into our app test
55
+ // const test2 = test.extend<{ foo: boolean }>({
56
+ // foo: [true, { option: true }],
57
+ // });
58
+ //
59
+ // a('lighthouse audit', async ({ foo, page }) => {
60
+ //
61
+ // });
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./suite"), exports);
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLogicSuite = exports.createComponentSuite = exports.runCases = exports.sinon = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fast_foundation_1 = require("@microsoft/fast-foundation");
6
+ const component_1 = require("../component");
7
+ const utils_1 = require("../utils");
8
+ const uvu_1 = require("../uvu");
9
+ var sinon_1 = require("sinon");
10
+ Object.defineProperty(exports, "sinon", { enumerable: true, get: function () { return tslib_1.__importDefault(sinon_1).default; } });
11
+ /**
12
+ * Assert a set of test cases.
13
+ * @param fn - The function to test.
14
+ * @param cases - An array of test cases to run against the function.
15
+ * @param assertion - Optional uvu assertion method, defaults to assert.equal.
16
+ * @public
17
+ * @remarks
18
+ * This is available as part of the suite context, but can also be run standalone.
19
+ */
20
+ function runCases(fn, cases, assertion = uvu_1.assert.equal) {
21
+ uvu_1.assert.type(fn, 'function');
22
+ cases.forEach(([actual, expects, msg], index) => assertion(fn(...actual), expects, msg !== null && msg !== void 0 ? msg : JSON.stringify(actual, null, 2)));
23
+ }
24
+ exports.runCases = runCases;
25
+ /**
26
+ * Create component test suite.
27
+ *
28
+ * @example
29
+ * Simple suite using the tag name of the component.
30
+ * ```ts
31
+ * import { createComponentSuite } from '@genesislcap/foundation-testing';
32
+ * import { Component } from './component';
33
+ * Component; // < As we're using tag name in the Suite, we hold a reference to avoid tree shaking.
34
+ * const Suite = createComponentSuite<Component>('Component', 'my-component');
35
+ * // test cases...
36
+ * Suite.run();
37
+ * ```
38
+ *
39
+ * @example
40
+ * Mocking a DI dependency for a composable component.
41
+ * ```ts
42
+ * const connectMock = new ConnectMock();
43
+ * const mocks = [Registration.instance(Connect, connectMock)];
44
+ * const Suite = createComponentSuite<ConnectionIndicator>('ConnectionIndicator Component', () => connectionIndicator(), null, mocks);
45
+ * // test cases...
46
+ * Suite.run();
47
+ * ```
48
+ *
49
+ * @example
50
+ * An element will be required to test anything that directly or in-directly makes use of the DI container, for example,
51
+ * a service that can be injected into components, or has its own injected dependencies.
52
+ * ```ts
53
+ * import { Service } from './service';
54
+ * @customElement({
55
+ * name: 'test-element',
56
+ * template: html`<slot></slot>`,
57
+ * })
58
+ * class TestElement extends FASTElement {}
59
+ * const mocks = [...];
60
+ * const Suite = createComponentSuite<TestElement>('Service', 'test-element', null, mocks);
61
+ * // test cases...
62
+ * Suite.run();
63
+ * ```
64
+ *
65
+ * Importing the service should invoke the Service's DI registration, so in your test cases you can simply query the
66
+ * container to get a reference to your service.
67
+ * ```ts
68
+ * Suite('Service.x does something expected', async ({ container }) => {
69
+ * const myService = container.get(Service);
70
+ * // assert
71
+ * });
72
+ * ```
73
+ *
74
+ * You can optionally add the service to the test element for lookup convenience, but this is not required.
75
+ * ```ts
76
+ * class TestElement extends FASTElement {
77
+ * @Service service: Service;
78
+ * }
79
+ * Suite('Element has service injected', async ({ element }) => {
80
+ * assert.ok(element.service);
81
+ * });
82
+ * ```
83
+ *
84
+ * @typeParam TElement - The element interface.
85
+ * @param title - Title of the test suite
86
+ * @param elementNameOrGetter - Element tag name or getter which is used to create the element within the fixture
87
+ * @param context - Optional component context {@link ComponentContext}
88
+ * @param registrations - Optional array of DI container registrations
89
+ * @returns The test suite
90
+ * @public
91
+ * @remarks
92
+ * Used to test function output given certain input arguments.
93
+ */
94
+ function createComponentSuite(title, elementNameOrGetter, context, registrations = []) {
95
+ const Suite = (0, uvu_1.suite)(title, Object.assign({ element: undefined, disconnect: undefined, designSystem: undefined, container: undefined, runCases: undefined }, context));
96
+ Suite.before.each((c) => tslib_1.__awaiter(this, void 0, void 0, function* () {
97
+ try {
98
+ const parent = document.createElement('div');
99
+ const designSystem = fast_foundation_1.DesignSystem.getOrCreate(parent).register(...registrations);
100
+ const container = fast_foundation_1.DI.getOrCreateDOMContainer(parent);
101
+ const fixtureOptions = {
102
+ parent,
103
+ designSystem,
104
+ };
105
+ /**
106
+ * We might need to spread element getters for some test case, for example look at the following Fast test:
107
+ * packages/web-components/fast-foundation/src/picker/picker.spec.ts
108
+ */
109
+ const { element, connect, disconnect } = yield (0, component_1.fixture)(typeof elementNameOrGetter === 'string' ? elementNameOrGetter : elementNameOrGetter(), fixtureOptions);
110
+ c.element = element;
111
+ c.disconnect = disconnect;
112
+ c.designSystem = designSystem;
113
+ c.container = container;
114
+ c.runCases = runCases.bind(Suite);
115
+ yield connect();
116
+ }
117
+ catch (e) {
118
+ utils_1.logger.error(e);
119
+ throw e;
120
+ }
121
+ }));
122
+ /* eslint-disable require-atomic-updates */
123
+ Suite.after.each((c) => tslib_1.__awaiter(this, void 0, void 0, function* () {
124
+ try {
125
+ if (c.disconnect) {
126
+ yield c.disconnect();
127
+ }
128
+ c.element = undefined;
129
+ c.disconnect = undefined;
130
+ c.designSystem = undefined;
131
+ c.container = undefined;
132
+ c.runCases = undefined;
133
+ }
134
+ catch (e) {
135
+ utils_1.logger.error(e);
136
+ throw e;
137
+ }
138
+ }));
139
+ return Suite;
140
+ }
141
+ exports.createComponentSuite = createComponentSuite;
142
+ /**
143
+ * Create logic test suite.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * import { createLogicSuite } from '@genesislcap/foundation-testing';
148
+ * import { myFunction } from './logic';
149
+ * const Suite = createLogicSuite('myFunction');
150
+ * Suite('myFunction should provide expected results', ({ runCases }) => {
151
+ * runCases(myFunction, [
152
+ * [['1'], true],
153
+ * [[123], true],
154
+ * [['60%'], true],
155
+ * [['$60'], false],
156
+ * [['1.1'], false],
157
+ * [[''], false],
158
+ * [[true], false],
159
+ * [[null], false],
160
+ * [[undefined], false],
161
+ * ]);
162
+ * });
163
+ * Suite.run();
164
+ * ```
165
+ *
166
+ * @typeParam TContext - The context interface.
167
+ * @param title - Title of the test suite
168
+ * @param context - Optional context which extends {@link LogicContext}
169
+ * @returns The test suite
170
+ * @public
171
+ * @remarks
172
+ * Used to test function output given certain input arguments.
173
+ */
174
+ function createLogicSuite(title, context) {
175
+ const Suite = (0, uvu_1.suite)(title, Object.assign({ runCases: (fn, cases) => {
176
+ uvu_1.assert.type(fn, 'function');
177
+ cases.forEach(([actual, expects, msg]) => uvu_1.assert.is(fn(...actual), expects, msg));
178
+ } }, context));
179
+ return Suite;
180
+ }
181
+ exports.createLogicSuite = createLogicSuite;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared tsm config for apps to import and use in their tsm.js files.
3
+ *
4
+ * @example
5
+ * ```js
6
+ * const config = require('@genesislcap/foundation-testing/tsm');
7
+ *
8
+ * module.exports = config;
9
+ * ```
10
+ *
11
+ * @public
12
+ */
13
+ const config = {
14
+ '.css': {
15
+ loader: 'text',
16
+ },
17
+ '.svg': {
18
+ loader: 'dataurl',
19
+ },
20
+ '.jpg': {
21
+ loader: 'dataurl',
22
+ },
23
+ '.png': {
24
+ loader: 'dataurl',
25
+ },
26
+ };
27
+ module.exports = config;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.restoreTestHarness = exports.resetTestHarness = exports.testSpy = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const sinon_1 = tslib_1.__importDefault(require("sinon"));
6
+ /**
7
+ * Predicate function used to check whether the object's value is a function
8
+ * we want to spy on. We want to spy on all functions, except the constructor
9
+ * @internal
10
+ */
11
+ const shouldSpy = (proto) => (k) => typeof proto[k] === 'function' && k !== 'constructor';
12
+ /**
13
+ * Decorator: Used on a test harness class based on a `FoundationElement` to give it extra functionality
14
+ * which can be used during testing. *Important* this is to be used on a parent element compared to
15
+ * the element under test.
16
+ *
17
+ *
18
+ * @remarks
19
+ * Access the test functionality using the type with {@link WithTestHarness}
20
+ *
21
+ * Reset after each test with {@link resetTestHarness}
22
+ *
23
+ * @example
24
+ *
25
+ * // Testing the first function call
26
+ * async (\{ element \}) =\> \{
27
+ *
28
+ assert.ok(
29
+ element.layout.addItemFromChild.calledWith(\{
30
+ type: 'component',
31
+ componentType: 'test',
32
+ title: `Item test`,
33
+ reorderEnabled: true,
34
+ isClosable: false,
35
+ size: undefined,
36
+ \})
37
+ );
38
+ *
39
+ * // Reset the tester at the end of every test even if you don't assert on any of it!
40
+ * resetTestHarness(element.layout);
41
+ * \}
42
+ *
43
+ * @public
44
+ */
45
+ function testSpy(constructor) {
46
+ constructor['_test'] = sinon_1.default.createSandbox();
47
+ Object.getOwnPropertyNames(constructor.prototype)
48
+ .filter(shouldSpy(constructor.prototype))
49
+ .forEach((p) => constructor['_test'].spy(constructor.prototype, p));
50
+ }
51
+ exports.testSpy = testSpy;
52
+ /**
53
+ * Resets the history of the spied functions on the objects so previous running tests
54
+ * don't affect the current test.
55
+ * Must be called at the end of every test even if not asserting on the spies.
56
+ * @param wrapper - `WithTestHarness<T>` object to reset
57
+ * @public
58
+ */
59
+ function resetTestHarness(wrapper) {
60
+ wrapper.constructor['_test'].resetHistory();
61
+ }
62
+ exports.resetTestHarness = resetTestHarness;
63
+ /**
64
+ * Restores the spied functions back to the original functions without the spies.
65
+ * @param wrapper - `WithTestHarness<T>` object to restore
66
+ * @public
67
+ */
68
+ function restoreTestHarness(wrapper) {
69
+ wrapper.constructor['_test'].restore();
70
+ }
71
+ exports.restoreTestHarness = restoreTestHarness;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./logger"), exports);
5
+ tslib_1.__exportStar(require("./harness"), exports);
6
+ tslib_1.__exportStar(require("./promise"), exports);
7
+ tslib_1.__exportStar(require("./types"), exports);
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logger = void 0;
4
+ const foundation_logger_1 = require("@genesislcap/foundation-logger");
5
+ /**
6
+ * Test logger
7
+ * @public
8
+ * @remarks
9
+ * Exported so you can set log levels differently across packages when needed.
10
+ */
11
+ exports.logger = (0, foundation_logger_1.createLogger)('foundation-testing');
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.delayedResolve = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Delayed resolve utility.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * test('delayed resolve', async () => {
11
+ * const mockAPI = delayedResolve({ foo: 'bar' }, 2_000);
12
+ * const result = await mockAPI();
13
+ * });
14
+ * ```
15
+ * @param result - The result of the promise.
16
+ * @param duration - An optional duration in milliseconds. Defaults to 500.
17
+ * @public
18
+ */
19
+ const delayedResolve = (result, duration = 500) => () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
20
+ yield new Promise((resolve) => {
21
+ setTimeout(resolve, duration);
22
+ });
23
+ return result;
24
+ });
25
+ exports.delayedResolve = delayedResolve;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ // https://github.com/type-challenges/type-challenges/blob/main/utils/index.d.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./uvu"), exports);
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.timeout = exports.test = exports.suite = exports.assert = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const uvu_1 = require("uvu");
6
+ Object.defineProperty(exports, "suite", { enumerable: true, get: function () { return uvu_1.suite; } });
7
+ Object.defineProperty(exports, "test", { enumerable: true, get: function () { return uvu_1.test; } });
8
+ const assert = tslib_1.__importStar(require("uvu/assert"));
9
+ exports.assert = assert;
10
+ /**
11
+ * Timeout utility.
12
+ *
13
+ * @example A promise that never ends.
14
+ * ```ts
15
+ * test(
16
+ * 'should fail',
17
+ * timeout(async () => {
18
+ * await new Promise(() => {});
19
+ * assert.ok(true);
20
+ * }, 5_000),
21
+ * );
22
+ * ```
23
+ *
24
+ * @example A slow API.
25
+ * ```ts
26
+ * test(
27
+ * 'should fail',
28
+ * timeout(async () => {
29
+ * const slowMockAPI = delayedResolve({ foo: 'bar' }, 4_000);
30
+ * await slowMockAPI();
31
+ * assert.unreachable('should have timed out');
32
+ * }, 2_000),
33
+ * );
34
+ * ```
35
+ *
36
+ * @param callback - The async test function.
37
+ * @param duration - An optional duration in milliseconds. Defaults to 5_000.
38
+ * @public
39
+ */
40
+ const timeout = (callback, duration = 5000) => {
41
+ return (context) => {
42
+ let timer;
43
+ return Promise.race([
44
+ callback(context),
45
+ new Promise((resolve, reject) => {
46
+ timer = setTimeout(() => reject(new Error('timed out')), duration);
47
+ }),
48
+ ]).finally(() => {
49
+ clearTimeout(timer);
50
+ });
51
+ };
52
+ };
53
+ exports.timeout = timeout;
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.137.0",
4
+ "version": "14.137.1-alpha-51939fc.0",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -16,14 +16,17 @@
16
16
  },
17
17
  "./e2e": {
18
18
  "types": "./dist/dts/playwright/index.d.ts",
19
+ "require": "./dist/cjs/playwright/index.js",
19
20
  "default": "./dist/esm/playwright/index.js"
20
21
  },
21
22
  "./jsdom": {
22
23
  "types": "./dist/dts/jsdom/setup.d.ts",
24
+ "require": "./dist/cjs/jsdom/setup.js",
23
25
  "default": "./dist/esm/jsdom/setup.js"
24
26
  },
25
27
  "./playwright": {
26
28
  "types": "./dist/dts/playwright/setup.d.ts",
29
+ "require": "./dist/cjs/playwright/setup.js",
27
30
  "default": "./dist/esm/playwright/setup.js"
28
31
  },
29
32
  "./package.json": "./package.json",
@@ -48,7 +51,7 @@
48
51
  "scripts": {
49
52
  "api:extract": "api-extractor run",
50
53
  "api:document": "api-documenter markdown -i dist -o docs/api",
51
- "build": "npm run clean && tsc -b ./tsconfig.json && npm run api:extract && npm run api:document",
54
+ "build": "npm run clean && tsc -b ./tsconfig.json && tsc -b ./tsconfig.cjs.json && npm run api:extract && npm run api:document",
52
55
  "clean": "rimraf dist tsconfig.tsbuildinfo",
53
56
  "dev": "tsc -b ./tsconfig.json -w",
54
57
  "test": "uvu -r tsm --tsmconfig ./src/tsm/tsm.ts -r @esbuild-kit/cjs-loader -r ./src/jsdom/setup.ts . test.ts"
@@ -63,7 +66,7 @@
63
66
  "typescript": "^4.5.5"
64
67
  },
65
68
  "dependencies": {
66
- "@genesislcap/foundation-logger": "14.137.0",
69
+ "@genesislcap/foundation-logger": "14.137.1-alpha-51939fc.0",
67
70
  "@microsoft/fast-element": "^1.12.0",
68
71
  "@microsoft/fast-foundation": "^2.49.4",
69
72
  "@playwright/test": "^1.18.1",
@@ -84,5 +87,5 @@
84
87
  "publishConfig": {
85
88
  "access": "public"
86
89
  },
87
- "gitHead": "e858204084c3ab29fa83c92dd9532c539e113f16"
90
+ "gitHead": "0a0d1a4c8ae8dd18e61a5db9bb3ff3f91851afc4"
88
91
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "declaration": false,
5
+ "declarationDir": null,
6
+ "declarationMap": false,
7
+ "module": "CommonJS",
8
+ "outDir": "./dist/cjs"
9
+ }
10
+ }