aboutsummaryrefslogtreecommitdiff
path: root/test/fixtures
diff options
context:
space:
mode:
Diffstat (limited to 'test/fixtures')
-rw-r--r--test/fixtures/dom-test.js58
-rw-r--r--test/fixtures/translator-test.js125
2 files changed, 183 insertions, 0 deletions
diff --git a/test/fixtures/dom-test.js b/test/fixtures/dom-test.js
new file mode 100644
index 00000000..8cfe80a9
--- /dev/null
+++ b/test/fixtures/dom-test.js
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 Yomitan Authors
+ * Copyright (C) 2020-2022 Yomichan 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 fs from 'fs';
+import {test} from 'vitest';
+import {builtinEnvironments} from 'vitest/environments';
+
+/**
+ * @param {import('jsdom').DOMWindow} window
+ */
+function prepareWindow(window) {
+ const {document} = 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;
+}
+
+/**
+ * @param {string} [htmlFilePath]
+ * @returns {import('vitest').TestAPI<{window: import('jsdom').DOMWindow}>}
+ */
+export function createDomTest(htmlFilePath) {
+ const html = typeof htmlFilePath === 'string' ? fs.readFileSync(htmlFilePath, {encoding: 'utf8'}) : '<!DOCTYPE html>';
+ return test.extend({
+ // eslint-disable-next-line no-empty-pattern
+ window: async ({}, use) => {
+ const env = builtinEnvironments.jsdom;
+ const {teardown} = await env.setup(global, {jsdom: {html}});
+ const window = /** @type {import('jsdom').DOMWindow} */ (/** @type {unknown} */ (global.window));
+ prepareWindow(window);
+ try {
+ await use(window);
+ } finally {
+ teardown(global);
+ }
+ }
+ });
+}
diff --git a/test/fixtures/translator-test.js b/test/fixtures/translator-test.js
new file mode 100644
index 00000000..b17c37d9
--- /dev/null
+++ b/test/fixtures/translator-test.js
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 Yomitan Authors
+ * Copyright (C) 2020-2022 Yomichan 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 {IDBKeyRange, indexedDB} from 'fake-indexeddb';
+import {readFileSync} from 'fs';
+import {fileURLToPath, pathToFileURL} from 'node:url';
+import {dirname, join, resolve} from 'path';
+import {expect, vi} from 'vitest';
+import {createDictionaryArchive} from '../../dev/util.js';
+import {AnkiNoteDataCreator} from '../../ext/js/data/sandbox/anki-note-data-creator.js';
+import {DictionaryDatabase} from '../../ext/js/language/dictionary-database.js';
+import {DictionaryImporter} from '../../ext/js/language/dictionary-importer.js';
+import {JapaneseUtil} from '../../ext/js/language/sandbox/japanese-util.js';
+import {Translator} from '../../ext/js/language/translator.js';
+import {DictionaryImporterMediaLoader} from '../mocks/dictionary-importer-media-loader.js';
+import {createDomTest} from './dom-test.js';
+
+const extDir = join(dirname(fileURLToPath(import.meta.url)), '../../ext');
+const deinflectionReasonsPath = join(extDir, 'data/deinflect.json');
+
+/** @type {import('dev/vm').PseudoChrome} */
+const chrome = {
+ runtime: {
+ getURL: (path) => {
+ return pathToFileURL(join(extDir, path.replace(/^\//, ''))).href;
+ }
+ }
+};
+
+/**
+ * @param {string} url
+ * @returns {Promise<import('dev/vm').PseudoFetchResponse>}
+ */
+async function fetch(url) {
+ let filePath;
+ try {
+ filePath = fileURLToPath(url);
+ } catch (e) {
+ filePath = resolve(extDir, url.replace(/^[/\\]/, ''));
+ }
+ await Promise.resolve();
+ const content = readFileSync(filePath, {encoding: null});
+ return {
+ ok: true,
+ status: 200,
+ statusText: 'OK',
+ text: async () => content.toString('utf8'),
+ json: async () => JSON.parse(content.toString('utf8'))
+ };
+}
+
+vi.stubGlobal('indexedDB', indexedDB);
+vi.stubGlobal('IDBKeyRange', IDBKeyRange);
+vi.stubGlobal('fetch', fetch);
+vi.stubGlobal('chrome', chrome);
+
+/**
+ * @param {string} dictionaryDirectory
+ * @param {string} dictionaryName
+ * @returns {Promise<{translator: Translator, ankiNoteDataCreator: AnkiNoteDataCreator}>}
+ */
+async function createTranslatorContext(dictionaryDirectory, dictionaryName) {
+ // Dictionary
+ const testDictionary = createDictionaryArchive(dictionaryDirectory, dictionaryName);
+ const testDictionaryContent = await testDictionary.generateAsync({type: 'arraybuffer'});
+
+ // Setup database
+ const dictionaryImporterMediaLoader = new DictionaryImporterMediaLoader();
+ const dictionaryImporter = new DictionaryImporter(dictionaryImporterMediaLoader);
+ const dictionaryDatabase = new DictionaryDatabase();
+ await dictionaryDatabase.prepare();
+
+ const {errors} = await dictionaryImporter.importDictionary(
+ dictionaryDatabase,
+ testDictionaryContent,
+ {prefixWildcardsSupported: true}
+ );
+
+ expect(errors.length).toEqual(0);
+
+ // Setup translator
+ const japaneseUtil = new JapaneseUtil(null);
+ const translator = new Translator({japaneseUtil, database: dictionaryDatabase});
+ const deinflectionReasons = JSON.parse(readFileSync(deinflectionReasonsPath, {encoding: 'utf8'}));
+ translator.prepare(deinflectionReasons);
+
+ // Assign properties
+ const ankiNoteDataCreator = new AnkiNoteDataCreator(japaneseUtil);
+ return {translator, ankiNoteDataCreator};
+}
+
+/**
+ * @param {string|undefined} htmlFilePath
+ * @param {string} dictionaryDirectory
+ * @param {string} dictionaryName
+ * @returns {Promise<import('vitest').TestAPI<{window: import('jsdom').DOMWindow, translator: Translator, ankiNoteDataCreator: AnkiNoteDataCreator}>>}
+ */
+export async function createTranslatorTest(htmlFilePath, dictionaryDirectory, dictionaryName) {
+ const test = createDomTest(htmlFilePath);
+ const {translator, ankiNoteDataCreator} = await createTranslatorContext(dictionaryDirectory, dictionaryName);
+ /** @type {import('vitest').TestAPI<{window: import('jsdom').DOMWindow, translator: Translator, ankiNoteDataCreator: AnkiNoteDataCreator}>} */
+ const result = test.extend({
+ window: async ({window}, use) => { await use(window); },
+ // eslint-disable-next-line no-empty-pattern
+ translator: async ({}, use) => { await use(translator); },
+ // eslint-disable-next-line no-empty-pattern
+ ankiNoteDataCreator: async ({}, use) => { await use(ankiNoteDataCreator); }
+ });
+ return result;
+}