From dcb26a8ef5c62bf6acbf8130895c37f56e8a9d3f Mon Sep 17 00:00:00 2001 From: Cashew <52880648+Scrub1492@users.noreply.github.com> Date: Sun, 14 Jan 2024 10:51:00 +0700 Subject: more test updates (#510) * test updates * remove duplicate * fix indentation * fix --- test/data/html/dom-text-scanner.html | 2 +- test/document-util.test.js | 291 ++++++++++++++++++----------------- test/dom-text-scanner.test.js | 12 +- test/fixtures/dom-test.js | 14 ++ test/options-util.test.js | 27 ++-- test/profile-conditions-util.test.js | 14 +- test/text-source-map.test.js | 26 ++-- 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 @@ - \ No newline at end of file + 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} */ (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} */ (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} */ (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} */ (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 @@ -35,6 +35,20 @@ function prepareWindow(window) { document.caretRangeFromPoint = () => null; } +/** + * + * @param {string} [htmlFilePath] + * @returns {Promise<{window: import('jsdom').DOMWindow; teardown: (global: unknown) => import('vitest').Awaitable}>} + */ +export async function setupDomTest(htmlFilePath) { + 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); + 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 = /<<>>/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 . */ -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); - } + }); }); } -- cgit v1.2.3