-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
79fc858
commit 994f2bb
Showing
19 changed files
with
345 additions
and
44 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,43 @@ | ||
import { describe, test, expect } from 'vitest' | ||
import { CustomError } from '@blackglory/errors' | ||
import { AbortError } from '@src/abort-error.js' | ||
import { AbortController } from '@src/abort-controller.js' | ||
import { AbortError as AbortErrorFromExtraFetch } from 'node-fetch' | ||
import { getError } from 'return-style' | ||
|
||
describe('AbortError', () => { | ||
describe('AbortError instanceof Error', () => { | ||
it('true', () => { | ||
const abortError = new AbortError() | ||
test('AbortError instanceof Error', () => { | ||
const abortError = new AbortError() | ||
|
||
expect(abortError).toBeInstanceOf(Error) | ||
}) | ||
expect(abortError).toBeInstanceOf(Error) | ||
}) | ||
|
||
describe('AbortError instanceof CustomError', () => { | ||
it('true', () => { | ||
const abortError = new AbortError() | ||
test('AbortError instanceof CustomError', () => { | ||
const abortError = new AbortError() | ||
|
||
expect(abortError).toBeInstanceOf(CustomError) | ||
}) | ||
expect(abortError).toBeInstanceOf(CustomError) | ||
}) | ||
|
||
describe('AbortError instanceof AbortError', () => { | ||
describe('non-native AbortError', () => { | ||
it('true', () => { | ||
const abortError = new AbortError() | ||
test('non-native AbortError', () => { | ||
const abortError = new AbortError() | ||
|
||
expect(abortError).toBeInstanceOf(AbortError) | ||
}) | ||
expect(abortError).toBeInstanceOf(AbortError) | ||
}) | ||
|
||
describe('native AbortError', () => { | ||
it('true', () => { | ||
const controller = new AbortController() | ||
controller.abort() | ||
test('native AbortError', () => { | ||
const controller = new AbortController() | ||
controller.abort() | ||
|
||
const abortError = getError(() => controller.signal.throwIfAborted()) | ||
const abortError = getError(() => controller.signal.throwIfAborted()) | ||
|
||
expect(abortError).toBeInstanceOf(AbortError) | ||
}) | ||
expect(abortError).toBeInstanceOf(AbortError) | ||
}) | ||
|
||
describe('AbortError from ExtraFetch instanceof AbortError ', () => { | ||
it('true', () => { | ||
const abortError = new AbortErrorFromExtraFetch() | ||
test('AbortError from ExtraFetch instanceof AbortError ', () => { | ||
const abortError = new AbortErrorFromExtraFetch() | ||
|
||
expect(abortError).toBeInstanceOf(AbortError) | ||
}) | ||
expect(abortError).toBeInstanceOf(AbortError) | ||
}) | ||
}) | ||
}) |
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,254 @@ | ||
import { describe, test, vi, expect } from 'vitest' | ||
import { lastCallOnly } from '@src/last-call-only.js' | ||
import { waitForTimeout, waitForAllMacrotasksProcessed } from '@blackglory/wait-for' | ||
import { getErrorPromise, toResultPromise } from 'return-style' | ||
import { AbortError } from '@src/abort-error.js' | ||
|
||
describe('lastCallOnly', () => { | ||
describe('without signal', () => { | ||
test('first call', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
|
||
const newFn = lastCallOnly(fn) | ||
const result = await newFn('foo', undefined) | ||
|
||
expect(fn).toBeCalledTimes(1) | ||
expect(result).toBe('foo') | ||
}) | ||
|
||
describe('not first call', () => { | ||
test('sync', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', undefined)) | ||
const promise2 = toResultPromise(newFn('bar', undefined)) | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(1) | ||
expect(fn.mock.calls[0][1].aborted).toBe(false) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrap()).toBe('bar') | ||
}) | ||
|
||
test('async', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', undefined)) | ||
await waitForAllMacrotasksProcessed() | ||
const promise2 = toResultPromise(newFn('bar', undefined)) | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(2) | ||
expect(fn.mock.calls[0][1].aborted).toBe(true) | ||
expect(fn.mock.calls[1][1].aborted).toBe(false) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrap()).toBe('bar') | ||
}) | ||
}) | ||
}) | ||
|
||
describe('with aborted signal', () => { | ||
test('first call', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
controller.abort() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const err = await getErrorPromise(newFn('foo', controller.signal)) | ||
|
||
expect(fn).toBeCalledTimes(0) | ||
expect(err).toBeInstanceOf(AbortError) | ||
}) | ||
|
||
describe.each([ | ||
['Falsy', undefined] | ||
, ['AbortSignal', new AbortController().signal] | ||
])('not first call (first call with %s)', (_, firstCallSignal) => { | ||
test('sync', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
controller.abort() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', firstCallSignal)) | ||
const promise2 = toResultPromise(newFn('bar', controller.signal)) | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(0) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrapErr()).toBeInstanceOf(AbortError) | ||
}) | ||
|
||
test('async', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
controller.abort() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', firstCallSignal)) | ||
await waitForAllMacrotasksProcessed() | ||
const promise2 = toResultPromise(newFn('bar', controller.signal)) | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(1) | ||
expect(fn.mock.calls[0][1].aborted).toBe(true) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrapErr()).toBeInstanceOf(AbortError) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('with non-aborted signal', () => { | ||
describe('first call', () => { | ||
test('no abort', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const result = await newFn('foo', controller.signal) | ||
|
||
expect(fn).toBeCalledTimes(1) | ||
expect(fn.mock.calls[0][1].aborted).toBe(false) | ||
expect(result).toBe('foo') | ||
}) | ||
|
||
test('abort', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise = getErrorPromise(newFn('foo', controller.signal)) | ||
await waitForAllMacrotasksProcessed() | ||
controller.abort() | ||
const err = await promise | ||
|
||
expect(fn).toBeCalledTimes(1) | ||
expect(fn.mock.calls[0][1].aborted).toBe(true) | ||
expect(err).toBeInstanceOf(AbortError) | ||
}) | ||
}) | ||
|
||
describe.each([ | ||
['Falsy', undefined] | ||
, ['AbortSignal', new AbortController().signal] | ||
])('not first call (first call with %s)', (_, firstCallSignal) => { | ||
describe('sync', () => { | ||
test('no abort', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', firstCallSignal)) | ||
const promise2 = toResultPromise(newFn('bar', controller.signal)) | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(1) | ||
expect(fn.mock.calls[0][1].aborted).toBe(false) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrap()).toBe('bar') | ||
}) | ||
|
||
test('abort', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', firstCallSignal)) | ||
const promise2 = toResultPromise(newFn('bar', controller.signal)) | ||
await waitForAllMacrotasksProcessed() | ||
controller.abort() | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(1) | ||
expect(fn.mock.calls[0][1].aborted).toBe(true) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrapErr()).toBeInstanceOf(AbortError) | ||
}) | ||
}) | ||
|
||
describe('async', () => { | ||
test('no abort', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', firstCallSignal)) | ||
await waitForAllMacrotasksProcessed() | ||
const promise2 = toResultPromise(newFn('bar', controller.signal)) | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(2) | ||
expect(fn.mock.calls[0][1].aborted).toBe(true) | ||
expect(fn.mock.calls[1][1].aborted).toBe(false) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrap()).toBe('bar') | ||
}) | ||
|
||
test('abort', async () => { | ||
const fn = vi.fn(async (value: string, signal: AbortSignal) => { | ||
await waitForTimeout(500, signal) | ||
return value | ||
}) | ||
const controller = new AbortController() | ||
|
||
const newFn = lastCallOnly(fn) | ||
const promise1 = toResultPromise(newFn('foo', firstCallSignal)) | ||
await waitForAllMacrotasksProcessed() | ||
const promise2 = toResultPromise(newFn('bar', controller.signal)) | ||
await waitForAllMacrotasksProcessed() | ||
controller.abort() | ||
const result1 = await promise1 | ||
const result2 = await promise2 | ||
|
||
expect(fn).toBeCalledTimes(2) | ||
expect(fn.mock.calls[0][1].aborted).toBe(true) | ||
expect(fn.mock.calls[1][1].aborted).toBe(true) | ||
expect(result1.unwrapErr()).toBeInstanceOf(AbortError) | ||
expect(result2.unwrapErr()).toBeInstanceOf(AbortError) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
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
Oops, something went wrong.