From 3f8734403f273949aaa694b3d521ff397ce95d01 Mon Sep 17 00:00:00 2001 From: rajsite Date: Sat, 22 Jul 2023 20:54:36 -0500 Subject: [PATCH 01/13] Parameterize test pattern --- .../anchor-button/tests/anchor-button.spec.ts | 78 +++++++--------- .../src/utilities/tests/parameterized.ts | 93 ++++++++++++------- 2 files changed, 92 insertions(+), 79 deletions(-) diff --git a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts index 171230620f..d32b3af960 100644 --- a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts +++ b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts @@ -2,7 +2,7 @@ import { html } from '@microsoft/fast-element'; import { AnchorButton } from '..'; import { waitForUpdatesAsync } from '../../testing/async-helpers'; import { fixture, Fixture } from '../../utilities/tests/fixture'; -import { getSpecTypeByNamedList } from '../../utilities/tests/parameterized'; +import { parameterize } from '../../utilities/tests/parameterized'; async function setup(): Promise> { return fixture( @@ -49,55 +49,47 @@ describe('AnchorButton', () => { expect(element.control!.getAttribute('href')).toBeNull(); }); - const attributeNames: { name: string }[] = [ - { name: 'download' }, - { name: 'hreflang' }, - { name: 'ping' }, - { name: 'referrerpolicy' }, - { name: 'rel' }, - { name: 'target' }, - { name: 'type' }, - { name: 'aria-atomic' }, - { name: 'aria-busy' }, - { name: 'aria-controls' }, - { name: 'aria-current' }, - { name: 'aria-describedby' }, - { name: 'aria-details' }, - { name: 'aria-disabled' }, - { name: 'aria-errormessage' }, - { name: 'aria-expanded' }, - { name: 'aria-flowto' }, - { name: 'aria-haspopup' }, - { name: 'aria-hidden' }, - { name: 'aria-invalid' }, - { name: 'aria-keyshortcuts' }, - { name: 'aria-label' }, - { name: 'aria-labelledby' }, - { name: 'aria-live' }, - { name: 'aria-owns' }, - { name: 'aria-relevant' }, - { name: 'aria-roledescription' } - ]; + const attributeNames = { + download: 'download', + hreflang: 'hreflang', + ping: 'ping', + referrerpolicy: 'referrerpolicy', + rel: 'rel', + target: 'target', + type: 'type', + ariaAtomic: 'aria-atomic', + ariaBusy: 'aria-busy', + ariaControls: 'aria-controls', + ariaCurrent: 'aria-current', + ariaDescribedby: 'aria-describedby', + ariaDetails: 'aria-details', + ariaDisabled: 'aria-disabled', + ariaErrormessage: 'aria-errormessage', + ariaExpanded: 'aria-expanded', + ariaFlowto: 'aria-flowto', + ariaHaspopup: 'aria-haspopup', + ariaHidden: 'aria-hidden', + ariaInvalid: 'aria-invalid', + ariaKeyshortcuts: 'aria-keyshortcuts', + ariaLabel: 'aria-label', + ariaLabelledby: 'aria-labelledby', + ariaLive: 'aria-live', + ariaOwns: 'aria-owns', + ariaRelevant: 'aria-relevant', + ariaRoledescription: 'aria-roledescription', + }; describe('should reflect value to the internal control', () => { - const focused: string[] = []; - const disabled: string[] = []; - for (const attribute of attributeNames) { - const specType = getSpecTypeByNamedList( - attribute, - focused, - disabled - ); - // eslint-disable-next-line @typescript-eslint/no-loop-func - specType(`for attribute ${attribute.name}`, async () => { + parameterize(attributeNames, (spec, _, value) => { + spec(`for attribute ${value}`, async () => { await connect(); - element.setAttribute(attribute.name, 'foo'); + element.setAttribute(value, 'foo'); await waitForUpdatesAsync(); - expect(element.control!.getAttribute(attribute.name)).toBe( + expect(element.control!.getAttribute(value)).toBe( 'foo' ); }); - } + }); }); }); diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index e8d2f8be95..6165bbf19f 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -1,23 +1,7 @@ // eslint-disable-next-line no-restricted-globals type SpecTypes = typeof fit | typeof xit | typeof it; /** - * Note: you should probably use one of the specialized versions of this function instead - * - * Used to focus or disable specific tests in a parameterized test run. - * In the following example the test for the `cats` case is focused for debugging - * and no tests are disabled: - * @example - * const rainTypes = ['cats', 'dogs', 'frogs', 'men']; - * describe('Different rains', () => { - * const isFocused = rainType => rainType === 'cats'; - * const isDisabled = () => false; - * for (const rainType of rainTypes) { - * const specType = getSpecType(rainType, isFocused, isDisabled); - * specType(`of type ${rainType} exist`, () => { - * expect(rainType).toBeTruthy(); - * }); - * } - * }); + * @deprecated switch to `parameterize` instead */ const getSpecType = ( value: T, @@ -35,25 +19,7 @@ const getSpecType = ( }; /** - * Used to focus or disable specific tests in a parameterized test run using a list of named objects. - * In the following example the test for the `cats-and-dogs` case is focused for debugging - * and no tests are disabled: - * @example - * const rainTypes = [ - * { name: 'cats-and-dogs', type: 'idiom' }, - * { name: 'frogs' type: 'idiom'}, - * { name: 'men', type: 'lyrics'} - * ] as const; - * describe('Different rains', () => { - * const focused = ['cats-and-dogs']; - * const disabled = []; - * for (const rainType of rainTypes) { - * const specType = getSpecTypeByNamedList(rainType, focused, disabled); - * specType(`of type ${rainType.name} exist`, () => { - * expect(rainType.type).toBeDefined(); - * }); - * } - * }); + * @deprecated switch to `parameterize` instead */ export const getSpecTypeByNamedList = ( value: T, @@ -64,3 +30,58 @@ export const getSpecTypeByNamedList = ( (x: T) => focusList.includes(x.name), (x: T) => disabledList.includes(x.name) ); + +// eslint-disable-next-line no-restricted-globals +type Fit = typeof fit; +type Xit = typeof xit; +type It = typeof it; +/** + * One of the jasmine spec functions: fit, xit, or it + */ +type Spec = Fit | Xit | It; +/** + * One of the jasmine spec functions: fit or xit + */ +type SpecOverride = Fit | Xit; + +/** + * Used to create a parameterized test using a an object of test names and test values. + * In the following example: + * - the test named `catsAndDogs` is focused for debugging + * - the test named `frogs` is configured to always be disabled + * - the test named `men` is configured to run normally + * @example + * const rainTests = { + * catsAndDogs: 'idiom', + * frogs: 'idiom', + * men: 'lyrics' + * } as const; + * describe('Different rains', () => { + * parameterize(rainTests, (spec, name, value) => { + * spec(`of type ${name} exist`, () => { + * expect(value).toBeDefined(); + * }); + * }, { + * catsAndDogs: fit, + * frogs: xit + * }); + * }); + */ +export const parameterize = ( + testCases: T, + test: (spec: Spec, name: keyof T, value: T[keyof T]) => void, + specOverrides?: { + [P in keyof T]?: SpecOverride + }): void => { + Object.entries(testCases).forEach(testCase => { + const name = testCase[0] as keyof T; + const value = testCase[1] as T[keyof T]; + const override = specOverrides?.[name]; + // eslint-disable-next-line no-restricted-globals + if (override && !(override === fit || override === xit)) { + throw new Error('Must configure overrides with one of the jasmine spec functions: fit or xit'); + } + const spec = override ?? it; + test(spec, name, value); + }); +}; From 19c3bacc7cef9225e6e63c3c774949d762bb3a1e Mon Sep 17 00:00:00 2001 From: rajsite Date: Sat, 22 Jul 2023 20:57:33 -0500 Subject: [PATCH 02/13] Lint --- .../src/anchor-button/tests/anchor-button.spec.ts | 6 ++---- .../src/utilities/tests/parameterized.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts index d32b3af960..9118e94457 100644 --- a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts +++ b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts @@ -76,7 +76,7 @@ describe('AnchorButton', () => { ariaLive: 'aria-live', ariaOwns: 'aria-owns', ariaRelevant: 'aria-relevant', - ariaRoledescription: 'aria-roledescription', + ariaRoledescription: 'aria-roledescription' }; describe('should reflect value to the internal control', () => { parameterize(attributeNames, (spec, _, value) => { @@ -86,9 +86,7 @@ describe('AnchorButton', () => { element.setAttribute(value, 'foo'); await waitForUpdatesAsync(); - expect(element.control!.getAttribute(value)).toBe( - 'foo' - ); + expect(element.control!.getAttribute(value)).toBe('foo'); }); }); }); diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index 6165bbf19f..e7b12495d1 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -71,15 +71,18 @@ export const parameterize = ( testCases: T, test: (spec: Spec, name: keyof T, value: T[keyof T]) => void, specOverrides?: { - [P in keyof T]?: SpecOverride - }): void => { + [P in keyof T]?: SpecOverride; + } +): void => { Object.entries(testCases).forEach(testCase => { const name = testCase[0] as keyof T; const value = testCase[1] as T[keyof T]; const override = specOverrides?.[name]; // eslint-disable-next-line no-restricted-globals if (override && !(override === fit || override === xit)) { - throw new Error('Must configure overrides with one of the jasmine spec functions: fit or xit'); + throw new Error( + 'Must configure overrides with one of the jasmine spec functions: fit or xit' + ); } const spec = override ?? it; test(spec, name, value); From 366cb16060a509f314daae06fd1fb3de326e2a06 Mon Sep 17 00:00:00 2001 From: rajsite Date: Sat, 22 Jul 2023 20:58:19 -0500 Subject: [PATCH 03/13] Change files --- ...le-components-5528f3eb-a2ce-4e80-9df4-6ddb04f8b015.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@ni-nimble-components-5528f3eb-a2ce-4e80-9df4-6ddb04f8b015.json diff --git a/change/@ni-nimble-components-5528f3eb-a2ce-4e80-9df4-6ddb04f8b015.json b/change/@ni-nimble-components-5528f3eb-a2ce-4e80-9df4-6ddb04f8b015.json new file mode 100644 index 0000000000..4f80d95663 --- /dev/null +++ b/change/@ni-nimble-components-5528f3eb-a2ce-4e80-9df4-6ddb04f8b015.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "Parameterize test pattern", + "packageName": "@ni/nimble-components", + "email": "rajsite@users.noreply.github.com", + "dependentChangeType": "none" +} From 921f2eb5b80e369aa7c652ae99456a9d8300e42e Mon Sep 17 00:00:00 2001 From: rajsite Date: Sun, 23 Jul 2023 01:09:30 -0500 Subject: [PATCH 04/13] Add parameterizeList helper --- .../anchor-button/tests/anchor-button.spec.ts | 68 +++++++++---------- .../src/utilities/tests/parameterized.ts | 43 +++++++++++- 2 files changed, 76 insertions(+), 35 deletions(-) diff --git a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts index 9118e94457..0d5fa7f131 100644 --- a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts +++ b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts @@ -2,7 +2,7 @@ import { html } from '@microsoft/fast-element'; import { AnchorButton } from '..'; import { waitForUpdatesAsync } from '../../testing/async-helpers'; import { fixture, Fixture } from '../../utilities/tests/fixture'; -import { parameterize } from '../../utilities/tests/parameterized'; +import { parameterizeList } from '../../utilities/tests/parameterized'; async function setup(): Promise> { return fixture( @@ -49,44 +49,44 @@ describe('AnchorButton', () => { expect(element.control!.getAttribute('href')).toBeNull(); }); - const attributeNames = { - download: 'download', - hreflang: 'hreflang', - ping: 'ping', - referrerpolicy: 'referrerpolicy', - rel: 'rel', - target: 'target', - type: 'type', - ariaAtomic: 'aria-atomic', - ariaBusy: 'aria-busy', - ariaControls: 'aria-controls', - ariaCurrent: 'aria-current', - ariaDescribedby: 'aria-describedby', - ariaDetails: 'aria-details', - ariaDisabled: 'aria-disabled', - ariaErrormessage: 'aria-errormessage', - ariaExpanded: 'aria-expanded', - ariaFlowto: 'aria-flowto', - ariaHaspopup: 'aria-haspopup', - ariaHidden: 'aria-hidden', - ariaInvalid: 'aria-invalid', - ariaKeyshortcuts: 'aria-keyshortcuts', - ariaLabel: 'aria-label', - ariaLabelledby: 'aria-labelledby', - ariaLive: 'aria-live', - ariaOwns: 'aria-owns', - ariaRelevant: 'aria-relevant', - ariaRoledescription: 'aria-roledescription' - }; + const attributeNames = [ + 'download', + 'hreflang', + 'ping', + 'referrerpolicy', + 'rel', + 'target', + 'type', + 'aria-atomic', + 'aria-busy', + 'aria-controls', + 'aria-current', + 'aria-describedby', + 'aria-details', + 'aria-disabled', + 'aria-errormessage', + 'aria-expanded', + 'aria-flowto', + 'aria-haspopup', + 'aria-hidden', + 'aria-invalid', + 'aria-keyshortcuts', + 'aria-label', + 'aria-labelledby', + 'aria-live', + 'aria-owns', + 'aria-relevant', + 'aria-roledescription' + ] as const; describe('should reflect value to the internal control', () => { - parameterize(attributeNames, (spec, _, value) => { - spec(`for attribute ${value}`, async () => { + parameterizeList(attributeNames, (spec, name) => { + spec(`for attribute ${name}`, async () => { await connect(); - element.setAttribute(value, 'foo'); + element.setAttribute(name, 'foo'); await waitForUpdatesAsync(); - expect(element.control!.getAttribute(value)).toBe('foo'); + expect(element.control!.getAttribute(name)).toBe('foo'); }); }); }); diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index e7b12495d1..5779d181b9 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -45,7 +45,7 @@ type Spec = Fit | Xit | It; type SpecOverride = Fit | Xit; /** - * Used to create a parameterized test using a an object of test names and test values. + * Used to create a parameterized test using an object of test names and arbitrary test values. * In the following example: * - the test named `catsAndDogs` is focused for debugging * - the test named `frogs` is configured to always be disabled @@ -88,3 +88,44 @@ export const parameterize = ( test(spec, name, value); }); }; + +type ObjectFromList = { + [K in T extends readonly (infer U)[] ? U : never]: K +}; + +/** + * Used to create a parameterized test using an array of test names. + * In the following example: + * - the test named `cats-and-dogs` is focused for debugging + * - the test named `frogs` is configured to always be disabled + * - the test named `men` is configured to run normally + * @example + * const rainTests = [ + * 'cats-and-dogs', + * 'frogs', + * 'men' + * ] as const; + * describe('Different rains', () => { + * parameterizeList(rainTests, (spec, name) => { + * spec(`of type ${name} exist`, () => { + * expect(name).toBeDefined(); + * }); + * }, { + * 'cats-and-dogs': fit, + * frogs: xit + * }); + * }); + */ +export const parameterizeList = ( + list: T, + test: (spec: Spec, name: keyof ObjectFromList) => void, + specOverrides?: { + [P in keyof ObjectFromList]?: SpecOverride; + } +): void => { + const testCases = list.reduce<{ [key: string]: string }>((result, entry) => { + result[entry] = entry; + return result; + }, {}) as ObjectFromList; + parameterize(testCases, test, specOverrides); +}; From e3ff123123a24f40649d7d5b8aa0ccdea33ca09a Mon Sep 17 00:00:00 2001 From: rajsite Date: Sun, 23 Jul 2023 01:12:17 -0500 Subject: [PATCH 05/13] lint --- .../src/utilities/tests/parameterized.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index 5779d181b9..50da30044f 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -90,7 +90,7 @@ export const parameterize = ( }; type ObjectFromList = { - [K in T extends readonly (infer U)[] ? U : never]: K + [K in T extends readonly (infer U)[] ? U : never]: K; }; /** @@ -123,9 +123,12 @@ export const parameterizeList = ( [P in keyof ObjectFromList]?: SpecOverride; } ): void => { - const testCases = list.reduce<{ [key: string]: string }>((result, entry) => { - result[entry] = entry; - return result; - }, {}) as ObjectFromList; + const testCases = list.reduce<{ [key: string]: string }>( + (result, entry) => { + result[entry] = entry; + return result; + }, + {} + ) as ObjectFromList; parameterize(testCases, test, specOverrides); }; From 897b44fc05bedc2d2b8f56ab62bdb4748552cfef Mon Sep 17 00:00:00 2001 From: rajsite Date: Sun, 23 Jul 2023 01:18:07 -0500 Subject: [PATCH 06/13] Doc tweak --- .../nimble-components/src/utilities/tests/parameterized.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index 50da30044f..f28f852cf4 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -49,7 +49,7 @@ type SpecOverride = Fit | Xit; * In the following example: * - the test named `catsAndDogs` is focused for debugging * - the test named `frogs` is configured to always be disabled - * - the test named `men` is configured to run normally + * - the test named `men` will run normally as it has no override * @example * const rainTests = { * catsAndDogs: 'idiom', @@ -98,7 +98,7 @@ type ObjectFromList = { * In the following example: * - the test named `cats-and-dogs` is focused for debugging * - the test named `frogs` is configured to always be disabled - * - the test named `men` is configured to run normally + * - the test named `men` will run normally as it has no override * @example * const rainTests = [ * 'cats-and-dogs', From 629be28ca8b14a90c1617f12e06b2d6c7d1dcc78 Mon Sep 17 00:00:00 2001 From: rajsite Date: Sun, 23 Jul 2023 11:52:52 -0500 Subject: [PATCH 07/13] Test --- .../src/utilities/tests/parameterized.ts | 22 +- .../utilities/tests/tests/parametized.spec.ts | 227 ++++++++++++++++++ 2 files changed, 241 insertions(+), 8 deletions(-) create mode 100644 packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index f28f852cf4..b3e3b75f5f 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -74,18 +74,24 @@ export const parameterize = ( [P in keyof T]?: SpecOverride; } ): void => { - Object.entries(testCases).forEach(testCase => { - const name = testCase[0] as keyof T; - const value = testCase[1] as T[keyof T]; - const override = specOverrides?.[name]; + const testCaseNames = Object.keys(testCases) as (keyof T)[]; + if (specOverrides) { + const overrideNames = Object.keys(specOverrides) as (keyof typeof specOverrides)[]; + if (!overrideNames.every(overrideName => testCaseNames.includes(overrideName))) { + throw new Error( + 'Parameterized test override names must match test case name' + ); + } // eslint-disable-next-line no-restricted-globals - if (override && !(override === fit || override === xit)) { + if (!overrideNames.every(overrideName => [fit, xit].includes(specOverrides[overrideName]!))) { throw new Error( - 'Must configure overrides with one of the jasmine spec functions: fit or xit' + 'Must configure override with one of the jasmine spec functions: fit or xit' ); } - const spec = override ?? it; - test(spec, name, value); + } + testCaseNames.forEach(testCaseName => { + const spec = specOverrides?.[testCaseName] ?? it; + test(spec, testCaseName, testCases[testCaseName]); }); }; diff --git a/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts new file mode 100644 index 0000000000..d93d7a94bd --- /dev/null +++ b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts @@ -0,0 +1,227 @@ +import { parameterize, parameterizeList } from '../parameterized'; + +// eslint-disable-next-line no-restricted-globals +const FIT = fit; +const IT = it; +const XIT = xit; + +interface ParameterizeTestArgs { + spec: typeof IT | typeof XIT | typeof FIT; + name: string; + value: unknown; +} +const paramertizeTestArgs = ([ + spec, + name, + value +]: unknown[]): ParameterizeTestArgs => ({ + spec, + name, + value +} as ParameterizeTestArgs); + +describe('Funtion parameterize', () => { + describe('can parameterize simple objects', () => { + it('with test enabled', () => { + const testcases = { + case1: 'one' + } as const; + const spy = jasmine.createSpy(); + parameterize(testcases, spy); + + expect(spy).toHaveBeenCalledTimes(1); + const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(IT); + expect(name).toBe('case1'); + expect(value).toBe('one'); + }); + + it('with test focused', () => { + const testcases = { + case1: 'one' + } as const; + const spy = jasmine.createSpy(); + parameterize(testcases, spy, { + case1: FIT + }); + + expect(spy).toHaveBeenCalledTimes(1); + const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(FIT); + expect(name).toBe('case1'); + expect(value).toBe('one'); + }); + + it('with test disabled', () => { + const testcases = { + case1: 'one' + } as const; + const spy = jasmine.createSpy(); + parameterize(testcases, spy, { + case1: XIT + }); + + expect(spy).toHaveBeenCalledTimes(1); + const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(XIT); + expect(name).toBe('case1'); + expect(value).toBe('one'); + }); + + it('with various test cases enabled and disabled', () => { + const testcases = { + case1: 'one', + case2: 'two', + case3: 'three' + } as const; + const spy = jasmine.createSpy(); + parameterize(testcases, spy, { + case2: XIT, + case3: FIT + }); + + expect(spy).toHaveBeenCalledTimes(3); + { + const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(IT); + expect(name).toBe('case1'); + expect(value).toBe('one'); + } + { + const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(1)); + expect(spec).toBe(XIT); + expect(name).toBe('case2'); + expect(value).toBe('two'); + } + { + const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(2)); + expect(spec).toBe(FIT); + expect(name).toBe('case3'); + expect(value).toBe('three'); + } + }); + }); + describe('errors', () => { + it('for override not in test cases', () => { + const testcases = { + case1: 'one' + } as { [key: string]: string }; + + expect(() => { + parameterize(testcases, () => {}, { + unknown: XIT + }); + }).toThrowError(/override names must match test case name/); + }); + it('for override not referencing supported xit or fit', () => { + const testcases = { + case1: 'one' + } as const; + + expect(() => { + parameterize(testcases, () => {}, { + case1: IT + }); + }).toThrowError(/jasmine spec functions: fit or xit/); + }); + }); +}); + +interface ParameterizeListTestArgs { + spec: typeof IT | typeof XIT | typeof FIT; + name: string; +} +const paramertizeListTestArgs = ([ + spec, + name, +]: unknown[]): ParameterizeListTestArgs => ({ + spec, + name, +} as ParameterizeListTestArgs); + +describe('Funtion parameterizeList', () => { + describe('can parameterize simple lists', () => { + it('with test enabled', () => { + const testcases = ['case1'] as const; + const spy = jasmine.createSpy(); + parameterizeList(testcases, spy); + + expect(spy).toHaveBeenCalledTimes(1); + const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(IT); + expect(name).toBe('case1'); + }); + + it('with test focused', () => { + const testcases = ['case1'] as const; + const spy = jasmine.createSpy(); + parameterizeList(testcases, spy, { + case1: FIT + }); + + expect(spy).toHaveBeenCalledTimes(1); + const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(FIT); + expect(name).toBe('case1'); + }); + + it('with test disabled', () => { + const testcases = ['case1'] as const; + const spy = jasmine.createSpy(); + parameterizeList(testcases, spy, { + case1: XIT + }); + + expect(spy).toHaveBeenCalledTimes(1); + const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(XIT); + expect(name).toBe('case1'); + }); + + it('with various test cases enabled and disabled', () => { + const testcases = ['case1', 'case2', 'case3']; + const spy = jasmine.createSpy(); + parameterizeList(testcases, spy, { + case2: XIT, + case3: FIT + }); + + expect(spy).toHaveBeenCalledTimes(3); + { + const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + expect(spec).toBe(IT); + expect(name).toBe('case1'); + } + { + const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(1)); + expect(spec).toBe(XIT); + expect(name).toBe('case2'); + } + { + const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(2)); + expect(spec).toBe(FIT); + expect(name).toBe('case3'); + } + }); + }); + describe('errors', () => { + it('for override not in test cases', () => { + const testcases = ['case1'] as string[]; + + expect(() => { + parameterizeList(testcases, () => {}, { + unknown: XIT + }); + }).toThrowError(/override names must match test case name/); + }); + it('for override not referencing supported xit or fit', () => { + const testcases = ['case1'] as const; + + expect(() => { + parameterizeList(testcases, () => {}, { + case1: IT + }); + }).toThrowError(/jasmine spec functions: fit or xit/); + }); + }); +}); From 2d9c52052bbeb79161ec682b168481c2fb595478 Mon Sep 17 00:00:00 2001 From: rajsite Date: Sun, 23 Jul 2023 11:59:59 -0500 Subject: [PATCH 08/13] Lint --- .../src/utilities/tests/parameterized.ts | 14 +++-- .../utilities/tests/tests/parametized.spec.ts | 52 ++++++++++++++----- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index b3e3b75f5f..99cd3ab476 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -76,14 +76,20 @@ export const parameterize = ( ): void => { const testCaseNames = Object.keys(testCases) as (keyof T)[]; if (specOverrides) { - const overrideNames = Object.keys(specOverrides) as (keyof typeof specOverrides)[]; - if (!overrideNames.every(overrideName => testCaseNames.includes(overrideName))) { + const overrideNames = Object.keys( + specOverrides + ) as (keyof typeof specOverrides)[]; + if ( + !overrideNames.every(overrideName => testCaseNames.includes(overrideName)) + ) { throw new Error( 'Parameterized test override names must match test case name' ); } - // eslint-disable-next-line no-restricted-globals - if (!overrideNames.every(overrideName => [fit, xit].includes(specOverrides[overrideName]!))) { + if ( + // eslint-disable-next-line no-restricted-globals + !overrideNames.every(overrideName => [fit, xit].includes(specOverrides[overrideName]!)) + ) { throw new Error( 'Must configure override with one of the jasmine spec functions: fit or xit' ); diff --git a/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts index d93d7a94bd..5c1d33fca2 100644 --- a/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts +++ b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts @@ -30,7 +30,9 @@ describe('Funtion parameterize', () => { parameterize(testcases, spy); expect(spy).toHaveBeenCalledTimes(1); - const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + const { spec, name, value } = paramertizeTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(IT); expect(name).toBe('case1'); expect(value).toBe('one'); @@ -46,7 +48,9 @@ describe('Funtion parameterize', () => { }); expect(spy).toHaveBeenCalledTimes(1); - const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + const { spec, name, value } = paramertizeTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(FIT); expect(name).toBe('case1'); expect(value).toBe('one'); @@ -62,7 +66,9 @@ describe('Funtion parameterize', () => { }); expect(spy).toHaveBeenCalledTimes(1); - const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + const { spec, name, value } = paramertizeTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(XIT); expect(name).toBe('case1'); expect(value).toBe('one'); @@ -82,19 +88,25 @@ describe('Funtion parameterize', () => { expect(spy).toHaveBeenCalledTimes(3); { - const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(0)); + const { spec, name, value } = paramertizeTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(IT); expect(name).toBe('case1'); expect(value).toBe('one'); } { - const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(1)); + const { spec, name, value } = paramertizeTestArgs( + spy.calls.argsFor(1) + ); expect(spec).toBe(XIT); expect(name).toBe('case2'); expect(value).toBe('two'); } { - const { spec, name, value } = paramertizeTestArgs(spy.calls.argsFor(2)); + const { spec, name, value } = paramertizeTestArgs( + spy.calls.argsFor(2) + ); expect(spec).toBe(FIT); expect(name).toBe('case3'); expect(value).toBe('three'); @@ -133,10 +145,10 @@ interface ParameterizeListTestArgs { } const paramertizeListTestArgs = ([ spec, - name, + name ]: unknown[]): ParameterizeListTestArgs => ({ spec, - name, + name } as ParameterizeListTestArgs); describe('Funtion parameterizeList', () => { @@ -147,7 +159,9 @@ describe('Funtion parameterizeList', () => { parameterizeList(testcases, spy); expect(spy).toHaveBeenCalledTimes(1); - const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + const { spec, name } = paramertizeListTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(IT); expect(name).toBe('case1'); }); @@ -160,7 +174,9 @@ describe('Funtion parameterizeList', () => { }); expect(spy).toHaveBeenCalledTimes(1); - const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + const { spec, name } = paramertizeListTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(FIT); expect(name).toBe('case1'); }); @@ -173,7 +189,9 @@ describe('Funtion parameterizeList', () => { }); expect(spy).toHaveBeenCalledTimes(1); - const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + const { spec, name } = paramertizeListTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(XIT); expect(name).toBe('case1'); }); @@ -188,17 +206,23 @@ describe('Funtion parameterizeList', () => { expect(spy).toHaveBeenCalledTimes(3); { - const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(0)); + const { spec, name } = paramertizeListTestArgs( + spy.calls.argsFor(0) + ); expect(spec).toBe(IT); expect(name).toBe('case1'); } { - const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(1)); + const { spec, name } = paramertizeListTestArgs( + spy.calls.argsFor(1) + ); expect(spec).toBe(XIT); expect(name).toBe('case2'); } { - const { spec, name } = paramertizeListTestArgs(spy.calls.argsFor(2)); + const { spec, name } = paramertizeListTestArgs( + spy.calls.argsFor(2) + ); expect(spec).toBe(FIT); expect(name).toBe('case3'); } From f75a5464036fc9860c33e006065ef5795ae2c697 Mon Sep 17 00:00:00 2001 From: rajsite Date: Sun, 23 Jul 2023 12:20:38 -0500 Subject: [PATCH 09/13] update deprecation comment --- .../nimble-components/src/utilities/tests/parameterized.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index 99cd3ab476..9a4c96fd3a 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line no-restricted-globals type SpecTypes = typeof fit | typeof xit | typeof it; /** - * @deprecated switch to `parameterize` instead + * @deprecated switch to `parameterize` or `parameterizeList` instead */ const getSpecType = ( value: T, @@ -19,7 +19,7 @@ const getSpecType = ( }; /** - * @deprecated switch to `parameterize` instead + * @deprecated switch to `parameterize` or `parameterizeList` instead */ export const getSpecTypeByNamedList = ( value: T, From ed2590441352cf3b2ce5c4a4f5213074eb7ccd0e Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Sep 2023 11:45:04 -0500 Subject: [PATCH 10/13] parameterizeNamedList --- .../anchor-button/tests/anchor-button.spec.ts | 58 +++++++++---------- .../src/utilities/tests/parameterized.ts | 38 ++++++------ .../utilities/tests/tests/parametized.spec.ts | 32 +++++----- 3 files changed, 68 insertions(+), 60 deletions(-) diff --git a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts index 0d5fa7f131..6a152617f8 100644 --- a/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts +++ b/packages/nimble-components/src/anchor-button/tests/anchor-button.spec.ts @@ -2,7 +2,7 @@ import { html } from '@microsoft/fast-element'; import { AnchorButton } from '..'; import { waitForUpdatesAsync } from '../../testing/async-helpers'; import { fixture, Fixture } from '../../utilities/tests/fixture'; -import { parameterizeList } from '../../utilities/tests/parameterized'; +import { parameterizeNamedList } from '../../utilities/tests/parameterized'; async function setup(): Promise> { return fixture( @@ -50,36 +50,36 @@ describe('AnchorButton', () => { }); const attributeNames = [ - 'download', - 'hreflang', - 'ping', - 'referrerpolicy', - 'rel', - 'target', - 'type', - 'aria-atomic', - 'aria-busy', - 'aria-controls', - 'aria-current', - 'aria-describedby', - 'aria-details', - 'aria-disabled', - 'aria-errormessage', - 'aria-expanded', - 'aria-flowto', - 'aria-haspopup', - 'aria-hidden', - 'aria-invalid', - 'aria-keyshortcuts', - 'aria-label', - 'aria-labelledby', - 'aria-live', - 'aria-owns', - 'aria-relevant', - 'aria-roledescription' + { name: 'download' }, + { name: 'hreflang' }, + { name: 'ping' }, + { name: 'referrerpolicy' }, + { name: 'rel' }, + { name: 'target' }, + { name: 'type' }, + { name: 'aria-atomic' }, + { name: 'aria-busy' }, + { name: 'aria-controls' }, + { name: 'aria-current' }, + { name: 'aria-describedby' }, + { name: 'aria-details' }, + { name: 'aria-disabled' }, + { name: 'aria-errormessage' }, + { name: 'aria-expanded' }, + { name: 'aria-flowto' }, + { name: 'aria-haspopup' }, + { name: 'aria-hidden' }, + { name: 'aria-invalid' }, + { name: 'aria-keyshortcuts' }, + { name: 'aria-label' }, + { name: 'aria-labelledby' }, + { name: 'aria-live' }, + { name: 'aria-owns' }, + { name: 'aria-relevant' }, + { name: 'aria-roledescription' } ] as const; describe('should reflect value to the internal control', () => { - parameterizeList(attributeNames, (spec, name) => { + parameterizeNamedList(attributeNames, (spec, name) => { spec(`for attribute ${name}`, async () => { await connect(); diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index 9a4c96fd3a..87d30bc945 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line no-restricted-globals type SpecTypes = typeof fit | typeof xit | typeof it; /** - * @deprecated switch to `parameterize` or `parameterizeList` instead + * @deprecated switch to `parameterize` or `parameterizeNamedList` instead */ const getSpecType = ( value: T, @@ -19,7 +19,7 @@ const getSpecType = ( }; /** - * @deprecated switch to `parameterize` or `parameterizeList` instead + * @deprecated switch to `parameterize` or `parameterizeNamedList` instead */ export const getSpecTypeByNamedList = ( value: T, @@ -101,26 +101,26 @@ export const parameterize = ( }); }; -type ObjectFromList = { - [K in T extends readonly (infer U)[] ? U : never]: K; +type ObjectFromNamedList = { + [K in T extends readonly { name: infer U }[] ? U : never]: T[number]; }; /** - * Used to create a parameterized test using an array of test names. + * Used to create a parameterized test using an array of tests with names. * In the following example: * - the test named `cats-and-dogs` is focused for debugging * - the test named `frogs` is configured to always be disabled * - the test named `men` will run normally as it has no override * @example * const rainTests = [ - * 'cats-and-dogs', - * 'frogs', - * 'men' + * { name: 'cats-and-dogs', type: 'idiom' }, + * { name: 'frogs' type: 'idiom'}, + * { name: 'men', type: 'lyrics'} * ] as const; * describe('Different rains', () => { - * parameterizeList(rainTests, (spec, name) => { + * parameterizeNamedList(rainTests, (spec, name, value) => { * spec(`of type ${name} exist`, () => { - * expect(name).toBeDefined(); + * expect(value.type).toBeDefined(); * }); * }, { * 'cats-and-dogs': fit, @@ -128,19 +128,23 @@ type ObjectFromList = { * }); * }); */ -export const parameterizeList = ( +export const parameterizeNamedList = ( list: T, - test: (spec: Spec, name: keyof ObjectFromList) => void, + test: ( + spec: Spec, + name: keyof ObjectFromNamedList, + value: T[number] + ) => void, specOverrides?: { - [P in keyof ObjectFromList]?: SpecOverride; + [P in keyof ObjectFromNamedList]?: SpecOverride; } ): void => { - const testCases = list.reduce<{ [key: string]: string }>( + const testCases = list.reduce<{ [key: string]: { name: string } }>( (result, entry) => { - result[entry] = entry; + result[entry.name] = entry; return result; }, {} - ) as ObjectFromList; - parameterize(testCases, test, specOverrides); + ) as ObjectFromNamedList; + parameterize>(testCases, test, specOverrides); }; diff --git a/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts index 5c1d33fca2..f51f3ff0cc 100644 --- a/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts +++ b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts @@ -1,4 +1,4 @@ -import { parameterize, parameterizeList } from '../parameterized'; +import { parameterize, parameterizeNamedList } from '../parameterized'; // eslint-disable-next-line no-restricted-globals const FIT = fit; @@ -151,12 +151,12 @@ const paramertizeListTestArgs = ([ name } as ParameterizeListTestArgs); -describe('Funtion parameterizeList', () => { +describe('Funtion parameterizeNamedList', () => { describe('can parameterize simple lists', () => { it('with test enabled', () => { - const testcases = ['case1'] as const; + const testcases = [{ name: 'case1' }] as const; const spy = jasmine.createSpy(); - parameterizeList(testcases, spy); + parameterizeNamedList(testcases, spy); expect(spy).toHaveBeenCalledTimes(1); const { spec, name } = paramertizeListTestArgs( @@ -167,9 +167,9 @@ describe('Funtion parameterizeList', () => { }); it('with test focused', () => { - const testcases = ['case1'] as const; + const testcases = [{ name: 'case1' }] as const; const spy = jasmine.createSpy(); - parameterizeList(testcases, spy, { + parameterizeNamedList(testcases, spy, { case1: FIT }); @@ -182,9 +182,9 @@ describe('Funtion parameterizeList', () => { }); it('with test disabled', () => { - const testcases = ['case1'] as const; + const testcases = [{ name: 'case1' }] as const; const spy = jasmine.createSpy(); - parameterizeList(testcases, spy, { + parameterizeNamedList(testcases, spy, { case1: XIT }); @@ -197,9 +197,13 @@ describe('Funtion parameterizeList', () => { }); it('with various test cases enabled and disabled', () => { - const testcases = ['case1', 'case2', 'case3']; + const testcases = [ + { name: 'case1' }, + { name: 'case2' }, + { name: 'case3' } + ] as const; const spy = jasmine.createSpy(); - parameterizeList(testcases, spy, { + parameterizeNamedList(testcases, spy, { case2: XIT, case3: FIT }); @@ -230,19 +234,19 @@ describe('Funtion parameterizeList', () => { }); describe('errors', () => { it('for override not in test cases', () => { - const testcases = ['case1'] as string[]; + const testcases = [{ name: 'case1' }] as { name: string }[]; expect(() => { - parameterizeList(testcases, () => {}, { + parameterizeNamedList(testcases, () => {}, { unknown: XIT }); }).toThrowError(/override names must match test case name/); }); it('for override not referencing supported xit or fit', () => { - const testcases = ['case1'] as const; + const testcases = [{ name: 'case1' }] as const; expect(() => { - parameterizeList(testcases, () => {}, { + parameterizeNamedList(testcases, () => {}, { case1: IT }); }).toThrowError(/jasmine spec functions: fit or xit/); From 9090d9db746492b82ec27956395503f051a772c2 Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Sep 2023 12:06:10 -0500 Subject: [PATCH 11/13] Comment on jasmine global aliases --- .../nimble-components/src/utilities/tests/parameterized.ts | 5 ++++- .../src/utilities/tests/tests/parametized.spec.ts | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/nimble-components/src/utilities/tests/parameterized.ts b/packages/nimble-components/src/utilities/tests/parameterized.ts index 87d30bc945..c452ade592 100644 --- a/packages/nimble-components/src/utilities/tests/parameterized.ts +++ b/packages/nimble-components/src/utilities/tests/parameterized.ts @@ -30,7 +30,10 @@ export const getSpecTypeByNamedList = ( (x: T) => focusList.includes(x.name), (x: T) => disabledList.includes(x.name) ); - +// The following aliases are just to reduce the number +// of eslint disables in this source file. In normal +// test code use the globals directly so eslint can +// guard accidental check-ins of fit, etc. // eslint-disable-next-line no-restricted-globals type Fit = typeof fit; type Xit = typeof xit; diff --git a/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts index f51f3ff0cc..de74974c7b 100644 --- a/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts +++ b/packages/nimble-components/src/utilities/tests/tests/parametized.spec.ts @@ -1,5 +1,9 @@ import { parameterize, parameterizeNamedList } from '../parameterized'; +// The following aliases are just to reduce the number +// of eslint disables in this test file. In normal +// test code use the globals directly so eslint can +// guard accidental check-ins of fit, etc. // eslint-disable-next-line no-restricted-globals const FIT = fit; const IT = it; From d9aef999141291594b0c01d6a10e2c386adbbd1a Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Sep 2023 12:22:42 -0500 Subject: [PATCH 12/13] Use paramterizeNamedList on more complex test --- .../src/anchor-tabs/tests/anchor-tabs.spec.ts | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts b/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts index 1bfb03c2fc..2cb495a87f 100644 --- a/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts +++ b/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts @@ -13,7 +13,7 @@ import '../../anchor-tab'; import type { AnchorTab } from '../../anchor-tab'; import { waitForUpdatesAsync } from '../../testing/async-helpers'; import { fixture, Fixture } from '../../utilities/tests/fixture'; -import { getSpecTypeByNamedList } from '../../utilities/tests/parameterized'; +import { parameterizeNamedList } from '../../utilities/tests/parameterized'; describe('AnchorTabs', () => { let element: AnchorTabs; @@ -182,46 +182,51 @@ describe('AnchorTabs', () => { await disconnect(); }); - const navigationTests: { - name: string, - disabledIndex?: number, - hiddenIndex?: number, - initialFocusIndex: number, - keyName: string, - expectedFinalFocusIndex: number - }[] = [ + const navigationTests = [ { name: 'should focus next tab when right arrow key pressed', + disabledIndex: undefined, + hiddenIndex: undefined, initialFocusIndex: 0, keyName: keyArrowRight, expectedFinalFocusIndex: 1 }, { name: 'should focus previous tab when left arrow key pressed', + disabledIndex: undefined, + hiddenIndex: undefined, initialFocusIndex: 1, keyName: keyArrowLeft, expectedFinalFocusIndex: 0 }, { name: 'should wrap to first tab when arrowing right on last tab', + disabledIndex: undefined, + hiddenIndex: undefined, initialFocusIndex: 2, keyName: keyArrowRight, expectedFinalFocusIndex: 0 }, { name: 'should wrap to last tab when arrowing left on first tab', + disabledIndex: undefined, + hiddenIndex: undefined, initialFocusIndex: 0, keyName: keyArrowLeft, expectedFinalFocusIndex: 2 }, { name: 'should focus first tab when Home key pressed', + disabledIndex: undefined, + hiddenIndex: undefined, initialFocusIndex: 1, keyName: keyHome, expectedFinalFocusIndex: 0 }, { name: 'should focus last tab when End key pressed', + disabledIndex: undefined, + hiddenIndex: undefined, initialFocusIndex: 1, keyName: keyEnd, expectedFinalFocusIndex: 2 @@ -229,6 +234,7 @@ describe('AnchorTabs', () => { { name: 'should skip disabled tab when arrowing right', disabledIndex: 1, + hiddenIndex: undefined, initialFocusIndex: 0, keyName: keyArrowRight, expectedFinalFocusIndex: 2 @@ -236,6 +242,7 @@ describe('AnchorTabs', () => { { name: 'should skip disabled tab when arrowing left', disabledIndex: 1, + hiddenIndex: undefined, initialFocusIndex: 2, keyName: keyArrowLeft, expectedFinalFocusIndex: 0 @@ -243,6 +250,7 @@ describe('AnchorTabs', () => { { name: 'should skip disabled when arrowing right on last tab', disabledIndex: 0, + hiddenIndex: undefined, initialFocusIndex: 2, keyName: keyArrowRight, expectedFinalFocusIndex: 1 @@ -250,6 +258,7 @@ describe('AnchorTabs', () => { { name: 'should skip disabled when arrowing left on first tab', disabledIndex: 2, + hiddenIndex: undefined, initialFocusIndex: 0, keyName: keyArrowLeft, expectedFinalFocusIndex: 1 @@ -257,6 +266,7 @@ describe('AnchorTabs', () => { { name: 'should focus first enabled tab when Home key pressed', disabledIndex: 0, + hiddenIndex: undefined, initialFocusIndex: 2, keyName: keyHome, expectedFinalFocusIndex: 1 @@ -264,12 +274,14 @@ describe('AnchorTabs', () => { { name: 'should focus last enabled tab when End key pressed', disabledIndex: 2, + hiddenIndex: undefined, initialFocusIndex: 0, keyName: keyEnd, expectedFinalFocusIndex: 1 }, { name: 'should skip hidden tab when arrowing right', + disabledIndex: undefined, hiddenIndex: 1, initialFocusIndex: 0, keyName: keyArrowRight, @@ -277,6 +289,7 @@ describe('AnchorTabs', () => { }, { name: 'should skip hidden tab when arrowing left', + disabledIndex: undefined, hiddenIndex: 1, initialFocusIndex: 2, keyName: keyArrowLeft, @@ -284,6 +297,7 @@ describe('AnchorTabs', () => { }, { name: 'should skip hidden when arrowing right on last tab', + disabledIndex: undefined, hiddenIndex: 0, initialFocusIndex: 2, keyName: keyArrowRight, @@ -291,6 +305,7 @@ describe('AnchorTabs', () => { }, { name: 'should skip hidden when arrowing left on first tab', + disabledIndex: undefined, hiddenIndex: 2, initialFocusIndex: 0, keyName: keyArrowLeft, @@ -298,6 +313,7 @@ describe('AnchorTabs', () => { }, { name: 'should focus first visible tab when Home key pressed', + disabledIndex: undefined, hiddenIndex: 0, initialFocusIndex: 2, keyName: keyHome, @@ -305,45 +321,38 @@ describe('AnchorTabs', () => { }, { name: 'should focus last visible tab when End key pressed', + disabledIndex: undefined, hiddenIndex: 2, initialFocusIndex: 0, keyName: keyEnd, expectedFinalFocusIndex: 1 } - ]; + ] as const; describe('navigation', () => { - const focused: string[] = []; - const disabled: string[] = []; - for (const test of navigationTests) { - const specType = getSpecTypeByNamedList( - test, - focused, - disabled - ); - // eslint-disable-next-line @typescript-eslint/no-loop-func - specType(test.name, async () => { + parameterizeNamedList(navigationTests, (spec, name, value) => { + spec(name, async () => { await connect(); - if (test.disabledIndex !== undefined) { - tab(test.disabledIndex).disabled = true; + if (value.disabledIndex !== undefined) { + tab(value.disabledIndex).disabled = true; await waitForUpdatesAsync(); } - if (test.hiddenIndex !== undefined) { - tab(test.hiddenIndex).hidden = true; + if (value.hiddenIndex !== undefined) { + tab(value.hiddenIndex).hidden = true; await waitForUpdatesAsync(); } - tab(test.initialFocusIndex).focus(); - tab(test.initialFocusIndex).dispatchEvent( - new KeyboardEvent('keydown', { key: test.keyName }) + tab(value.initialFocusIndex).focus(); + tab(value.initialFocusIndex).dispatchEvent( + new KeyboardEvent('keydown', { key: value.keyName }) ); await waitForUpdatesAsync(); expect(document.activeElement).toBe( - tab(test.expectedFinalFocusIndex) + tab(value.expectedFinalFocusIndex) ); - expect(tab(test.expectedFinalFocusIndex).ariaSelected).toBe( + expect(tab(value.expectedFinalFocusIndex).ariaSelected).toBe( 'true' ); }); - } + }); }); it('should skip past other tabs when pressing tab key after click', async () => { From 39b1a96a46738516849ca6e5ca49759b37094b58 Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Sep 2023 12:28:58 -0500 Subject: [PATCH 13/13] lint --- .../src/anchor-tabs/tests/anchor-tabs.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts b/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts index 2cb495a87f..62fadfc126 100644 --- a/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts +++ b/packages/nimble-components/src/anchor-tabs/tests/anchor-tabs.spec.ts @@ -348,9 +348,9 @@ describe('AnchorTabs', () => { expect(document.activeElement).toBe( tab(value.expectedFinalFocusIndex) ); - expect(tab(value.expectedFinalFocusIndex).ariaSelected).toBe( - 'true' - ); + expect( + tab(value.expectedFinalFocusIndex).ariaSelected + ).toBe('true'); }); }); });