Implementing the hooks API

This commit is contained in:
mfrachet 2021-06-02 16:37:58 +02:00
parent 8295a086e6
commit c6f188d5ae
7 changed files with 1121 additions and 9 deletions

View File

@ -4,6 +4,7 @@ import { BrowserRouter } from 'react-router-dom';
import { QueryClientProvider, QueryClient } from 'react-query';
import { ThemeProvider } from 'styled-components';
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
import createHook from '@strapi/hooks';
import configureStore from './core/store/configureStore';
import { Plugin } from './core/apis';
import basename from './utils/basename';
@ -12,7 +13,6 @@ import LanguageProvider from './components/LanguageProvider';
import AutoReloadOverlayBlockerProvider from './components/AutoReloadOverlayBlockerProvider';
import OverlayBlocker from './components/OverlayBlocker';
import Fonts from './components/Fonts';
import GlobalStyle from './components/GlobalStyle';
import Notifications from './components/Notifications';
import themes from './themes';
@ -42,6 +42,7 @@ class StrapiApp {
this.plugins = {};
this.reducers = reducers;
this.translations = translations;
this.hooksDict = {};
}
addComponents = components => {
@ -143,6 +144,24 @@ class StrapiApp {
this.plugins[plugin.pluginId] = plugin;
};
createHook = name => {
this.hooksDict[name] = createHook();
};
registerHook = (name, fn) => {
this.hooksDict[name].register(fn);
};
runHookSeries = (name, asynchronous = false) =>
asynchronous ? this.hooksDict[name].runSeriesAsync() : this.hooksDict[name].runSeries();
runHookWaterfall = (name, initialValue, asynchronous = false) =>
asynchronous
? this.hooksDict[name].runWaterfallAsync(initialValue)
: this.hooksDict[name].runWaterfall(initialValue);
runHookParallel = name => this.hooksDict[name].runParallel();
render() {
const store = this.createStore();

View File

@ -49,4 +49,92 @@ describe('ADMIN | StrapiApp', () => {
expect(store.getState()).toEqual(fixtures.store.state);
});
describe('Hook api', () => {
it('runs the "moto" hooks in series', () => {
const app = StrapiApp({ middlewares, reducers, library });
app.createHook('hello');
app.createHook('moto');
app.registerHook('hello', () => 5);
app.registerHook('moto', () => 1);
app.registerHook('moto', () => 2);
app.registerHook('moto', () => 3);
const [a, b, c] = app.runHookSeries('moto');
expect(a).toBe(1);
expect(b).toBe(2);
expect(c).toBe(3);
});
it('runs the "moto" hooks in series asynchronously', async () => {
const app = StrapiApp({ middlewares, reducers, library });
app.createHook('hello');
app.createHook('moto');
app.registerHook('hello', () => Promise.resolve(5));
app.registerHook('moto', () => 1);
app.registerHook('moto', () => 2);
app.registerHook('moto', () => 3);
const [a, b, c] = await app.runHookSeries('moto', true);
expect(a).toBe(1);
expect(b).toBe(2);
expect(c).toBe(3);
});
it('runs the "moto" hooks in waterfall', () => {
const app = StrapiApp({ middlewares, reducers, library });
app.createHook('hello');
app.createHook('moto');
app.registerHook('hello', () => 5);
app.registerHook('moto', n => n + 1);
app.registerHook('moto', n => n + 2);
app.registerHook('moto', n => n + 3);
const res = app.runHookWaterfall('moto', 1);
expect(res).toBe(7);
});
it('runs the "moto" hooks in waterfall asynchronously', async () => {
const app = StrapiApp({ middlewares, reducers, library });
app.createHook('hello');
app.createHook('moto');
app.registerHook('hello', () => 5);
app.registerHook('moto', n => n + 1);
app.registerHook('moto', n => Promise.resolve(n + 2));
app.registerHook('moto', n => n + 3);
const res = await app.runHookWaterfall('moto', 1, true);
expect(res).toBe(7);
});
it('runs the "moto" hooks in parallel', async () => {
const app = StrapiApp({ middlewares, reducers, library });
app.createHook('hello');
app.createHook('moto');
app.registerHook('hello', () => 5);
app.registerHook('moto', () => 1);
app.registerHook('moto', () => 2);
app.registerHook('moto', () => 3);
const [a, b, c] = await app.runHookParallel('moto');
expect(a).toBe(1);
expect(b).toBe(2);
expect(c).toBe(3);
});
});
});

View File

@ -38,6 +38,7 @@
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@fortawesome/react-fontawesome": "^0.1.14",
"@strapi/helper-plugin": "3.6.0",
"@strapi/hooks": "^0.1.0",
"@strapi/utils": "3.6.0",
"axios": "^0.21.1",
"babel-loader": "8.2.2",

View File

@ -0,0 +1,47 @@
'use strict';
const createHook = () => {
const _handlers = [];
return {
register(fn) {
_handlers.push(fn);
},
delete: handler => {
_handlers.splice(_handlers.indexOf(handler), 1);
},
runWaterfall(args) {
return _handlers.reduce((acc, fn) => fn(acc), args);
},
async runWaterfallAsync(args) {
let result = args;
for (const fn of _handlers) {
result = await fn(result);
}
return result;
},
runSeries(...args) {
return _handlers.map(fn => fn(...args));
},
async runSeriesAsync(...args) {
const result = [];
for (const fn of _handlers) {
result.push(await fn(...args));
}
return result;
},
runParallel(...args) {
return Promise.all(
_handlers.map(fn => {
return fn(...args);
})
);
},
};
};
module.exports = createHook;

View File

@ -0,0 +1,89 @@
'use strict';
const createHook = require('../');
describe('createHook', () => {
let hooksContainer;
beforeEach(() => {
hooksContainer = createHook();
});
afterEach(() => {
jest.resetAllMocks();
});
it('calls all of the mocks sequentially', () => {
hooksContainer.register(() => 1);
hooksContainer.register(() => 2);
hooksContainer.register(() => 3);
const [a, b, c] = hooksContainer.runSeries();
expect(a).toBe(1);
expect(b).toBe(2);
expect(c).toBe(3);
});
it('calls all of the mocks sequentially when they resolve async code', async () => {
hooksContainer.register(() => Promise.resolve(1));
hooksContainer.register(() => Promise.resolve(2));
hooksContainer.register(() => Promise.resolve(3));
const [a, b, c] = await hooksContainer.runSeriesAsync();
expect(a).toBe(1);
expect(b).toBe(2);
expect(c).toBe(3);
});
it('calls all of the mocks in a waterfall fashion', () => {
hooksContainer.register(n => n + 1);
hooksContainer.register(n => n + 2);
hooksContainer.register(n => n * 3);
const res = hooksContainer.runWaterfall(1);
expect(res).toBe(12);
});
it('calls all of the mocks in a waterfall fashion when they resolve async code', async () => {
hooksContainer.register(n => Promise.resolve(n + 1));
hooksContainer.register(n => n + 2);
hooksContainer.register(n => Promise.resolve(n * 3));
const res = await hooksContainer.runWaterfallAsync(1);
expect(res).toBe(12);
});
it('calls all of the mocks in parallel', async () => {
hooksContainer.register(() => Promise.resolve(1));
hooksContainer.register(() => Promise.resolve(2));
hooksContainer.register(() => Promise.resolve(3));
const [a, b, c] = await hooksContainer.runParallel();
expect(a).toBe(1);
expect(b).toBe(2);
expect(c).toBe(3);
});
it('removes a hook when calling delete', () => {
const fn1 = () => 1;
const fn2 = () => 2;
const fn3 = () => 3;
hooksContainer.register(fn1);
hooksContainer.register(fn2);
hooksContainer.register(fn3);
hooksContainer.delete(fn3);
const [a, b, c] = hooksContainer.runSeries();
expect(a).toBe(1);
expect(b).toBe(2);
expect(c).toBe(undefined);
});
});

View File

@ -0,0 +1,12 @@
{
"name": "@strapi/hooks",
"main": "./lib/index.js",
"version": "0.1.0",
"license": "SEE LICENSE IN LICENSE",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^27.0.3"
}
}

872
yarn.lock

File diff suppressed because it is too large Load Diff