Skip to content

Commit

Permalink
feat: iss 398/viv 224 core declarative config (#416)
Browse files Browse the repository at this point in the history
* issue #398: initial commit

* issue #398: tests to start with :)

* issue #398: added karma HTML to perform tests within iframes

* issue #398: fixing tests stuff

* issue #398: fixing one of the tests - implemented

* issue #398: implemented theme context configuration

* issue #398: fixing Sonar remarks

* issue #398: added some tests

* issue #398: added actualy CSS variables verification to the encapsulated tests

* issue #398: fixing CR of Yinon

Co-authored-by: yinon <yinon@hotmail.com>
  • Loading branch information
gullerya and yinonov authored Nov 3, 2020
1 parent 3807b27 commit 6228dfe
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 93 deletions.
50 changes: 0 additions & 50 deletions common/core/src/vvd-configurer.ts

This file was deleted.

95 changes: 82 additions & 13 deletions common/core/src/vvd-core.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,95 @@
import configurer, { Configuration } from './vvd-configurer.js';
import fonts from '@vonage/vvd-fonts/vvd-fonts.js';
import schemeService from '@vonage/vvd-scheme';
import schemeService, { SchemeOption } from '@vonage/vvd-scheme';

let coreAutoInitDone: Promise<Array<unknown>>;
if (configurer.initialConfiguration.autoInit) {
coreAutoInitDone = applyConfiguration(configurer.initialConfiguration);
const VVD_CONTEXT_ATTRIBUTE = 'data-vvd-context',
NONE_INIT_VALUE = 'none',
VALID_CONFIGURATION_KEYS = ['scheme'];

export interface Configuration {
scheme?: SchemeOption;
}

interface InitialConfiguration extends Configuration {
autoInit: boolean;
}

let coreAutoInitDone: Promise<Record<string, unknown>>;
const initialConfiguration = _buildConfiguration();
if (initialConfiguration.autoInit) {
coreAutoInitDone = _applyConfiguration(initialConfiguration);
} else {
coreAutoInitDone = Promise.reject('auto-init unavailable when "none" used');
coreAutoInitDone = Promise.reject(
`auto-init unavailable when '${NONE_INIT_VALUE}' used`
);
}

export default Object.freeze({
set: applyConfiguration,
set: safeApplyConfiguration,
settled: coreAutoInitDone,
});

async function applyConfiguration(configuration: Partial<Configuration>) {
configurer.validateConfiguration(configuration);
return init(configuration);
async function safeApplyConfiguration(
configuration: Partial<Configuration>
): Promise<Record<string, unknown>> {
_validateConfiguration(configuration);
return _applyConfiguration(configuration);
}

async function init(
async function _applyConfiguration(
configuration: Partial<Configuration>
): Promise<Array<unknown>> {
return Promise.all([fonts.init(), schemeService.set(configuration.scheme)]);
): Promise<Record<string, unknown>> {
const allResults = await Promise.all([
fonts.init(),
schemeService.set(configuration.scheme),
]);
return Object.freeze({
fonts: allResults[0],
scheme: allResults[1],
});
}

function _buildConfiguration(): InitialConfiguration {
const result: InitialConfiguration = {
autoInit: true,
};
const vvdContextAttrValue = document.documentElement.getAttribute(
VVD_CONTEXT_ATTRIBUTE
);
if (vvdContextAttrValue === NONE_INIT_VALUE) {
result.autoInit = false;
} else if (vvdContextAttrValue) {
const parsed = _parseVvdContextAttr(vvdContextAttrValue);
Object.assign(result, parsed);
}
return result;
}

function _validateConfiguration(configuration: Partial<Configuration>) {
const extraParams = Object.keys(configuration).filter(
(k) => !VALID_CONFIGURATION_KEYS.includes(k)
);

if (extraParams.length) {
console.warn(
`unexpected configuration part/s '${extraParams}', only some of '${VALID_CONFIGURATION_KEYS}' expected`
);
}
}

function _parseVvdContextAttr(value: string): Record<string, unknown> {
const tokens = value.trim().split(/\s+/);
return tokens.reduce((result, token) => {
if (/^theme:/.test(token)) {
if (result.scheme) {
console.error(
`theme vivid context defined multiple times, only the first (${result.scheme}) will be effective`
);
} else {
result.scheme = token.replace(/^theme:/, '');
}
} else {
console.warn(`unsupported token '${token}' in vivid context`);
}
return result;
}, {} as Record<string, unknown>);
}
15 changes: 15 additions & 0 deletions common/core/test/core-setup.test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>Vivid - injectable isolated document as testing playground</title>
<script type="module">
window.executeSetup = async () => {
window.vvdCore = (await import('../vvd-core.js')).default;
};
</script>
</head>

<body></body>

</html>
183 changes: 163 additions & 20 deletions common/core/test/core.test.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,170 @@
import vvdCore from '../vvd-core.js';
import {
randomAlpha,
getFrameLoadedInjected,
} from '../../../test/test-helpers.js';
import {
assertBaseVarsMatch,
PRINCIPAL_VARIABLES_FILTER,
} from '../../../test/style-utils.js';

const CONTEXT_ATTR = 'data-vvd-context';
const CORE_SETUP_HTML_TAG = 'coreSetupTest';
const LIGHT = 'light';
const DARK = 'dark';
const NONE = 'none';

describe('vvd-core service', () => {
it('verify basic core API', async () => {
assert.isDefined(vvdCore, 'core service is defined');
assert.isObject(vvdCore, 'core service is a defaultly exported object');
assert.isFunction(vvdCore.set, 'core service has "set" API method');
assert.isDefined(
vvdCore.settled,
'core service has "settled" object (Promise)'
);
assert.isFunction(
vvdCore.settled.then,
'core service has "settled" object - ensure it is Promise'
);
describe('basic APIs', () => {
it('is should init to default', async () => {
const r = randomAlpha();
const vvdCore = (await import(`../vvd-core.js?t=${r}`)).default;
assert.isDefined(vvdCore, 'core service is defined');
assert.isObject(vvdCore, 'core service is a defaultly exported object');
assert.isFunction(vvdCore.set, 'core service has "set" API method');
assert.isDefined(
vvdCore.settled,
'core service has "settled" object (Promise)'
);
assert.isFunction(
vvdCore.settled.then,
'core service has "settled" object - ensure it is Promise'
);
});

it('should init to none', async () => {
document.documentElement.setAttribute(CONTEXT_ATTR, 'none');
const r = randomAlpha();
const vvdCore = (await import(`../vvd-core.js?t=${r}`)).default;
document.documentElement.removeAttribute(CONTEXT_ATTR);
try {
await vvdCore.settled;
} catch (e) {
expect(e).exist;
expect(e.includes('auto-init unavailable')).true;
}
});

it('should init to dark', async () => {
document.documentElement.setAttribute(CONTEXT_ATTR, `theme:${DARK}`);
const r = randomAlpha();
const vvdCore = (await import(`../vvd-core.js?t=${r}`)).default;
document.documentElement.removeAttribute(CONTEXT_ATTR);
const coreInitResult = await vvdCore.settled;

assertInitResult(coreInitResult, DARK);
});

it('should perform set', async () => {
const r = randomAlpha();
const vvdCore = (await import(`../vvd-core.js?t=${r}`)).default;
const coreInitResult = await vvdCore.set({ scheme: LIGHT });

assertInitResult(coreInitResult, LIGHT);
});

it('should not fail on abnormal calls', async () => {
document.documentElement.setAttribute(
CONTEXT_ATTR,
`illegal theme:${DARK} theme:${LIGHT}`
);
const r = randomAlpha();
const vvdCore = (await import(`../vvd-core.js?t=${r}`)).default;
document.documentElement.removeAttribute(CONTEXT_ATTR);
let coreInitResult = await vvdCore.settled;

assertInitResult(coreInitResult, DARK);

coreInitResult = await vvdCore.set({
scheme: DARK,
illegal: { some: null },
});
assertInitResult(coreInitResult, DARK);
});
});

it('should perform and auto-init to default when no data-vvd-context provided', async () => {
const vvdCoreDedicated = (await import('../vvd-core.js')).default;
assert.isDefined(vvdCoreDedicated.settled);
const readyResult = await vvdCoreDedicated.settled;
assert.isArray(readyResult);
readyResult.forEach((r) => {
assert.isObject(r);
describe('switch flows in encapsulated environment and assert variables set', () => {
it('should perform auto-init to default when no data-vvd-context provided', async () => {
await getFrameLoadedInjected(CORE_SETUP_HTML_TAG, async (iframe) => {
const iframeWindow = iframe.contentWindow;
await iframeWindow.executeSetup();
const coreInitResult = await iframeWindow.vvdCore.settled;

assertInitResult(coreInitResult, LIGHT);
assertBaseVarsMatch(
LIGHT,
PRINCIPAL_VARIABLES_FILTER,
iframe.contentDocument.body
);
});
});

it('should perform auto-init to a value in data-vvd-context, when provided', async () => {
const vvdContextTheme = DARK;
await getFrameLoadedInjected(CORE_SETUP_HTML_TAG, async (iframe) => {
iframe.contentDocument.documentElement.setAttribute(
CONTEXT_ATTR,
`theme:${vvdContextTheme}`
);

const iframeWindow = iframe.contentWindow;
await iframeWindow.executeSetup();
const coreInitResult = await iframeWindow.vvdCore.settled;

assertInitResult(coreInitResult, vvdContextTheme);
assertBaseVarsMatch(
vvdContextTheme,
PRINCIPAL_VARIABLES_FILTER,
iframe.contentDocument.body
);
});
});

it('should NOT perform auto-init when data-vvd-context is "none"', async () => {
const vvdContextNone = NONE;
await getFrameLoadedInjected(CORE_SETUP_HTML_TAG, async (iframe) => {
iframe.contentDocument.documentElement.setAttribute(
CONTEXT_ATTR,
vvdContextNone
);

const iframeWindow = iframe.contentWindow;
await iframeWindow.executeSetup();

try {
await iframeWindow.vvdCore.settled;
} catch (e) {
expect(e).exist;
expect(e.includes('auto-init unavailable')).true;
}
});
});

it('should perform init to a first value in data-vvd-context, when many provided', async () => {
const vvdContextTheme = LIGHT;
await getFrameLoadedInjected(CORE_SETUP_HTML_TAG, async (iframe) => {
iframe.contentDocument.documentElement.setAttribute(
CONTEXT_ATTR,
`theme:${vvdContextTheme} theme:${DARK}`
);

const iframeWindow = iframe.contentWindow;
await iframeWindow.executeSetup();
const coreInitResult = await iframeWindow.vvdCore.settled;

assertInitResult(coreInitResult, vvdContextTheme);
assertBaseVarsMatch(
vvdContextTheme,
PRINCIPAL_VARIABLES_FILTER,
iframe.contentDocument.body
);
});
});
});
});

function assertInitResult(tested, expectedScheme) {
expect(tested).exist;
expect(tested.scheme).exist;
expect(tested.scheme.option).equal(expectedScheme);
expect(tested.scheme.scheme).equal(expectedScheme);
}
Loading

0 comments on commit 6228dfe

Please sign in to comment.