@ima/plugin-testing-integration
Version:
IMA.js plugin for integration testing
216 lines (215 loc) • 8.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: Object.getOwnPropertyDescriptor(all, name).get
});
}
_export(exports, {
get clearImaApp () {
return clearImaApp;
},
get initImaApp () {
return initImaApp;
}
});
const _nodeassert = require("node:assert");
const _cli = require("@ima/cli");
const _core = require("@ima/core");
const _helpers = require("@ima/helpers");
const _server = require("@ima/server");
const _jsdom = require("jsdom");
const _aop = require("./aop");
const _bootConfigExtensions = require("./bootConfigExtensions");
const _configuration = require("./configuration");
const _helpers1 = require("./helpers");
const _localization = require("./localization");
const setIntervalNative = global.setInterval;
const setTimeoutNative = global.setTimeout;
const setImmediateNative = global.setImmediate;
let timers = [];
/**
* Clears IMA Application instance from environment
*
* @param {object} app Object from initImaApp method
*/ function clearImaApp(app) {
global.setInterval = setIntervalNative;
global.setTimeout = setTimeoutNative;
global.setImmediate = setImmediateNative;
timers.forEach(({ clear })=>clear());
(0, _aop.unAopAll)();
app.oc.clear();
}
/**
* Initializes IMA application with our production-like configuration
* Reinitializes jsdom with configuration, that will work with our application
*
* @param {import('@ima/core').AppConfigFunctions} [bootConfigMethods] Object, that can contain methods for ima boot configuration
* @returns {Promise<object>}
*/ async function initImaApp(bootConfigMethods = {}) {
const config = (0, _configuration.getConfig)();
const bootConfigExtensions = (0, _bootConfigExtensions.getBootConfigExtensions)();
const imaConfig = await (0, _cli.resolveImaConfig)({
rootDir: config.rootDir
});
// JSDom needs to be initialized before we start importing project files,
// since some packages can do some client/server detection at this point
await _initJSDom();
_installTimerWrappers();
await config.prebootScript();
const defaultBootConfigMethods = (0, _helpers1.requireFromProject)(config.appMainPath).getInitialAppConfigFunctions();
/**
* Initializes JSDOM environment for the application run
*/ async function _initJSDom() {
const content = await _getIMAResponseContent();
// SPA jsdom interpreter
const jsdom = new _jsdom.JSDOM(content, {
pretendToBeVisual: true,
url: `${config.protocol}//${config.host}/`
});
// Setup node environment to work with jsdom window
const { window } = jsdom;
global.window = window;
global.jsdom = jsdom;
global.document = window.document;
// Extend node global with created window vars
Object.defineProperties(global, {
...Object.getOwnPropertyDescriptors(window),
...Object.getOwnPropertyDescriptors(global)
});
// @TODO: The way we copy `window` properties to `global` is not correct,
// we should switch to `global-jsdom`, or take its implementation
// as an inspiration for our own implementation
global.CustomEvent = window.CustomEvent; // Hotfix for Node 19+, we can remove this once we switch to `global-jsdom`
global.File = window.File; // Hotfix for Node 19+, we can remove this once we switch to `global-jsdom`
// set debug before IMA env debug
global.$Debug = true;
// Mock dictionary
global.$IMA.i18n = (0, _localization.generateDictionary)(imaConfig.languages, config.locale);
// Mock scroll for ClientWindow.scrollTo for ima/core page routing scroll
global.window.scrollTo = ()=>{};
// Replace window fetch by node fetch
global.window.fetch = global.fetch;
// Required for JSDOM XPath selectors
global.console.assert = _nodeassert.strict;
// Call all page scripts (jsdom build-in runScript creates new V8 context, unable to mix with node context)
const pageScripts = jsdom.window.document.getElementsByTagName('script');
if (typeof config.pageScriptEvalFn === 'function') {
for (const script of pageScripts){
config.pageScriptEvalFn(script);
}
}
}
/**
* Wraps the global timer methods to collect their return values,
* which can be later cleared in clearImaApp function
*/ function _installTimerWrappers() {
global.setInterval = (...args)=>{
let timer = setIntervalNative(...args);
timers.push({
timer,
clear: ()=>global.clearInterval(timer)
});
return timer;
};
global.setTimeout = (...args)=>{
let timer = setTimeoutNative(...args);
timers.push({
timer,
clear: ()=>global.clearTimeout(timer)
});
return timer;
};
global.setImmediate = (...args)=>{
let timer = setImmediateNative(...args);
timers.push({
timer,
clear: ()=>global.clearImmediate(timer)
});
return timer;
};
}
/**
* @param {string} method
* @returns {Function} Function merging bootConfigMethods from param
* and web default boot config methods
*/ function _getBootConfigForMethod(method) {
return (...args)=>{
const results = [];
results.push(defaultBootConfigMethods[method](...args) || {});
results.push(bootConfigExtensions[method](...args) || {});
if (typeof bootConfigMethods[method] === 'function') {
results.push(bootConfigMethods[method](...args) || {});
}
if (method === 'initSettings') {
return (0, _helpers.assignRecursively)({}, ...results);
}
return null;
};
}
/**
* Get response content for the application run
*
* @returns {string} html content
*/ async function _getIMAResponseContent() {
// Mock devUtils to override manifest loading
const devUtils = {
manifestRequire: ()=>({})
};
// Prepare serverApp with environment override
const { serverApp } = await (0, _server.createIMAServer)({
devUtils,
applicationFolder: config.applicationFolder,
processEnvironment: (currentEnvironment)=>config.processEnvironment({
...currentEnvironment,
$Server: {
...currentEnvironment.$Server,
concurrency: 0,
serveSPA: {
allow: true
},
degradation: {
isSPA: ()=>true
}
},
$Debug: true
})
});
// Generate request response
const response = await serverApp.requestHandler({
get: ()=>'',
headers: ()=>'',
originalUrl: config.host,
protocol: config.protocol.replace(':', '')
}, {
status: ()=>200,
send: ()=>{},
set: ()=>{},
locals: {
language: config.locale,
host: config.host,
protocol: config.protocol,
path: config.path || '',
root: config.root || '',
languagePartPath: config.languagePartPath || ''
}
});
return response.content;
}
const app = (0, _core.createImaApp)();
const bootConfig = (0, _core.getClientBootConfig)({
initSettings: _getBootConfigForMethod('initSettings'),
initBindApp: _getBootConfigForMethod('initBindApp'),
initServicesApp: _getBootConfigForMethod('initServicesApp'),
initRoutes: _getBootConfigForMethod('initRoutes')
});
await (0, _core.onLoad)();
await (0, _core.bootClientApp)(app, bootConfig);
// To use ima route handler in jsdom
app.oc.get('$Router').listen();
return Object.assign({}, app, bootConfigExtensions.getAppExtension(app));
}
//# sourceMappingURL=app.js.map