Skip to content

Commit

Permalink
feat(ovh-shell): initial import
Browse files Browse the repository at this point in the history
Signed-off-by: frenauvh <[email protected]>
Co-authored-by: Jisay <[email protected]>
Co-authored-by: Zakaria Sahmane <[email protected]>
Co-authored-by: Cyrille Bourgois <[email protected]>

BREAKING CHANGE: create ovh-shell library
  • Loading branch information
frenautvh authored and antleblanc committed Feb 14, 2022
1 parent 34630f7 commit 30c8bf5
Show file tree
Hide file tree
Showing 39 changed files with 4,678 additions and 171 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
'angular/mocks': true,
browser: true,
jquery: true,
jest: true,
},
globals: {
fixture: false,
Expand All @@ -29,7 +30,7 @@ module.exports = {
},
},
{
files: ['**/*.ts'],
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
Expand Down
8 changes: 8 additions & 0 deletions babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"env": {
"test": {
"presets": ["react-app"]
}
}
}
35 changes: 35 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = {
roots: ['<rootDir>/packages/components', '<rootDir>/packages/manager'],
projects: ['<rootDir>/packages/manager/apps/shell/jest.config.js'],
setupFilesAfterEnv: ['<rootDir>/jest/mocks/jest.setup.js'],
collectCoverageFrom: ['packages/**/*.{js,jsx,ts,tsx}', '!packages/**/*.d.ts'],
testMatch: [
'<rootDir>/packages/**/__tests__/**/*.{spec,test}.{js,jsx,ts,tsx}',
'<rootDir>/packages/**/*.{spec,test}.{js,jsx,ts,tsx}',
],
testEnvironment: 'jsdom',
transform: {
'^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': '<rootDir>/node_modules/babel-jest',
'^.+\\.scss$': 'jest-scss-transform',
'^.+\\.css$': '<rootDir>/jest/mocks/cssMock.js',
},
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$',
'^.+\\.module\\.(css|sass|scss)$',
'<rootDir>/node_modules/(?!lodash-es)',
],
moduleNameMapper: {
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
'^lodash-es$': 'lodash',
},
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
resetMocks: true,
globals: {
'ts-jest': {
isolatedModules: true,
},
},
};
9 changes: 9 additions & 0 deletions jest/mocks/cssMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
process() {
return 'module.exports = {};';
},
getCacheKey() {
// The output is always the same.
return 'cssTransform';
},
};
6 changes: 6 additions & 0 deletions jest/mocks/jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const fetchPolyfill = require('whatwg-fetch');

global.fetch = fetchPolyfill.fetch;
global.Request = fetchPolyfill.Request;
global.Headers = fetchPolyfill.Headers;
global.Response = fetchPolyfill.Response;
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"release": "scripts/release.js",
"split": "scripts/split.js",
"start": "node -r esm scripts/start-application.js",
"test:jest": "jest --runInBand",
"test": "yarn lint",
"test:e2e:chromium": "rimraf ./reports && testcafe chromium",
"test:e2e:chromium:headless": "rimraf ./reports && testcafe chromium:headless",
Expand All @@ -53,6 +54,8 @@
"@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.27.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^27.2.1",
"babel-preset-react-app": "^10.0.0",
"commander": "^6.0.0",
"concat-stream": "^2.0.0",
"conventional-changelog-cli": "^2.0.25",
Expand All @@ -72,7 +75,12 @@
"git-url-parse": "^11.1.2",
"htmlhint": "^0.15.1",
"husky": "^7.0.0",
"identity-obj-proxy": "^3.0.0",
"inquirer": "^7.0.6",
"jest": "^27.2.1",
"jest-circus": "^27.2.1",
"jest-scss-transform": "^1.0.1",
"jest-watch-typeahead": "^0.6.4",
"lerna": "^3.18.1",
"lint-staged": "^11.0.0",
"lodash": "^4.17.11",
Expand All @@ -95,7 +103,8 @@
"stylelint-scss": "^3.18.0",
"testcafe": "1.8.2",
"testcafe-angular-selectors": "^0.4.1",
"testcafe-reporter-html": "^1.4.6"
"testcafe-reporter-html": "^1.4.6",
"whatwg-fetch": "^3.6.2"
},
"engines": {
"node": "^14 || ^16",
Expand Down
1 change: 1 addition & 0 deletions packages/components/ovh-shell/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ovh-shell
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { loadFeature, defineFeature } from 'jest-cucumber';
import DirectClientMessageBus from '../../src/message-bus/direct-client';
import Shell from '../../src/shell/shell';
import ShellClient from '../../src/client/shell-client';
import { IShellPluginMethodCall } from '../../src/common';

const feature = loadFeature('../../features/client/shell-client.feature', {
loadRelativePath: true,
});

defineFeature(feature, (test) => {
test('Plugin method invokation', ({ given, when, and, then }) => {
const shellClientMessageBus = new DirectClientMessageBus();
const shellMessageBus = new DirectClientMessageBus();
const shell = new Shell();
const shellClient = new ShellClient();
const callback = jest.fn((param: string) => param);
const pluginName = 'test';

given('I have one plugin registered in my shell', () => {
shell.getPluginManager().registerPlugin(pluginName, {
callback,
});
});

and(
'My shell and shell client are configured with a direct message bus',
() => {
shell.setMessageBus(shellMessageBus);
shellClient.setMessageBus(shellClientMessageBus);
shellClientMessageBus.addPeer(shellMessageBus);
shellMessageBus.addPeer(shellClientMessageBus);
},
);

when('I invoke a method from my plugin', () => {
const pluginInvokation: IShellPluginMethodCall = {
plugin: pluginName,
method: 'callback',
args: ['param'],
};
shellClient.invokePluginMethod(pluginInvokation);
});

then('The invokation should be resolved with the method result', () => {
expect(callback).toHaveBeenCalledWith('param');
});
});

test('Plugin event listener', ({ given, when, and, then }) => {
const shellClientMessageBus = new DirectClientMessageBus();
const shellMessageBus = new DirectClientMessageBus();
const shell = new Shell();
const shellClient = new ShellClient();
const callback = jest.fn((param: string) => param);
const callback2 = jest.fn((param: string) => param);

given(
'My shell and shell client are configured with a direct message bus',
() => {
shell.setMessageBus(shellMessageBus);
shellClient.setMessageBus(shellClientMessageBus);
shellClientMessageBus.addPeer(shellMessageBus);
shellMessageBus.addPeer(shellClientMessageBus);
},
);

and('I add an event listener on the shell client', () => {
shellClient.addEventListener('foo', callback);
shellClient.addEventListener('bar', callback2);
});

when('The shell emits an event', () => {
shell.emitEvent('foo', 'param');
});

then(
'The event emitted from the shell should be received by the client',
() => {
expect(callback).toHaveBeenCalledWith('param');
expect(callback2).not.toHaveBeenCalled();
},
);
});
});
106 changes: 106 additions & 0 deletions packages/components/ovh-shell/__tests__/message-bus/iframe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { loadFeature, defineFeature } from 'jest-cucumber';
import IFrameMessageBus from '../../src/message-bus/iframe';

const feature = loadFeature('../../features/message-bus/iframe.feature', {
loadRelativePath: true,
});

defineFeature(feature, (test) => {
test('Message bus sends a message with an iframe', ({
given,
when,
then,
}) => {
let iframeMessageBus: IFrameMessageBus;
let success = false;
let iframe: HTMLIFrameElement;
const message = { id: 'test-message' };

given('My message bus has an iframe', () => {
iframe = window.document.createElement('iframe');
iframeMessageBus = new IFrameMessageBus(iframe);
window.document.body.appendChild(iframe);
iframe.contentWindow.addEventListener('message', (e) => {
if (e.data.message.id === message.id) {
success = true;
}
});
});

when('The message bus sends a message', () => {
iframeMessageBus.send(message);
});

then('The message should be usable through the event Message', () => {
return new Promise<void>((resolve) => {
// Set Timeout is necessary because we have to
// wait for the listener to trigger
setTimeout(() => {
expect(success).toBeTruthy();
resolve();
}, 100);
});
});
});

test('Message bus sends a message without iframe', ({
given,
when,
then,
}) => {
let iframeMessageBus: IFrameMessageBus;
let success = false;
const message = { id: 'test-message' };
given('My message bus has no iframe', () => {
iframeMessageBus = new IFrameMessageBus();
window.parent.addEventListener('message', (e) => {
if (e.data.message.id === message.id) {
success = true;
}
});
});

when('The message bus sends a message', () => {
iframeMessageBus.send(message);
});

then('The message should be usable through the event Message', () => {
return new Promise<void>((resolve) => {
// Set Timeout is necessary because we have to
// wait for the listener to trigger
setTimeout(() => {
expect(success).toBeTruthy();
resolve();
}, 100);
});
});
});

test('Message bus receives a message', ({ given, when, and, then }) => {
let iframeMessageBus: IFrameMessageBus;
const log = jest.fn((entry) => entry);

given('I have a message bus instance', () => {
iframeMessageBus = new IFrameMessageBus();
});

when('My message bus receives a callback', () => {
iframeMessageBus.onReceive(log);
});

and('A post message is called', () => {
iframeMessageBus.send('test');
});

then('The callback should trigger with post message data', () => {
return new Promise<void>((resolve) => {
// Set Timeout is necessary because we have to
// wait for the listener to trigger
setTimeout(() => {
expect(log).toHaveBeenCalledWith('test');
resolve();
}, 100);
});
});
});
});
66 changes: 66 additions & 0 deletions packages/components/ovh-shell/__tests__/plugin/i18n/i18n.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { loadFeature, defineFeature, DefineStepFunction } from 'jest-cucumber';
import { Environment } from '@ovh-ux/manager-config';
import { KeyPairName } from '@ovh-ux/manager-config/types/locale';

import i18n from '../../../src/plugin/i18n';
import DirectClientMessageBus from '../../../src/message-bus/direct-client';
import Shell from '../../../src/shell/shell';

const feature = loadFeature('../../../features/plugin/i18n/i18n.feature', {
loadRelativePath: true,
});

defineFeature(feature, (test) => {
let i18nPlugin: CallableFunction;

const shellMessageBus = new DirectClientMessageBus();
const shell = new Shell(shellMessageBus);
const environment = new Environment();

// define i18n instanciation
const givenI18nPluginInstanciated = (given: DefineStepFunction) => {
given('I have a i18n plugin instanciated', () => {
i18nPlugin = i18n(shell, environment);
});
};

test('Retrieving locale when nothing has changed', ({ given, when, then }) => {
let locale: String;

givenI18nPluginInstanciated(given);

when('I try to get the locale', () => {
locale = i18nPlugin.getLocale();
});

then('I should get the default locale', () => {
expect(locale).toBe('en_GB');
});
});

test('Setting the locale', ({ given, when, then }) => {
givenI18nPluginInstanciated(given);

when('I try to change the locale', () => {
i18nPlugin.setLocale('fr_FR');
});

then('I should retrieve the setted locale', () => {
expect(i18nPlugin.getLocale()).toBe('fr_FR');
});
});

test('Getting the list of available locales', ({ given, when, then }) => {
let availableLocales: Array<KeyPairName>;

givenI18nPluginInstanciated(given);

when('I try to get the list of available locales', () => {
availableLocales = i18nPlugin.getAvailableLocales();
});

then('I should retrieve at least one available locale', () => {
expect(availableLocales.length).toBeGreaterThan(0);
});
});
});
Loading

0 comments on commit 30c8bf5

Please sign in to comment.