diff --git a/strcalc/src/main/frontend/components/app.js b/strcalc/src/main/frontend/components/app.js index d955841..2bac7a3 100644 --- a/strcalc/src/main/frontend/components/app.js +++ b/strcalc/src/main/frontend/components/app.js @@ -10,8 +10,8 @@ * @module init */ -import Introduction from './introduction' -import Calculator from './calculator' +import Introduction from './introduction.js' +import Calculator from './calculator.js' export default class App { /** diff --git a/strcalc/src/main/frontend/components/calculators.js b/strcalc/src/main/frontend/components/calculators.js index 772ce7d..6ab3691 100644 --- a/strcalc/src/main/frontend/components/calculators.js +++ b/strcalc/src/main/frontend/components/calculators.js @@ -4,7 +4,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { postFormData } from './request' +import { postFormData } from './request.js' export const DEFAULT_ENDPOINT = './add' diff --git a/strcalc/src/main/frontend/main.js b/strcalc/src/main/frontend/main.js index d8abf40..39d9e5d 100644 --- a/strcalc/src/main/frontend/main.js +++ b/strcalc/src/main/frontend/main.js @@ -16,7 +16,7 @@ * @module main */ import App from './components/app.js' -import calculators from './components/calculators' +import calculators from './components/calculators.js' /** * Calls the app initializer with production parameters. diff --git a/strcalc/src/main/frontend/main.test.js b/strcalc/src/main/frontend/main.test.js index 7585a0c..f831d1e 100644 --- a/strcalc/src/main/frontend/main.test.js +++ b/strcalc/src/main/frontend/main.test.js @@ -4,16 +4,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { describe, afterEach, expect, test } from 'vitest' -import { PageLoader } from './test/page-loader.js' -import StringCalculatorPage from './test/page' +import { afterEach, beforeAll, describe, expect, test } from 'vitest' +import StringCalculatorPage from './test/page.js' +import TestPageOpener from 'test-page-opener' describe('String Calculator UI on initial page load', () => { - const loader = new PageLoader('/strcalc/') - afterEach(() => loader.closeAll()) + let opener + + beforeAll(async () => opener = await TestPageOpener.create('/strcalc/')) + afterEach(() => opener.closeAll()) test('contains the "Hello, World!" placeholder', async () => { - const { document } = await loader.load('index.html') + const { document } = await opener.open('index.html') const appElem = document.querySelector('#app') const e = new StringCalculatorPage(appElem, document).title() diff --git a/strcalc/src/main/frontend/package.json b/strcalc/src/main/frontend/package.json index b1ace10..e4ec739 100644 --- a/strcalc/src/main/frontend/package.json +++ b/strcalc/src/main/frontend/package.json @@ -44,6 +44,7 @@ "handlebars": "^4.7.8", "jsdoc-cli-wrapper": "^1.0.4", "jsdom": "^23.1.0", + "test-page-opener": "^1.0.3", "vite": "^5.0.11", "vitest": "^1.1.3", "webdriverio": "^8.27.0" diff --git a/strcalc/src/main/frontend/pnpm-lock.yaml b/strcalc/src/main/frontend/pnpm-lock.yaml index d8aaf82..e6321e1 100644 --- a/strcalc/src/main/frontend/pnpm-lock.yaml +++ b/strcalc/src/main/frontend/pnpm-lock.yaml @@ -41,6 +41,9 @@ devDependencies: jsdom: specifier: ^23.1.0 version: 23.1.0 + test-page-opener: + specifier: ^1.0.3 + version: 1.0.3 vite: specifier: ^5.0.11 version: 5.0.11 @@ -3546,6 +3549,13 @@ packages: minimatch: 3.1.2 dev: true + /test-page-opener@1.0.3: + resolution: {integrity: sha512-vHDT/nd39MhUS5ScCAZtdDKsbHN+e7954lIBeKYk/Ei7bBE6dwROPJs9b4tPHjmWjOcL6TT+6Pivhss2Oz7Jzg==} + engines: {node: '>= 18.0.0'} + dependencies: + istanbul-lib-coverage: 3.2.2 + dev: true + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true diff --git a/strcalc/src/main/frontend/test/page-loader.js b/strcalc/src/main/frontend/test/page-loader.js deleted file mode 100644 index 7b5d9f3..0000000 --- a/strcalc/src/main/frontend/test/page-loader.js +++ /dev/null @@ -1,346 +0,0 @@ -/* eslint-env browser, node */ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -/** - * Exports test helper utilities for this project. - * @module test-helpers - */ - -/** - * Enables tests to load page URLs both in the browser and in Node using JSDom. - */ -export class PageLoader { - static #impl - - #basePath - #loaded - - constructor(basePath) { - if (!basePath.startsWith('/') || !basePath.endsWith('/')) { - const msg = 'basePath should start with \'/\' and ' + - 'end with \'/\', got: ' - throw new Error(`${msg}"${basePath}"`) - } - this.#basePath = basePath - this.#loaded = [] - } - - async load(pagePath) { - if (pagePath.startsWith('/')) { - const msg = 'page path should not start with \'/\', got: ' - throw new Error(`${msg}"${pagePath}"`) - } - - const impl = await PageLoader.getImpl() - const page = await impl.load(this.#basePath, pagePath) - - this.#loaded.push(page) - return page - } - - closeAll() { - this.#loaded.forEach(p => p.close()) - this.#loaded = [] - } - - static async getImpl() { - if (this.#impl) { - return this.#impl - } - - if (globalThis.window) { - return this.#impl = new BrowserPageLoader(globalThis.window) - } - - const {JSDOM} = await import('jsdom') - return this.#impl = new JsdomPageLoader(JSDOM, importModulesDynamically) - } -} - -class BrowserPageLoader { - #window - - constructor(window) { - this.#window = window - } - - // Loads a page and returns {window, document, close() } using the browser. - async load(basePath, pagePath) { - const w = this.#window.open(`${basePath}${pagePath}`) - return new Promise(resolve => { - const listener = () => { - this.#setCoverageStore(w) - resolve({window: w, document: w.document, close() {w.close()}}) - } - w.addEventListener('load', listener, {once: true}) - }) - } - - // This is an egregious, brittle hack that's very specific to Vitest's - // Istanbul coverage provider. It also only collects coverage from the last - // page loaded; it loses coverage information for all other pages. - // - // But as long as a test function calls BrowserPageLoader.load() only once, it - // should work pretty well. - #setCoverageStore(openedWindow) { - const COVERAGE_STORE_KEY = '__VITEST_COVERAGE__' - - if (COVERAGE_STORE_KEY in openedWindow) { - this.#window[COVERAGE_STORE_KEY] = openedWindow[COVERAGE_STORE_KEY] - } - } -} - -// Returns window and document objects from a JSDOM-parsed HTML file. -// -// It will execute