@vpmedia/simplify 1.63.0 โ 1.65.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/CHANGELOG.md +34 -0
- package/package.json +5 -5
- package/src/index.js +2 -4
- package/src/logging/Logger.test.js +4 -1
- package/src/pagelifecycle/const.js +16 -0
- package/src/pagelifecycle/util.test.js +102 -0
- package/src/typecheck/TypeCheckError.js +4 -2
- package/src/typecheck/TypeChecker.test.js +70 -0
- package/src/util/async.js +5 -2
- package/src/util/async.test.js +11 -1
- package/src/util/error.js +5 -3
- package/src/util/error.test.js +1 -0
- package/src/util/fetch.js +0 -1
- package/src/util/fetch.test.js +15 -7
- package/src/util/number.js +46 -44
- package/src/util/number.test.js +28 -17
- package/src/util/validate.js +1 -1
- package/tests/mocks/handlers/fetch.js +34 -0
- package/tests/mocks/handlers.js +3 -0
- package/types/index.d.ts +2 -2
- package/types/pagelifecycle/const.d.ts +2 -0
- package/types/pagelifecycle/const.d.ts.map +1 -1
- package/types/typecheck/TypeCheckError.d.ts +6 -1
- package/types/typecheck/TypeCheckError.d.ts.map +1 -1
- package/types/util/async.d.ts +1 -1
- package/types/util/async.d.ts.map +1 -1
- package/types/util/error.d.ts +1 -1
- package/types/util/error.d.ts.map +1 -1
- package/types/util/fetch.d.ts.map +1 -1
- package/types/util/number.d.ts +2 -4
- package/types/util/number.d.ts.map +1 -1
- package/vitest.setup.js +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
## [1.65.0] - 2026-01-30
|
|
2
|
+
|
|
3
|
+
### ๐ผ Other
|
|
4
|
+
|
|
5
|
+
- *(deps)* Bump dependency versions
|
|
6
|
+
|
|
7
|
+
### ๐ Refactor
|
|
8
|
+
|
|
9
|
+
- Relax fixFloatPrecision error handling
|
|
10
|
+
|
|
11
|
+
### โ๏ธ Miscellaneous Tasks
|
|
12
|
+
|
|
13
|
+
- Release
|
|
14
|
+
- *(release)* V1.65.0
|
|
15
|
+
## [1.64.0] - 2026-01-30
|
|
16
|
+
|
|
17
|
+
### ๐ผ Other
|
|
18
|
+
|
|
19
|
+
- *(deps)* Bump dependency versions
|
|
20
|
+
- *(deps)* Bump dependency versions
|
|
21
|
+
|
|
22
|
+
### ๐ Refactor
|
|
23
|
+
|
|
24
|
+
- Improve error handling
|
|
25
|
+
- Improve type guards and performance optimizations
|
|
26
|
+
|
|
27
|
+
### ๐งช Testing
|
|
28
|
+
|
|
29
|
+
- Improve test coverage
|
|
30
|
+
|
|
31
|
+
### โ๏ธ Miscellaneous Tasks
|
|
32
|
+
|
|
33
|
+
- Release
|
|
34
|
+
- *(release)* V1.64.0
|
|
1
35
|
## [1.63.0] - 2026-01-28
|
|
2
36
|
|
|
3
37
|
### ๐ผ Other
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vpmedia/simplify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.65.0",
|
|
4
4
|
"description": "@vpmedia/simplify",
|
|
5
5
|
"author": "Andras Csizmadia <andras@vpmedia.hu> (www.vpmedia.hu)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,23 +22,23 @@
|
|
|
22
22
|
"eventemitter3": "^5.0.4"
|
|
23
23
|
},
|
|
24
24
|
"optionalDependencies": {
|
|
25
|
-
"@sentry/browser": "^10.
|
|
25
|
+
"@sentry/browser": "^10.38.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@commitlint/cli": "^20.3.1",
|
|
29
29
|
"@commitlint/config-conventional": "^20.3.1",
|
|
30
30
|
"@eslint/js": "^9.39.2",
|
|
31
|
-
"@types/node": "^25.0
|
|
31
|
+
"@types/node": "^25.1.0",
|
|
32
32
|
"@vitest/coverage-v8": "^4.0.18",
|
|
33
33
|
"eslint": "^9.39.2",
|
|
34
|
-
"eslint-plugin-jsdoc": "^62.
|
|
34
|
+
"eslint-plugin-jsdoc": "^62.5.0",
|
|
35
35
|
"eslint-plugin-oxlint": "^1.42.0",
|
|
36
36
|
"eslint-plugin-unicorn": "^62.0.0",
|
|
37
37
|
"globals": "^17.2.0",
|
|
38
38
|
"jsdom": "^27.4.0",
|
|
39
39
|
"msw": "^2.12.7",
|
|
40
40
|
"oxlint": "^1.42.0",
|
|
41
|
-
"oxlint-tsgolint": "^0.11.
|
|
41
|
+
"oxlint-tsgolint": "^0.11.3",
|
|
42
42
|
"prettier": "^3.8.1",
|
|
43
43
|
"typescript": "^5.9.3",
|
|
44
44
|
"typescript-eslint": "^8.54.0",
|
package/src/index.js
CHANGED
|
@@ -23,9 +23,7 @@ export { delayPromise, loadJSON } from './util/async.js';
|
|
|
23
23
|
export { getErrorDetails, getTypedError } from './util/error.js';
|
|
24
24
|
export { FetchError, fetchRetry, HTTP_0_ANY } from './util/fetch.js';
|
|
25
25
|
export {
|
|
26
|
-
addFloat,
|
|
27
26
|
deg2rad,
|
|
28
|
-
fixFloat,
|
|
29
27
|
fixFloatPrecision,
|
|
30
28
|
getRandomInt,
|
|
31
29
|
isEqual,
|
|
@@ -34,9 +32,9 @@ export {
|
|
|
34
32
|
isInRange,
|
|
35
33
|
isLess,
|
|
36
34
|
isLessOrEqual,
|
|
37
|
-
|
|
35
|
+
rad2deg,
|
|
38
36
|
} from './util/number.js';
|
|
39
|
-
export {
|
|
37
|
+
export { deepMerge, getObjArrayPropSum, getObjValueByPath, purgeObject, setObjValueByPath } from './util/object.js';
|
|
40
38
|
export { getURLParam, sanitizeURLParam } from './util/query.js';
|
|
41
39
|
export { serverDataToState } from './util/state.js';
|
|
42
40
|
export { addLeadingZero, capitalize, getTypeFromValue, saveAsFile, underscoreToCamelCase } from './util/string.js';
|
|
@@ -52,10 +52,13 @@ test('Tests Logger custom handler', () => {
|
|
|
52
52
|
logger.info('info');
|
|
53
53
|
expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_INFO);
|
|
54
54
|
expect(testLogHandler.emitLogMessage).toBe('info');
|
|
55
|
-
//
|
|
55
|
+
// warning
|
|
56
56
|
logger.warn('warning');
|
|
57
57
|
expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_WARNING);
|
|
58
58
|
expect(testLogHandler.emitLogMessage).toBe('warning');
|
|
59
|
+
logger.warning('warning');
|
|
60
|
+
expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_WARNING);
|
|
61
|
+
expect(testLogHandler.emitLogMessage).toBe('warning');
|
|
59
62
|
// error
|
|
60
63
|
logger.error('error');
|
|
61
64
|
expect(testLogHandler.emitLogLevel).toBe(LOG_LEVEL_ERROR);
|
|
@@ -4,11 +4,27 @@ export const PAGE_LIFECYCLE_STATE_PASSIVE = 'passive';
|
|
|
4
4
|
export const PAGE_LIFECYCLE_STATE_FROZEN = 'frozen';
|
|
5
5
|
export const PAGE_LIFECYCLE_STATE_TERMINATED = 'terminated';
|
|
6
6
|
|
|
7
|
+
export const PAGE_LIFECYCLE_STATES = new Set([
|
|
8
|
+
PAGE_LIFECYCLE_STATE_ACTIVE,
|
|
9
|
+
PAGE_LIFECYCLE_STATE_FROZEN,
|
|
10
|
+
PAGE_LIFECYCLE_STATE_HIDDEN,
|
|
11
|
+
PAGE_LIFECYCLE_STATE_PASSIVE,
|
|
12
|
+
PAGE_LIFECYCLE_STATE_TERMINATED,
|
|
13
|
+
]);
|
|
14
|
+
|
|
7
15
|
export const DOCUMENT_STATE_DOM_LOADED = 'domLoaded';
|
|
8
16
|
export const DOCUMENT_STATE_FULLY_LOADED = 'fullyLoaded';
|
|
9
17
|
export const DOCUMENT_STATE_COMPLETE = 'complete';
|
|
10
18
|
export const DOCUMENT_STATE_INTERACTIVE = 'interactive';
|
|
11
19
|
export const DOCUMENT_STATE_LOADING = 'loading';
|
|
12
20
|
|
|
21
|
+
export const DOCUMENT_STATES = new Set([
|
|
22
|
+
DOCUMENT_STATE_COMPLETE,
|
|
23
|
+
DOCUMENT_STATE_DOM_LOADED,
|
|
24
|
+
DOCUMENT_STATE_FULLY_LOADED,
|
|
25
|
+
DOCUMENT_STATE_INTERACTIVE,
|
|
26
|
+
DOCUMENT_STATE_LOADING,
|
|
27
|
+
]);
|
|
28
|
+
|
|
13
29
|
export const PAGE_LIFECYCLE_STATE_CHANGE_EVENT = 'pageLifecycleStateChange';
|
|
14
30
|
export const DOCUMENT_STATE_CHANGE_EVENT = 'documentStateChange';
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
DOCUMENT_STATE_CHANGE_EVENT,
|
|
4
|
+
DOCUMENT_STATES,
|
|
5
|
+
PAGE_LIFECYCLE_STATE_CHANGE_EVENT,
|
|
6
|
+
PAGE_LIFECYCLE_STATE_HIDDEN,
|
|
7
|
+
PAGE_LIFECYCLE_STATES,
|
|
8
|
+
} from './const.js';
|
|
9
|
+
import {
|
|
10
|
+
addPageLifecycleCallback,
|
|
11
|
+
getDocumentState,
|
|
12
|
+
getPageLifecycleEventEmitter,
|
|
13
|
+
getPageLifecycleState,
|
|
14
|
+
initPageLifecycle,
|
|
15
|
+
isPageLifecycleInitialized,
|
|
16
|
+
} from './util.js';
|
|
17
|
+
import { delayPromise } from '../util/async.js';
|
|
18
|
+
|
|
19
|
+
describe('Page Lifecycle', () => {
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
initPageLifecycle();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should initialize page lifecycle', () => {
|
|
25
|
+
expect(isPageLifecycleInitialized()).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should return current page lifecycle state', () => {
|
|
29
|
+
const state = getPageLifecycleState();
|
|
30
|
+
expect(state).toBeOneOf(PAGE_LIFECYCLE_STATES);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return current document state', () => {
|
|
34
|
+
const state = getDocumentState();
|
|
35
|
+
expect(state).toBeOneOf(DOCUMENT_STATES);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return event emitter instance', () => {
|
|
39
|
+
const emitter = getPageLifecycleEventEmitter();
|
|
40
|
+
expect(emitter).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle page lifecycle state changes', () => {
|
|
44
|
+
const emitter = getPageLifecycleEventEmitter();
|
|
45
|
+
let stateChanged = false;
|
|
46
|
+
|
|
47
|
+
emitter.on(PAGE_LIFECYCLE_STATE_CHANGE_EVENT, (data) => {
|
|
48
|
+
stateChanged = true;
|
|
49
|
+
expect(data).toHaveProperty('previousState');
|
|
50
|
+
expect(data).toHaveProperty('nextState');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Trigger visibility change
|
|
54
|
+
const originalVisibilityState = document.visibilityState;
|
|
55
|
+
Object.defineProperty(document, 'visibilityState', {
|
|
56
|
+
value: originalVisibilityState === 'visible' ? 'hidden' : 'visible',
|
|
57
|
+
writable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
document.dispatchEvent(new Event('visibilitychange'));
|
|
62
|
+
|
|
63
|
+
// Restore original state
|
|
64
|
+
Object.defineProperty(document, 'visibilityState', {
|
|
65
|
+
value: originalVisibilityState,
|
|
66
|
+
writable: true,
|
|
67
|
+
configurable: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(stateChanged).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle document state changes', () => {
|
|
74
|
+
const emitter = getPageLifecycleEventEmitter();
|
|
75
|
+
let stateChanged = false;
|
|
76
|
+
|
|
77
|
+
emitter.on(DOCUMENT_STATE_CHANGE_EVENT, (data) => {
|
|
78
|
+
stateChanged = true;
|
|
79
|
+
expect(data).toHaveProperty('previousState');
|
|
80
|
+
expect(data).toHaveProperty('nextState');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Trigger ready state change
|
|
84
|
+
const originalReadyState = document.readyState;
|
|
85
|
+
Object.defineProperty(document, 'readyState', {
|
|
86
|
+
value: originalReadyState === 'complete' ? 'interactive' : 'complete',
|
|
87
|
+
writable: true,
|
|
88
|
+
configurable: true,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
document.dispatchEvent(new Event('readystatechange'));
|
|
92
|
+
|
|
93
|
+
// Restore original state
|
|
94
|
+
Object.defineProperty(document, 'readyState', {
|
|
95
|
+
value: originalReadyState,
|
|
96
|
+
writable: true,
|
|
97
|
+
configurable: true,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(stateChanged).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -2,9 +2,11 @@ export class TypeCheckError extends TypeError {
|
|
|
2
2
|
/**
|
|
3
3
|
* Creates a new `TypeCheckError` instance.
|
|
4
4
|
* @param {string} message - Error message.
|
|
5
|
+
* @param {{ cause?: unknown, value?: unknown }} [options] - Error options.
|
|
5
6
|
*/
|
|
6
|
-
constructor(message) {
|
|
7
|
-
super(message);
|
|
7
|
+
constructor(message, options) {
|
|
8
|
+
super(message, options);
|
|
8
9
|
this.name = 'TypeCheckError';
|
|
10
|
+
this.value = options?.value;
|
|
9
11
|
}
|
|
10
12
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { typeChecker } from './TypeChecker.js';
|
|
3
|
+
import { TypeCheckError } from './TypeCheckError.js';
|
|
4
|
+
|
|
5
|
+
const stringValidator = (value) => typeof value === 'string';
|
|
6
|
+
|
|
7
|
+
describe('TypeChecker', () => {
|
|
8
|
+
it('should check value type correctly', () => {
|
|
9
|
+
const result = typeChecker.check('test', stringValidator);
|
|
10
|
+
expect(result).toBe('test');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should throw TypeCheckError for invalid type', () => {
|
|
14
|
+
expect(() => typeChecker.check(123, stringValidator)).toThrow(TypeCheckError);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should swallow errors when enabled', () => {
|
|
18
|
+
typeChecker.setSwallowErrors(true);
|
|
19
|
+
const result = typeChecker.check(123, stringValidator);
|
|
20
|
+
expect(result).toBe(123);
|
|
21
|
+
typeChecker.setSwallowErrors(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should check array type correctly', () => {
|
|
25
|
+
const result = typeChecker.checkArray(['a', 'b', 'c'], stringValidator);
|
|
26
|
+
expect(result).toEqual(['a', 'b', 'c']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should throw TypeCheckError for invalid array type', () => {
|
|
30
|
+
expect(() => typeChecker.checkArray([1, 2, 3], stringValidator)).toThrow(TypeCheckError);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should swallow array errors when enabled', () => {
|
|
34
|
+
typeChecker.setSwallowErrors(true);
|
|
35
|
+
const result = typeChecker.checkArray([1, 2, 3], stringValidator);
|
|
36
|
+
expect(result).toEqual([1, 2, 3]);
|
|
37
|
+
typeChecker.setSwallowErrors(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should check enum value correctly', () => {
|
|
41
|
+
const choices = ['option1', 'option2', 'option3'];
|
|
42
|
+
const result = typeChecker.checkEnum('option2', choices);
|
|
43
|
+
expect(result).toBe('option2');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should throw TypeCheckError for invalid enum value', () => {
|
|
47
|
+
const choices = ['option1', 'option2', 'option3'];
|
|
48
|
+
expect(() => typeChecker.checkEnum('invalid', choices)).toThrow(TypeCheckError);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should swallow enum errors when enabled', () => {
|
|
52
|
+
typeChecker.setSwallowErrors(true);
|
|
53
|
+
const choices = ['option1', 'option2', 'option3'];
|
|
54
|
+
const result = typeChecker.checkEnum('invalid', choices);
|
|
55
|
+
expect(result).toBe('invalid');
|
|
56
|
+
typeChecker.setSwallowErrors(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should handle number enum values', () => {
|
|
60
|
+
const choices = [1, 2, 3];
|
|
61
|
+
const result = typeChecker.checkEnum(2, choices);
|
|
62
|
+
expect(result).toBe(2);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should handle mixed string and number enum values', () => {
|
|
66
|
+
const choices = ['option1', 2, 'option3'];
|
|
67
|
+
const result = typeChecker.checkEnum(2, choices);
|
|
68
|
+
expect(result).toBe(2);
|
|
69
|
+
});
|
|
70
|
+
});
|
package/src/util/async.js
CHANGED
|
@@ -11,9 +11,12 @@ export const delayPromise = (delayMS) =>
|
|
|
11
11
|
/**
|
|
12
12
|
* Load JSON file using a fetch GET request.
|
|
13
13
|
* @param {string} url - URL to load.
|
|
14
|
-
* @returns {Promise<
|
|
14
|
+
* @returns {Promise<unknown>} The parsed JSON data.
|
|
15
15
|
*/
|
|
16
16
|
export const loadJSON = async (url) => {
|
|
17
17
|
const response = await fetch(url);
|
|
18
|
-
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new DOMException(`Fetch error ${response.status}`, 'FetchError');
|
|
20
|
+
}
|
|
21
|
+
return response.json();
|
|
19
22
|
};
|
package/src/util/async.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { delayPromise } from './async.js';
|
|
1
|
+
import { delayPromise, loadJSON } from './async.js';
|
|
2
2
|
|
|
3
3
|
describe('delayPromise', () => {
|
|
4
4
|
test('Returns a promise that resolves after specified delay', async () => {
|
|
@@ -19,3 +19,13 @@ describe('delayPromise', () => {
|
|
|
19
19
|
expect(end - start).toBeGreaterThanOrEqual(0);
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
|
+
|
|
23
|
+
describe('loadJSON', () => {
|
|
24
|
+
test('Load JSON data', async () => {
|
|
25
|
+
const data = await loadJSON('/test.json');
|
|
26
|
+
expect(data).toMatchObject({
|
|
27
|
+
method: 'GET',
|
|
28
|
+
success: true,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
package/src/util/error.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
const DEFAULT_EXCLUDES = new Set(['stack']);
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Retrieves detailed information from an error object.
|
|
3
5
|
* @param {Error} error - The error to extract details from.
|
|
4
|
-
* @param {string[]} [excludes] - An array of property names to exclude from the result.
|
|
6
|
+
* @param {null | undefined | string[]} [excludes] - An array of property names to exclude from the result.
|
|
5
7
|
* @returns {object} - An object containing the error details.
|
|
6
8
|
*/
|
|
7
|
-
export const getErrorDetails = (error, excludes
|
|
9
|
+
export const getErrorDetails = (error, excludes) => {
|
|
8
10
|
const errorDetails = {
|
|
9
11
|
name: error.name,
|
|
10
12
|
type: error.constructor?.name ?? typeof error,
|
|
@@ -16,7 +18,7 @@ export const getErrorDetails = (error, excludes = ['stack']) => {
|
|
|
16
18
|
errorDetails.cause = error.cause;
|
|
17
19
|
}
|
|
18
20
|
for (const key of Object.getOwnPropertyNames(error)) {
|
|
19
|
-
if (!excludes
|
|
21
|
+
if (!excludes?.includes(key) && !DEFAULT_EXCLUDES.has(key)) {
|
|
20
22
|
errorDetails[key] = error[key];
|
|
21
23
|
}
|
|
22
24
|
}
|
package/src/util/error.test.js
CHANGED
|
@@ -9,6 +9,7 @@ describe('error', () => {
|
|
|
9
9
|
expect(errorDetails.type).toBe('Error');
|
|
10
10
|
expect(errorDetails.message).toBe('Test error');
|
|
11
11
|
expect(errorDetails.cause).toBe('Test cause');
|
|
12
|
+
expect(errorDetails.stack).toBe(undefined);
|
|
12
13
|
});
|
|
13
14
|
|
|
14
15
|
test('getErrorDetails with Error cause', () => {
|
package/src/util/fetch.js
CHANGED
package/src/util/fetch.test.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { HTTP_404_NOT_FOUND } from '../const/http_status.js';
|
|
2
2
|
import { fetchRetry, FetchError } from './fetch.js';
|
|
3
3
|
|
|
4
|
+
describe('FetchError', () => {
|
|
5
|
+
test('constructor', () => {
|
|
6
|
+
const error = new FetchError('message', 'url', { method: 'GET' }, null);
|
|
7
|
+
expect(error.message).toEqual('message');
|
|
8
|
+
expect(error.resource).toEqual('url');
|
|
9
|
+
expect(error.fetchOptions).toMatchObject({ method: 'GET' });
|
|
10
|
+
expect(error.response).toBe(null);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
4
14
|
describe('fetchRetry', () => {
|
|
5
15
|
test('fetch OK', async () => {
|
|
6
|
-
const response = await fetchRetry('
|
|
16
|
+
const response = await fetchRetry('/test.json', {
|
|
7
17
|
cache: 'no-cache',
|
|
8
18
|
keepalive: false,
|
|
9
19
|
method: 'GET',
|
|
@@ -11,16 +21,14 @@ describe('fetchRetry', () => {
|
|
|
11
21
|
});
|
|
12
22
|
const json = await response.json();
|
|
13
23
|
const expectedJSON = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
title: 'delectus aut autem',
|
|
17
|
-
userId: 1,
|
|
24
|
+
success: true,
|
|
25
|
+
method: 'GET',
|
|
18
26
|
};
|
|
19
27
|
expect(json).toEqual(expectedJSON);
|
|
20
28
|
});
|
|
21
29
|
test('fetch unknown scheme', async () => {
|
|
22
30
|
try {
|
|
23
|
-
await fetchRetry('htps://
|
|
31
|
+
await fetchRetry('htps://', {});
|
|
24
32
|
} catch (error) {
|
|
25
33
|
const typedError = error instanceof Error ? error : new Error(String(error));
|
|
26
34
|
expect(typedError.message).toEqual('fetch failed');
|
|
@@ -32,7 +40,7 @@ describe('fetchRetry', () => {
|
|
|
32
40
|
test('fetch 404 error with retry', async () => {
|
|
33
41
|
try {
|
|
34
42
|
await fetchRetry(
|
|
35
|
-
'
|
|
43
|
+
'/test_error.json',
|
|
36
44
|
{
|
|
37
45
|
cache: 'no-cache',
|
|
38
46
|
keepalive: false,
|
package/src/util/number.js
CHANGED
|
@@ -1,63 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
* Converts degrees to radians.
|
|
3
|
-
* @param {number} deg - Degree value.
|
|
4
|
-
* @returns {number} Radian value.
|
|
5
|
-
*/
|
|
6
|
-
export const deg2rad = (deg) => deg * (Math.PI / 180);
|
|
1
|
+
import { TypeCheckError } from '../typecheck/TypeCheckError.js';
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
export const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
|
|
3
|
+
const DEG_TO_RAD = Math.PI / 180;
|
|
4
|
+
const RAD_TO_DEG = 180 / Math.PI;
|
|
5
|
+
|
|
6
|
+
const PRECISION = 12;
|
|
7
|
+
const EPSILON = 1e-11;
|
|
15
8
|
|
|
16
9
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @param {number
|
|
19
|
-
* @returns {number}
|
|
10
|
+
* Converts degrees to radians.
|
|
11
|
+
* @param {number} degrees - Angle in degrees.
|
|
12
|
+
* @returns {number} Angle in radians.
|
|
13
|
+
* @throws {TypeCheckError}
|
|
20
14
|
*/
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
value = Number(value);
|
|
15
|
+
export const deg2rad = (degrees) => {
|
|
16
|
+
if (!Number.isFinite(degrees)) {
|
|
17
|
+
throw new TypeCheckError('Argument degrees must be a finite number', { value: degrees });
|
|
25
18
|
}
|
|
26
|
-
|
|
27
|
-
const valuePlusOne = value + 1;
|
|
28
|
-
return Number.parseFloat(valuePlusOne.toPrecision(12)) - 1;
|
|
29
|
-
}
|
|
30
|
-
return Number.parseFloat(value.toPrecision(12));
|
|
19
|
+
return degrees * DEG_TO_RAD;
|
|
31
20
|
};
|
|
32
21
|
|
|
33
22
|
/**
|
|
34
|
-
*
|
|
35
|
-
* @param {number}
|
|
36
|
-
* @
|
|
37
|
-
* @
|
|
23
|
+
* Converts radians to degrees.
|
|
24
|
+
* @param {number} radians - Angle in radians.
|
|
25
|
+
* @returns {number} Angle in degrees.
|
|
26
|
+
* @throws {TypeCheckError}
|
|
38
27
|
*/
|
|
39
|
-
export const
|
|
28
|
+
export const rad2deg = (radians) => {
|
|
29
|
+
if (!Number.isFinite(radians)) {
|
|
30
|
+
throw new TypeCheckError('Argument radians must be a finite number', { value: radians });
|
|
31
|
+
}
|
|
32
|
+
return radians * RAD_TO_DEG;
|
|
33
|
+
};
|
|
40
34
|
|
|
41
35
|
/**
|
|
42
|
-
*
|
|
43
|
-
* @param {number}
|
|
44
|
-
* @param {number}
|
|
45
|
-
* @returns {number}
|
|
36
|
+
* Returns random integer in range.
|
|
37
|
+
* @param {number} min - Min value.
|
|
38
|
+
* @param {number} max - Max value.
|
|
39
|
+
* @returns {number} Random integer in given range.
|
|
40
|
+
* @throws {TypeError}
|
|
46
41
|
*/
|
|
47
|
-
export const
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
export const getRandomInt = (min, max) => {
|
|
43
|
+
if (!Number.isFinite(min)) {
|
|
44
|
+
throw new TypeCheckError('Argument min must be finite number', { value: min });
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isFinite(max)) {
|
|
47
|
+
throw new TypeCheckError('Argument max must be finite number', { value: max });
|
|
48
|
+
}
|
|
49
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
54
|
-
* @param {number}
|
|
55
|
-
* @
|
|
56
|
-
* @returns {number} The processed value.
|
|
53
|
+
* Normalizes floating point precision (e.g. 0.20000000000000004 โ 0.2).
|
|
54
|
+
* @param {number | string} value - Input value.
|
|
55
|
+
* @returns {number} Fixed float precision value.
|
|
57
56
|
*/
|
|
58
|
-
export const
|
|
59
|
-
const
|
|
60
|
-
|
|
57
|
+
export const fixFloatPrecision = (value) => {
|
|
58
|
+
const parsedValue = typeof value === 'string' ? Number(value) : value;
|
|
59
|
+
if (!Number.isFinite(parsedValue)) {
|
|
60
|
+
return Number.NaN;
|
|
61
|
+
}
|
|
62
|
+
return Math.abs(parsedValue) < EPSILON ? 0 : Number(parsedValue.toPrecision(PRECISION));
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
/**
|
package/src/util/number.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
/* eslint-disable unicorn/no-useless-undefined */
|
|
2
|
+
|
|
3
|
+
import { TypeCheckError } from '../typecheck/TypeCheckError.js';
|
|
1
4
|
import {
|
|
2
|
-
addFloat,
|
|
3
|
-
fixFloat,
|
|
4
5
|
fixFloatPrecision,
|
|
5
6
|
getRandomInt,
|
|
6
7
|
isEqual,
|
|
@@ -9,12 +10,20 @@ import {
|
|
|
9
10
|
isInRange,
|
|
10
11
|
isLess,
|
|
11
12
|
isLessOrEqual,
|
|
12
|
-
subFloat,
|
|
13
13
|
deg2rad,
|
|
14
|
+
rad2deg,
|
|
14
15
|
} from './number.js';
|
|
15
16
|
|
|
16
|
-
test('Converts degrees to radians', () => {
|
|
17
|
+
test('Converts angle in degrees to radians', () => {
|
|
17
18
|
expect(deg2rad(90)).toBe(1.5707963267948966);
|
|
19
|
+
// @ts-expect-error
|
|
20
|
+
expect(() => deg2rad('')).toThrowError(TypeCheckError);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('Converts angle in radians to degrees', () => {
|
|
24
|
+
expect(rad2deg(1.5707963267948966)).toBe(90);
|
|
25
|
+
// @ts-expect-error
|
|
26
|
+
expect(() => rad2deg('')).toThrowError(TypeCheckError);
|
|
18
27
|
});
|
|
19
28
|
|
|
20
29
|
describe('fixFloatPrecision', () => {
|
|
@@ -32,18 +41,32 @@ describe('fixFloatPrecision', () => {
|
|
|
32
41
|
|
|
33
42
|
test('Handles very small numbers', () => {
|
|
34
43
|
expect(fixFloatPrecision(0.0000000000001)).toBe(0);
|
|
44
|
+
expect(fixFloatPrecision(-0.0000000000001)).toBe(0);
|
|
35
45
|
});
|
|
36
46
|
|
|
37
47
|
test('Handles integer numbers', () => {
|
|
38
48
|
expect(fixFloatPrecision(5)).toBe(5);
|
|
39
49
|
});
|
|
40
50
|
|
|
41
|
-
test('Handles string input', () => {
|
|
51
|
+
test('Handles string number input', () => {
|
|
42
52
|
expect(fixFloatPrecision('5.123456789')).toBe(5.123456789);
|
|
43
53
|
});
|
|
54
|
+
|
|
55
|
+
test('Throws error for invalid input', () => {
|
|
56
|
+
expect(fixFloatPrecision('abc')).toBe(Number.NaN);
|
|
57
|
+
expect(fixFloatPrecision(null)).toBe(Number.NaN);
|
|
58
|
+
expect(fixFloatPrecision(undefined)).toBe(Number.NaN);
|
|
59
|
+
});
|
|
44
60
|
});
|
|
45
61
|
|
|
46
62
|
describe('getRandomInt', () => {
|
|
63
|
+
test('Throws error if min or max is not finite number', () => {
|
|
64
|
+
// @ts-expect-error
|
|
65
|
+
expect(() => getRandomInt('', 1)).toThrowError(TypeCheckError);
|
|
66
|
+
// @ts-expect-error
|
|
67
|
+
expect(() => getRandomInt(1, '')).toThrowError(TypeCheckError);
|
|
68
|
+
});
|
|
69
|
+
|
|
47
70
|
test('Returns random integer within range when min equals max', () => {
|
|
48
71
|
expect(getRandomInt(1, 1)).toBe(1);
|
|
49
72
|
});
|
|
@@ -66,18 +89,6 @@ describe('getRandomInt', () => {
|
|
|
66
89
|
});
|
|
67
90
|
});
|
|
68
91
|
|
|
69
|
-
test('fixFloat()', () => {
|
|
70
|
-
expect(fixFloat(0.20000000000000004)).toBe(0.2);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('addFloat()', () => {
|
|
74
|
-
expect(addFloat(0.20000000000000004, 0.1000001)).toBe(0.3);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('subFloat()', () => {
|
|
78
|
-
expect(subFloat(0.20000000000000004, 0.1000001)).toBe(0.1);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
92
|
describe('number', () => {
|
|
82
93
|
test('isEq', () => {
|
|
83
94
|
expect(isEqual(1, 0)).toBe(false);
|
package/src/util/validate.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { http, HttpResponse } from 'msw';
|
|
2
|
+
|
|
3
|
+
export const fetchHandlers = [
|
|
4
|
+
http.get('/test.json', () => {
|
|
5
|
+
const response = HttpResponse.json(
|
|
6
|
+
{
|
|
7
|
+
success: true,
|
|
8
|
+
method: 'GET',
|
|
9
|
+
},
|
|
10
|
+
{ status: 200 }
|
|
11
|
+
);
|
|
12
|
+
return response;
|
|
13
|
+
}),
|
|
14
|
+
http.post('/test.json', () => {
|
|
15
|
+
const response = HttpResponse.json(
|
|
16
|
+
{
|
|
17
|
+
success: true,
|
|
18
|
+
method: 'POST',
|
|
19
|
+
},
|
|
20
|
+
{ status: 200 }
|
|
21
|
+
);
|
|
22
|
+
return response;
|
|
23
|
+
}),
|
|
24
|
+
http.post('/test_error.json', () => {
|
|
25
|
+
const response = HttpResponse.json(
|
|
26
|
+
{
|
|
27
|
+
success: false,
|
|
28
|
+
method: 'POST',
|
|
29
|
+
},
|
|
30
|
+
{ status: 404 }
|
|
31
|
+
);
|
|
32
|
+
return response;
|
|
33
|
+
}),
|
|
34
|
+
];
|
package/types/index.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export { typeCheck, typeCheckArray, typeCheckEnum } from "./typecheck/util.js";
|
|
|
17
17
|
export { delayPromise, loadJSON } from "./util/async.js";
|
|
18
18
|
export { getErrorDetails, getTypedError } from "./util/error.js";
|
|
19
19
|
export { FetchError, fetchRetry, HTTP_0_ANY } from "./util/fetch.js";
|
|
20
|
-
export {
|
|
21
|
-
export {
|
|
20
|
+
export { deg2rad, fixFloatPrecision, getRandomInt, isEqual, isGreater, isGreaterOrEqual, isInRange, isLess, isLessOrEqual, rad2deg } from "./util/number.js";
|
|
21
|
+
export { deepMerge, getObjArrayPropSum, getObjValueByPath, purgeObject, setObjValueByPath } from "./util/object.js";
|
|
22
22
|
export { getURLParam, sanitizeURLParam } from "./util/query.js";
|
|
23
23
|
export { addLeadingZero, capitalize, getTypeFromValue, saveAsFile, underscoreToCamelCase } from "./util/string.js";
|
|
24
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -3,11 +3,13 @@ export const PAGE_LIFECYCLE_STATE_ACTIVE: "active";
|
|
|
3
3
|
export const PAGE_LIFECYCLE_STATE_PASSIVE: "passive";
|
|
4
4
|
export const PAGE_LIFECYCLE_STATE_FROZEN: "frozen";
|
|
5
5
|
export const PAGE_LIFECYCLE_STATE_TERMINATED: "terminated";
|
|
6
|
+
export const PAGE_LIFECYCLE_STATES: Set<string>;
|
|
6
7
|
export const DOCUMENT_STATE_DOM_LOADED: "domLoaded";
|
|
7
8
|
export const DOCUMENT_STATE_FULLY_LOADED: "fullyLoaded";
|
|
8
9
|
export const DOCUMENT_STATE_COMPLETE: "complete";
|
|
9
10
|
export const DOCUMENT_STATE_INTERACTIVE: "interactive";
|
|
10
11
|
export const DOCUMENT_STATE_LOADING: "loading";
|
|
12
|
+
export const DOCUMENT_STATES: Set<string>;
|
|
11
13
|
export const PAGE_LIFECYCLE_STATE_CHANGE_EVENT: "pageLifecycleStateChange";
|
|
12
14
|
export const DOCUMENT_STATE_CHANGE_EVENT: "documentStateChange";
|
|
13
15
|
//# sourceMappingURL=const.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../../src/pagelifecycle/const.js"],"names":[],"mappings":"AAAA,0CAA2C,QAAQ,CAAC;AACpD,0CAA2C,QAAQ,CAAC;AACpD,2CAA4C,SAAS,CAAC;AACtD,0CAA2C,QAAQ,CAAC;AACpD,8CAA+C,YAAY,CAAC;AAE5D,wCAAyC,WAAW,CAAC;AACrD,0CAA2C,aAAa,CAAC;AACzD,sCAAuC,UAAU,CAAC;AAClD,yCAA0C,aAAa,CAAC;AACxD,qCAAsC,SAAS,CAAC;AAEhD,gDAAiD,0BAA0B,CAAC;AAC5E,0CAA2C,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../../src/pagelifecycle/const.js"],"names":[],"mappings":"AAAA,0CAA2C,QAAQ,CAAC;AACpD,0CAA2C,QAAQ,CAAC;AACpD,2CAA4C,SAAS,CAAC;AACtD,0CAA2C,QAAQ,CAAC;AACpD,8CAA+C,YAAY,CAAC;AAE5D,gDAMG;AAEH,wCAAyC,WAAW,CAAC;AACrD,0CAA2C,aAAa,CAAC;AACzD,sCAAuC,UAAU,CAAC;AAClD,yCAA0C,aAAa,CAAC;AACxD,qCAAsC,SAAS,CAAC;AAEhD,0CAMG;AAEH,gDAAiD,0BAA0B,CAAC;AAC5E,0CAA2C,qBAAqB,CAAC"}
|
|
@@ -2,7 +2,12 @@ export class TypeCheckError extends TypeError {
|
|
|
2
2
|
/**
|
|
3
3
|
* Creates a new `TypeCheckError` instance.
|
|
4
4
|
* @param {string} message - Error message.
|
|
5
|
+
* @param {{ cause?: unknown, value?: unknown }} [options] - Error options.
|
|
5
6
|
*/
|
|
6
|
-
constructor(message: string
|
|
7
|
+
constructor(message: string, options?: {
|
|
8
|
+
cause?: unknown;
|
|
9
|
+
value?: unknown;
|
|
10
|
+
});
|
|
11
|
+
value: unknown;
|
|
7
12
|
}
|
|
8
13
|
//# sourceMappingURL=TypeCheckError.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TypeCheckError.d.ts","sourceRoot":"","sources":["../../src/typecheck/TypeCheckError.js"],"names":[],"mappings":"AAAA;IACE
|
|
1
|
+
{"version":3,"file":"TypeCheckError.d.ts","sourceRoot":"","sources":["../../src/typecheck/TypeCheckError.js"],"names":[],"mappings":"AAAA;IACE;;;;OAIG;IACH,qBAHW,MAAM,YACN;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAM9C;IADC,eAA2B;CAE9B"}
|
package/types/util/async.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/util/async.js"],"names":[],"mappings":"AAKO,sCAHI,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAKtB;AAOG,8BAHI,MAAM,GACJ,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/util/async.js"],"names":[],"mappings":"AAKO,sCAHI,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAKtB;AAOG,8BAHI,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAQ5B"}
|
package/types/util/error.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/util/error.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/util/error.js"],"names":[],"mappings":"AAQO,uCAJI,KAAK,aACL,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,GACzB,MAAM,CAmBlB;AAOM,qCAHI,OAAO,GACL,KAAK,CAEiF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/util/fetch.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/util/fetch.js"],"names":[],"mappings":"AAcA,yBAA0B,CAAC,CAAC;AAE5B;IACE;;;;;;OAMG;IACH,qBALW,MAAM,YACN,MAAM,GAAG,GAAG,GAAG,OAAO,gBACtB,WAAW,YACX,QAAQ,EASlB;IAJC,iCAAwB;IACxB,0BAAgC;IAChC,mBAAwB;IACxB,cAAqC;CAExC;AASM,qCALI,MAAM,GAAG,GAAG,GAAG,OAAO,gBACtB,WAAW,iBACX;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,GAC9E,OAAO,CAAC,QAAQ,CAAC,CAkD7B"}
|
package/types/util/number.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
export function deg2rad(
|
|
1
|
+
export function deg2rad(degrees: number): number;
|
|
2
|
+
export function rad2deg(radians: number): number;
|
|
2
3
|
export function getRandomInt(min: number, max: number): number;
|
|
3
4
|
export function fixFloatPrecision(value: number | string): number;
|
|
4
|
-
export function fixFloat(value: number, p?: number): number;
|
|
5
|
-
export function addFloat(a: number, b: number): number;
|
|
6
|
-
export function subFloat(a: number, b: number): number;
|
|
7
5
|
export function isGreater(value: number, min: number): boolean;
|
|
8
6
|
export function isGreaterOrEqual(value: number, min: number): boolean;
|
|
9
7
|
export function isLess(value: number, min: number): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/util/number.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/util/number.js"],"names":[],"mappings":"AAcO,iCAJI,MAAM,GACJ,MAAM,CAQlB;AAQM,iCAJI,MAAM,GACJ,MAAM,CAQlB;AASM,kCALI,MAAM,OACN,MAAM,GACJ,MAAM,CAWlB;AAOM,yCAHI,MAAM,GAAG,MAAM,GACb,MAAM,CAQlB;AASM,iCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGgC;AAS7C,wCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGwC;AASrD,8BALI,MAAM,OACN,MAAM,GACJ,OAAO,CAG6B;AAS1C,qCALI,MAAM,OACN,MAAM,GACJ,OAAO,CAGqC;AAUlD,iCANI,MAAM,OACN,MAAM,OACN,MAAM,GACJ,OAAO,CAGsD;AASnE,+BALI,MAAM,YACN,MAAM,GACJ,OAAO,CAG0C"}
|
package/vitest.setup.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { setupServer } from 'msw/node';
|
|
2
|
+
import { beforeAll } from 'vitest';
|
|
3
|
+
import { handlers } from './tests/mocks/handlers.js';
|
|
4
|
+
|
|
5
|
+
const server = setupServer(...handlers);
|
|
6
|
+
|
|
7
|
+
beforeAll(() => {
|
|
8
|
+
server.listen();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
server.resetHandlers();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterAll(() => {
|
|
16
|
+
server.close();
|
|
17
|
+
});
|