-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Added `limitExecutionCount`, which can wrap around a function and prevent repeated execution with an exception being thrown. - Wrapped `runDomSampler` with `limitExecutionCount`.
- Loading branch information
1 parent
6cb11b2
commit e8c7341
Showing
6 changed files
with
190 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import type { LimitExecutionCountReset } from './execution-count' | ||
import { limitExecutionCount } from './execution-count' | ||
|
||
describe('limitExecutionCount', () => { | ||
it('the wrapped function should retain its type', () => { | ||
function putNumberOnString(str: string, num: number): string { | ||
return `${str}-${num}` | ||
} | ||
const wrappedFunction = limitExecutionCount( | ||
{ maximumExecutionCount: 1 }, | ||
putNumberOnString, | ||
).wrappedFunction | ||
expect(wrappedFunction('hello', 1)).toBe('hello-1') | ||
}) | ||
it('should let a function be called the maximum number of times', () => { | ||
const fn = jest.fn() | ||
const { wrappedFunction } = limitExecutionCount({ maximumExecutionCount: 3 }, fn) | ||
wrappedFunction() | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(fn).toHaveBeenCalledTimes(3) | ||
}) | ||
it('should let a function be called the maximum number of times, then reset and called the maximum number of times again', () => { | ||
const fn = jest.fn() | ||
const { wrappedFunction, resetCount } = limitExecutionCount({ maximumExecutionCount: 3 }, fn) | ||
wrappedFunction() | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(fn).toHaveBeenCalledTimes(3) | ||
resetCount() | ||
wrappedFunction() | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(fn).toHaveBeenCalledTimes(6) | ||
}) | ||
it('should let a function be called the maximum number of times, then reset via the reset array and called the maximum number of times again', () => { | ||
const fn = jest.fn() | ||
const resetArray: Array<LimitExecutionCountReset> = [] | ||
const { wrappedFunction } = limitExecutionCount( | ||
{ maximumExecutionCount: 3, addToResetArray: resetArray }, | ||
fn, | ||
) | ||
wrappedFunction() | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(fn).toHaveBeenCalledTimes(3) | ||
resetArray.forEach((reset) => reset()) | ||
wrappedFunction() | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(fn).toHaveBeenCalledTimes(6) | ||
}) | ||
it('should throw an exception if the function is called more than the maximum number of times', () => { | ||
const fn = jest.fn() | ||
const { wrappedFunction } = limitExecutionCount({ maximumExecutionCount: 2 }, fn) | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(() => { | ||
wrappedFunction() | ||
}).toThrowErrorMatchingInlineSnapshot(`"Function exceeded maximum execution count of 2."`) | ||
}) | ||
it('should throw an exception if the function is called more than the maximum number of times, after a reset', () => { | ||
const fn = jest.fn() | ||
const { wrappedFunction, resetCount } = limitExecutionCount({ maximumExecutionCount: 2 }, fn) | ||
wrappedFunction() | ||
wrappedFunction() | ||
resetCount() | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(() => { | ||
wrappedFunction() | ||
}).toThrowErrorMatchingInlineSnapshot(`"Function exceeded maximum execution count of 2."`) | ||
}) | ||
it('should throw an exception if the function is called more than the maximum number of times, after a reset via the reset array', () => { | ||
const fn = jest.fn() | ||
const resetArray: Array<LimitExecutionCountReset> = [] | ||
const { wrappedFunction } = limitExecutionCount( | ||
{ maximumExecutionCount: 2, addToResetArray: resetArray }, | ||
fn, | ||
) | ||
wrappedFunction() | ||
wrappedFunction() | ||
resetArray.forEach((reset) => reset()) | ||
wrappedFunction() | ||
wrappedFunction() | ||
expect(() => { | ||
wrappedFunction() | ||
}).toThrowErrorMatchingInlineSnapshot(`"Function exceeded maximum execution count of 2."`) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
export type LimitExecutionCountReset = () => void | ||
|
||
export interface LimitExecutionCountOptions { | ||
maximumExecutionCount: number | ||
addToResetArray: Array<LimitExecutionCountReset> | null | ||
} | ||
|
||
export interface LimitExecutionCountState { | ||
executionCount: number | ||
} | ||
|
||
export interface LimitExecutionCountResult<Args extends ReadonlyArray<unknown>, Result> { | ||
wrappedFunction: (...args: Args) => Result | ||
resetCount: LimitExecutionCountReset | ||
} | ||
|
||
const defaultOptions: LimitExecutionCountOptions = { | ||
maximumExecutionCount: 1, | ||
addToResetArray: null, | ||
} | ||
|
||
export function limitExecutionCount<Args extends ReadonlyArray<unknown>, Result>( | ||
options: Partial<LimitExecutionCountOptions>, | ||
fn: (...args: Args) => Result, | ||
): LimitExecutionCountResult<Args, Result> { | ||
// Setup the default and specified options and state. | ||
const fullySpecifiedOptions: LimitExecutionCountOptions = { | ||
...defaultOptions, | ||
...options, | ||
} | ||
let state: LimitExecutionCountState = { | ||
executionCount: 0, | ||
} | ||
|
||
// Wrap the function supplied by the user. | ||
const wrappedFunction = (...args: Args): Result => { | ||
if (state.executionCount >= fullySpecifiedOptions.maximumExecutionCount) { | ||
throw new Error( | ||
`Function exceeded maximum execution count of ${fullySpecifiedOptions.maximumExecutionCount}.`, | ||
) | ||
} | ||
|
||
state.executionCount += 1 | ||
return fn(...args) | ||
} | ||
|
||
// Create the function for resetting the count. | ||
const resetCount = (): void => { | ||
state.executionCount = 0 | ||
} | ||
|
||
// Add into the reset array. | ||
if (options.addToResetArray != null) { | ||
options.addToResetArray.push(resetCount) | ||
} | ||
|
||
// Build the result. | ||
return { | ||
wrappedFunction: wrappedFunction, | ||
resetCount: resetCount, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters