quasqui
Version:
The meta-framework suite designed from scratch for frontend-focused modern web development.
595 lines (477 loc) • 14.3 kB
text/typescript
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable max-lines */
import { enable, disable } from 'farrow-pipeline/asyncHooks.node';
import {
createPipeline,
createAsyncPipeline,
createContext,
createContainer,
} from 'farrow-pipeline';
import { createManager, createAsyncManager, useRunner } from '../src/manager';
import { createWaterfall, createAsyncWaterfall } from '../src/waterfall';
import {
createWorkflow,
createAsyncWorkflow,
createParallelWorkflow,
} from '../src/workflow';
import { main } from './fixtures/async/core';
import foo from './fixtures/async/base/foo';
import bar, { getBar } from './fixtures/async/base/bar';
import dFoo from './fixtures/async/dynamic/foo';
import dBar, { getNumber } from './fixtures/async/dynamic/bar';
import { sleep } from './helpers';
describe('async manager', () => {
it('base usage', async () => {
const manager = main.clone().usePlugin(foo).usePlugin(bar);
expect(getBar()).toBe(0);
const runner = await manager.init({});
expect(getBar()).toBe(1);
runner.preDev();
expect(getBar()).toBe(2);
runner.postDev();
expect(getBar()).toBe(3);
});
it('should support async initializer', async () => {
const manager = createAsyncManager();
const countContext = createContext(0);
const useCount = () => countContext.use().value;
const plugin = manager.createPlugin(async () => {
await sleep(0);
expect(useCount()).toBe(1);
});
manager.usePlugin(plugin);
manager.run(() => {
countContext.set(1);
});
enable();
await manager.init();
disable();
const result0 = manager.run(() => countContext.get());
expect(result0).toBe(1);
});
it('support support dynamicly register', async () => {
const manager = main.clone().usePlugin(dFoo).usePlugin(dBar);
expect(getNumber()).toBe(0);
const runner = await manager.init();
expect(getNumber()).toBe(1);
runner.preDev();
expect(getNumber()).toBe(2);
});
it('could without progress hook in plugin', async () => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const foo = createWaterfall<number>();
const manager = createAsyncManager({ foo });
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin = manager.createPlugin(() => {});
manager.usePlugin(plugin);
const runner = await manager.init();
expect(runner.foo(0)).toBe(0);
});
describe('pre order plugin', () => {
it('default order is right order', async () => {
const manager = createAsyncManager();
const list: number[] = [];
const plugin0 = manager.createPlugin(
() => {
list.push(0);
},
{ name: 'plugin0' },
);
const plugin1 = manager.createPlugin(
() => {
list.push(1);
},
{
name: 'plugin1',
pre: ['plugin0'],
},
);
const plugin2 = manager.createPlugin(
() => {
list.push(2);
},
{ name: 'plugin2' },
);
manager.usePlugin(plugin0, plugin1, plugin2);
await manager.init();
expect(list).toStrictEqual([0, 1, 2]);
});
it('default order is incorrect order', async () => {
const manager = createAsyncManager();
const list: number[] = [];
const plugin0 = manager.createPlugin(
() => {
list.push(0);
},
{
name: 'plugin0',
pre: ['plugin1'],
},
);
const plugin1 = manager.createPlugin(
() => {
list.push(1);
},
{ name: 'plugin1' },
);
const plugin2 = manager.createPlugin(
() => {
list.push(2);
},
{ name: 'plugin2' },
);
manager.usePlugin(plugin0, plugin1, plugin2);
await manager.init();
expect(list).toStrictEqual([1, 0, 2]);
});
});
describe('post order plugin', () => {
it('default order is right order', async () => {
const manager = createAsyncManager();
const list: number[] = [];
const plugin0 = manager.createPlugin(
() => {
list.push(0);
},
{
name: 'plugin0',
post: ['plugin1'],
},
);
const plugin1 = manager.createPlugin(
() => {
list.push(1);
},
{ name: 'plugin1' },
);
const plugin2 = manager.createPlugin(
() => {
list.push(2);
},
{ name: 'plugin2' },
);
manager.usePlugin(plugin0, plugin1, plugin2);
await manager.init();
expect(list).toStrictEqual([0, 1, 2]);
});
it('default order is incorrect order', async () => {
const manager = createAsyncManager();
const list: number[] = [];
const plugin0 = manager.createPlugin(
() => {
list.push(0);
},
{ name: 'plugin0' },
);
const plugin1 = manager.createPlugin(
() => {
list.push(1);
},
{
name: 'plugin1',
post: ['plugin0'],
},
);
const plugin2 = manager.createPlugin(
() => {
list.push(2);
},
{ name: 'plugin2' },
);
manager.usePlugin(plugin0, plugin1, plugin2);
await manager.init();
expect(list).toStrictEqual([1, 0, 2]);
});
it('should support more plugin', async () => {
const manager = createAsyncManager();
let status = 0;
const plugin1 = manager.createPlugin(
() => {
status = 1;
},
{ name: 'plugin1' },
);
const plugin2 = manager.createPlugin(() => {
status = 2;
});
const plugin3 = manager.createPlugin(() => {
status = 3;
});
const plugin4 = manager.createPlugin(
() => {
status = 4;
},
{ post: ['plugin1'] },
);
manager.usePlugin(plugin1);
manager.usePlugin(plugin2);
manager.usePlugin(plugin3);
manager.usePlugin(plugin4);
await manager.init({});
expect(status).toBe(1);
});
});
describe('rival plugin', () => {
it('should throw error when attaching rival plugin', async () => {
const manager = createAsyncManager();
let count = 0;
const plugin0 = manager.createPlugin(
() => {
count = 0;
},
{ name: 'plugin0' },
);
const plugin1 = manager.createPlugin(
() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
count += 1;
},
{
name: 'plugin1',
rivals: ['plugin0'],
},
);
manager.usePlugin(plugin0, plugin1);
await expect(manager.init).rejects.toThrowError();
});
it('should not throw error without attaching rival plugin', async () => {
const manager = createAsyncManager();
let count = 0;
const plugin0 = manager.createPlugin(
() => {
count = 0;
},
{ name: 'plugin0' },
);
const plugin1 = manager.createPlugin(
() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
count += 1;
},
{
name: 'plugin1',
rivals: ['plugin2'],
},
);
manager.usePlugin(plugin0, plugin1);
await manager.init();
});
});
describe('required plugin', () => {
it('should throw error when it is without required plugin', async () => {
const manager = createAsyncManager();
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin0 = manager.createPlugin(() => {}, {
name: 'plugin0',
required: ['plugin1'],
});
manager.usePlugin(plugin0);
await expect(manager.init).rejects.toThrowError();
});
it('should not throw error without attaching rival plugin', async () => {
const manager = createAsyncManager();
let count = 0;
const plugin0 = manager.createPlugin(
() => {
count = 0;
},
{
name: 'plugin0',
required: ['plugin1'],
},
);
const plugin1 = manager.createPlugin(
() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
count += 1;
},
{ name: 'plugin1' },
);
manager.usePlugin(plugin0, plugin1);
await manager.init();
});
});
it('should de-duplicate plugins', async () => {
const manager = createAsyncManager();
let count = 0;
const plugin = manager.createPlugin(() => {
count += 1;
});
manager.usePlugin(plugin, plugin);
manager.usePlugin(plugin);
await manager.init();
expect(count).toBe(1);
});
it('should support manager clone', async () => {
const manager0 = createAsyncManager();
let count = 0;
const plugin = manager0.createPlugin(() => {
count += 1;
});
manager0.usePlugin(plugin);
await manager0.init();
expect(count).toBe(1);
const manager1 = manager0.clone();
manager1.usePlugin(plugin);
await manager1.init();
expect(count).toBe(2);
});
it('isPlugin if exclusive plugins of manager', () => {
const manager0 = createManager();
const manager1 = createAsyncManager();
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin = manager0.createPlugin(() => {});
expect(manager0.isPlugin(plugin)).toBeTruthy();
expect(manager1.isPlugin(plugin)).toBeFalsy();
expect(manager1.isPlugin({})).toBeFalsy();
expect(manager1.isPlugin('' as any)).toBeFalsy();
});
it('usePlugin should only add exclusive plugins of manager', async () => {
const manager0 = createManager();
const manager1 = createAsyncManager();
let count = 0;
const plugin = manager0.createPlugin(() => {
count += 1;
});
manager1.usePlugin(plugin as any);
await manager1.init();
expect(count).toBe(0);
});
it('should support clear plugins', async () => {
const manager = createAsyncManager();
let count = 0;
const plugin = manager.createPlugin(() => {
count += 1;
});
manager.usePlugin(plugin);
manager.clear();
await manager.init();
expect(count).toBe(0);
});
it('should run under the internal container', async () => {
const manager = createAsyncManager();
const Count = createContext(0);
const plugin = manager.createPlugin(() => {
Count.set(1);
});
manager.usePlugin(plugin);
await manager.init();
expect(manager.run(Count.get)).toBe(1);
const container = createContainer();
await manager.init({ container });
expect(manager.run(Count.get, { container })).toBe(1);
});
it('should support all progress', async () => {
const manager = createAsyncManager({
pipeline: createPipeline(),
asyncPipeline: createAsyncPipeline(),
waterfall: createWaterfall(),
asyncWaterfall: createAsyncWaterfall(),
workflow: createWorkflow(),
asyncWorkflow: createAsyncWorkflow(),
parallelWorkflow: createParallelWorkflow(),
});
const list: string[] = [];
const plugin = manager.createPlugin(() => ({
pipeline: () => {
list.push('pipeline');
},
asyncPipeline: () => {
list.push('asyncPipeline');
},
waterfall: () => {
list.push('waterfall');
},
asyncWaterfall: () => {
list.push('asyncWaterfall');
},
workflow: () => {
list.push('workflow');
},
asyncWorkflow: () => {
list.push('asyncWorkflow');
},
parallelWorkflow: () => {
list.push('parallelWorkflow');
},
}));
manager.usePlugin(plugin);
const runner = await manager.init();
runner.pipeline({});
await runner.asyncPipeline({});
runner.waterfall({});
await runner.asyncWaterfall({});
runner.workflow({});
await runner.asyncWorkflow({});
await runner.parallelWorkflow({});
expect(list).toStrictEqual([
'pipeline',
'asyncPipeline',
'waterfall',
'asyncWaterfall',
'workflow',
'asyncWorkflow',
'parallelWorkflow',
]);
});
describe('useRunner', () => {
it('should work well', async () => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const foo = createAsyncPipeline();
// eslint-disable-next-line @typescript-eslint/no-shadow
const bar = createAsyncPipeline();
const manager = createAsyncManager({ foo, bar });
let count = 0;
const plugin = manager.createPlugin(() => ({
foo: async () => {
await sleep(0);
const runner = manager.useRunner();
runner.bar({});
},
bar: () => {
count = 1;
},
}));
manager.usePlugin(plugin);
const runner = await manager.init();
enable();
await runner.foo({});
disable();
expect(count).toBe(1);
});
it('should throw error useRunner out plugin hook', async () => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const foo = createAsyncPipeline();
// eslint-disable-next-line @typescript-eslint/no-shadow
const bar = createAsyncPipeline();
const manager = createAsyncManager({ foo, bar });
let count = 0;
const plugin = manager.createPlugin(() => {
const runner = manager.useRunner();
return {
foo: async () => {
await sleep(0);
runner.bar({});
},
bar: () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
count = 1;
},
};
});
manager.usePlugin(plugin);
await expect(manager.init).rejects.toThrowError(
new Error(
`Can't call useRunner out of scope, it should be placed in hooks of plugin`,
),
);
});
it('should throw error useRunner out plugin', () => {
expect(useRunner).toThrowError(
new Error(
`Can't call useContainer out of scope, it should be placed on top of the function`,
),
);
});
});
});