summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/data/html/dom-text-scanner.html2
-rw-r--r--test/document-util.test.js291
-rw-r--r--test/dom-text-scanner.test.js12
-rw-r--r--test/fixtures/dom-test.js14
-rw-r--r--test/options-util.test.js27
-rw-r--r--test/profile-conditions-util.test.js14
-rw-r--r--test/text-source-map.test.js26
7 files changed, 206 insertions, 180 deletions
diff --git a/test/data/html/dom-text-scanner.html b/test/data/html/dom-text-scanner.html
index ff4d0493..1f537d4e 100644
--- a/test/data/html/dom-text-scanner.html
+++ b/test/data/html/dom-text-scanner.html
@@ -392,4 +392,4 @@
</test-case>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/test/document-util.test.js b/test/document-util.test.js
index cc8db706..f332d384 100644
--- a/test/document-util.test.js
+++ b/test/document-util.test.js
@@ -18,12 +18,12 @@
import {fileURLToPath} from 'node:url';
import path from 'path';
-import {describe, expect} from 'vitest';
+import {afterAll, describe, expect, test} 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 {createDomTest} from './fixtures/dom-test.js';
+import {setupDomTest} from './fixtures/dom-test.js';
import {parseJson} from '../dev/json.js';
const dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -110,165 +110,176 @@ function findImposterElement(document) {
return document.querySelector('div[style*="2147483646"]>*');
}
-const test = createDomTest(path.join(dirname, 'data/html/document-util.html'));
+const documentUtilTestEnv = await setupDomTest(path.join(dirname, 'data/html/document-util.html'));
-describe('DocumentUtil', () => {
- test('Text scanning functions', ({window}) => {
- const {document} = window;
- for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('test-case[data-test-type=scan]'))) {
- // Get test parameters
- /** @type {import('test/document-util').DocumentUtilTestData} */
- const {
- elementFromPointSelector,
- caretRangeFromPointSelector,
- startNodeSelector,
- startOffset,
- endNodeSelector,
- endOffset,
- resultType,
- sentenceScanExtent,
- sentence,
- hasImposter,
- terminateAtNewlines
- } = parseJson(/** @type {string} */ (testElement.dataset.testData));
+describe('Document utility tests', () => {
+ const {window, teardown} = documentUtilTestEnv;
+ afterAll(() => teardown(global));
- const elementFromPointValue = querySelectorChildOrSelf(testElement, elementFromPointSelector);
- const caretRangeFromPointValue = querySelectorChildOrSelf(testElement, caretRangeFromPointSelector);
- const startNode = getChildTextNodeOrSelf(window, querySelectorChildOrSelf(testElement, startNodeSelector));
- const endNode = getChildTextNodeOrSelf(window, querySelectorChildOrSelf(testElement, endNodeSelector));
+ describe('DocumentUtil', () => {
+ describe('Text scanning functions', () => {
+ let testIndex = 0;
+ const {document} = window;
+ for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('test-case[data-test-type=scan]'))) {
+ test(`test-case-${testIndex++}`, () => {
+ // Get test parameters
+ /** @type {import('test/document-util').DocumentUtilTestData} */
+ const {
+ elementFromPointSelector,
+ caretRangeFromPointSelector,
+ startNodeSelector,
+ startOffset,
+ endNodeSelector,
+ endOffset,
+ resultType,
+ sentenceScanExtent,
+ sentence,
+ hasImposter,
+ terminateAtNewlines
+ } = parseJson(/** @type {string} */ (testElement.dataset.testData));
- // Defaults to true
- const terminateAtNewlines2 = typeof terminateAtNewlines === 'boolean' ? terminateAtNewlines : true;
+ const elementFromPointValue = querySelectorChildOrSelf(testElement, elementFromPointSelector);
+ const caretRangeFromPointValue = querySelectorChildOrSelf(testElement, caretRangeFromPointSelector);
+ const startNode = getChildTextNodeOrSelf(window, querySelectorChildOrSelf(testElement, startNodeSelector));
+ const endNode = getChildTextNodeOrSelf(window, querySelectorChildOrSelf(testElement, endNodeSelector));
- expect(elementFromPointValue).not.toStrictEqual(null);
- expect(caretRangeFromPointValue).not.toStrictEqual(null);
- expect(startNode).not.toStrictEqual(null);
- expect(endNode).not.toStrictEqual(null);
+ // Defaults to true
+ const terminateAtNewlines2 = typeof terminateAtNewlines === 'boolean' ? terminateAtNewlines : true;
- // Setup functions
- document.elementFromPoint = () => elementFromPointValue;
+ expect(elementFromPointValue).not.toStrictEqual(null);
+ expect(caretRangeFromPointValue).not.toStrictEqual(null);
+ expect(startNode).not.toStrictEqual(null);
+ expect(endNode).not.toStrictEqual(null);
- document.caretRangeFromPoint = (x, y) => {
- const imposter = getChildTextNodeOrSelf(window, findImposterElement(document));
- expect(!!imposter).toStrictEqual(!!hasImposter);
+ // Setup functions
+ document.elementFromPoint = () => elementFromPointValue;
- const range = document.createRange();
- range.setStart(/** @type {Node} */ (imposter ? imposter : startNode), startOffset);
- range.setEnd(/** @type {Node} */ (imposter ? imposter : startNode), endOffset);
+ document.caretRangeFromPoint = (x, y) => {
+ const imposter = getChildTextNodeOrSelf(window, findImposterElement(document));
+ expect(!!imposter).toStrictEqual(!!hasImposter);
- // Override getClientRects to return a rect guaranteed to contain (x, y)
- range.getClientRects = () => {
- /** @type {import('test/document-types').PseudoDOMRectList} */
- const domRectList = Object.assign(
- [new DOMRect(x - 1, y - 1, 2, 2)],
- {
- /**
- * @this {DOMRect[]}
- * @param {number} index
- * @returns {DOMRect}
- */
- item: function item(index) { return this[index]; }
- }
- );
- return domRectList;
- };
- return range;
- };
+ const range = document.createRange();
+ range.setStart(/** @type {Node} */ (imposter ? imposter : startNode), startOffset);
+ range.setEnd(/** @type {Node} */ (imposter ? imposter : startNode), endOffset);
- // Test docRangeFromPoint
- const source = DocumentUtil.getRangeFromPoint(0, 0, {
- deepContentScan: false,
- normalizeCssZoom: true
- });
- switch (resultType) {
- case 'TextSourceRange':
- expect(getPrototypeOfOrNull(source)).toStrictEqual(TextSourceRange.prototype);
- break;
- case 'TextSourceElement':
- expect(getPrototypeOfOrNull(source)).toStrictEqual(TextSourceElement.prototype);
- break;
- case 'null':
- expect(source).toStrictEqual(null);
- break;
- default:
- expect.unreachable();
- break;
- }
- if (source === null) { continue; }
+ // Override getClientRects to return a rect guaranteed to contain (x, y)
+ range.getClientRects = () => {
+ /** @type {import('test/document-types').PseudoDOMRectList} */
+ const domRectList = Object.assign(
+ [new DOMRect(x - 1, y - 1, 2, 2)],
+ {
+ /**
+ * @this {DOMRect[]}
+ * @param {number} index
+ * @returns {DOMRect}
+ */
+ item: function item(index) { return this[index]; }
+ }
+ );
+ return domRectList;
+ };
+ return range;
+ };
- // Sentence info
- const terminatorString = '…。..??!!';
- const terminatorMap = new Map();
- for (const char of terminatorString) {
- terminatorMap.set(char, [false, true]);
- }
- const quoteArray = [['「', '」'], ['『', '』'], ['\'', '\''], ['"', '"']];
- const forwardQuoteMap = new Map();
- const backwardQuoteMap = new Map();
- for (const [char1, char2] of quoteArray) {
- forwardQuoteMap.set(char1, [char2, false]);
- backwardQuoteMap.set(char2, [char1, false]);
- }
+ // Test docRangeFromPoint
+ const source = DocumentUtil.getRangeFromPoint(0, 0, {
+ deepContentScan: false,
+ normalizeCssZoom: true
+ });
+ switch (resultType) {
+ case 'TextSourceRange':
+ expect(getPrototypeOfOrNull(source)).toStrictEqual(TextSourceRange.prototype);
+ break;
+ case 'TextSourceElement':
+ expect(getPrototypeOfOrNull(source)).toStrictEqual(TextSourceElement.prototype);
+ break;
+ case 'null':
+ expect(source).toStrictEqual(null);
+ break;
+ default:
+ expect.unreachable();
+ break;
+ }
+ if (source === null) { return; }
+
+ // Sentence info
+ const terminatorString = '…。..??!!';
+ const terminatorMap = new Map();
+ for (const char of terminatorString) {
+ terminatorMap.set(char, [false, true]);
+ }
+ const quoteArray = [['「', '」'], ['『', '』'], ['\'', '\''], ['"', '"']];
+ const forwardQuoteMap = new Map();
+ const backwardQuoteMap = new Map();
+ for (const [char1, char2] of quoteArray) {
+ forwardQuoteMap.set(char1, [char2, false]);
+ backwardQuoteMap.set(char2, [char1, false]);
+ }
- // Test docSentenceExtract
- const sentenceActual = DocumentUtil.extractSentence(
- source,
- false,
- sentenceScanExtent,
- terminateAtNewlines2,
- terminatorMap,
- forwardQuoteMap,
- backwardQuoteMap
- ).text;
- expect(sentenceActual).toStrictEqual(sentence);
+ // Test docSentenceExtract
+ const sentenceActual = DocumentUtil.extractSentence(
+ source,
+ false,
+ sentenceScanExtent,
+ terminateAtNewlines2,
+ terminatorMap,
+ forwardQuoteMap,
+ backwardQuoteMap
+ ).text;
+ expect(sentenceActual).toStrictEqual(sentence);
- // Clean
- source.cleanup();
- }
+ // Clean
+ source.cleanup();
+ });
+ }
+ });
});
-});
-describe('DOMTextScanner', () => {
- test('Seek functions', async ({window}) => {
- const {document} = window;
- for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('test-case[data-test-type=text-source-range-seek]'))) {
- // Get test parameters
- /** @type {import('test/document-util').DOMTextScannerTestData} */
- const {
- seekNodeSelector,
- seekNodeIsText,
- seekOffset,
- seekLength,
- seekDirection,
- expectedResultNodeSelector,
- expectedResultNodeIsText,
- expectedResultOffset,
- expectedResultContent
- } = parseJson(/** @type {string} */ (testElement.dataset.testData));
+ describe('DOMTextScanner', () => {
+ describe('Seek functions', () => {
+ let testIndex = 0;
+ const {document} = window;
+ for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('test-case[data-test-type=text-source-range-seek]'))) {
+ test(`test-case-${testIndex++}`, () => {
+ // Get test parameters
+ /** @type {import('test/document-util').DOMTextScannerTestData} */
+ const {
+ seekNodeSelector,
+ seekNodeIsText,
+ seekOffset,
+ seekLength,
+ seekDirection,
+ expectedResultNodeSelector,
+ expectedResultNodeIsText,
+ expectedResultOffset,
+ expectedResultContent
+ } = parseJson(/** @type {string} */ (testElement.dataset.testData));
- /** @type {?Node} */
- let seekNode = testElement.querySelector(/** @type {string} */ (seekNodeSelector));
- if (seekNodeIsText && seekNode !== null) {
- seekNode = seekNode.firstChild;
- }
+ /** @type {?Node} */
+ let seekNode = testElement.querySelector(/** @type {string} */ (seekNodeSelector));
+ if (seekNodeIsText && seekNode !== null) {
+ seekNode = seekNode.firstChild;
+ }
- const expectedResultContent2 = expectedResultContent.join('\n');
+ const expectedResultContent2 = expectedResultContent.join('\n');
- /** @type {?Node} */
- let expectedResultNode = testElement.querySelector(/** @type {string} */ (expectedResultNodeSelector));
- if (expectedResultNodeIsText && expectedResultNode !== null) {
- expectedResultNode = expectedResultNode.firstChild;
- }
+ /** @type {?Node} */
+ let expectedResultNode = testElement.querySelector(/** @type {string} */ (expectedResultNodeSelector));
+ if (expectedResultNodeIsText && expectedResultNode !== null) {
+ expectedResultNode = expectedResultNode.firstChild;
+ }
- const {node, offset, content} = (
+ const {node, offset, content} = (
seekDirection === 'forward' ?
new DOMTextScanner(/** @type {Node} */ (seekNode), seekOffset, true, false).seek(seekLength) :
new DOMTextScanner(/** @type {Node} */ (seekNode), seekOffset, true, false).seek(-seekLength)
- );
+ );
- expect(node).toStrictEqual(expectedResultNode);
- expect(offset).toStrictEqual(expectedResultOffset);
- expect(content).toStrictEqual(expectedResultContent2);
- }
+ expect(node).toStrictEqual(expectedResultNode);
+ expect(offset).toStrictEqual(expectedResultOffset);
+ expect(content).toStrictEqual(expectedResultContent2);
+ });
+ }
+ });
});
});
diff --git a/test/dom-text-scanner.test.js b/test/dom-text-scanner.test.js
index da38d24c..12e3193a 100644
--- a/test/dom-text-scanner.test.js
+++ b/test/dom-text-scanner.test.js
@@ -18,10 +18,10 @@
import {fileURLToPath} from 'node:url';
import path from 'path';
-import {describe, expect} from 'vitest';
+import {afterAll, describe, expect, test} from 'vitest';
import {parseJson} from '../dev/json.js';
import {DOMTextScanner} from '../ext/js/dom/dom-text-scanner.js';
-import {createDomTest} from './fixtures/dom-test.js';
+import {setupDomTest} from './fixtures/dom-test.js';
const dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -101,11 +101,13 @@ function createAbsoluteGetComputedStyle(window) {
};
}
-
-const test = createDomTest(path.join(dirname, 'data/html/dom-text-scanner.html'));
+const domTestEnv = await setupDomTest(path.join(dirname, 'data/html/dom-text-scanner.html'));
describe('DOMTextScanner', () => {
- test('Seek tests', ({window}) => {
+ const {window, teardown} = domTestEnv;
+ afterAll(() => teardown(global));
+
+ test('Seek tests', () => {
const {document} = window;
window.getComputedStyle = createAbsoluteGetComputedStyle(window);
diff --git a/test/fixtures/dom-test.js b/test/fixtures/dom-test.js
index 8cfe80a9..e0e7d647 100644
--- a/test/fixtures/dom-test.js
+++ b/test/fixtures/dom-test.js
@@ -36,6 +36,20 @@ function prepareWindow(window) {
}
/**
+ *
+ * @param {string} [htmlFilePath]
+ * @returns {Promise<{window: import('jsdom').DOMWindow; teardown: (global: unknown) => import('vitest').Awaitable<void>}>}
+ */
+export async function setupDomTest(htmlFilePath) {
+ 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);
+ return {window, teardown};
+}
+
+/**
* @param {string} [htmlFilePath]
* @returns {import('vitest').TestAPI<{window: import('jsdom').DOMWindow}>}
*/
diff --git a/test/options-util.test.js b/test/options-util.test.js
index 41743b9f..a34cc93a 100644
--- a/test/options-util.test.js
+++ b/test/options-util.test.js
@@ -21,7 +21,7 @@
import fs from 'fs';
import {fileURLToPath} from 'node:url';
import path from 'path';
-import {expect, test, vi} from 'vitest';
+import {expect, test, describe, vi} from 'vitest';
import {OptionsUtil} from '../ext/js/data/options-util.js';
import {TemplatePatcher} from '../ext/js/templates/template-patcher.js';
import {chrome, fetch} from './mocks/common.js';
@@ -628,7 +628,7 @@ async function testUpdate() {
/** */
async function testDefault() {
- test('Default', async () => {
+ describe('Default', () => {
/** @type {((options: import('options-util').IntermediateOptions) => void)[]} */
const data = [
(options) => options,
@@ -640,27 +640,22 @@ async function testDefault() {
}
];
- const optionsUtil = new OptionsUtil();
- await optionsUtil.prepare();
+ test.each(data)('default-test-%#', async (modify) => {
+ const optionsUtil = new OptionsUtil();
+ await optionsUtil.prepare();
- for (const modify of data) {
const options = optionsUtil.getDefault();
-
const optionsModified = structuredClone(options);
modify(optionsModified);
-
const optionsUpdated = await optionsUtil.update(structuredClone(optionsModified));
expect(structuredClone(optionsUpdated)).toStrictEqual(structuredClone(options));
- }
+ });
});
}
/** */
async function testFieldTemplatesUpdate() {
- test('FieldTemplatesUpdate', async () => {
- const optionsUtil = new OptionsUtil();
- await optionsUtil.prepare();
-
+ describe('FieldTemplatesUpdate', () => {
const templatePatcher = new TemplatePatcher();
/**
* @param {string} fileName
@@ -1577,7 +1572,11 @@ async function testFieldTemplatesUpdate() {
];
const updatesPattern = /<<<UPDATE-ADDITIONS>>>/g;
- for (const {old, expected, oldVersion, newVersion} of data) {
+
+ test.each(data)('field-templates-update-test-%#', async ({old, expected, oldVersion, newVersion}) => {
+ const optionsUtil = new OptionsUtil();
+ await optionsUtil.prepare();
+
const options = /** @type {import('core').SafeAny} */ (createOptionsTestData1());
options.profiles[0].options.anki.fieldTemplates = old;
options.version = oldVersion;
@@ -1587,7 +1586,7 @@ async function testFieldTemplatesUpdate() {
const optionsUpdated = structuredClone(await optionsUtil.update(options, newVersion));
const fieldTemplatesActual = optionsUpdated.profiles[0].options.anki.fieldTemplates;
expect(fieldTemplatesActual).toStrictEqual(expected2);
- }
+ });
});
}
diff --git a/test/profile-conditions-util.test.js b/test/profile-conditions-util.test.js
index d5c8f8d2..a217eb7a 100644
--- a/test/profile-conditions-util.test.js
+++ b/test/profile-conditions-util.test.js
@@ -18,12 +18,12 @@
/* eslint-disable no-multi-spaces */
-import {expect, test} from 'vitest';
+import {describe, expect, test} from 'vitest';
import {ProfileConditionsUtil} from '../ext/js/background/profile-conditions-util.js';
/** */
function testNormalizeContext() {
- test('NormalizeContext', () => {
+ describe('NormalizeContext', () => {
/** @type {{context: import('settings').OptionsContext, expected: import('profile-conditions-util').NormalizedOptionsContext}[]} */
const data = [
// Empty
@@ -51,17 +51,17 @@ function testNormalizeContext() {
}
];
- for (const {context, expected} of data) {
+ test.each(data)('normalize-context-test-%#', ({context, expected}) => {
const profileConditionsUtil = new ProfileConditionsUtil();
const actual = profileConditionsUtil.normalizeContext(context);
expect(actual).toStrictEqual(expected);
- }
+ });
});
}
/** */
function testSchemas() {
- test('Schemas', () => {
+ describe('Schemas', () => {
/** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('ext/json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */
const data = [
// Empty
@@ -1100,7 +1100,7 @@ function testSchemas() {
}
];
- for (const {conditionGroups, expectedSchema, inputs} of data) {
+ test.each(data)('schemas-test-%#', ({conditionGroups, expectedSchema, inputs}) => {
const profileConditionsUtil = new ProfileConditionsUtil();
const schema = profileConditionsUtil.createSchema(conditionGroups);
if (typeof expectedSchema !== 'undefined') {
@@ -1113,7 +1113,7 @@ function testSchemas() {
expect(actual).toStrictEqual(expected);
}
}
- }
+ });
});
}
diff --git a/test/text-source-map.test.js b/test/text-source-map.test.js
index 54b39319..f798112b 100644
--- a/test/text-source-map.test.js
+++ b/test/text-source-map.test.js
@@ -16,28 +16,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import {expect, test} from 'vitest';
+import {describe, expect, test} from 'vitest';
import {TextSourceMap} from '../ext/js/general/text-source-map.js';
/** */
function testSource() {
- test('Source', () => {
+ describe('Source', () => {
const data = [
['source1'],
['source2'],
['source3']
];
- for (const [source] of data) {
+ test.each(data)('source-test-%#', (source) => {
const sourceMap = new TextSourceMap(source);
expect(source).toStrictEqual(sourceMap.source);
- }
+ });
});
}
/** */
function testEquals() {
- test('Equals', () => {
+ describe('Equals', () => {
/** @type {[args1: [source1: string, mapping1: ?(number[])], args2: [source2: string, mapping2: ?(number[])], expectedEquals: boolean][]} */
const data = [
[['source1', null], ['source1', null], true],
@@ -69,19 +69,19 @@ function testEquals() {
[['source3', [1, 1, 1, 1, 1, 1, 1]], ['source6', [1, 1, 1, 1, 1, 1, 1]], false]
];
- for (const [[source1, mapping1], [source2, mapping2], expectedEquals] of data) {
+ test.each(data)('equals-test-%#', ([source1, mapping1], [source2, mapping2], expectedEquals) => {
const sourceMap1 = new TextSourceMap(source1, mapping1);
const sourceMap2 = new TextSourceMap(source2, mapping2);
expect(sourceMap1.equals(sourceMap1)).toBe(true);
expect(sourceMap2.equals(sourceMap2)).toBe(true);
expect(sourceMap1.equals(sourceMap2)).toStrictEqual(expectedEquals);
- }
+ });
});
}
/** */
function testGetSourceLength() {
- test('GetSourceLength', () => {
+ describe('GetSourceLength', () => {
/** @type {[args: [source: string, mapping: number[]], finalLength: number, expectedValue: number][]} */
const data = [
[['source', [1, 1, 1, 1, 1, 1]], 1, 1],
@@ -101,16 +101,16 @@ function testGetSourceLength() {
[['source', [6, 6]], 1, 6]
];
- for (const [[source, mapping], finalLength, expectedValue] of data) {
+ test.each(data)('get-source-length-test-%#', ([source, mapping], finalLength, expectedValue) => {
const sourceMap = new TextSourceMap(source, mapping);
expect(sourceMap.getSourceLength(finalLength)).toStrictEqual(expectedValue);
- }
+ });
});
}
/** */
function testCombineInsert() {
- test('CombineInsert', () => {
+ describe('CombineInsert', () => {
/** @type {[args: [source: string, mapping: ?(number[])], expectedArgs: [expectedSource: string, expectedMapping: ?(number[])], operations: [operation: string, arg1: number, arg2: number][]][]} */
const data = [
// No operations
@@ -214,7 +214,7 @@ function testCombineInsert() {
]
];
- for (const [[source, mapping], [expectedSource, expectedMapping], operations] of data) {
+ test.each(data)('combine-insert-test-%#', ([source, mapping], [expectedSource, expectedMapping], operations) => {
const sourceMap = new TextSourceMap(source, mapping);
const expectedSourceMap = new TextSourceMap(expectedSource, expectedMapping);
for (const [operation, ...args] of operations) {
@@ -228,7 +228,7 @@ function testCombineInsert() {
}
}
expect(sourceMap.equals(expectedSourceMap)).toBe(true);
- }
+ });
});
}