Skip to content

Commit

Permalink
test: a bunch of tests for ActionQueue and related code
Browse files Browse the repository at this point in the history
  • Loading branch information
Anber committed Aug 9, 2023
1 parent 4b75151 commit db09696
Show file tree
Hide file tree
Showing 33 changed files with 3,486 additions and 885 deletions.
98 changes: 98 additions & 0 deletions packages/babel/__tests__/queue/AsyncActionQueue.test.ts
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);
}
});
});
180 changes: 180 additions & 0 deletions packages/babel/__tests__/queue/GenericQueue.test.ts
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);
});
});
74 changes: 74 additions & 0 deletions packages/babel/__tests__/queue/PriorityQueue.test.ts
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]);
});
});
});
Loading

0 comments on commit db09696

Please sign in to comment.