diff options
| -rw-r--r-- | .eslintrc.json | 2 | ||||
| -rw-r--r-- | test/anki-note-builder.test.js | 2 | ||||
| -rw-r--r-- | test/document-test.js | 58 | ||||
| -rw-r--r-- | test/document-util.test.js | 86 | ||||
| -rw-r--r-- | test/dom-text-scanner.test.js | 49 | ||||
| -rw-r--r-- | vitest.config.js | 1 | 
6 files changed, 87 insertions, 111 deletions
| diff --git a/.eslintrc.json b/.eslintrc.json index f1c995a0..8282d206 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -765,7 +765,7 @@          },          {              "files": [ -                "test/**" +                "test/**/*.test.js"              ],              "plugins": [                  "vitest" 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 <https://www.gnu.org/licenses/>.   */ +// @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 <https://www.gnu.org/licenses/>. + */ + +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'}) : '<!DOCTYPE html>'; +            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 <https://www.gnu.org/licenses/>.   */ -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)); @@ -69,27 +68,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   * @returns {?Element} @@ -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<HTMLElement>} */ (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<HTMLElement>} */ (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,25 +16,15 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -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   * @returns {?Node} @@ -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<HTMLElement>} */ (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(); +}); diff --git a/vitest.config.js b/vitest.config.js index 025eec17..b0d1e4e3 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -23,7 +23,6 @@ export default defineConfig({              'dev/lib/**',              'test/playwright/**'          ], -        environment: 'jsdom',          // @ts-expect-error - Appears to not be defined in the type definitions (https://vitest.dev/advanced/pool)          poolOptions: {              threads: { |