Skip to content

Commit

Permalink
Merge branch 'master' into add-descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Lyroxide authored Jun 17, 2024
2 parents 74c1481 + 1d2a915 commit d9696a7
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 56 deletions.
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch via NPM",
"request": "launch",
"runtimeArgs": [
"run",
"test:unit",
"--",
// "test/text-source-range.test.js" // Replace and uncomment this line to run a single test file
],
"runtimeExecutable": "npm",
"skipFiles": [
"<node_internals>/**"
],
"type": "node"
}
]
}
7 changes: 7 additions & 0 deletions ext/js/dom/dom-text-scanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export class DOMTextScanner {
const resetOffset = (ruby !== null);
if (resetOffset) { node = ruby; }

/** @type {Node} */
this._initialNode = node;
/** @type {Node} */
this._node = node;
/** @type {number} */
Expand Down Expand Up @@ -129,11 +131,16 @@ export class DOMTextScanner {
}
} else if (nodeType === ELEMENT_NODE) {
lastNode = node;
const initialNodeAtBeginningOfNodeGoingBackwards = node === this._initialNode && this._offset === 0 && !forward;
const initialNodeAtEndOfNodeGoingForwards = node === this._initialNode && this._offset === node.childNodes.length && forward;
this._offset = 0;
({enterable, newlines} = DOMTextScanner.getElementSeekInfo(/** @type {Element} */ (node)));
if (newlines > this._newlines && generateLayoutContent) {
this._newlines = newlines;
}
if (initialNodeAtBeginningOfNodeGoingBackwards || initialNodeAtEndOfNodeGoingForwards) {
enterable = false;
}
}

/** @type {Node[]} */
Expand Down
17 changes: 17 additions & 0 deletions test/data/html/text-source-range.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TextSourceRange Tests</title>
</head>
<body>
<h1>TextSourceRange Tests</h1>
<test-case id="text-source-range-lazy">
<p></p>
</test-case>

<test-case id="text-source-range">
<p></p>
</test-case>
</body>
</html>
1 change: 1 addition & 0 deletions test/data/json.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
{"path": ".eslintrc.json", "ignore": true},
{"path": ".vscode/settings.json", "ignore": true},
{"path": ".vscode/extensions.json", "ignore": true},
{"path": ".vscode/launch.json", "ignore": true},
{"path": "dev/jsconfig.json", "ignore": true},
{"path": "ext/manifest.json", "ignore": true},
{"path": "test/data/dictionaries/invalid-dictionary1/index.json", "ignore": true},
Expand Down
111 changes: 55 additions & 56 deletions test/dom-text-scanner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,84 +112,83 @@ function createAbsoluteGetComputedStyle(window) {

const domTestEnv = await setupDomTest(path.join(dirname, 'data/html/dom-text-scanner.html'));

describe('DOMTextScanner', () => {
describe('DOMTextScanner seek tests', () => {
const {window, teardown} = domTestEnv;
afterAll(() => teardown(global));

test('Seek tests', () => {
const {document} = window;
window.getComputedStyle = createAbsoluteGetComputedStyle(window);
const {document} = window;
window.getComputedStyle = createAbsoluteGetComputedStyle(window);

for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('test-case'))) {
/** @type {import('test/dom-text-scanner').TestData|import('test/dom-text-scanner').TestData[]} */
let testData = parseJson(/** @type {string} */ (testElement.dataset.testData));
if (!Array.isArray(testData)) {
testData = [testData];
}
for (const testDataItem of testData) {
const {
node: nodeSelector,
offset,
length,
forcePreserveWhitespace,
generateLayoutContent,
reversible,
expected: {
node: expectedNodeSelector,
offset: expectedOffset,
content: expectedContent,
remainder: expectedRemainder,
},
} = testDataItem;

const node = querySelectorTextNode(testElement, nodeSelector);
const expectedNode = querySelectorTextNode(testElement, expectedNodeSelector);

expect(node).not.toEqual(null);
expect(expectedNode).not.toEqual(null);
if (node === null || expectedNode === null) { continue; }

// Standard test
{
const scanner = new DOMTextScanner(node, offset, forcePreserveWhitespace, generateLayoutContent);
scanner.seek(length);
for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('test-case'))) {
const testDescription = testElement.querySelector('test-description')?.textContent || 'Test description not found.';

const {node: actualNode1, offset: actualOffset1, content: actualContent1, remainder: actualRemainder1} = scanner;
expect(actualContent1).toStrictEqual(expectedContent);
expect(actualOffset1).toStrictEqual(expectedOffset);
expect(actualNode1).toStrictEqual(expectedNode);
expect(actualRemainder1).toStrictEqual(expectedRemainder || 0);
}
/** @type {import('test/dom-text-scanner').TestData|import('test/dom-text-scanner').TestData[]} */
let testData = parseJson(/** @type {string} */ (testElement.dataset.testData));
if (!Array.isArray(testData)) {
testData = [testData];
}

// Substring tests
for (const testDataItem of testData) {
const {
node: nodeSelector,
offset,
length,
forcePreserveWhitespace,
generateLayoutContent,
reversible,
expected: {
node: expectedNodeSelector,
offset: expectedOffset,
content: expectedContent,
remainder: expectedRemainder,
},
} = testDataItem;

const node = querySelectorTextNode(testElement, nodeSelector);
const expectedNode = querySelectorTextNode(testElement, expectedNodeSelector);

expect(node).not.toEqual(null);
expect(expectedNode).not.toEqual(null);
if (node === null || expectedNode === null) { continue; }

test(`${testDescription} standard test`, () => {
const scanner = new DOMTextScanner(node, offset, forcePreserveWhitespace, generateLayoutContent);
scanner.seek(length);

const {node: actualNode1, offset: actualOffset1, content: actualContent1, remainder: actualRemainder1} = scanner;
expect(actualContent1).toStrictEqual(expectedContent);
expect(actualOffset1).toStrictEqual(expectedOffset);
expect(actualNode1).toStrictEqual(expectedNode);
expect(actualRemainder1).toStrictEqual(expectedRemainder || 0);
});

test(`${testDescription} substring test`, () => {
for (let i = 1; i <= length; ++i) {
const scanner = new DOMTextScanner(node, offset, forcePreserveWhitespace, generateLayoutContent);
scanner.seek(length - i);

const {content: actualContent} = scanner;
expect(actualContent).toStrictEqual(expectedContent.substring(0, expectedContent.length - i));
}
});

if (reversible === false) { continue; }

// Reversed test
{
const scanner = new DOMTextScanner(expectedNode, expectedOffset, forcePreserveWhitespace, generateLayoutContent);
scanner.seek(-length);
test.runIf(reversible)(`${testDescription} reversed test`, () => {
const scanner = new DOMTextScanner(expectedNode, expectedOffset, forcePreserveWhitespace, generateLayoutContent);
scanner.seek(-length);

const {content: actualContent} = scanner;
expect(actualContent).toStrictEqual(expectedContent);
}
const {content: actualContent} = scanner;
expect(actualContent).toStrictEqual(expectedContent);
});

// Reversed substring tests
test.runIf(reversible)(`${testDescription} reversed substring test`, () => {
for (let i = 1; i <= length; ++i) {
const scanner = new DOMTextScanner(expectedNode, expectedOffset, forcePreserveWhitespace, generateLayoutContent);
scanner.seek(-(length - i));

const {content: actualContent} = scanner;
expect(actualContent).toStrictEqual(expectedContent.substring(i));
}
}
});
}
});
}
});
80 changes: 80 additions & 0 deletions test/text-source-range.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2024 Yomitan 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 {fail} from 'node:assert';
import {fileURLToPath} from 'node:url';
import path from 'path';
import {afterAll, describe, expect, test} from 'vitest';
import {TextSourceRange} from '../ext/js/dom/text-source-range.js';
import {setupDomTest} from './fixtures/dom-test.js';


const dirname = path.dirname(fileURLToPath(import.meta.url));
const textSourceRangeTestEnv = await setupDomTest(path.join(dirname, 'data/html/text-source-range.html'));


describe('TextSourceRange', () => {
const {window, teardown} = textSourceRangeTestEnv;
afterAll(() => teardown(global));

test('lazy', () => {
const {document} = window;
const testElement /** @type {NodeListOf<HTMLElement>} */ = document.getElementById('text-source-range-lazy');
if (testElement === null) {
fail('test element not found');
}

const range = new Range();
range.selectNodeContents(testElement);

const source = TextSourceRange.createLazy(range);
const startLength = source.setStartOffset(200, false);
const endLength = source.setEndOffset(200, true, false);

const text = source.text();
const textLength = text.length;

expect(startLength).toBeLessThanOrEqual(textLength);
expect(endLength).toBeLessThan(textLength);
const count = (text.match(//g) || []).length;
expect(count).toEqual(1);
});

test('standard', () => {
const {document} = window;
const testElement /** @type {NodeListOf<HTMLElement>} */ = document.getElementById('text-source-range');
if (testElement === null) {
fail('test element not found');
}

const range = new Range();

range.selectNodeContents(testElement);

const source = TextSourceRange.create(range);
const startLength = source.setStartOffset(15, false);
const endLength = source.setEndOffset(15, true, false);

const text = source.text();
const textLength = text.length;

expect(startLength).toBeLessThan(textLength);
expect(endLength).toBeLessThan(textLength);
const count = (text.match(//g) || []).length;
expect(count).toEqual(1);
});
});

0 comments on commit d9696a7

Please sign in to comment.