Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more test updates #510

Merged
merged 4 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion test/data/html/dom-text-scanner.html
Original file line number Diff line number Diff line change
Expand Up @@ -392,4 +392,4 @@ <h1>DOMTextScanner Tests</h1>
</test-case>

</body>
</html>
</html>
291 changes: 151 additions & 140 deletions test/document-util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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);
});
}
});
});
});
12 changes: 7 additions & 5 deletions test/dom-text-scanner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down Expand Up @@ -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);

Expand Down
14 changes: 14 additions & 0 deletions test/fixtures/dom-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<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}>}
Expand Down
Loading