@olton/latte
Version:
Simple test framework for JavaScript and TypeScript with DOM supports
242 lines (216 loc) • 6.49 kB
JavaScript
import { BrowserErrorCaptureEnum } from 'happy-dom'
import { GlobalRegistrator } from '@happy-dom/global-registrator'
import { merge } from '../helpers/merge.js'
import { readFileSync, existsSync } from 'fs'
import jsdomGlobal from 'global-jsdom'
import {fetch as fetchPolyfill} from 'whatwg-fetch'
const navigationDefaults = {
disableMainFrameNavigation: false,
disableChildFrameNavigation: false,
disableChildPageNavigation: false,
disableFallbackToSetURL: false,
crossOriginPolicy: 'no-cors'
}
const navigatorDefaults = {
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
maxTouchPoints: 5
}
const timerDefaults = {
maxTimeout: -1,
maxIntervalTime: -1,
maxIntervalIterations: -1
}
const deviceDefaults = {
prefersColorScheme: 'light',
mediaType: 'screen'
}
const windowDefaults = {
url: 'http://localhost:8000/',
disableJavaScriptEvaluation: false,
disableJavaScriptFileLoading: false,
disableCSSFileLoading: false,
disableComputedStyleRendering: false,
handleDisabledFileLoadingAsSuccess: false,
errorCapture: BrowserErrorCaptureEnum.processLevel, // processLevel, tryAndCatch, disabled
navigation: navigationDefaults,
navigator: navigatorDefaults,
timer: timerDefaults,
device: deviceDefaults
}
// Конфігурація за замовчуванням для jsdom
const jsdomDefaults = {
url: 'http://localhost:8000/',
contentType: 'text/html',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
includeNodeLocations: false,
runScripts: 'dangerously',
resources: 'usable',
pretendToBeVisual: true
}
let cleanup = null;
const setup = async (options) => {
try {
await GlobalRegistrator.unregister()
} catch (e) {}
if (global.config.domEnv === 'jsdom') {
try {
// Використовуємо jsdom-global для автоматичної установки всіх глобальних змінних
cleanup = jsdomGlobal('', {
...jsdomDefaults,
...options
});
if (typeof window.fetch === "undefined") window.fetch = fetchPolyfill;
// Встановлюємо HTML-структуру
document.documentElement.innerHTML = `
<html lang="en">
<head>
<title>Test page</title>
</head>
<body>
</body>
</html>
`;
} catch (e) {
console.error('JSDOM Initialization Error:', e);
throw e;
}
} else {
// Ініціалізація happy-dom як раніше
GlobalRegistrator.register(merge(windowDefaults, options))
}
flush()
}
const bye = async () => {
if (global.config && global.config.domEnv === 'jsdom') {
// Очищення глобальних змінних jsdom
if (typeof cleanup === 'function') {
cleanup();
cleanup = null;
}
} else {
// Відключення happy-dom як раніше
await GlobalRegistrator.unregister()
}
}
const html = (str) => {
document.body.innerHTML = str
}
const flush = () => {
document.documentElement.innerHTML = `
<html lang="en">
<head>
<title>Test page</title>
</head>
<body>
</body>
</html>
`
}
const $ = (selector) => {
return document.querySelector(selector)
}
const $$ = (selector) => {
return document.querySelectorAll(selector)
}
const js = {
fromFile: async (file) => {
if (!existsSync(file)) {
throw new Error(`File ${file} not found`)
}
const script = document.createElement('script')
script.textContent = readFileSync(file, 'utf-8')
document.head.appendChild(script)
},
fromString: async (str) => {
const script = document.createElement('script')
script.textContent = str
document.head.appendChild(script)
},
fromUrl: async (url) => {
const script = document.createElement('script')
script.src = url
document.head.appendChild(script)
}
}
const css = {
fromFile: async (file) => {
if (!existsSync(file)) {
throw new Error(`File ${file} not found`)
}
const style = document.createElement('style')
style.textContent = readFileSync(file, 'utf-8')
document.head.appendChild(style)
},
fromString: async (str) => {
const style = document.createElement('style')
style.textContent = str
document.head.appendChild(style)
},
fromUrl: async (url) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
document.head.appendChild(link)
},
fromObject: async (obj) => {
const _css = Object.keys(obj).map(selector => {
const properties = Object.keys(obj[selector]).map(property => {
return `${property}: ${obj[selector][property]}`
}).join(';\n')
return `${selector} {${properties}}\n`
}).join('')
const style = document.createElement('style')
style.textContent = _css
document.head.appendChild(style)
}
}
// Пример расширения DOM-модуля (src/dom/helpers.js)
function waitFor (selector, timeout = 5000) {
return new Promise((resolve, reject) => {
const element = document.querySelector(selector)
if (element) {
resolve(element)
return
}
const observer = new MutationObserver(() => {
const element = document.querySelector(selector)
if (element) {
observer.disconnect()
resolve(element)
}
})
observer.observe(document.body, {
childList: true,
subtree: true
})
setTimeout(() => {
observer.disconnect()
reject(new Error(`Then element ${selector} not found during ${timeout}мс`))
}, timeout)
})
}
const waitForObject = (name) => {
return new Promise((resolve) => {
const checkObject = setInterval(() => {
if (window[name]) {
// if (typeof cb === 'function') {
// cb()
// }
clearInterval(checkObject);
resolve(window[name]);
}
}, 100);
});
}
export {
setup,
bye,
html,
js,
css,
$,
$$,
flush,
waitFor,
waitForObject,
}