Add some basic document tests
This commit is contained in:
parent
85aab699e9
commit
934c3239f2
106
test/data/html/test-document1.html
Normal file
106
test/data/html/test-document1.html
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<title>Yomichan Tests</title>
|
||||||
|
<link rel="icon" type="image/gif" href="data:image/gif;base64,R0lGODlhEAAQAKEBAAAAAP///////////yH5BAEKAAIALAAAAAAQABAAAAImFI6Zpt0B4YkS0TCpq07xbmEgcGVRUpLaI46ZG7ppalY0jDCwUAAAOw==" />
|
||||||
|
<link rel="stylesheet" href="test-stylesheet.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Yomichan Tests</h1>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="test"
|
||||||
|
data-element-from-point-selector="span"
|
||||||
|
data-caret-range-from-point-selector="span"
|
||||||
|
data-start-node-selector="span"
|
||||||
|
data-start-offset="0"
|
||||||
|
data-end-node-selector="span"
|
||||||
|
data-end-offset="0"
|
||||||
|
data-result-type="TextSourceRange",
|
||||||
|
data-sentence-extent="100"
|
||||||
|
data-sentence="真白「心配してくださって、ありがとございます」"
|
||||||
|
>
|
||||||
|
<span>真白「心配してくださって、ありがとございます」</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="test"
|
||||||
|
data-element-from-point-selector="span"
|
||||||
|
data-caret-range-from-point-selector="span"
|
||||||
|
data-start-node-selector="span"
|
||||||
|
data-start-offset="5"
|
||||||
|
data-end-node-selector="span"
|
||||||
|
data-end-offset="5"
|
||||||
|
data-result-type="TextSourceRange",
|
||||||
|
data-sentence-extent="100"
|
||||||
|
data-sentence="心配してくださって、ありがとございます"
|
||||||
|
>
|
||||||
|
<span>真白「心配してくださって、ありがとございます」</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="test"
|
||||||
|
data-element-from-point-selector="input"
|
||||||
|
data-caret-range-from-point-selector="input"
|
||||||
|
data-start-node-selector="input"
|
||||||
|
data-start-offset="0"
|
||||||
|
data-end-node-selector="input"
|
||||||
|
data-end-offset="0"
|
||||||
|
data-result-type="TextSourceRange",
|
||||||
|
data-sentence-extent="100"
|
||||||
|
data-sentence="真白「心配してくださって、ありがとございます」"
|
||||||
|
data-has-imposter="true"
|
||||||
|
>
|
||||||
|
<input type="text" value="真白「心配してくださって、ありがとございます」" style="width: 100%; box-sizing: border-box; font-family: inherit; font-size: inherit; border: 1px solid #d8d8d8; padding: 0.2em;" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="test"
|
||||||
|
data-element-from-point-selector="textarea"
|
||||||
|
data-caret-range-from-point-selector="textarea"
|
||||||
|
data-start-node-selector="textarea"
|
||||||
|
data-start-offset="0"
|
||||||
|
data-end-node-selector="textarea"
|
||||||
|
data-end-offset="0"
|
||||||
|
data-result-type="TextSourceRange",
|
||||||
|
data-sentence-extent="100"
|
||||||
|
data-sentence="真白「心配してくださって、ありがとございます」"
|
||||||
|
data-has-imposter="true"
|
||||||
|
>
|
||||||
|
<textarea style="width: 100%; height: 3em; box-sizing: border-box; font-family: inherit; font-size: inherit; border: 1px solid #d8d8d8; padding: 0.2em;">真白「心配してくださって、ありがとございます」</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="test"
|
||||||
|
data-element-from-point-selector="button"
|
||||||
|
data-caret-range-from-point-selector="button"
|
||||||
|
data-start-node-selector="button"
|
||||||
|
data-start-offset="0"
|
||||||
|
data-end-node-selector="button"
|
||||||
|
data-end-offset="0"
|
||||||
|
data-result-type="TextSourceElement",
|
||||||
|
data-sentence-extent="100"
|
||||||
|
data-sentence="よみちゃん"
|
||||||
|
>
|
||||||
|
<button style="width: 100%; box-sizing: border-box; font-family: inherit; font-size: inherit; border: 1px solid #d8d8d8; background-color: #f0f0f0; padding: 0.2em;">よみちゃん</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="test"
|
||||||
|
data-element-from-point-selector="img"
|
||||||
|
data-caret-range-from-point-selector="img"
|
||||||
|
data-start-node-selector="img"
|
||||||
|
data-start-offset="0"
|
||||||
|
data-end-node-selector="img"
|
||||||
|
data-end-offset="0"
|
||||||
|
data-result-type="TextSourceElement"
|
||||||
|
data-sentence="よみちゃん"
|
||||||
|
>
|
||||||
|
<img src="data:image/gif;base64,R0lGODdhBwAHAIABAAAAAP///ywAAAAABwAHAAACDIRvEaC32FpCbEkKCgA7" alt="よみちゃん" title="よみちゃん" style="width: 70px; height: 70px; image-rendering: crisp-edges; image-rendering: pixelated; display: block;" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
32
test/data/html/test-stylesheet.css
Normal file
32
test/data/html/test-stylesheet.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
body {
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
max-width: 680px;
|
||||||
|
padding: 0 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
counter-reset: test-id;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test {
|
||||||
|
background-color: #ffffff;
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 0.5em;
|
||||||
|
box-shadow: rgba(64, 64, 64, 0.3) 0px 1px 2px 0px, rgba(64, 64, 64, 0.15) 0px 1px 3px 1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test:before {
|
||||||
|
content: "Test " counter(test-id);
|
||||||
|
display: block;
|
||||||
|
counter-increment: test-id;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
border-bottom: 1px solid #d8d8d8;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
180
test/test-document.js
Normal file
180
test/test-document.js
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const assert = require('assert');
|
||||||
|
const {JSDOM} = require('jsdom');
|
||||||
|
const yomichanTest = require('./yomichan-test');
|
||||||
|
|
||||||
|
|
||||||
|
// DOMRect class definition
|
||||||
|
class DOMRect {
|
||||||
|
constructor(x, y, width, height) {
|
||||||
|
this._x = x;
|
||||||
|
this._y = y;
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
get x() { return this._x; }
|
||||||
|
get y() { return this._y; }
|
||||||
|
get width() { return this._width; }
|
||||||
|
get height() { return this._height; }
|
||||||
|
get left() { return this._x + Math.min(0, this._width); }
|
||||||
|
get right() { return this._x + Math.max(0, this._width); }
|
||||||
|
get top() { return this._y + Math.min(0, this._height); }
|
||||||
|
get bottom() { return this._y + Math.max(0, this._height); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function querySelectorChildOrSelf(element, selector) {
|
||||||
|
return selector ? element.querySelector(selector) : element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChildTextNodeOrSelf(dom, node) {
|
||||||
|
if (node === null) { return null; }
|
||||||
|
const Node = dom.window.Node;
|
||||||
|
const childNode = node.firstChild;
|
||||||
|
return (childNode !== null && childNode.nodeType === Node.TEXT_NODE ? childNode : node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPrototypeOfOrNull(value) {
|
||||||
|
try {
|
||||||
|
return Object.getPrototypeOf(value);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findImposterElement(document) {
|
||||||
|
// Finds the imposter element based on it's z-index style
|
||||||
|
return document.querySelector('div[style*="2147483646"]>*');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function testDocument1() {
|
||||||
|
const dom = createJSDOM(path.join(__dirname, 'data', 'html', 'test-document1.html'));
|
||||||
|
const window = dom.window;
|
||||||
|
const document = window.document;
|
||||||
|
const Node = window.Node;
|
||||||
|
const Range = window.Range;
|
||||||
|
|
||||||
|
const {DOM} = yomichanTest.requireScript(
|
||||||
|
'ext/mixed/js/dom.js',
|
||||||
|
['DOM']
|
||||||
|
);
|
||||||
|
const {TextSourceRange, TextSourceElement} = yomichanTest.requireScript(
|
||||||
|
'ext/fg/js/source.js',
|
||||||
|
['TextSourceRange', 'TextSourceElement'],
|
||||||
|
{document, window, Range, Node}
|
||||||
|
);
|
||||||
|
const {docRangeFromPoint, docSentenceExtract} = yomichanTest.requireScript(
|
||||||
|
'ext/fg/js/document.js',
|
||||||
|
['docRangeFromPoint', 'docSentenceExtract'],
|
||||||
|
{document, window, Node, TextSourceElement, TextSourceRange, DOM}
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await testDocument1Inner(dom, {docRangeFromPoint, docSentenceExtract, TextSourceRange, TextSourceElement});
|
||||||
|
} finally {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testDocument1Inner(dom, {docRangeFromPoint, docSentenceExtract, TextSourceRange, TextSourceElement}) {
|
||||||
|
const document = dom.window.document;
|
||||||
|
|
||||||
|
for (const testElement of document.querySelectorAll('.test')) {
|
||||||
|
// Get test parameters
|
||||||
|
let {
|
||||||
|
elementFromPointSelector,
|
||||||
|
caretRangeFromPointSelector,
|
||||||
|
startNodeSelector,
|
||||||
|
startOffset,
|
||||||
|
endNodeSelector,
|
||||||
|
endOffset,
|
||||||
|
resultType,
|
||||||
|
sentenceExtent,
|
||||||
|
sentence,
|
||||||
|
hasImposter
|
||||||
|
} = testElement.dataset;
|
||||||
|
|
||||||
|
const elementFromPointValue = querySelectorChildOrSelf(testElement, elementFromPointSelector);
|
||||||
|
const caretRangeFromPointValue = querySelectorChildOrSelf(testElement, caretRangeFromPointSelector);
|
||||||
|
const startNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, startNodeSelector));
|
||||||
|
const endNode = getChildTextNodeOrSelf(dom, querySelectorChildOrSelf(testElement, endNodeSelector));
|
||||||
|
|
||||||
|
startOffset = parseInt(startOffset, 10);
|
||||||
|
endOffset = parseInt(endOffset, 10);
|
||||||
|
sentenceExtent = parseInt(sentenceExtent, 10);
|
||||||
|
|
||||||
|
assert.notStrictEqual(elementFromPointValue, null);
|
||||||
|
assert.notStrictEqual(caretRangeFromPointValue, null);
|
||||||
|
assert.notStrictEqual(startNode, null);
|
||||||
|
assert.notStrictEqual(endNode, null);
|
||||||
|
|
||||||
|
// Setup functions
|
||||||
|
document.elementFromPoint = () => elementFromPointValue;
|
||||||
|
|
||||||
|
document.caretRangeFromPoint = (x, y) => {
|
||||||
|
const imposter = getChildTextNodeOrSelf(dom, findImposterElement(document));
|
||||||
|
assert.strictEqual(!!imposter, hasImposter === 'true');
|
||||||
|
|
||||||
|
const range = document.createRange();
|
||||||
|
range.setStart(imposter ? imposter : startNode, startOffset);
|
||||||
|
range.setEnd(imposter ? imposter : startNode, endOffset);
|
||||||
|
|
||||||
|
// Override getClientRects to return a rect guaranteed to contain (x, y)
|
||||||
|
range.getClientRects = () => [new DOMRect(x - 1, y - 1, 2, 2)];
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test docRangeFromPoint
|
||||||
|
const source = docRangeFromPoint(0, 0, false);
|
||||||
|
switch (resultType) {
|
||||||
|
case 'TextSourceRange':
|
||||||
|
assert.strictEqual(getPrototypeOfOrNull(source), TextSourceRange.prototype);
|
||||||
|
break;
|
||||||
|
case 'TextSourceElement':
|
||||||
|
assert.strictEqual(getPrototypeOfOrNull(source), TextSourceElement.prototype);
|
||||||
|
break;
|
||||||
|
case 'null':
|
||||||
|
assert.strictEqual(source, null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert.ok(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (source === null) { continue; }
|
||||||
|
|
||||||
|
// Test docSentenceExtract
|
||||||
|
const sentenceActual = docSentenceExtract(source, sentenceExtent).text;
|
||||||
|
assert.strictEqual(sentenceActual, sentence);
|
||||||
|
|
||||||
|
// Clean
|
||||||
|
source.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
await testDocument1();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (require.main === module) { main(); }
|
32
test/test-stylesheet.css
Normal file
32
test/test-stylesheet.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
body {
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
max-width: 680px;
|
||||||
|
padding: 0 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
counter-reset: test-id;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test {
|
||||||
|
background-color: #ffffff;
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 0.5em;
|
||||||
|
box-shadow: rgba(64, 64, 64, 0.3) 0px 1px 2px 0px, rgba(64, 64, 64, 0.15) 0px 1px 3px 1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test:before {
|
||||||
|
content: "Test " counter(test-id);
|
||||||
|
display: block;
|
||||||
|
counter-increment: test-id;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
border-bottom: 1px solid #d8d8d8;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user