@ni/jasmine-parameterized 0.2.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.
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Jasmine Parameterized
2
+
3
+ The `@ni/jasmine-parameterized` library provides utility functions for writing [Jasmine](https://jasmine.github.io/) parameterized tests.
4
+
5
+ # Usage
6
+
7
+ 1. Install in your app's `devDependencies`:
8
+
9
+ ```
10
+ npm install -D @ni/jasmine-parameterized
11
+ ```
12
+
13
+ 2. Use `parameterizeSpec` to write strictly-typed jasmine tests that run for different test scenarios, where each test can be focused or excluded as needed by name.
14
+
15
+ ### `parameterizeSpec`
16
+ Use `parameterizeSpec` to create a parameterized test using an array of tests with names.
17
+
18
+ In the following example:
19
+ - the test named `cats-and-dogs` is focused for debugging
20
+ - the test named `frogs` is configured to always be disabled
21
+ - the test named `men` will run normally as it has no override
22
+
23
+ ```ts
24
+ import { parameterizeSpec } from '@ni/jasmine-parameterized';
25
+ const rainTests = [
26
+ { name: 'cats-and-dogs', type: 'idiom' },
27
+ { name: 'frogs' type: 'idiom'},
28
+ { name: 'men', type: 'lyrics'}
29
+ ] as const;
30
+ describe('Different rains', () => {
31
+ parameterizeSpec(rainTests, (spec, name, value) => {
32
+ spec(`of type ${name} exist`, () => {
33
+ expect(value.type).toBeDefined();
34
+ });
35
+ }, {
36
+ 'cats-and-dogs': fit,
37
+ frogs: xit
38
+ });
39
+ });
40
+ ```
@@ -0,0 +1 @@
1
+ export { parameterizeSpec } from './parameterized.js';
@@ -0,0 +1,2 @@
1
+ export { parameterizeSpec } from './parameterized.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export { parameterizeSpec } from './parameterized.js';\n"]}
@@ -0,0 +1,72 @@
1
+ /// <reference types="jasmine" />
2
+ declare type Fit = typeof fit;
3
+ declare type Xit = typeof xit;
4
+ declare type It = typeof it;
5
+ /**
6
+ * One of the jasmine spec functions: fit, xit, or it
7
+ */
8
+ declare type Spec = Fit | Xit | It;
9
+ /**
10
+ * One of the jasmine spec functions: fit or xit
11
+ */
12
+ declare type SpecOverride = Fit | Xit;
13
+ /**
14
+ * Used to create a parameterized test using an object of test names and arbitrary test values.
15
+ * In the following example:
16
+ * - the test named `catsAndDogs` is focused for debugging
17
+ * - the test named `frogs` is configured to always be disabled
18
+ * - the test named `men` will run normally as it has no override
19
+ * @example
20
+ * const rainTests = {
21
+ * catsAndDogs: 'idiom',
22
+ * frogs: 'idiom',
23
+ * men: 'lyrics'
24
+ * } as const;
25
+ * describe('Different rains', () => {
26
+ * parameterize(rainTests, (spec, name, value) => {
27
+ * spec(`of type ${name} exist`, () => {
28
+ * expect(value).toBeDefined();
29
+ * });
30
+ * }, {
31
+ * catsAndDogs: fit,
32
+ * frogs: xit
33
+ * });
34
+ * });
35
+ */
36
+ export declare const parameterize: <T extends object>(testCases: T, test: (spec: Spec, name: keyof T, value: T[keyof T]) => void, specOverrides?: { [P in keyof T]?: SpecOverride | undefined; } | undefined) => void;
37
+ declare type ObjectFromNamedList<T extends readonly {
38
+ name: string;
39
+ }[]> = {
40
+ [K in T extends readonly {
41
+ name: infer U;
42
+ }[] ? U : never]: T[number];
43
+ };
44
+ /**
45
+ * Used to create a parameterized test using an array of tests with names.
46
+ * In the following example:
47
+ * - the test named `cats-and-dogs` is focused for debugging
48
+ * - the test named `frogs` is configured to always be disabled
49
+ * - the test named `men` will run normally as it has no override
50
+ * @example
51
+ * const rainTests = [
52
+ * { name: 'cats-and-dogs', type: 'idiom' },
53
+ * { name: 'frogs' type: 'idiom'},
54
+ * { name: 'men', type: 'lyrics'}
55
+ * ] as const;
56
+ * describe('Different rains', () => {
57
+ * parameterizeSpec(rainTests, (spec, name, value) => {
58
+ * spec(`of type ${name} exist`, () => {
59
+ * expect(value.type).toBeDefined();
60
+ * });
61
+ * }, {
62
+ * 'cats-and-dogs': fit,
63
+ * frogs: xit
64
+ * });
65
+ * });
66
+ */
67
+ export declare const parameterizeSpec: <T extends readonly {
68
+ name: string;
69
+ }[]>(list: T, test: (spec: Spec, name: T extends readonly {
70
+ name: infer U;
71
+ }[] ? U : never, value: T[number]) => void, specOverrides?: (ObjectFromNamedList<T> extends infer T_1 ? { [P in keyof T_1]?: SpecOverride | undefined; } : never) | undefined) => void;
72
+ export {};
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Used to create a parameterized test using an object of test names and arbitrary test values.
3
+ * In the following example:
4
+ * - the test named `catsAndDogs` is focused for debugging
5
+ * - the test named `frogs` is configured to always be disabled
6
+ * - the test named `men` will run normally as it has no override
7
+ * @example
8
+ * const rainTests = {
9
+ * catsAndDogs: 'idiom',
10
+ * frogs: 'idiom',
11
+ * men: 'lyrics'
12
+ * } as const;
13
+ * describe('Different rains', () => {
14
+ * parameterize(rainTests, (spec, name, value) => {
15
+ * spec(`of type ${name} exist`, () => {
16
+ * expect(value).toBeDefined();
17
+ * });
18
+ * }, {
19
+ * catsAndDogs: fit,
20
+ * frogs: xit
21
+ * });
22
+ * });
23
+ */
24
+ export const parameterize = (testCases, test, specOverrides) => {
25
+ const testCaseNames = Object.keys(testCases);
26
+ if (specOverrides) {
27
+ const overrideNames = Object.keys(specOverrides);
28
+ if (!overrideNames.every(overrideName => testCaseNames.includes(overrideName))) {
29
+ throw new Error('Parameterized test override names must match test case name');
30
+ }
31
+ if (
32
+ // eslint-disable-next-line no-restricted-globals
33
+ !overrideNames.every(overrideName => [fit, xit].includes(specOverrides[overrideName]))) {
34
+ throw new Error('Must configure override with one of the jasmine spec functions: fit or xit');
35
+ }
36
+ }
37
+ testCaseNames.forEach(testCaseName => {
38
+ const spec = specOverrides?.[testCaseName] ?? it;
39
+ test(spec, testCaseName, testCases[testCaseName]);
40
+ });
41
+ };
42
+ /**
43
+ * Used to create a parameterized test using an array of tests with names.
44
+ * In the following example:
45
+ * - the test named `cats-and-dogs` is focused for debugging
46
+ * - the test named `frogs` is configured to always be disabled
47
+ * - the test named `men` will run normally as it has no override
48
+ * @example
49
+ * const rainTests = [
50
+ * { name: 'cats-and-dogs', type: 'idiom' },
51
+ * { name: 'frogs' type: 'idiom'},
52
+ * { name: 'men', type: 'lyrics'}
53
+ * ] as const;
54
+ * describe('Different rains', () => {
55
+ * parameterizeSpec(rainTests, (spec, name, value) => {
56
+ * spec(`of type ${name} exist`, () => {
57
+ * expect(value.type).toBeDefined();
58
+ * });
59
+ * }, {
60
+ * 'cats-and-dogs': fit,
61
+ * frogs: xit
62
+ * });
63
+ * });
64
+ */
65
+ export const parameterizeSpec = (list, test, specOverrides) => {
66
+ const testCases = list.reduce((result, entry) => {
67
+ if (result[entry.name]) {
68
+ throw new Error(`Duplicate name found in test case list: ${entry.name}. Make sure all test names are unique.`);
69
+ }
70
+ result[entry.name] = entry;
71
+ return result;
72
+ }, {});
73
+ parameterize(testCases, test, specOverrides);
74
+ };
75
+ //# sourceMappingURL=parameterized.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parameterized.js","sourceRoot":"","sources":["../../src/parameterized.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CACxB,SAAY,EACZ,IAA4D,EAC5D,aAEC,EACG,EAAE;IACN,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAgB,CAAC;IAC5D,IAAI,aAAa,EAAE;QACf,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAC7B,aAAa,CACkB,CAAC;QACpC,IACI,CAAC,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,EAC5E;YACE,MAAM,IAAI,KAAK,CACX,6DAA6D,CAChE,CAAC;SACL;QACD;QACI,iDAAiD;QACjD,CAAC,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAE,CAAC,CAAC,EACzF;YACE,MAAM,IAAI,KAAK,CACX,4EAA4E,CAC/E,CAAC;SACL;KACJ;IACD,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;QACjC,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC5B,IAAO,EACP,IAIS,EACT,aAEC,EACG,EAAE;IACN,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CACzB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QACd,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;YACpB,MAAM,IAAI,KAAK,CACX,2CAA2C,KAAK,CAAC,IAAI,wCAAwC,CAChG,CAAC;SACL;QACD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QAC3B,OAAO,MAAM,CAAC;IAClB,CAAC,EACD,EAAE,CACqB,CAAC;IAC5B,YAAY,CAAyB,SAAS,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;AACzE,CAAC,CAAC","sourcesContent":["// The following aliases are just to reduce the number\n// of eslint disables in this source file. In normal\n// test code use the globals directly so eslint can\n// guard accidental check-ins of fit, etc.\n// eslint-disable-next-line no-restricted-globals\ntype Fit = typeof fit;\ntype Xit = typeof xit;\ntype It = typeof it;\n/**\n * One of the jasmine spec functions: fit, xit, or it\n */\ntype Spec = Fit | Xit | It;\n/**\n * One of the jasmine spec functions: fit or xit\n */\ntype SpecOverride = Fit | Xit;\n\n/**\n * Used to create a parameterized test using an object of test names and arbitrary test values.\n * In the following example:\n * - the test named `catsAndDogs` is focused for debugging\n * - the test named `frogs` is configured to always be disabled\n * - the test named `men` will run normally as it has no override\n * @example\n * const rainTests = {\n * catsAndDogs: 'idiom',\n * frogs: 'idiom',\n * men: 'lyrics'\n * } as const;\n * describe('Different rains', () => {\n * parameterize(rainTests, (spec, name, value) => {\n * spec(`of type ${name} exist`, () => {\n * expect(value).toBeDefined();\n * });\n * }, {\n * catsAndDogs: fit,\n * frogs: xit\n * });\n * });\n */\nexport const parameterize = <T extends object>(\n testCases: T,\n test: (spec: Spec, name: keyof T, value: T[keyof T]) => void,\n specOverrides?: {\n [P in keyof T]?: SpecOverride;\n }\n): void => {\n const testCaseNames = Object.keys(testCases) as (keyof T)[];\n if (specOverrides) {\n const overrideNames = Object.keys(\n specOverrides\n ) as (keyof typeof specOverrides)[];\n if (\n !overrideNames.every(overrideName => testCaseNames.includes(overrideName))\n ) {\n throw new Error(\n 'Parameterized test override names must match test case name'\n );\n }\n if (\n // eslint-disable-next-line no-restricted-globals\n !overrideNames.every(overrideName => [fit, xit].includes(specOverrides[overrideName]!))\n ) {\n throw new Error(\n 'Must configure override with one of the jasmine spec functions: fit or xit'\n );\n }\n }\n testCaseNames.forEach(testCaseName => {\n const spec = specOverrides?.[testCaseName] ?? it;\n test(spec, testCaseName, testCases[testCaseName]);\n });\n};\n\ntype ObjectFromNamedList<T extends readonly { name: string }[]> = {\n [K in T extends readonly { name: infer U }[] ? U : never]: T[number];\n};\n\n/**\n * Used to create a parameterized test using an array of tests with names.\n * In the following example:\n * - the test named `cats-and-dogs` is focused for debugging\n * - the test named `frogs` is configured to always be disabled\n * - the test named `men` will run normally as it has no override\n * @example\n * const rainTests = [\n * { name: 'cats-and-dogs', type: 'idiom' },\n * { name: 'frogs' type: 'idiom'},\n * { name: 'men', type: 'lyrics'}\n * ] as const;\n * describe('Different rains', () => {\n * parameterizeSpec(rainTests, (spec, name, value) => {\n * spec(`of type ${name} exist`, () => {\n * expect(value.type).toBeDefined();\n * });\n * }, {\n * 'cats-and-dogs': fit,\n * frogs: xit\n * });\n * });\n */\nexport const parameterizeSpec = <T extends readonly { name: string }[]>(\n list: T,\n test: (\n spec: Spec,\n name: keyof ObjectFromNamedList<T>,\n value: T[number]\n ) => void,\n specOverrides?: {\n [P in keyof ObjectFromNamedList<T>]?: SpecOverride;\n }\n): void => {\n const testCases = list.reduce<{ [key: string]: { name: string } }>(\n (result, entry) => {\n if (result[entry.name]) {\n throw new Error(\n `Duplicate name found in test case list: ${entry.name}. Make sure all test names are unique.`\n );\n }\n result[entry.name] = entry;\n return result;\n },\n {}\n ) as ObjectFromNamedList<T>;\n parameterize<ObjectFromNamedList<T>>(testCases, test, specOverrides);\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@ni/jasmine-parameterized",
3
+ "version": "0.2.0",
4
+ "description": "A utility to write parameterized jasmine tests",
5
+ "scripts": {
6
+ "build": "tsc -b",
7
+ "lint": "eslint .",
8
+ "format": "eslint . --fix",
9
+ "pack": "npm pack",
10
+ "invoke-publish": "npm publish",
11
+ "tdd": "npm run build && npm run test",
12
+ "test": "npm run test:node && npm run test:browser",
13
+ "test:node": "jasmine",
14
+ "test:browser": "karma start karma.conf.cjs --browsers=ChromeHeadlessOpt --single-run",
15
+ "test:browser:debugger": "karma start karma.conf.cjs --browsers=ChromeDebugging"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/ni/nimble.git",
20
+ "directory": "packages/jasmine-parameterized"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "import": {
29
+ "types": "./dist/esm/index.d.ts",
30
+ "default": "./dist/esm/index.js"
31
+ }
32
+ }
33
+ },
34
+ "types": "./dist/esm/index.d.ts",
35
+ "author": "National Instruments",
36
+ "license": "MIT",
37
+ "devDependencies": {
38
+ "@ni/eslint-config-javascript": "^4.2.0",
39
+ "@ni/eslint-config-typescript": "^4.2.0",
40
+ "@types/jasmine": "^4.3.1",
41
+ "jasmine": "^4.2.0",
42
+ "jasmine-core": "^4.5.0",
43
+ "karma": "^6.3.0",
44
+ "karma-jasmine": "^5.1.0",
45
+ "karma-jasmine-html-reporter": "^2.0.0",
46
+ "karma-spec-reporter": "^0.0.36",
47
+ "playwright": "^1.30.0",
48
+ "typescript": "~4.8.2"
49
+ }
50
+ }