UNPKG

@ima/plugin-testing-integration

Version:

IMA.js plugin for integration testing

213 lines (212 loc) 8.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { clearImaApp: function() { return clearImaApp; }, initImaApp: function() { 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 } }, $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