Skip to content

Commit

Permalink
Add parameterize suite (#2037)
Browse files Browse the repository at this point in the history
# Pull Request

## 🤨 Rationale

Create `parameterizeSuite` function within `jasmine-parameterize`
package.

Resolves #1827 

## 👩‍💻 Implementation

- Refactored the existing internal `parameterize` function within
`jasmine-parameterize` so that it can be called for specs and suites
- Create `parameterizeSuite` function within `jasmine-parameterize`
package
- Leverage the new `parameterizeSuite` function in one place within
`nimble-components`

## 🧪 Testing

- Extended the testing of `parameterize` to cover both spec and suite
usages
- Copied the `parameterizeSpec` tests and adapted for testing
`parameterizeSuite`
- Made sure the `parameterizeSuite` function worked as expected by using
it within the `nimble-select` tests

## ✅ Checklist

<!--- Review the list and put an x in the boxes that apply or ~~strike
through~~ around items that don't (along with an explanation). -->

- [ ] I have updated the project documentation to reflect my changes or
determined no changes are needed.

---------

Co-authored-by: Milan Raj <rajsite@users.noreply.github.com>
  • Loading branch information
mollykreis and rajsite authored Apr 26, 2024
1 parent 1412ad5 commit 116f0bc
Show file tree
Hide file tree
Showing 15 changed files with 662 additions and 251 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Create `parameterizeSuite` function",
"packageName": "@ni/jasmine-parameterized",
"email": "20542556+mollykreis@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Update tests to use `parameterizeSuite`",
"packageName": "@ni/nimble-components",
"email": "20542556+mollykreis@users.noreply.github.com",
"dependentChangeType": "none"
}
34 changes: 34 additions & 0 deletions packages/jasmine-parameterized/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,37 @@ describe('Different rains', () => {
});
});
```

### `parameterizeSuite`

Use `parameterizeSuite` to create a parameterized test suite using an array of test scenarios with names.

In the following example:

- the suite named `cats-and-dogs` is focused for debugging
- the suite named `frogs` is configured to always be disabled
- the suite named `men` will run normally as it has no override

```ts
import { parameterizeSuite } from '@ni/jasmine-parameterized';
const rainTests = [
{ name: 'cats-and-dogs', type: 'idiom' },
{ name: 'frogs' type: 'idiom'},
{ name: 'men', type: 'lyrics'}
] as const;
parameterizeSuite(rainTests, (suite, name, value) => {
suite(`with ${name}`, () => {
it('expect type to be defined', () => {
expect(value.type).toBeDefined();
});

it('expect type to have a non-zero length', () => {
const length = value.type.length;
expect(length).toBeGreaterThan(0);
});
});
}, {
'cats-and-dogs': fdescribe,
frogs: xdescribe
});
```
3 changes: 2 additions & 1 deletion packages/jasmine-parameterized/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { parameterizeSpec } from './parameterized.js';
export { parameterizeSpec } from './parameterize-spec.js';
export { parameterizeSuite } from './parameterize-suite.js';
51 changes: 51 additions & 0 deletions packages/jasmine-parameterized/src/parameterize-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { parameterize } from './parameterize.js';
import { ObjectFromNamedList, Spec, SpecOverride } from './types.js';

/**
* 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 = [
* { name: 'cats-and-dogs', type: 'idiom' },
* { name: 'frogs' type: 'idiom'},
* { name: 'men', type: 'lyrics'}
* ] as const;
* describe('Different rains', () => {
* parameterizeSpec(rainTests, (spec, name, value) => {
* spec(`of type ${name} exist`, () => {
* expect(value.type).toBeDefined();
* });
* }, {
* 'cats-and-dogs': fit,
* frogs: xit
* });
* });
*/
export const parameterizeSpec = <T extends readonly { name: string }[]>(
list: T,
test: (
spec: Spec,
name: keyof ObjectFromNamedList<T>,
value: T[number]
) => void,
specOverrides?: {
[P in keyof ObjectFromNamedList<T>]?: SpecOverride;
}
): void => {
const testCases = list.reduce<{ [key: string]: { name: string } }>(
(result, entry) => {
if (result[entry.name]) {
throw new Error(
`Duplicate name found in test case list: ${entry.name}. Make sure all test names are unique.`
);
}
result[entry.name] = entry;
return result;
},
{}
) as ObjectFromNamedList<T>;
parameterize<ObjectFromNamedList<T>>('spec', testCases, test, specOverrides);
};
56 changes: 56 additions & 0 deletions packages/jasmine-parameterized/src/parameterize-suite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { parameterize } from './parameterize.js';
import { ObjectFromNamedList, Suite, SuiteOverride } from './types.js';

/**
* Used to create a parameterized suite using an array of test scenarios with names.
* In the following example:
* - the suite named `cats-and-dogs` is focused for debugging
* - the suite named `frogs` is configured to always be disabled
* - the suite named `men` will run normally as it has no override
* @example
* const rainTests = [
* { name: 'cats-and-dogs', type: 'idiom' },
* { name: 'frogs' type: 'idiom'},
* { name: 'men', type: 'lyrics'}
* ] as const;
* parameterizeSuite(rainTests, (suite, name, value) => {
* suite(`with ${name}`, () => {
* it('expect type to be defined', () => {
* expect(value.type).toBeDefined();
* });
*
* it('expect type to have a non-zero length', () => {
* const length = value.type.length;
* expect(length).toBeGreaterThan(0);
* });
* });
* }, {
* 'cats-and-dogs': fdescribe,
* frogs: xdescribe
* });
*/
export const parameterizeSuite = <T extends readonly { name: string }[]>(
list: T,
test: (
suite: Suite,
name: keyof ObjectFromNamedList<T>,
value: T[number]
) => void,
specOverrides?: {
[P in keyof ObjectFromNamedList<T>]?: SuiteOverride;
}
): void => {
const testCases = list.reduce<{ [key: string]: { name: string } }>(
(result, entry) => {
if (result[entry.name]) {
throw new Error(
`Duplicate name found in test suite list: ${entry.name}. Make sure all test suite names are unique.`
);
}
result[entry.name] = entry;
return result;
},
{}
) as ObjectFromNamedList<T>;
parameterize<ObjectFromNamedList<T>>('suite', testCases, test, specOverrides);
};
84 changes: 84 additions & 0 deletions packages/jasmine-parameterized/src/parameterize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Spec, SpecOverride, Suite, SuiteOverride } from './types.js';

/**
* Used to create a parameterized test or suite using an object of 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
* - the test named `men` will run normally as it has no override
* @example
* const rainTests = {
* catsAndDogs: 'idiom',
* frogs: 'idiom',
* men: 'lyrics'
* } as const;
* describe('Different rains', () => {
* parameterize('spec', rainTests, (spec, name, value) => {
* spec(`of type ${name} exist`, () => {
* expect(value).toBeDefined();
* });
* }, {
* catsAndDogs: fit,
* frogs: xit
* });
* });
*/
export function parameterize<T extends object>(
testType: 'spec',
testCases: T,
test: (spec: Spec, name: keyof T, value: T[keyof T]) => void,
overrides?: {
[P in keyof T]?: SpecOverride;
}
): void;
export function parameterize<T extends object>(
testType: 'suite',
testCases: T,
test: (spec: Suite, name: keyof T, value: T[keyof T]) => void,
overrides?: {
[P in keyof T]?: SuiteOverride;
}
): void;
export function parameterize<T extends object, U = Spec | Suite>(
testType: 'spec' | 'suite',
testCases: T,
test: (spec: U, name: keyof T, value: T[keyof T]) => void,
overrides?: {
[P in keyof T]?: U extends Spec ? SpecOverride : SuiteOverride;
}
): void {
const testCaseNames = Object.keys(testCases) as (keyof T)[];
if (overrides) {
const overrideNames = Object.keys(
overrides
) as (keyof typeof overrides)[];
if (
!overrideNames.every(overrideName => testCaseNames.includes(overrideName))
) {
throw new Error(
'Parameterized test override names must match test case name'
);
}
if (
testType === 'spec'
// eslint-disable-next-line no-restricted-globals
&& !overrideNames.every(overrideName => [fit, xit].includes(overrides[overrideName] as Spec))
) {
throw new Error('Must configure override with one of the jasmine spec functions: fit or xit');
}
if (
testType === 'suite'
// eslint-disable-next-line no-restricted-globals
&& !overrideNames.every(overrideName => [fdescribe, xdescribe].includes(overrides[overrideName] as Suite))
) {
throw new Error(
'Must configure override with one of the jasmine suite functions: fdescribe or xdescribe'
);
}
}
testCaseNames.forEach(testCaseName => {
const defaultTest = testType === 'spec' ? it : describe;
const spec = overrides?.[testCaseName] ?? defaultTest;
test(spec as U, testCaseName, testCases[testCaseName]);
});
}
126 changes: 0 additions & 126 deletions packages/jasmine-parameterized/src/parameterized.ts

This file was deleted.

Loading

0 comments on commit 116f0bc

Please sign in to comment.