-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Disallow Cancelable value in delay(msec, value) (#84)
* Disallow Cancelable value in delay(msec, value) * Add jest.config.js to npmignore
- Loading branch information
Showing
10 changed files
with
243 additions
and
104 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,12 @@ | ||
module.exports = { | ||
projects: [ | ||
'<rootDir>/internal/collections-hash', | ||
'<rootDir>/internal/ts-worker', | ||
'<rootDir>/packages/async-autoresetevent/jest.config.js', | ||
'<rootDir>/packages/async-barrier/jest.config.js', | ||
'<rootDir>/packages/async-conditionvariable/jest.config.js', | ||
'<rootDir>/packages/async-countdown/jest.config.js', | ||
'<rootDir>/packages/async-deferred/jest.config.js', | ||
'<rootDir>/packages/async-iter-fn/jest.config.js', | ||
'<rootDir>/packages/async-iter-fromsync/jest.config.js', | ||
'<rootDir>/packages/async-iter-query/jest.config.js', | ||
'<rootDir>/packages/async-manualresetevent/jest.config.js', | ||
'<rootDir>/packages/async-mutex/jest.config.js', | ||
'<rootDir>/packages/async-queue/jest.config.js', | ||
'<rootDir>/packages/async-readerwriterlock/jest.config.js', | ||
'<rootDir>/packages/async-semaphore/jest.config.js', | ||
'<rootDir>/packages/async-stack/jest.config.js', | ||
'<rootDir>/packages/canceltoken/jest.config.js', | ||
'<rootDir>/packages/collection-core/jest.config.js', | ||
'<rootDir>/packages/collection-core-shim/jest.config.js', | ||
'<rootDir>/packages/collections-hashmap/jest.config.js', | ||
'<rootDir>/packages/collections-hashset/jest.config.js', | ||
'<rootDir>/packages/collections-multimap/jest.config.js', | ||
'<rootDir>/packages/collections-sortedmap/jest.config.js', | ||
'<rootDir>/packages/collections-sortedset/jest.config.js', | ||
'<rootDir>/packages/decorators/jest.config.js', | ||
'<rootDir>/packages/disposable/jest.config.js', | ||
'<rootDir>/packages/equatable/jest.config.js', | ||
'<rootDir>/packages/equatable-shim/jest.config.js', | ||
'<rootDir>/packages/fn/jest.config.js', | ||
'<rootDir>/packages/fn-partial/jest.config.js', | ||
'<rootDir>/packages/indexed-object/jest.config.js', | ||
'<rootDir>/packages/iter-fn/jest.config.js', | ||
'<rootDir>/packages/iter-lookup/jest.config.js', | ||
'<rootDir>/packages/iter-query/jest.config.js', | ||
'<rootDir>/packages/lazy/jest.config.js', | ||
'<rootDir>/packages/ref/jest.config.js', | ||
'<rootDir>/packages/reflect-metadata-compat/jest.config.js', | ||
'<rootDir>/packages/struct-type/jest.config.js', | ||
'<rootDir>/packages/threading-autoresetevent/jest.config.js', | ||
'<rootDir>/packages/threading-conditionvariable/jest.config.js', | ||
'<rootDir>/packages/threading-manualresetevent/jest.config.js', | ||
'<rootDir>/packages/threading-mutex/jest.config.js', | ||
'<rootDir>/packages/threading-sleep/jest.config.js' | ||
], | ||
}; | ||
const fs = require("node:fs"); | ||
const path = require("node:path"); | ||
const projects = []; | ||
for (const workspaceRoot of ["internal", "packages"]) { | ||
for (const entry of fs.readdirSync(workspaceRoot, { withFileTypes: true })) { | ||
if (!entry.isDirectory()) continue; | ||
if (fs.existsSync(path.join(workspaceRoot, entry.name, "jest.config.js"))) { | ||
projects.push(`<rootDir>/${workspaceRoot}/${entry.name}/jest.config.js`); | ||
} | ||
} | ||
} | ||
module.exports = { projects }; |
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 |
---|---|---|
@@ -1,46 +1,12 @@ | ||
module.exports = { | ||
projects: [ | ||
'<rootDir>/internal/collections-hash/jest.esm.config.js', | ||
'<rootDir>/packages/async-autoresetevent/jest.esm.config.js', | ||
'<rootDir>/packages/async-barrier/jest.esm.config.js', | ||
'<rootDir>/packages/async-conditionvariable/jest.esm.config.js', | ||
'<rootDir>/packages/async-countdown/jest.esm.config.js', | ||
'<rootDir>/packages/async-deferred/jest.esm.config.js', | ||
'<rootDir>/packages/async-iter-fn/jest.esm.config.js', | ||
'<rootDir>/packages/async-iter-fromsync/jest.esm.config.js', | ||
'<rootDir>/packages/async-iter-query/jest.esm.config.js', | ||
'<rootDir>/packages/async-manualresetevent/jest.esm.config.js', | ||
'<rootDir>/packages/async-mutex/jest.esm.config.js', | ||
'<rootDir>/packages/async-queue/jest.esm.config.js', | ||
'<rootDir>/packages/async-readerwriterlock/jest.esm.config.js', | ||
'<rootDir>/packages/async-semaphore/jest.esm.config.js', | ||
'<rootDir>/packages/async-stack/jest.esm.config.js', | ||
'<rootDir>/packages/canceltoken/jest.esm.config.js', | ||
'<rootDir>/packages/collection-core/jest.esm.config.js', | ||
'<rootDir>/packages/collection-core-shim/jest.esm.config.js', | ||
'<rootDir>/packages/collections-hashmap/jest.esm.config.js', | ||
'<rootDir>/packages/collections-hashset/jest.esm.config.js', | ||
'<rootDir>/packages/collections-multimap/jest.esm.config.js', | ||
'<rootDir>/packages/collections-sortedmap/jest.esm.config.js', | ||
'<rootDir>/packages/collections-sortedset/jest.esm.config.js', | ||
'<rootDir>/packages/decorators/jest.esm.config.js', | ||
'<rootDir>/packages/disposable/jest.esm.config.js', | ||
'<rootDir>/packages/equatable/jest.esm.config.js', | ||
'<rootDir>/packages/equatable-shim/jest.esm.config.js', | ||
'<rootDir>/packages/fn/jest.esm.config.js', | ||
'<rootDir>/packages/fn-partial/jest.esm.config.js', | ||
'<rootDir>/packages/indexed-object/jest.esm.config.js', | ||
'<rootDir>/packages/iter-fn/jest.esm.config.js', | ||
'<rootDir>/packages/iter-lookup/jest.esm.config.js', | ||
'<rootDir>/packages/iter-query/jest.esm.config.js', | ||
'<rootDir>/packages/lazy/jest.esm.config.js', | ||
'<rootDir>/packages/ref/jest.esm.config.js', | ||
'<rootDir>/packages/reflect-metadata-compat/jest.esm.config.js', | ||
'<rootDir>/packages/struct-type/jest.esm.config.js', | ||
'<rootDir>/packages/threading-autoresetevent/jest.esm.config.js', | ||
'<rootDir>/packages/threading-conditionvariable/jest.esm.config.js', | ||
'<rootDir>/packages/threading-manualresetevent/jest.esm.config.js', | ||
'<rootDir>/packages/threading-mutex/jest.esm.config.js', | ||
'<rootDir>/packages/threading-sleep/jest.esm.config.js', | ||
], | ||
}; | ||
const fs = require("node:fs"); | ||
const path = require("node:path"); | ||
const projects = []; | ||
for (const workspaceRoot of ["internal", "packages"]) { | ||
for (const entry of fs.readdirSync(workspaceRoot, { withFileTypes: true })) { | ||
if (!entry.isDirectory()) continue; | ||
if (fs.existsSync(path.join(workspaceRoot, entry.name, "jest.esm.config.js"))) { | ||
projects.push(`<rootDir>/${workspaceRoot}/${entry.name}/jest.esm.config.js`); | ||
} | ||
} | ||
} | ||
module.exports = { projects }; |
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 |
---|---|---|
|
@@ -8,4 +8,5 @@ api-extractor.json | |
tsconfig*.json | ||
*.tsbuildinfo | ||
.npmfiles | ||
*.tgz | ||
*.tgz | ||
/jest*.config.js |
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,5 @@ | ||
module.exports = { | ||
...require("../../jest.base.config"), | ||
displayName: "async-delay", | ||
roots: ["<rootDir>"] | ||
}; |
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,4 @@ | ||
module.exports = { | ||
...require("../../jest.esm.base.config"), | ||
displayName: `${require("./jest.config.js").displayName}`, | ||
}; |
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,183 @@ | ||
import { Cancelable, CancelableCancelSignal, CancelError, CancelSubscription } from "@esfx/cancelable"; | ||
import { delay } from ".."; | ||
|
||
const waitOne = () => new Promise(res => jest.requireActual("timers").setImmediate(res)); | ||
|
||
jest.useFakeTimers(); | ||
describe("delay(msec)", () => { | ||
it("rejects if msec not number", async () => { | ||
expect.assertions(1); | ||
await expect(delay({} as any)).rejects.toThrow(TypeError); | ||
}); | ||
it("rejects if msec is negative", async () => { | ||
expect.assertions(1); | ||
await expect(delay(-1)).rejects.toThrow(TypeError); | ||
}); | ||
it("rejects if msec is Infinity", async () => { | ||
expect.assertions(1); | ||
await expect(delay(Infinity)).rejects.toThrow(TypeError); | ||
}); | ||
it("rejects if msec is NaN", async () => { | ||
expect.assertions(1); | ||
await expect(delay(NaN)).rejects.toThrow(TypeError); | ||
}); | ||
it("does not resolve synchronously when msec = 0", async () => { | ||
expect.assertions(2); | ||
|
||
let delayPromiseResolved = false; | ||
const delayPromise = delay(0).then(() => { delayPromiseResolved = true }); | ||
expect(delayPromiseResolved).toBe(false); // not resolved synchronously | ||
|
||
jest.advanceTimersByTime(1); | ||
await waitOne(); | ||
expect(delayPromiseResolved).toBe(true); // resolved immediately | ||
|
||
await delayPromise; | ||
}); | ||
it("resolves after timeout", async () => { | ||
expect.assertions(3); | ||
|
||
let delayPromiseResolved = false; | ||
const delayPromise = delay(100).then(() => { delayPromiseResolved = true }); | ||
expect(delayPromiseResolved).toBe(false); // not resolved synchronously | ||
|
||
await waitOne(); | ||
expect(delayPromiseResolved).toBe(false); // not resolved immediately | ||
|
||
jest.advanceTimersByTime(100); | ||
|
||
await delayPromise; | ||
expect(delayPromiseResolved).toBe(true); // resolved after timeout | ||
}); | ||
it("resolves to undefined", async () => { | ||
expect.assertions(1); | ||
const delayPromise = delay(100); | ||
jest.advanceTimersByTime(100); | ||
await expect(delayPromise).resolves.toBeUndefined(); | ||
}); | ||
}); | ||
describe("delay(msec, value)", () => { | ||
it("rejects if value is Canceable", async () => { | ||
const cancelable: Cancelable = { [Cancelable.cancelSignal]: jest.fn() }; | ||
expect.assertions(1); | ||
await expect(delay(0, cancelable as unknown)).rejects.toThrow(TypeError); | ||
}); | ||
it("resolves to value when non-promise", async () => { | ||
expect.assertions(1); | ||
const delayPromise = delay(100, 1); | ||
jest.advanceTimersByTime(100); | ||
await expect(delayPromise).resolves.toBe(1); | ||
}); | ||
it("resolves to awaited value when promise", async () => { | ||
expect.assertions(1); | ||
const delayPromise = delay(100, Promise.resolve(1)); | ||
jest.advanceTimersByTime(100); | ||
await expect(delayPromise).resolves.toBe(1); | ||
}); | ||
it("rejects if promise value rejects", async () => { | ||
expect.assertions(1); | ||
const delayPromise = delay(100, Promise.reject("rejected")); | ||
jest.advanceTimersByTime(100); | ||
await expect(delayPromise).rejects.toBe("rejected"); | ||
}); | ||
}); | ||
describe("delay(cancelable, msec)", () => { | ||
it("rejects if cancelable is canceled", async () => { | ||
expect.assertions(1); | ||
const cancelable: CancelableCancelSignal = { | ||
[Cancelable.cancelSignal]() { return this; }, | ||
signaled: true, | ||
reason: new CancelError(), | ||
subscribe: jest.fn() | ||
}; | ||
const delayPromise = delay(cancelable, 0); | ||
jest.advanceTimersByTime(0); | ||
await expect(delayPromise).rejects.toBe(cancelable.reason); | ||
}); | ||
it("rejects if cancelable is non-null, non-undefined, non-cancelable", async () => { | ||
expect.assertions(1); | ||
const delayPromise = delay({} as any, 0); | ||
jest.advanceTimersByTime(0); | ||
await expect(delayPromise).rejects.toThrow(TypeError); | ||
}); | ||
it("cancelable can be null", async () => { | ||
expect.assertions(1); | ||
const delayPromise = delay(null, 0); | ||
jest.advanceTimersByTime(0); | ||
await expect(delayPromise).resolves.toBeUndefined(); | ||
}); | ||
it("cancelable can be undefined", async () => { | ||
expect.assertions(1); | ||
const delayPromise = delay(undefined, 0); | ||
jest.advanceTimersByTime(0); | ||
await expect(delayPromise).resolves.toBeUndefined(); | ||
}); | ||
it("cancelable can Cancelable", async () => { | ||
expect.assertions(1); | ||
const subscription = CancelSubscription.create(jest.fn()); | ||
const cancelable: CancelableCancelSignal = { | ||
[Cancelable.cancelSignal]() { return this; }, | ||
signaled: false, | ||
reason: undefined, | ||
subscribe: jest.fn().mockReturnValue(subscription) | ||
}; | ||
const delayPromise = delay(cancelable, 0); | ||
jest.advanceTimersByTime(0); | ||
await expect(delayPromise).resolves.toBeUndefined(); | ||
}); | ||
it("subscribes to Cancelable", async () => { | ||
expect.assertions(1); | ||
const subscription = CancelSubscription.create(jest.fn()); | ||
const cancelable: CancelableCancelSignal = { | ||
[Cancelable.cancelSignal]() { return this; }, | ||
signaled: false, | ||
reason: undefined, | ||
subscribe: jest.fn().mockReturnValue(subscription) | ||
}; | ||
delay(cancelable, 0); | ||
expect(cancelable.subscribe).toHaveBeenCalled(); | ||
}); | ||
it("unsubscribes from Cancelable subscription on resolve", async () => { | ||
expect.assertions(1); | ||
const unsubscribe = jest.fn(); | ||
const subscription = CancelSubscription.create(unsubscribe); | ||
const cancelable: CancelableCancelSignal = { | ||
[Cancelable.cancelSignal]() { return this; }, | ||
signaled: false, | ||
reason: undefined, | ||
subscribe: jest.fn().mockReturnValue(subscription) | ||
}; | ||
const delayPromise = delay(cancelable, 0); | ||
jest.advanceTimersByTime(0); | ||
await delayPromise; | ||
expect(unsubscribe).toHaveBeenCalled(); | ||
}); | ||
it("rejects if Cancelable is signaled after start", async () => { | ||
expect.assertions(1); | ||
let onSignaled!: () => void; | ||
let signaled = false; | ||
let reason: unknown = undefined; | ||
const unsubscribe = jest.fn(); | ||
const subscription = CancelSubscription.create(unsubscribe); | ||
const cancelable: CancelableCancelSignal = { | ||
[Cancelable.cancelSignal]() { return this; }, | ||
get signaled() { return signaled; }, | ||
get reason() { return reason; }, | ||
subscribe: (cb) => (onSignaled = cb, subscription), | ||
}; | ||
const delayPromise = delay(cancelable, 0); | ||
signaled = true; | ||
reason = new CancelError(); | ||
onSignaled(); | ||
await expect(delayPromise).rejects.toThrow(CancelError); | ||
}); | ||
}); | ||
describe("delay(cancelable, msec, value)", () => { | ||
it("does not reject if value is Canceable", async () => { | ||
expect.assertions(1); | ||
const cancelable: Cancelable = { [Cancelable.cancelSignal]: jest.fn() }; | ||
const delayPromise = delay(undefined, 0, cancelable); | ||
jest.advanceTimersByTime(0); | ||
await expect(delayPromise).resolves.toBe(cancelable); | ||
}); | ||
}); |
Oops, something went wrong.