quasqui
Version:
The meta-framework suite designed from scratch for frontend-focused modern web development.
565 lines (447 loc) • 13.5 kB
text/typescript
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable max-lines */
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/sync/core';
import foo from './fixtures/sync/base/foo';
import bar, { getBar } from './fixtures/sync/base/bar';
import dFoo from './fixtures/sync/dynamic/foo';
import dBar, { getNumber, setNumber } from './fixtures/sync/dynamic/bar';
describe('sync manager', () => {
it('base useage', () => {
const countContext = createContext(0);
const useCount = () => countContext.use().value;
const manager = createManager();
manager.usePlugin(
manager.createPlugin(() => {
expect(useCount()).toBe(1);
}),
);
manager.run(() => {
countContext.set(1);
});
expect(manager.run(() => countContext.get())).toBe(1);
});
it('with sub waterfall', () => {
type Context = {
count: number;
};
type Config = {
mode: string;
};
const prepare = createWaterfall<{
config: Config;
}>();
const preDev = createWaterfall<{
context: Context;
}>();
const postDev = createWaterfall<{
context: Context;
}>();
const manager = createManager({ prepare, preDev, postDev });
const runner = manager.init();
const prepareResult = runner.prepare({ config: { mode: 'test' } });
const preDevResult = runner.preDev({ context: { count: 0 } });
const postDevResult = runner.postDev({ context: { count: 1 } });
expect(prepareResult).toStrictEqual({ config: { mode: 'test' } });
expect(preDevResult).toStrictEqual({ context: { count: 0 } });
expect(postDevResult).toStrictEqual({ context: { count: 1 } });
});
it('could without progress hook in plugin', () => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const foo = createWaterfall<number>();
const manager = createManager({ foo });
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin = manager.createPlugin(() => {});
manager.usePlugin(plugin);
const runner = manager.init();
expect(runner.foo(0)).toBe(0);
});
describe('pre order plugin', () => {
it('default order is right order', () => {
const manager = createManager();
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);
manager.init();
expect(list).toStrictEqual([0, 1, 2]);
});
it('default order is incorrect order', () => {
const manager = createManager();
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);
manager.init();
expect(list).toStrictEqual([1, 0, 2]);
});
});
describe('post order plugin', () => {
it('default order is right order', () => {
const manager = createManager();
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);
manager.init();
expect(list).toStrictEqual([0, 1, 2]);
});
it('default order is incorrect order', () => {
const manager = createManager();
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);
manager.init();
expect(list).toStrictEqual([1, 0, 2]);
});
it('should support more plugin', () => {
const manager = createManager();
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);
manager.init({});
expect(status).toBe(1);
});
});
describe('rivals plugin', () => {
it('should throw error when attaching rival plugin', () => {
const manager = createManager();
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin1 = manager.createPlugin(() => {}, { name: 'plugin1' });
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin2 = manager.createPlugin(() => {}, { rivals: ['plugin1'] });
manager.usePlugin(plugin1);
manager.usePlugin(plugin2);
expect(() => manager.init({})).toThrowError();
});
it('should not throw error without attaching rival plugin', () => {
const manager = createManager();
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);
manager.init();
});
});
describe('required plugin', () => {
it('should throw error when it is without required plugin', () => {
const manager = createManager();
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin0 = manager.createPlugin(() => {}, {
name: 'plugin0',
required: ['plugin1'],
});
manager.usePlugin(plugin0);
expect(manager.init).toThrowError();
});
it('should not throw error without attaching rival plugin', () => {
const manager = createManager();
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);
manager.init();
});
});
it('common attach plugin', () => {
const manager = main.clone().usePlugin(foo).usePlugin(bar);
expect(getBar()).toBe(0);
const runner = manager.init();
expect(getBar()).toBe(1);
runner.preDev();
expect(getBar()).toBe(2);
runner.postDev();
expect(getBar()).toBe(3);
});
it('should support dynamic register', () => {
const manager = main.clone().usePlugin(dFoo).usePlugin(dBar);
expect(getNumber()).toBe(0);
const runner = manager.init();
expect(getNumber()).toBe(1);
runner.preDev();
expect(getNumber()).toBe(2);
});
it('should de-duplicate plugins', () => {
const manager = createManager();
let count = 0;
const plugin = manager.createPlugin(() => {
count += 1;
});
manager.usePlugin(plugin, plugin);
manager.usePlugin(plugin);
manager.init();
expect(count).toBe(1);
});
it('isPlugin: exclusive plugins of manager', () => {
const manager0 = createAsyncManager();
const manager1 = createManager();
// 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: exclusive plugins of manager', () => {
const manager0 = createAsyncManager();
const manager1 = createManager();
let count = 0;
const plugin = manager0.createPlugin(() => {
count += 1;
});
manager1.usePlugin(plugin as any);
manager1.init();
expect(count).toBe(0);
});
it('should support clear plugins', () => {
setNumber(0);
const manager = main.clone().usePlugin(dFoo).usePlugin(dBar);
manager.clear();
expect(getNumber()).toBe(0);
const runner = manager.init();
expect(getNumber()).toBe(0);
runner.preDev();
expect(getNumber()).toBe(0);
});
it('should run under the internal container', () => {
const manager = createManager();
const Count = createContext(0);
const plugin = manager.createPlugin(() => {
Count.set(1);
});
manager.usePlugin(plugin);
manager.init();
expect(manager.run(Count.get)).toBe(1);
const container = createContainer();
manager.init({ container });
expect(manager.run(Count.get, { container })).toBe(1);
});
it('should support all progress', async () => {
const manager = createManager({
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 = 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',
]);
});
it('should throw error when progress is illegal', () => {
const manager = createManager({ test: '' as any });
// eslint-disable-next-line @typescript-eslint/no-empty-function
const plugin = manager.createPlugin(() => {});
manager.usePlugin(plugin);
expect(manager.init).toThrowError();
});
describe('useRunner', () => {
it('base usage', () => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const foo = createPipeline();
// eslint-disable-next-line @typescript-eslint/no-shadow
const bar = createPipeline();
const manager = createManager({ foo, bar });
let count = 0;
const plugin = manager.createPlugin(() => ({
foo: () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const runner = useRunner();
runner.bar();
},
bar: () => {
count = 1;
},
}));
manager.usePlugin(plugin);
const runner = manager.init();
runner.foo({});
expect(count).toBe(1);
});
it('can not use useRunner out plugin hook', () => {
expect(useRunner).toThrowError(
new Error(
`Can't call useContainer out of scope, it should be placed on top of the function`,
),
);
});
});
});