From 77d27113d347b4724302f1c72de1f238e04aeead Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 16 Dec 2023 19:52:00 -0500 Subject: Fix DOM tests (#363) * Only apply eslint rules to .test.js files * Fix tests * Use built-in environment * Fix tests * Optional file name * Remove environment --- test/anki-note-builder.test.js | 2 + test/document-test.js | 58 ++++++++++++++++++++++++++++ test/document-util.test.js | 86 +++++++++--------------------------------- test/dom-text-scanner.test.js | 49 ++++-------------------- 4 files changed, 86 insertions(+), 109 deletions(-) create mode 100644 test/document-test.js (limited to 'test') diff --git a/test/anki-note-builder.test.js b/test/anki-note-builder.test.js index 96dacab6..50b66f18 100644 --- a/test/anki-note-builder.test.js +++ b/test/anki-note-builder.test.js @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +// @vitest-environment jsdom + import 'fake-indexeddb/auto'; import fs from 'fs'; import {fileURLToPath} from 'node:url'; diff --git a/test/document-test.js b/test/document-test.js new file mode 100644 index 00000000..9d763816 --- /dev/null +++ b/test/document-test.js @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Yomitan Authors + * Copyright (C) 2020-2022 Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import fs from 'fs'; +import {test} from 'vitest'; +import {builtinEnvironments} from 'vitest/environments'; + +/** + * @param {import('jsdom').DOMWindow} window + */ +function prepareWindow(window) { + const {document} = window; + + // Define innerText setter as an alias for textContent setter + Object.defineProperty(window.HTMLDivElement.prototype, 'innerText', { + set(value) { this.textContent = value; } + }); + + // Placeholder for feature detection + document.caretRangeFromPoint = () => null; +} + +/** + * @param {string} [htmlFilePath] + * @returns {import('vitest').TestAPI<{window: import('jsdom').DOMWindow}>} + */ +export function domTest(htmlFilePath) { + return test.extend({ + // eslint-disable-next-line no-empty-pattern + window: async ({}, use) => { + const html = typeof htmlFilePath === 'string' ? fs.readFileSync(htmlFilePath, {encoding: 'utf8'}) : ''; + const env = builtinEnvironments.jsdom; + const {teardown} = await env.setup(global, {jsdom: {html}}); + const window = /** @type {import('jsdom').DOMWindow} */ (/** @type {unknown} */ (global.window)); + prepareWindow(window); + try { + await use(window); + } finally { + teardown(global); + } + } + }); +} diff --git a/test/document-util.test.js b/test/document-util.test.js index 8c6ab69b..10857df9 100644 --- a/test/document-util.test.js +++ b/test/document-util.test.js @@ -16,15 +16,14 @@ * along with this program. If not, see . */ -import fs from 'fs'; -import {JSDOM} from 'jsdom'; import {fileURLToPath} from 'node:url'; import path from 'path'; -import {expect, test} from 'vitest'; +import {describe, expect} from 'vitest'; import {DocumentUtil} from '../ext/js/dom/document-util.js'; import {DOMTextScanner} from '../ext/js/dom/dom-text-scanner.js'; import {TextSourceElement} from '../ext/js/dom/text-source-element.js'; import {TextSourceRange} from '../ext/js/dom/text-source-range.js'; +import {domTest} from './document-test.js'; const dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -68,27 +67,6 @@ class DOMRect { } -/** - * @param {string} fileName - * @returns {JSDOM} - */ -function createJSDOM(fileName) { - const domSource = fs.readFileSync(fileName, {encoding: 'utf8'}); - const dom = new JSDOM(domSource); - const document = dom.window.document; - const window = dom.window; - - // Define innerText setter as an alias for textContent setter - Object.defineProperty(window.HTMLDivElement.prototype, 'innerText', { - set(value) { this.textContent = value; } - }); - - // Placeholder for feature detection - document.caretRangeFromPoint = () => null; - - return dom; -} - /** * @param {Element} element * @param {string|undefined} selector @@ -99,13 +77,13 @@ function querySelectorChildOrSelf(element, selector) { } /** - * @param {JSDOM} dom + * @param {import('jsdom').DOMWindow} window * @param {?Node} node * @returns {?Text|Node} */ -function getChildTextNodeOrSelf(dom, node) { +function getChildTextNodeOrSelf(window, node) { if (node === null) { return null; } - const Node = dom.window.Node; + const Node = window.Node; const childNode = node.firstChild; return (childNode !== null && childNode.nodeType === Node.TEXT_NODE ? childNode : node); } @@ -131,27 +109,10 @@ function findImposterElement(document) { return document.querySelector('div[style*="2147483646"]>*'); } - -/** */ -async function testDocument1() { - const dom = createJSDOM(path.join(dirname, 'data', 'html', 'test-document1.html')); - const window = dom.window; - - try { - await testDocumentTextScanningFunctions(dom); - await testTextSourceRangeSeekFunctions(dom); - } finally { - window.close(); - } -} - -/** - * @param {JSDOM} dom - */ -async function testDocumentTextScanningFunctions(dom) { - const document = dom.window.document; - - test('DocumentTextScanningFunctions', () => { +describe('DocumentUtil', () => { + const testDoc = domTest(path.join(dirname, 'data/html/test-document1.html')); + testDoc('Text scanning functions', ({window}) => { + const {document} = window; for (const testElement of /** @type {NodeListOf} */ (document.querySelectorAll('.test[data-test-type=scan]'))) { // Get test parameters const { @@ -170,8 +131,8 @@ async function testDocumentTextScanningFunctions(dom) { const elementFromPointValue = querySelectorChildOrSelf(testElement, elementFromPointSelector); const caretRangeFromPointValue = querySelectorChildOrSelf(testElement, caretRangeFromPointSelector); - const startNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, startNodeSelector)); - const endNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, endNodeSelector)); + const startNode = getChildTextNodeOrSelf(window, querySelectorChildOrSelf(testElement, startNodeSelector)); + const endNode = getChildTextNodeOrSelf(window, querySelectorChildOrSelf(testElement, endNodeSelector)); const startOffset2 = parseInt(/** @type {string} */ (startOffset), 10); const endOffset2 = parseInt(/** @type {string} */ (endOffset), 10); @@ -187,7 +148,7 @@ async function testDocumentTextScanningFunctions(dom) { document.elementFromPoint = () => elementFromPointValue; document.caretRangeFromPoint = (x, y) => { - const imposter = getChildTextNodeOrSelf(dom, findImposterElement(document)); + const imposter = getChildTextNodeOrSelf(window, findImposterElement(document)); expect(!!imposter).toStrictEqual(hasImposter === 'true'); const range = document.createRange(); @@ -264,15 +225,12 @@ async function testDocumentTextScanningFunctions(dom) { source.cleanup(); } }); -} +}); -/** - * @param {JSDOM} dom - */ -async function testTextSourceRangeSeekFunctions(dom) { - const document = dom.window.document; - - test('TextSourceRangeSeekFunctions', async () => { +describe('DOMTextScanner', () => { + const testDoc = domTest(path.join(dirname, 'data/html/test-document1.html')); + testDoc('Seek functions', async ({window}) => { + const {document} = window; for (const testElement of /** @type {NodeListOf} */ (document.querySelectorAll('.test[data-test-type=text-source-range-seek]'))) { // Get test parameters const { @@ -314,12 +272,4 @@ async function testTextSourceRangeSeekFunctions(dom) { expect(content).toStrictEqual(expectedResultContent); } }); -} - - -/** */ -async function main() { - await testDocument1(); -} - -await main(); +}); diff --git a/test/dom-text-scanner.test.js b/test/dom-text-scanner.test.js index f6a7410a..76e95a09 100644 --- a/test/dom-text-scanner.test.js +++ b/test/dom-text-scanner.test.js @@ -16,24 +16,14 @@ * along with this program. If not, see . */ -import fs from 'fs'; -import {JSDOM} from 'jsdom'; import {fileURLToPath} from 'node:url'; import path from 'path'; -import {expect, test} from 'vitest'; +import {describe, expect} from 'vitest'; import {DOMTextScanner} from '../ext/js/dom/dom-text-scanner.js'; +import {domTest} from './document-test.js'; const dirname = path.dirname(fileURLToPath(import.meta.url)); -/** - * @param {string} fileName - * @returns {JSDOM} - */ -function createJSDOM(fileName) { - const domSource = fs.readFileSync(fileName, {encoding: 'utf8'}); - return new JSDOM(domSource); -} - /** * @param {Element} element * @param {string} selector @@ -111,13 +101,12 @@ function createAbsoluteGetComputedStyle(window) { } -/** - * @param {JSDOM} dom - */ -async function testDomTextScanner(dom) { - const document = dom.window.document; +describe('DOMTextScanner', () => { + const testDoc = domTest(path.join(dirname, 'data/html/test-dom-text-scanner.html')); + testDoc('Seek tests', ({window}) => { + const {document} = window; + window.getComputedStyle = createAbsoluteGetComputedStyle(window); - test('DomTextScanner', () => { for (const testElement of /** @type {NodeListOf} */ (document.querySelectorAll('y-test'))) { let testData = JSON.parse(/** @type {string} */ (testElement.dataset.testData)); if (!Array.isArray(testData)) { @@ -185,26 +174,4 @@ async function testDomTextScanner(dom) { } } }); -} - - -/** */ -async function testDocument1() { - const dom = createJSDOM(path.join(dirname, 'data', 'html', 'test-dom-text-scanner.html')); - const window = dom.window; - try { - window.getComputedStyle = createAbsoluteGetComputedStyle(window); - - await testDomTextScanner(dom); - } finally { - window.close(); - } -} - - -/** */ -async function main() { - await testDocument1(); -} - -await main(); +}); -- cgit v1.2.3