qunitx 1.1.5 → 1.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.
@@ -1,154 +1,115 @@
1
1
  const hasOwn = Object.prototype.hasOwnProperty;
2
- export function objectType(obj) {
3
- if (typeof obj === 'undefined') {
4
- return 'undefined';
5
- }
6
- // Consider: typeof null === object
7
- if (obj === null) {
8
- return 'null';
9
- }
10
- // slice(8, -1) extracts the type name from "[object Foo]" without a regex
11
- const type = Object.prototype.toString.call(obj).slice(8, -1);
12
- switch (type) {
13
- case 'Number':
14
- if (isNaN(obj)) {
15
- return 'nan';
16
- }
17
- return 'number';
18
- case 'String':
19
- case 'Boolean':
20
- case 'Array':
21
- case 'Set':
22
- case 'Map':
23
- case 'Date':
24
- case 'RegExp':
25
- case 'Function':
26
- case 'Symbol':
27
- return type.toLowerCase();
28
- default:
29
- return typeof obj;
30
- }
2
+ function objectType(obj) {
3
+ if (typeof obj === "undefined") {
4
+ return "undefined";
5
+ }
6
+ if (obj === null) {
7
+ return "null";
8
+ }
9
+ const type = Object.prototype.toString.call(obj).slice(8, -1);
10
+ switch (type) {
11
+ case "Number":
12
+ if (isNaN(obj)) {
13
+ return "nan";
14
+ }
15
+ return "number";
16
+ case "String":
17
+ case "Boolean":
18
+ case "Array":
19
+ case "Set":
20
+ case "Map":
21
+ case "Date":
22
+ case "RegExp":
23
+ case "Function":
24
+ case "Symbol":
25
+ return type.toLowerCase();
26
+ default:
27
+ return typeof obj;
28
+ }
31
29
  }
32
30
  function is(type, obj) {
33
- return objectType(obj) === type;
31
+ return objectType(obj) === type;
34
32
  }
35
- export function objectValues(obj, allowArray = true) {
36
- const vals = allowArray && is('array', obj) ? [] : {};
37
- for (const key in obj) {
38
- if (hasOwn.call(obj, key)) {
39
- const val = obj[key];
40
- vals[key] = val === Object(val) ? objectValues(val, allowArray) : val;
41
- }
33
+ function objectValues(obj, allowArray = true) {
34
+ const vals = allowArray && is("array", obj) ? [] : {};
35
+ for (const key in obj) {
36
+ if (hasOwn.call(obj, key)) {
37
+ const val = obj[key];
38
+ vals[key] = val === Object(val) ? objectValues(val, allowArray) : val;
42
39
  }
43
- return vals;
40
+ }
41
+ return vals;
44
42
  }
45
- /**
46
- *
47
- * Recursively clone an object into a plain object, taking only the
48
- * subset of own enumerable properties that exist a given model.
49
- *
50
- * @param {any} obj
51
- * @param {any} model
52
- * @return {Object}
53
- */
54
- export function objectValuesSubset(obj, model) {
55
- // Return primitive values unchanged to avoid false positives or confusing
56
- // results from assert.propContains().
57
- // E.g. an actual null or false wrongly equaling an empty object,
58
- // or an actual string being reported as object not matching a partial object.
59
- if (obj !== Object(obj)) {
60
- return obj;
43
+ function objectValuesSubset(obj, model) {
44
+ if (obj !== Object(obj)) {
45
+ return obj;
46
+ }
47
+ const subset = {};
48
+ for (const key in model) {
49
+ if (hasOwn.call(model, key) && hasOwn.call(obj, key)) {
50
+ subset[key] = objectValuesSubset(
51
+ obj[key],
52
+ model[key]
53
+ );
61
54
  }
62
- // Unlike objectValues(), subset arrays to a plain objects as well.
63
- // This enables subsetting [20, 30] with {1: 30}.
64
- const subset = {};
65
- for (const key in model) {
66
- if (hasOwn.call(model, key) && hasOwn.call(obj, key)) {
67
- subset[key] = objectValuesSubset(obj[key], model[key]);
68
- }
69
- }
70
- return subset;
55
+ }
56
+ return subset;
71
57
  }
72
- export function validateExpectedExceptionArgs(expected, message, assertionMethod) {
73
- const expectedType = objectType(expected);
74
- // 'expected' is optional unless doing string comparison
75
- if (expectedType === 'string') {
76
- if (message === undefined) {
77
- message = expected;
78
- expected = undefined;
79
- return [expected, message];
80
- }
81
- else {
82
- throw new Error('assert.' + assertionMethod + ' does not accept a string value for the expected argument.\n' + 'Use a non-string object value (e.g. RegExp or validator function) ' + 'instead if necessary.');
83
- }
84
- }
85
- const valid = !expected ||
86
- // TODO: be more explicit here
87
- expectedType === 'regexp' || expectedType === 'function' || expectedType === 'object';
88
- if (!valid) {
89
- throw new Error('Invalid expected value type (' + expectedType + ') ' + 'provided to assert.' + assertionMethod + '.');
58
+ function validateExpectedExceptionArgs(expected, message, assertionMethod) {
59
+ const expectedType = objectType(expected);
60
+ if (expectedType === "string") {
61
+ if (message === void 0) {
62
+ message = expected;
63
+ expected = void 0;
64
+ return [expected, message];
65
+ } else {
66
+ throw new Error("assert." + assertionMethod + " does not accept a string value for the expected argument.\nUse a non-string object value (e.g. RegExp or validator function) instead if necessary.");
90
67
  }
91
- return [expected, message];
68
+ }
69
+ const valid = !expected || // TODO: be more explicit here
70
+ expectedType === "regexp" || expectedType === "function" || expectedType === "object";
71
+ if (!valid) {
72
+ throw new Error("Invalid expected value type (" + expectedType + ") provided to assert." + assertionMethod + ".");
73
+ }
74
+ return [expected, message];
92
75
  }
93
- export function validateException(actual, expected, message) {
94
- let result = false;
95
- const expectedType = objectType(expected);
96
- // These branches should be exhaustive, based on validation done in validateExpectedException
97
- // We don't want to validate
98
- if (!expected) {
99
- result = true;
100
- // Expected is a regexp
101
- }
102
- else if (expectedType === 'regexp') {
103
- result = expected.test(errorString(actual));
104
- // Log the string form of the regexp
105
- expected = String(expected);
106
- // Expected is a constructor, maybe an Error constructor.
107
- // Note the extra check on its prototype - this is an implicit
108
- // requirement of "instanceof", else it will throw a TypeError.
76
+ function validateException(actual, expected, message) {
77
+ let result = false;
78
+ const expectedType = objectType(expected);
79
+ if (!expected) {
80
+ result = true;
81
+ } else if (expectedType === "regexp") {
82
+ result = expected.test(errorString(actual));
83
+ expected = String(expected);
84
+ } else if (expectedType === "function" && expected.prototype !== void 0 && actual instanceof expected) {
85
+ result = true;
86
+ } else if (expectedType === "object") {
87
+ result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
88
+ expected = errorString(expected);
89
+ } else if (expectedType === "function") {
90
+ try {
91
+ result = expected.call({}, actual) === true;
92
+ expected = null;
93
+ } catch (e) {
94
+ expected = errorString(e);
109
95
  }
110
- else if (expectedType === 'function' &&
111
- expected.prototype !== undefined &&
112
- actual instanceof expected) {
113
- result = true;
114
- // Expected is an Error object
115
- }
116
- else if (expectedType === 'object') {
117
- result =
118
- actual instanceof expected.constructor &&
119
- actual.name === expected.name &&
120
- actual.message === expected.message;
121
- // Log the string form of the Error object
122
- expected = errorString(expected);
123
- // Expected is a validation function which returns true if validation passed
124
- }
125
- else if (expectedType === 'function') {
126
- // protect against accidental semantics which could hard error in the test
127
- try {
128
- result = expected.call({}, actual) === true;
129
- expected = null;
130
- }
131
- catch (e) {
132
- // assign the "expected" to a nice error string to communicate the local failure to the user
133
- expected = errorString(e);
134
- }
135
- }
136
- return [result, expected, message];
96
+ }
97
+ return [result, expected, message];
137
98
  }
138
99
  function errorString(error) {
139
- // Use String() instead of toString() to handle non-object values like undefined or null.
140
- const resultErrorString = String(error);
141
- // If the error wasn't a subclass of Error but something like
142
- // an object literal with name and message properties...
143
- if (resultErrorString.slice(0, 7) === '[object') {
144
- // Based on https://es5.github.io/#x15.11.4.4
145
- return ((error.name || 'Error') +
146
- (error.message
147
- ? ': '.concat(error.message)
148
- : ''));
149
- }
150
- else {
151
- return resultErrorString;
152
- }
100
+ const resultErrorString = String(error);
101
+ if (resultErrorString.slice(0, 7) === "[object") {
102
+ return (error.name || "Error") + (error.message ? ": ".concat(error.message) : "");
103
+ } else {
104
+ return resultErrorString;
105
+ }
153
106
  }
154
- export default { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException };
107
+ var shared_default = { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException };
108
+ export {
109
+ shared_default as default,
110
+ objectType,
111
+ objectValues,
112
+ objectValuesSubset,
113
+ validateException,
114
+ validateExpectedExceptionArgs
115
+ };
@@ -1,28 +1,29 @@
1
1
  import TestContext from "./test-context.js";
2
- export default class ModuleContext {
3
- static Assert;
4
- static currentModuleChain = [];
5
- static get lastModule() {
6
- return this.currentModuleChain[this.currentModuleChain.length - 1];
7
- }
8
- name;
9
- assert;
10
- userContext;
11
- // Internal fallback assert for modules with no direct tests
12
- context = new TestContext();
13
- moduleChain = [];
14
- beforeEachHooks = [];
15
- afterEachHooks = [];
16
- tests = [];
17
- constructor(name) {
18
- const parentModule = ModuleContext.currentModuleChain[ModuleContext.currentModuleChain.length - 1];
19
- ModuleContext.currentModuleChain.push(this);
20
- this.moduleChain = ModuleContext.currentModuleChain.slice(0);
21
- this.name = parentModule ? `${parentModule.name} > ${name}` : name;
22
- this.assert = new ModuleContext.Assert(this);
23
- // User context uses prototype chain from parent module for proper QUnit-style inheritance:
24
- // parent before() sets props on parent userContext, child tests inherit via prototype.
25
- this.userContext = parentModule ? Object.create(parentModule.userContext) : Object.create(null);
26
- return Object.freeze(this);
27
- }
2
+ class ModuleContext {
3
+ static Assert;
4
+ static currentModuleChain = [];
5
+ static get lastModule() {
6
+ return this.currentModuleChain[this.currentModuleChain.length - 1];
7
+ }
8
+ name;
9
+ assert;
10
+ userContext;
11
+ // Internal fallback assert for modules with no direct tests
12
+ context = new TestContext();
13
+ moduleChain = [];
14
+ beforeEachHooks = [];
15
+ afterEachHooks = [];
16
+ tests = [];
17
+ constructor(name) {
18
+ const parentModule = ModuleContext.currentModuleChain[ModuleContext.currentModuleChain.length - 1];
19
+ ModuleContext.currentModuleChain.push(this);
20
+ this.moduleChain = ModuleContext.currentModuleChain.slice(0);
21
+ this.name = parentModule ? `${parentModule.name} > ${name}` : name;
22
+ this.assert = new ModuleContext.Assert(this);
23
+ this.userContext = parentModule ? Object.create(parentModule.userContext) : /* @__PURE__ */ Object.create(null);
24
+ return Object.freeze(this);
25
+ }
28
26
  }
27
+ export {
28
+ ModuleContext as default
29
+ };
@@ -1,94 +1,95 @@
1
- export default class TestContext {
2
- static Assert;
3
- #name;
4
- get name() {
5
- return this.#name;
6
- }
7
- set name(value) {
8
- this.#name = value;
9
- }
10
- #module;
11
- get module() {
12
- return this.#module;
13
- }
14
- set module(value) {
15
- this.#module = value;
16
- }
17
- #asyncOps = [];
18
- get asyncOps() {
19
- return this.#asyncOps;
20
- }
21
- set asyncOps(value) {
22
- this.#asyncOps = value;
23
- }
24
- #assert;
25
- get assert() {
26
- return this.#assert;
27
- }
28
- set assert(value) {
29
- this.#assert = value;
30
- }
31
- #timeout;
32
- get timeout() {
33
- return this.#timeout;
34
- }
35
- set timeout(value) {
36
- this.#timeout = value;
37
- }
38
- #steps = [];
39
- get steps() {
40
- return this.#steps;
41
- }
42
- set steps(value) {
43
- this.#steps = value;
44
- }
45
- #expectedAssertionCount;
46
- get expectedAssertionCount() {
47
- return this.#expectedAssertionCount;
48
- }
49
- set expectedAssertionCount(value) {
50
- this.#expectedAssertionCount = value;
51
- }
52
- #totalExecutedAssertions = 0;
53
- get totalExecutedAssertions() {
54
- return this.#totalExecutedAssertions;
55
- }
56
- set totalExecutedAssertions(value) {
57
- this.#totalExecutedAssertions = value;
58
- }
59
- userContext = {};
60
- constructor(name, moduleContext) {
61
- if (moduleContext) {
62
- this.name = `${moduleContext.name} | ${name}`;
63
- this.module = moduleContext;
64
- this.module.tests.push(this);
65
- this.assert = new TestContext.Assert(moduleContext, this);
66
- }
67
- }
68
- finish() {
69
- if (this.totalExecutedAssertions === 0) {
70
- this.assert.pushResult({
71
- result: false,
72
- actual: this.totalExecutedAssertions,
73
- expected: '> 0',
74
- message: `Expected at least one assertion to be run for test: ${this.name}`,
75
- });
76
- }
77
- else if (this.steps.length > 0) {
78
- this.assert.pushResult({
79
- result: false,
80
- actual: this.steps,
81
- expected: [],
82
- message: `Expected assert.verifySteps() to be called before end of test after using assert.step(). Unverified steps: ${this.steps.join(', ')}`,
83
- });
84
- }
85
- else if (this.expectedAssertionCount && this.expectedAssertionCount !== this.totalExecutedAssertions) {
86
- this.assert.pushResult({
87
- result: false,
88
- actual: this.totalExecutedAssertions,
89
- expected: this.expectedAssertionCount,
90
- message: `Expected ${this.expectedAssertionCount} assertions, but ${this.totalExecutedAssertions} were run for test: ${this.name}`,
91
- });
92
- }
93
- }
1
+ class TestContext {
2
+ static Assert;
3
+ #name;
4
+ get name() {
5
+ return this.#name;
6
+ }
7
+ set name(value) {
8
+ this.#name = value;
9
+ }
10
+ #module;
11
+ get module() {
12
+ return this.#module;
13
+ }
14
+ set module(value) {
15
+ this.#module = value;
16
+ }
17
+ #asyncOps = [];
18
+ get asyncOps() {
19
+ return this.#asyncOps;
20
+ }
21
+ set asyncOps(value) {
22
+ this.#asyncOps = value;
23
+ }
24
+ #assert;
25
+ get assert() {
26
+ return this.#assert;
27
+ }
28
+ set assert(value) {
29
+ this.#assert = value;
30
+ }
31
+ #timeout;
32
+ get timeout() {
33
+ return this.#timeout;
34
+ }
35
+ set timeout(value) {
36
+ this.#timeout = value;
37
+ }
38
+ #steps = [];
39
+ get steps() {
40
+ return this.#steps;
41
+ }
42
+ set steps(value) {
43
+ this.#steps = value;
44
+ }
45
+ #expectedAssertionCount;
46
+ get expectedAssertionCount() {
47
+ return this.#expectedAssertionCount;
48
+ }
49
+ set expectedAssertionCount(value) {
50
+ this.#expectedAssertionCount = value;
51
+ }
52
+ #totalExecutedAssertions = 0;
53
+ get totalExecutedAssertions() {
54
+ return this.#totalExecutedAssertions;
55
+ }
56
+ set totalExecutedAssertions(value) {
57
+ this.#totalExecutedAssertions = value;
58
+ }
59
+ userContext = {};
60
+ constructor(name, moduleContext) {
61
+ if (moduleContext) {
62
+ this.name = `${moduleContext.name} | ${name}`;
63
+ this.module = moduleContext;
64
+ this.module.tests.push(this);
65
+ this.assert = new TestContext.Assert(moduleContext, this);
66
+ }
67
+ }
68
+ finish() {
69
+ if (this.totalExecutedAssertions === 0) {
70
+ this.assert.pushResult({
71
+ result: false,
72
+ actual: this.totalExecutedAssertions,
73
+ expected: "> 0",
74
+ message: `Expected at least one assertion to be run for test: ${this.name}`
75
+ });
76
+ } else if (this.steps.length > 0) {
77
+ this.assert.pushResult({
78
+ result: false,
79
+ actual: this.steps,
80
+ expected: [],
81
+ message: `Expected assert.verifySteps() to be called before end of test after using assert.step(). Unverified steps: ${this.steps.join(", ")}`
82
+ });
83
+ } else if (this.expectedAssertionCount && this.expectedAssertionCount !== this.totalExecutedAssertions) {
84
+ this.assert.pushResult({
85
+ result: false,
86
+ actual: this.totalExecutedAssertions,
87
+ expected: this.expectedAssertionCount,
88
+ message: `Expected ${this.expectedAssertionCount} assertions, but ${this.totalExecutedAssertions} were run for test: ${this.name}`
89
+ });
90
+ }
91
+ }
94
92
  }
93
+ export {
94
+ TestContext as default
95
+ };
package/dist/types.js CHANGED
@@ -1 +0,0 @@
1
- export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qunitx",
3
3
  "type": "module",
4
- "version": "1.1.5",
4
+ "version": "1.2.0",
5
5
  "description": "A universal test framework for testing any js file on node.js, browser or deno with QUnit API",
6
6
  "author": "Izel Nakri",
7
7
  "license": "MIT",
@@ -84,7 +84,8 @@
84
84
  "lint": "deno lint shims/",
85
85
  "lint:docs": "deno doc --lint shims/deno/module.ts shims/deno/test.ts",
86
86
  "build": "node --experimental-strip-types build.ts",
87
- "pretest": "node --experimental-strip-types build.ts",
87
+ "build:cold": "rm -rf dist && node --experimental-strip-types build.ts",
88
+ "pretest": "node --experimental-strip-types build.ts && node --input-type=module -e \"import{symlinkSync}from'fs';try{symlinkSync('..','node_modules/qunitx','junction')}catch(_){}\"",
88
89
  "run:all": "npm run run:node && npm run run:deno",
89
90
  "run:node": "node --experimental-strip-types --test test/helpers/passing-tests.ts && node --experimental-strip-types --test test/helpers/failing-tests.ts",
90
91
  "run:deno": "deno test test/helpers/passing-tests.ts && deno test test/helpers/failing-tests.ts",
@@ -93,6 +94,7 @@
93
94
  "changelog:update": "git-cliff --output CHANGELOG.md",
94
95
  "prepare": "npm run build",
95
96
  "test": "npm run test:browser && npm run test:node && npm run test:deno",
97
+ "test:release": "bash scripts/test-release.sh",
96
98
  "test:dev": "npm run test | tee test-output.log",
97
99
  "test:browser": "qunitx test/index.ts --debug",
98
100
  "test:deno": "deno test --allow-read --allow-run test/index.ts",
@@ -106,10 +108,10 @@
106
108
  "docs": "deno doc --html --name=\"QUnitX\" --output=docs/src shims/deno/index.ts"
107
109
  },
108
110
  "devDependencies": {
111
+ "@types/node": "^22.0.0",
109
112
  "prettier": "^3.8.1",
110
113
  "qunit": "^2.25.0",
111
- "qunitx": "^1.0.3",
112
- "qunitx-cli": "^0.5.3",
114
+ "qunitx-cli": "^0.9.10",
113
115
  "typescript": "^6.0.2"
114
116
  },
115
117
  "volta": {
@@ -109,7 +109,7 @@ export { Assert };
109
109
  * });
110
110
  * ```
111
111
  */
112
- export const module = Module;
112
+ export { default as module } from './module.ts';
113
113
 
114
114
  /**
115
115
  * Defines an individual test. Wraps Deno's `it()` and handles the full QUnit
@@ -139,7 +139,7 @@ export const module = Module;
139
139
  * });
140
140
  * ```
141
141
  */
142
- export const test = Test;
142
+ export { default as test } from './test.ts';
143
143
 
144
144
  /**
145
145
  * The default export provides the full QUnitX API as a single object.
@@ -166,4 +166,4 @@ export const test = Test;
166
166
  * @property {object} config - Runtime configuration object (currently unused; reserved
167
167
  * for future QUnit config compatibility).
168
168
  */
169
- export default { AssertionError: Assert.AssertionError, module, test, config: {} };
169
+ export default { AssertionError: Assert.AssertionError, module: Module, test: Test, config: {} };
@@ -32,6 +32,9 @@ export type { HookFn, HooksObject, PushResultInfo } from '../types.ts';
32
32
  * });
33
33
  * ```
34
34
  */
35
+ export default function module(moduleName: string, moduleContent: (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void): void;
36
+ /** Defines a test module (suite) with optional Deno BDD runtime options forwarded to `describe()`. */
37
+ export default function module(moduleName: string, runtimeOptions: object, moduleContent: (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void): void;
35
38
  export default function module(
36
39
  moduleName: string,
37
40
  runtimeOptions: object | ((hooks: HooksObject<Assert>) => void),
@@ -34,6 +34,9 @@ export type { PushResultInfo } from '../types.ts';
34
34
  * });
35
35
  * ```
36
36
  */
37
+ export default function test(testName: string, testContent: (assert: Assert, meta: { testName: string; options: unknown }) => void | Promise<void>): void;
38
+ /** Defines an individual test with optional Deno BDD runtime options forwarded to `it()`. */
39
+ export default function test(testName: string, runtimeOptions: object, testContent: (assert: Assert, meta: { testName: string; options: unknown }) => void | Promise<void>): void;
37
40
  export default function test(
38
41
  testName: string,
39
42
  runtimeOptions: object | ((assert: Assert, meta: { testName: string; options: unknown }) => void | Promise<void>),
@@ -1,7 +1,6 @@
1
- // Type stub for jsr:@std/assert. Deno resolves the real module at runtime.
2
1
  declare module 'jsr:@std/assert' {
3
2
  class AssertionError extends Error {
4
- constructor(message?: string);
3
+ constructor(message: string, options?: ErrorOptions);
5
4
  }
6
5
  export { AssertionError };
7
6
  }
@@ -1,9 +1,9 @@
1
- // Type stub for jsr:@std/testing/bdd. Deno resolves the real module at runtime.
2
1
  declare module 'jsr:@std/testing/bdd' {
2
+ function describe(name: string, options: Record<string, unknown>, fn: () => void): void;
3
3
  function describe(name: string, fn: () => void): void;
4
- function describe(name: string, options: object, fn: () => void): void;
5
- function it(name: string, options: object, fn: () => void | Promise<void>): void;
6
4
  function beforeAll(fn: () => void | Promise<void>): void;
7
5
  function afterAll(fn: () => void | Promise<void>): void;
8
- export { describe, it, beforeAll, afterAll };
6
+ function it(name: string, options: Record<string, unknown>, fn: (...args: unknown[]) => void | Promise<void>): void;
7
+ function it(name: string, fn: (...args: unknown[]) => void | Promise<void>): void;
8
+ export { describe, beforeAll, afterAll, it };
9
9
  }