aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-03-01 14:41:17 -0500
committerGitHub <noreply@github.com>2020-03-01 14:41:17 -0500
commit7c615244f40222584984157223944e6aa6448d13 (patch)
treedee98802eeb1c6ca82e06fe0ec0cda12f38e3605 /test
parent314c567a47cd07dee7d6cda785257b2109afcad5 (diff)
parentbdb86e1713a196c9539306166d8dc4238adefc0d (diff)
Merge pull request #375 from toasted-nutbread/document-tests
Document tests
Diffstat (limited to 'test')
-rw-r--r--test/data/html/test-document1.html264
-rw-r--r--test/data/html/test-stylesheet.css32
-rw-r--r--test/test-document.js224
-rw-r--r--test/test-stylesheet.css32
4 files changed, 552 insertions, 0 deletions
diff --git a/test/data/html/test-document1.html b/test/data/html/test-document1.html
new file mode 100644
index 00000000..0754a314
--- /dev/null
+++ b/test/data/html/test-document1.html
@@ -0,0 +1,264 @@
+<!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-test-type="scan"
+ 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-test-type="scan"
+ 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-test-type="scan"
+ 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-test-type="scan"
+ 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-test-type="scan"
+ 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-test-type="scan"
+ 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>
+
+ <div
+ class="test"
+ data-test-type="text-source-range-seek"
+ data-seek-node-selector="span:nth-of-type(1)"
+ data-seek-node-is-text="true"
+ data-seek-offset="0"
+ data-seek-length="149"
+ data-seek-direction="forward"
+ data-expected-result-node-selector="span:nth-of-type(1)"
+ data-expected-result-node-is-text="true"
+ data-expected-result-offset="149"
+ data-expected-result-content="
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ "
+ >
+ <span>
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ </span><span>trailing content</span>
+ </div>
+
+ <div
+ class="test"
+ data-test-type="text-source-range-seek"
+ data-seek-node-selector="span:nth-of-type(1)"
+ data-seek-node-is-text="true"
+ data-seek-offset="149"
+ data-seek-length="149"
+ data-seek-direction="backward"
+ data-expected-result-node-selector="span:nth-of-type(1)"
+ data-expected-result-node-is-text="true"
+ data-expected-result-offset="0"
+ data-expected-result-content="
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ "
+ >
+ <span>
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ </span><span>trailing content</span>
+ </div>
+
+ <div
+ class="test"
+ data-test-type="text-source-range-seek"
+ data-seek-node-selector="span:nth-of-type(1)"
+ data-seek-node-is-text="true"
+ data-seek-offset="0"
+ data-seek-length="150"
+ data-seek-direction="forward"
+ data-expected-result-node-selector="span:nth-of-type(2)"
+ data-expected-result-node-is-text="true"
+ data-expected-result-offset="1"
+ data-expected-result-content="
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ t"
+ >
+ <span>
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ </span><span>trailing content</span>
+ </div>
+
+ <div
+ class="test"
+ data-test-type="text-source-range-seek"
+ data-seek-node-selector="span:nth-of-type(2)"
+ data-seek-node-is-text="true"
+ data-seek-offset="1"
+ data-seek-length="150"
+ data-seek-direction="backward"
+ data-expected-result-node-selector="span:nth-of-type(1)"
+ data-expected-result-node-is-text="true"
+ data-expected-result-offset="0"
+ data-expected-result-content="
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ t"
+ >
+ <span>
+ あいうえお
+ かきくけこ
+ さしすせそ
+ たちつてと
+ なにぬねの
+ はひふへほ
+ まみむめも
+ や&#x3000;ゆ&#x3000;よ
+ らりるれろ
+ わゐ&#x3000;ゑを
+ </span><span>trailing content</span>
+ </div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/test/data/html/test-stylesheet.css b/test/data/html/test-stylesheet.css
new file mode 100644
index 00000000..ab25732e
--- /dev/null
+++ b/test/data/html/test-stylesheet.css
@@ -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;
+}
diff --git a/test/test-document.js b/test/test-document.js
new file mode 100644
index 00000000..edf6bbea
--- /dev/null
+++ b/test/test-document.js
@@ -0,0 +1,224 @@
+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 testDocumentTextScanningFunctions(dom, {docRangeFromPoint, docSentenceExtract, TextSourceRange, TextSourceElement});
+ await testTextSourceRangeSeekFunctions(dom, {TextSourceRange});
+ } finally {
+ window.close();
+ }
+}
+
+async function testDocumentTextScanningFunctions(dom, {docRangeFromPoint, docSentenceExtract, TextSourceRange, TextSourceElement}) {
+ const document = dom.window.document;
+
+ for (const testElement of document.querySelectorAll('.test[data-test-type=scan]')) {
+ // 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 testTextSourceRangeSeekFunctions(dom, {TextSourceRange}) {
+ const document = dom.window.document;
+
+ for (const testElement of document.querySelectorAll('.test[data-test-type=text-source-range-seek]')) {
+ // Get test parameters
+ let {
+ seekNodeSelector,
+ seekNodeIsText,
+ seekOffset,
+ seekLength,
+ seekDirection,
+ expectedResultNodeSelector,
+ expectedResultNodeIsText,
+ expectedResultOffset,
+ expectedResultContent
+ } = testElement.dataset;
+
+ seekOffset = parseInt(seekOffset, 10);
+ seekLength = parseInt(seekLength, 10);
+ expectedResultOffset = parseInt(expectedResultOffset, 10);
+
+ let seekNode = testElement.querySelector(seekNodeSelector);
+ if (seekNodeIsText === 'true') {
+ seekNode = seekNode.firstChild;
+ }
+
+ let expectedResultNode = testElement.querySelector(expectedResultNodeSelector);
+ if (expectedResultNodeIsText === 'true') {
+ expectedResultNode = expectedResultNode.firstChild;
+ }
+
+ const {node, offset, content} = (
+ seekDirection === 'forward' ?
+ TextSourceRange.seekForward(seekNode, seekOffset, seekLength) :
+ TextSourceRange.seekBackward(seekNode, seekOffset, seekLength)
+ );
+
+ assert.strictEqual(node, expectedResultNode);
+ assert.strictEqual(offset, expectedResultOffset);
+ assert.strictEqual(content, expectedResultContent);
+ }
+}
+
+
+async function main() {
+ await testDocument1();
+}
+
+
+if (require.main === module) { main(); }
diff --git a/test/test-stylesheet.css b/test/test-stylesheet.css
new file mode 100644
index 00000000..ab25732e
--- /dev/null
+++ b/test/test-stylesheet.css
@@ -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;
+}