-
Notifications
You must be signed in to change notification settings - Fork 416
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: a bunch of tests for ActionQueue and related code
- Loading branch information
Showing
33 changed files
with
3,486 additions
and
885 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 |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* eslint-disable no-await-in-loop */ | ||
import { EventEmitter, getFileIdx } from '@linaria/utils'; | ||
|
||
import { AsyncActionQueue } from '../../src/transform-stages/queue/ActionQueue'; | ||
import type { | ||
IBaseServices, | ||
Handlers, | ||
} from '../../src/transform-stages/queue/GenericActionQueue'; | ||
import { rootLog } from '../../src/transform-stages/queue/rootLog'; | ||
import type { | ||
IBaseEntrypoint, | ||
IProcessEntrypointAction, | ||
} from '../../src/transform-stages/queue/types'; | ||
|
||
const createEntrypoint = (name: string): IBaseEntrypoint => ({ | ||
name, | ||
idx: getFileIdx(name).toString().padStart(5, '0'), | ||
only: ['default'], | ||
log: rootLog, | ||
parent: null, | ||
}); | ||
|
||
type AsyncHandlers = Handlers<Promise<void> | void, IBaseServices>; | ||
|
||
describe('AsyncActionQueue', () => { | ||
let services: IBaseServices; | ||
let handlers: AsyncHandlers; | ||
beforeEach(() => { | ||
handlers = { | ||
addToCodeCache: jest.fn(), | ||
transform: jest.fn(), | ||
explodeReexports: jest.fn(), | ||
processEntrypoint: jest.fn(), | ||
processImports: jest.fn(), | ||
getExports: jest.fn(), | ||
resolveImports: jest.fn(), | ||
}; | ||
|
||
services = { | ||
eventEmitter: EventEmitter.dummy, | ||
}; | ||
}); | ||
|
||
const createQueueFor = ( | ||
name: string, | ||
customHandlers: Partial<AsyncHandlers> = {} | ||
) => { | ||
const entrypoint = createEntrypoint(name); | ||
return new AsyncActionQueue( | ||
services, | ||
{ ...handlers, ...customHandlers }, | ||
entrypoint | ||
); | ||
}; | ||
|
||
it('should call actions according to its weight', async () => { | ||
const processEntrypoint = jest.fn( | ||
(_services: IBaseServices, action: IProcessEntrypointAction) => { | ||
action.next('transform', action.entrypoint, {}); | ||
action.next('addToCodeCache', action.entrypoint, { | ||
data: { | ||
imports: null, | ||
result: { | ||
code: '', | ||
metadata: undefined, | ||
}, | ||
only: [], | ||
}, | ||
}); | ||
action.next('explodeReexports', action.entrypoint, {}); | ||
action.next('processImports', action.entrypoint, { | ||
resolved: [], | ||
}); | ||
action.next('getExports', action.entrypoint, {}); | ||
action.next('resolveImports', action.entrypoint, { | ||
imports: null, | ||
}); | ||
} | ||
); | ||
|
||
const queue = createQueueFor('/foo/bar.js', { processEntrypoint }); | ||
await queue.runNext(); // processEntrypoint | ||
|
||
const rightOrder: (keyof AsyncHandlers)[] = [ | ||
'resolveImports', | ||
'getExports', | ||
'processImports', | ||
'explodeReexports', | ||
'transform', | ||
'addToCodeCache', | ||
]; | ||
|
||
for (let i = 0; i < rightOrder.length; i++) { | ||
await queue.runNext(); | ||
expect(handlers[rightOrder[i]]).toHaveBeenCalledTimes(1); | ||
} | ||
}); | ||
}); |
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,180 @@ | ||
/* eslint-disable no-await-in-loop */ | ||
import { EventEmitter, getFileIdx } from '@linaria/utils'; | ||
|
||
import { | ||
AsyncActionQueue, | ||
SyncActionQueue, | ||
} from '../../src/transform-stages/queue/ActionQueue'; | ||
import type { | ||
IBaseServices, | ||
Handlers, | ||
Handler, | ||
} from '../../src/transform-stages/queue/GenericActionQueue'; | ||
import { rootLog } from '../../src/transform-stages/queue/rootLog'; | ||
import type { | ||
IBaseEntrypoint, | ||
IGetExportsAction, | ||
IProcessEntrypointAction, | ||
IBaseAction, | ||
} from '../../src/transform-stages/queue/types'; | ||
|
||
const createEntrypoint = (name: string): IBaseEntrypoint => ({ | ||
name, | ||
idx: getFileIdx(name).toString().padStart(5, '0'), | ||
only: ['default'], | ||
log: rootLog, | ||
parent: null, | ||
}); | ||
|
||
type Res = Promise<void> | void; | ||
type UniversalHandlers = Handlers<Res, IBaseServices>; | ||
|
||
type GetHandler<T extends IBaseAction> = Handler<IBaseServices, T, Res>; | ||
|
||
type Queues = typeof AsyncActionQueue | typeof SyncActionQueue; | ||
|
||
describe.each<[string, Queues]>([ | ||
['AsyncActionQueue', AsyncActionQueue], | ||
['SyncActionQueue', SyncActionQueue], | ||
])('%s', (_name, Queue) => { | ||
let services: IBaseServices; | ||
let handlers: UniversalHandlers; | ||
beforeEach(() => { | ||
handlers = { | ||
addToCodeCache: jest.fn(), | ||
transform: jest.fn(), | ||
explodeReexports: jest.fn(), | ||
processEntrypoint: jest.fn(), | ||
processImports: jest.fn(), | ||
getExports: jest.fn(), | ||
resolveImports: jest.fn(), | ||
}; | ||
|
||
services = { | ||
eventEmitter: EventEmitter.dummy, | ||
}; | ||
}); | ||
|
||
const createQueueFor = ( | ||
name: string, | ||
customHandlers: Partial<UniversalHandlers> = {} | ||
) => { | ||
const entrypoint = createEntrypoint(name); | ||
return new Queue(services, { ...handlers, ...customHandlers }, entrypoint); | ||
}; | ||
|
||
describe('base', () => { | ||
it('should be defined', () => { | ||
expect(Queue).toBeDefined(); | ||
}); | ||
|
||
it('should create queue', () => { | ||
const queue = createQueueFor('/foo/bar.js'); | ||
expect(queue).toBeDefined(); | ||
expect(queue.isEmpty()).toBe(false); | ||
Object.values(handlers).forEach((handler) => { | ||
expect(handler).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
it('should run processEntrypoint', () => { | ||
const queue = createQueueFor('/foo/bar.js'); | ||
queue.runNext(); | ||
expect(handlers.processEntrypoint).toHaveBeenCalledTimes(1); | ||
expect(queue.isEmpty()).toBe(true); | ||
}); | ||
|
||
it('should process next calls', async () => { | ||
const processEntrypoint: GetHandler<IProcessEntrypointAction> = ( | ||
_services, | ||
action | ||
) => { | ||
action.next('transform', action.entrypoint, {}); | ||
}; | ||
|
||
const queue = createQueueFor('/foo/bar.js', { processEntrypoint }); | ||
await queue.runNext(); | ||
expect(queue.isEmpty()).toBe(false); | ||
await queue.runNext(); | ||
expect(queue.isEmpty()).toBe(true); | ||
expect(handlers.transform).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should call actions according to its weight', async () => { | ||
const processEntrypoint: GetHandler<IProcessEntrypointAction> = ( | ||
_services, | ||
action | ||
) => { | ||
action.next('transform', action.entrypoint, {}); | ||
action.next('addToCodeCache', action.entrypoint, { | ||
data: { | ||
imports: null, | ||
result: { | ||
code: '', | ||
metadata: undefined, | ||
}, | ||
only: [], | ||
}, | ||
}); | ||
action.next('explodeReexports', action.entrypoint, {}); | ||
action.next('processImports', action.entrypoint, { | ||
resolved: [], | ||
}); | ||
action.next('getExports', action.entrypoint, {}); | ||
action.next('resolveImports', action.entrypoint, { | ||
imports: null, | ||
}); | ||
}; | ||
|
||
const queue = createQueueFor('/foo/bar.js', { processEntrypoint }); | ||
await queue.runNext(); // processEntrypoint | ||
|
||
const rightOrder: (keyof UniversalHandlers)[] = [ | ||
'resolveImports', | ||
'getExports', | ||
'processImports', | ||
'explodeReexports', | ||
'transform', | ||
'addToCodeCache', | ||
]; | ||
|
||
for (let i = 0; i < rightOrder.length; i++) { | ||
await queue.runNext(); | ||
expect(handlers[rightOrder[i]]).toHaveBeenCalledTimes(1); | ||
} | ||
}); | ||
}); | ||
|
||
it('should work with events', async () => { | ||
const exports: string[] = ['resolved']; | ||
const onGetExports = jest.fn(); | ||
|
||
const processEntrypoint: GetHandler<IProcessEntrypointAction> = ( | ||
_services, | ||
action | ||
) => { | ||
action | ||
.next('getExports', action.entrypoint, {}) | ||
.on('resolve', onGetExports); | ||
}; | ||
|
||
const getExports: GetHandler<IGetExportsAction> = ( | ||
_services, | ||
_action, | ||
callbacks | ||
) => { | ||
callbacks.resolve(exports); | ||
}; | ||
|
||
const queue = createQueueFor('/foo/bar.js', { | ||
processEntrypoint, | ||
getExports, | ||
}); | ||
|
||
while (!queue.isEmpty()) { | ||
await queue.runNext(); | ||
} | ||
|
||
expect(onGetExports).toHaveBeenCalledWith(exports); | ||
}); | ||
}); |
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,74 @@ | ||
import { PriorityQueue } from '../../src/transform-stages/queue/PriorityQueue'; | ||
import { rootLog } from '../../src/transform-stages/queue/rootLog'; | ||
|
||
class NumberQueue extends PriorityQueue<number> { | ||
constructor() { | ||
super( | ||
rootLog, | ||
(i) => i.toString(), | ||
(a, b) => a < b | ||
); | ||
} | ||
|
||
public delete(item: string) { | ||
super.delete(item); | ||
} | ||
|
||
public dequeue() { | ||
return super.dequeue(); | ||
} | ||
|
||
public enqueue(item: number) { | ||
super.enqueue(item); | ||
} | ||
|
||
public dump() { | ||
const result = []; | ||
while (!this.isEmpty()) { | ||
result.push(this.dequeue()); | ||
} | ||
|
||
return result; | ||
} | ||
} | ||
|
||
describe('PriorityQueue', () => { | ||
it('should be defined', () => { | ||
expect(PriorityQueue).toBeDefined(); | ||
}); | ||
|
||
describe('Simple queue of numbers', () => { | ||
describe('emptiness', () => { | ||
it('should be empty', () => { | ||
const queue = new NumberQueue(); | ||
expect(queue.isEmpty()).toBe(true); | ||
}); | ||
|
||
it('should not be empty', () => { | ||
const queue = new NumberQueue(); | ||
queue.enqueue(1); | ||
expect(queue.isEmpty()).toBe(false); | ||
}); | ||
|
||
it('should be empty after dequeue', () => { | ||
const queue = new NumberQueue(); | ||
queue.enqueue(1); | ||
queue.dequeue(); | ||
expect(queue.isEmpty()).toBe(true); | ||
}); | ||
}); | ||
|
||
it('should dequeue in order', () => { | ||
const queue = new NumberQueue(); | ||
[2, 1, 3].forEach((i) => queue.enqueue(i)); | ||
expect(queue.dump()).toEqual([3, 2, 1]); | ||
}); | ||
|
||
it('should dequeue in order after delete', () => { | ||
const queue = new NumberQueue(); | ||
[2, 1, 4, 3, 5].forEach((i) => queue.enqueue(i)); | ||
queue.delete('3'); | ||
expect(queue.dump()).toEqual([5, 4, 2, 1]); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.