summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/test-all.js71
-rw-r--r--test/test-anki-note-builder.js322
-rw-r--r--test/test-cache-map.js137
-rw-r--r--test/test-core.js300
-rw-r--r--test/test-database.js982
-rw-r--r--test/test-document-util.js339
-rw-r--r--test/test-hotkey-util.js189
-rw-r--r--test/test-japanese-util.js915
-rw-r--r--test/test-json-schema.js1048
-rw-r--r--test/test-manifest.js49
-rw-r--r--test/test-object-property-accessor.js458
-rw-r--r--test/test-profile-conditions-util.js1136
-rw-r--r--test/test-text-source-map.js244
-rw-r--r--test/test-translator.js102
-rw-r--r--test/test-workers.js168
15 files changed, 0 insertions, 6460 deletions
diff --git a/test/test-all.js b/test/test-all.js
deleted file mode 100644
index 9219d278..00000000
--- a/test/test-all.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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/>.
- */
-
-const fs = require('fs');
-const path = require('path');
-const {spawnSync} = require('child_process');
-const {getArgs} = require('../dev/util');
-
-
-/**
- * @throws {Error}
- */
-function main() {
- const args = getArgs(process.argv.slice(2), new Map(/** @type {[key: string, value: (boolean|null|number|string|string[])][]} */ ([
- ['skip', []],
- [null, []]
- ])));
- const directories = /** @type {string[]} */ (args.get(null));
- const skipArg = /** @type {string[]} */ (args.get('skip'));
- const skip = new Set([__filename, ...skipArg].map((value) => path.resolve(value)));
-
- const node = process.execPath;
- const fileNamePattern = /\.js$/i;
-
- let first = true;
- for (const directory of directories) {
- const fileNames = fs.readdirSync(directory);
- for (const fileName of fileNames) {
- if (!fileNamePattern.test(fileName)) { continue; }
-
- const fullFileName = path.resolve(path.join(directory, fileName));
- if (skip.has(fullFileName)) { continue; }
-
- const stats = fs.lstatSync(fullFileName);
- if (!stats.isFile()) { continue; }
-
- process.stdout.write(`${first ? '' : '\n'}Running ${fileName}...\n`);
- first = false;
-
- const {error, status} = spawnSync(node, [fileName], {cwd: directory, stdio: 'inherit'});
-
- if (status !== null && status !== 0) {
- process.exit(status);
- return;
- }
- if (error) {
- throw error;
- }
- }
- }
-
- process.exit(0);
-}
-
-
-if (require.main === module) { main(); }
diff --git a/test/test-anki-note-builder.js b/test/test-anki-note-builder.js
deleted file mode 100644
index 8e0ab9d9..00000000
--- a/test/test-anki-note-builder.js
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (C) 2023 Yomitan Authors
- * Copyright (C) 2021-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/>.
- */
-
-const fs = require('fs');
-const path = require('path');
-const assert = require('assert');
-const {JSDOM} = require('jsdom');
-const {testMain} = require('../dev/util');
-const {TranslatorVM} = require('../dev/translator-vm');
-
-
-/**
- * @template T
- * @param {T} value
- * @returns {T}
- */
-function clone(value) {
- return JSON.parse(JSON.stringify(value));
-}
-
-/**
- * @returns {Promise<{vm: TranslatorVM, AnkiNoteBuilder: typeof AnkiNoteBuilder2, JapaneseUtil: typeof JapaneseUtil2}>}
- */
-async function createVM() {
- const dom = new JSDOM();
- const {Node, NodeFilter, document} = dom.window;
-
- const vm = new TranslatorVM({
- Node,
- NodeFilter,
- document,
- location: new URL('https://yomichan.test/')
- });
-
- const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', 'valid-dictionary1');
- await vm.prepare(dictionaryDirectory, 'Test Dictionary 2');
-
- vm.execute([
- 'js/data/anki-note-builder.js',
- 'js/data/anki-util.js',
- 'js/dom/sandbox/css-style-applier.js',
- 'js/display/sandbox/pronunciation-generator.js',
- 'js/display/sandbox/structured-content-generator.js',
- 'js/templates/sandbox/anki-template-renderer.js',
- 'js/templates/sandbox/anki-template-renderer-content-manager.js',
- 'js/templates/sandbox/template-renderer.js',
- 'js/templates/sandbox/template-renderer-media-provider.js',
- 'lib/handlebars.min.js'
- ]);
-
- /** @type {[typeof JapaneseUtil, typeof AnkiNoteBuilder, typeof AnkiTemplateRenderer]} */
- const [
- JapaneseUtil2,
- AnkiNoteBuilder2,
- AnkiTemplateRenderer2
- ] = vm.get([
- 'JapaneseUtil',
- 'AnkiNoteBuilder',
- 'AnkiTemplateRenderer'
- ]);
-
- class TemplateRendererProxy {
- constructor() {
- /** @type {?Promise<void>} */
- this._preparePromise = null;
- /** @type {AnkiTemplateRenderer} */
- this._ankiTemplateRenderer = new AnkiTemplateRenderer2();
- }
-
- /**
- * @param {string} template
- * @param {import('template-renderer').PartialOrCompositeRenderData} data
- * @param {import('anki-templates').RenderMode} type
- * @returns {Promise<import('template-renderer').RenderResult>}
- */
- async render(template, data, type) {
- await this._prepare();
- return await this._ankiTemplateRenderer.templateRenderer.render(template, data, type);
- }
-
- /**
- * @param {import('template-renderer').RenderMultiItem[]} items
- * @returns {Promise<import('core').Response<import('template-renderer').RenderResult>[]>}
- */
- async renderMulti(items) {
- await this._prepare();
- return await this._ankiTemplateRenderer.templateRenderer.renderMulti(items);
- }
-
- /**
- * @returns {Promise<void>}
- */
- _prepare() {
- if (this._preparePromise === null) {
- this._preparePromise = this._prepareInternal();
- }
- return this._preparePromise;
- }
-
- /** */
- async _prepareInternal() {
- await this._ankiTemplateRenderer.prepare();
- }
- }
- vm.set({TemplateRendererProxy});
-
- return {vm, AnkiNoteBuilder: AnkiNoteBuilder2, JapaneseUtil: JapaneseUtil2};
-}
-
-/**
- * @param {'terms'|'kanji'} type
- * @returns {string[]}
- */
-function getFieldMarkers(type) {
- switch (type) {
- case 'terms':
- return [
- 'audio',
- 'clipboard-image',
- 'clipboard-text',
- 'cloze-body',
- 'cloze-prefix',
- 'cloze-suffix',
- 'conjugation',
- 'dictionary',
- 'document-title',
- 'expression',
- 'frequencies',
- 'furigana',
- 'furigana-plain',
- 'glossary',
- 'glossary-brief',
- 'glossary-no-dictionary',
- 'part-of-speech',
- 'pitch-accents',
- 'pitch-accent-graphs',
- 'pitch-accent-positions',
- 'reading',
- 'screenshot',
- 'search-query',
- 'selection-text',
- 'sentence',
- 'sentence-furigana',
- 'tags',
- 'url'
- ];
- case 'kanji':
- return [
- 'character',
- 'clipboard-image',
- 'clipboard-text',
- 'cloze-body',
- 'cloze-prefix',
- 'cloze-suffix',
- 'dictionary',
- 'document-title',
- 'glossary',
- 'kunyomi',
- 'onyomi',
- 'screenshot',
- 'search-query',
- 'selection-text',
- 'sentence',
- 'sentence-furigana',
- 'stroke-count',
- 'tags',
- 'url'
- ];
- default:
- return [];
- }
-}
-
-/**
- * @param {import('dictionary').DictionaryEntry[]} dictionaryEntries
- * @param {'terms'|'kanji'} type
- * @param {import('settings').ResultOutputMode} mode
- * @param {string} template
- * @param {typeof AnkiNoteBuilder} AnkiNoteBuilder
- * @param {typeof JapaneseUtil} JapaneseUtil
- * @param {boolean} write
- * @returns {Promise<import('anki').NoteFields[]>}
- */
-async function getRenderResults(dictionaryEntries, type, mode, template, AnkiNoteBuilder, JapaneseUtil, write) {
- const markers = getFieldMarkers(type);
- /** @type {import('anki-note-builder').Field[]} */
- const fields = [];
- for (const marker of markers) {
- fields.push([marker, `{${marker}}`]);
- }
-
- const japaneseUtil = new JapaneseUtil(null);
- const clozePrefix = 'cloze-prefix';
- const clozeSuffix = 'cloze-suffix';
- const results = [];
- for (const dictionaryEntry of dictionaryEntries) {
- let source = '';
- switch (dictionaryEntry.type) {
- case 'kanji':
- source = dictionaryEntry.character;
- break;
- case 'term':
- if (dictionaryEntry.headwords.length > 0 && dictionaryEntry.headwords[0].sources.length > 0) {
- source = dictionaryEntry.headwords[0].sources[0].originalText;
- }
- break;
- }
- const ankiNoteBuilder = new AnkiNoteBuilder({japaneseUtil});
- const context = {
- url: 'url:',
- sentence: {
- text: `${clozePrefix}${source}${clozeSuffix}`,
- offset: clozePrefix.length
- },
- documentTitle: 'title',
- query: 'query',
- fullQuery: 'fullQuery'
- };
- /** @type {import('anki-note-builder').CreateNoteDetails} */
- const details = {
- dictionaryEntry,
- mode: 'test',
- context,
- template,
- deckName: 'deckName',
- modelName: 'modelName',
- fields,
- tags: ['yomichan'],
- checkForDuplicates: true,
- duplicateScope: 'collection',
- duplicateScopeCheckAllModels: false,
- resultOutputMode: mode,
- glossaryLayoutMode: 'default',
- compactTags: false,
- requirements: [],
- mediaOptions: null
- };
- const {note: {fields: noteFields}, errors} = await ankiNoteBuilder.createNote(details);
- if (!write) {
- for (const error of errors) {
- console.error(error);
- }
- assert.strictEqual(errors.length, 0);
- }
- results.push(noteFields);
- }
-
- return results;
-}
-
-
-/** */
-async function main() {
- const write = (process.argv[2] === '--write');
-
- const {vm, AnkiNoteBuilder, JapaneseUtil} = await createVM();
-
- const testInputsFilePath = path.join(__dirname, 'data', 'translator-test-inputs.json');
- const {optionsPresets, tests} = JSON.parse(fs.readFileSync(testInputsFilePath, {encoding: 'utf8'}));
-
- const testResults1FilePath = path.join(__dirname, 'data', 'anki-note-builder-test-results.json');
- const expectedResults1 = JSON.parse(fs.readFileSync(testResults1FilePath, {encoding: 'utf8'}));
- const actualResults1 = [];
-
- const template = fs.readFileSync(path.join(__dirname, '..', 'ext', 'data/templates/default-anki-field-templates.handlebars'), {encoding: 'utf8'});
-
- for (let i = 0, ii = tests.length; i < ii; ++i) {
- const test = tests[i];
- const expected1 = expectedResults1[i];
- switch (test.func) {
- case 'findTerms':
- {
- const {name, mode, text} = test;
- /** @type {import('translation').FindTermsOptions} */
- const options = vm.buildOptions(optionsPresets, test.options);
- const {dictionaryEntries} = clone(await vm.translator.findTerms(mode, text, options));
- const results = mode !== 'simple' ? clone(await getRenderResults(dictionaryEntries, 'terms', mode, template, AnkiNoteBuilder, JapaneseUtil, write)) : null;
- actualResults1.push({name, results});
- if (!write) {
- assert.deepStrictEqual(results, expected1.results);
- }
- }
- break;
- case 'findKanji':
- {
- const {name, text} = test;
- /** @type {import('translation').FindKanjiOptions} */
- const options = vm.buildOptions(optionsPresets, test.options);
- const dictionaryEntries = clone(await vm.translator.findKanji(text, options));
- const results = clone(await getRenderResults(dictionaryEntries, 'kanji', 'split', template, AnkiNoteBuilder, JapaneseUtil, write));
- actualResults1.push({name, results});
- if (!write) {
- assert.deepStrictEqual(results, expected1.results);
- }
- }
- break;
- }
- }
-
- if (write) {
- // Use 2 indent instead of 4 to save a bit of file size
- fs.writeFileSync(testResults1FilePath, JSON.stringify(actualResults1, null, 2), {encoding: 'utf8'});
- }
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-cache-map.js b/test/test-cache-map.js
deleted file mode 100644
index 56a31898..00000000
--- a/test/test-cache-map.js
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-const vm = new VM({console});
-vm.execute([
- 'js/general/cache-map.js'
-]);
-/** @type {typeof CacheMap} */
-const CacheMap2 = vm.getSingle('CacheMap');
-
-
-/** */
-function testConstructor() {
- const data = /** @type {[throws: boolean, create: () => void][]} */ ([
- [false, () => new CacheMap2(0)],
- [false, () => new CacheMap2(1)],
- [false, () => new CacheMap2(Number.MAX_VALUE)],
- [true, () => new CacheMap2(-1)],
- [true, () => new CacheMap2(1.5)],
- [true, () => new CacheMap2(Number.NaN)],
- [true, () => new CacheMap2(Number.POSITIVE_INFINITY)],
- // @ts-ignore - Ignore because it should throw an error
- [true, () => new CacheMap2('a')]
- ]);
-
- for (const [throws, create] of data) {
- if (throws) {
- assert.throws(create);
- } else {
- assert.doesNotThrow(create);
- }
- }
-}
-
-/** */
-function testApi() {
- const data = [
- {
- maxSize: 1,
- expectedSize: 0,
- calls: []
- },
- {
- maxSize: 10,
- expectedSize: 1,
- calls: [
- {func: 'get', args: ['a1-b-c'], returnValue: void 0},
- {func: 'has', args: ['a1-b-c'], returnValue: false},
- {func: 'set', args: ['a1-b-c', 32], returnValue: void 0},
- {func: 'get', args: ['a1-b-c'], returnValue: 32},
- {func: 'has', args: ['a1-b-c'], returnValue: true}
- ]
- },
- {
- maxSize: 10,
- expectedSize: 2,
- calls: [
- {func: 'set', args: ['a1-b-c', 32], returnValue: void 0},
- {func: 'get', args: ['a1-b-c'], returnValue: 32},
- {func: 'set', args: ['a1-b-c', 64], returnValue: void 0},
- {func: 'get', args: ['a1-b-c'], returnValue: 64},
- {func: 'set', args: ['a2-b-c', 96], returnValue: void 0},
- {func: 'get', args: ['a2-b-c'], returnValue: 96}
- ]
- },
- {
- maxSize: 2,
- expectedSize: 2,
- calls: [
- {func: 'has', args: ['a1-b-c'], returnValue: false},
- {func: 'has', args: ['a2-b-c'], returnValue: false},
- {func: 'has', args: ['a3-b-c'], returnValue: false},
- {func: 'set', args: ['a1-b-c', 1], returnValue: void 0},
- {func: 'has', args: ['a1-b-c'], returnValue: true},
- {func: 'has', args: ['a2-b-c'], returnValue: false},
- {func: 'has', args: ['a3-b-c'], returnValue: false},
- {func: 'set', args: ['a2-b-c', 2], returnValue: void 0},
- {func: 'has', args: ['a1-b-c'], returnValue: true},
- {func: 'has', args: ['a2-b-c'], returnValue: true},
- {func: 'has', args: ['a3-b-c'], returnValue: false},
- {func: 'set', args: ['a3-b-c', 3], returnValue: void 0},
- {func: 'has', args: ['a1-b-c'], returnValue: false},
- {func: 'has', args: ['a2-b-c'], returnValue: true},
- {func: 'has', args: ['a3-b-c'], returnValue: true}
- ]
- }
- ];
-
- for (const {maxSize, expectedSize, calls} of data) {
- const cache = new CacheMap2(maxSize);
- assert.strictEqual(cache.maxSize, maxSize);
- for (const call of calls) {
- const {func, args} = call;
- let returnValue;
- switch (func) {
- case 'get': returnValue = cache.get(args[0]); break;
- case 'set': returnValue = cache.set(args[0], args[1]); break;
- case 'has': returnValue = cache.has(args[0]); break;
- case 'clear': returnValue = cache.clear(); break;
- }
- if (Object.prototype.hasOwnProperty.call(call, 'returnValue')) {
- const {returnValue: expectedReturnValue} = call;
- assert.deepStrictEqual(returnValue, expectedReturnValue);
- }
- }
- assert.strictEqual(cache.size, expectedSize);
- }
-}
-
-
-/** */
-function main() {
- testConstructor();
- testApi();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-core.js b/test/test-core.js
deleted file mode 100644
index 2c48ac20..00000000
--- a/test/test-core.js
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-const vm = new VM();
-vm.execute([
- 'js/core.js',
- 'js/core/extension-error.js'
-]);
-const values = vm.get(['DynamicProperty', 'deepEqual']);
-const DynamicProperty2 = /** @type {typeof DynamicProperty} */ (values[0]);
-const deepEqual2 = /** @type {typeof deepEqual} */ (values[1]);
-
-
-/** */
-function testDynamicProperty() {
- const data = [
- {
- initialValue: 0,
- /** @type {{operation: ?string, expectedDefaultValue: number, expectedValue: number, expectedOverrideCount: number, expeectedEventOccurred: boolean, args: [value: number, priority?: number]}[]} */
- operations: [
- {
- operation: null,
- args: [0],
- expectedDefaultValue: 0,
- expectedValue: 0,
- expectedOverrideCount: 0,
- expeectedEventOccurred: false
- },
- {
- operation: 'set.defaultValue',
- args: [1],
- expectedDefaultValue: 1,
- expectedValue: 1,
- expectedOverrideCount: 0,
- expeectedEventOccurred: true
- },
- {
- operation: 'set.defaultValue',
- args: [1],
- expectedDefaultValue: 1,
- expectedValue: 1,
- expectedOverrideCount: 0,
- expeectedEventOccurred: false
- },
- {
- operation: 'set.defaultValue',
- args: [0],
- expectedDefaultValue: 0,
- expectedValue: 0,
- expectedOverrideCount: 0,
- expeectedEventOccurred: true
- },
- {
- operation: 'setOverride',
- args: [8],
- expectedDefaultValue: 0,
- expectedValue: 8,
- expectedOverrideCount: 1,
- expeectedEventOccurred: true
- },
- {
- operation: 'setOverride',
- args: [16],
- expectedDefaultValue: 0,
- expectedValue: 8,
- expectedOverrideCount: 2,
- expeectedEventOccurred: false
- },
- {
- operation: 'setOverride',
- args: [32, 1],
- expectedDefaultValue: 0,
- expectedValue: 32,
- expectedOverrideCount: 3,
- expeectedEventOccurred: true
- },
- {
- operation: 'setOverride',
- args: [64, -1],
- expectedDefaultValue: 0,
- expectedValue: 32,
- expectedOverrideCount: 4,
- expeectedEventOccurred: false
- },
- {
- operation: 'clearOverride',
- args: [-4],
- expectedDefaultValue: 0,
- expectedValue: 32,
- expectedOverrideCount: 3,
- expeectedEventOccurred: false
- },
- {
- operation: 'clearOverride',
- args: [-3],
- expectedDefaultValue: 0,
- expectedValue: 32,
- expectedOverrideCount: 2,
- expeectedEventOccurred: false
- },
- {
- operation: 'clearOverride',
- args: [-2],
- expectedDefaultValue: 0,
- expectedValue: 64,
- expectedOverrideCount: 1,
- expeectedEventOccurred: true
- },
- {
- operation: 'clearOverride',
- args: [-1],
- expectedDefaultValue: 0,
- expectedValue: 0,
- expectedOverrideCount: 0,
- expeectedEventOccurred: true
- }
- ]
- }
- ];
-
- for (const {initialValue, operations} of data) {
- const property = new DynamicProperty2(initialValue);
- const overrideTokens = [];
- let eventOccurred = false;
- const onChange = () => { eventOccurred = true; };
- property.on('change', onChange);
- for (const {operation, args, expectedDefaultValue, expectedValue, expectedOverrideCount, expeectedEventOccurred} of operations) {
- eventOccurred = false;
- switch (operation) {
- case 'set.defaultValue': property.defaultValue = args[0]; break;
- case 'setOverride': overrideTokens.push(property.setOverride(...args)); break;
- case 'clearOverride': property.clearOverride(overrideTokens[overrideTokens.length + args[0]]); break;
- }
- assert.strictEqual(eventOccurred, expeectedEventOccurred);
- assert.strictEqual(property.defaultValue, expectedDefaultValue);
- assert.strictEqual(property.value, expectedValue);
- assert.strictEqual(property.overrideCount, expectedOverrideCount);
- }
- property.off('change', onChange);
- }
-}
-
-/** */
-function testDeepEqual() {
- const data = [
- // Simple tests
- {
- value1: 0,
- value2: 0,
- expected: true
- },
- {
- value1: null,
- value2: null,
- expected: true
- },
- {
- value1: 'test',
- value2: 'test',
- expected: true
- },
- {
- value1: true,
- value2: true,
- expected: true
- },
- {
- value1: 0,
- value2: 1,
- expected: false
- },
- {
- value1: null,
- value2: false,
- expected: false
- },
- {
- value1: 'test1',
- value2: 'test2',
- expected: false
- },
- {
- value1: true,
- value2: false,
- expected: false
- },
-
- // Simple object tests
- {
- value1: {},
- value2: {},
- expected: true
- },
- {
- value1: {},
- value2: [],
- expected: false
- },
- {
- value1: [],
- value2: [],
- expected: true
- },
- {
- value1: {},
- value2: null,
- expected: false
- },
-
- // Complex object tests
- {
- value1: [1],
- value2: [],
- expected: false
- },
- {
- value1: [1],
- value2: [1],
- expected: true
- },
- {
- value1: [1],
- value2: [2],
- expected: false
- },
-
- {
- value1: {},
- value2: {test: 1},
- expected: false
- },
- {
- value1: {test: 1},
- value2: {test: 1},
- expected: true
- },
- {
- value1: {test: 1},
- value2: {test: {test2: false}},
- expected: false
- },
- {
- value1: {test: {test2: true}},
- value2: {test: {test2: false}},
- expected: false
- },
- {
- value1: {test: {test2: [true]}},
- value2: {test: {test2: [true]}},
- expected: true
- },
-
- // Recursive
- {
- value1: (() => { const x = {}; x.x = x; return x; })(),
- value2: (() => { const x = {}; x.x = x; return x; })(),
- expected: false
- }
- ];
-
- let index = 0;
- for (const {value1, value2, expected} of data) {
- const actual1 = deepEqual2(value1, value2);
- assert.strictEqual(actual1, expected, `Failed for test ${index}`);
-
- const actual2 = deepEqual2(value2, value1);
- assert.strictEqual(actual2, expected, `Failed for test ${index}`);
-
- ++index;
- }
-}
-
-
-/** */
-function main() {
- testDynamicProperty();
- testDeepEqual();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-database.js b/test/test-database.js
deleted file mode 100644
index 947e369b..00000000
--- a/test/test-database.js
+++ /dev/null
@@ -1,982 +0,0 @@
-/*
- * 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/>.
- */
-
-const path = require('path');
-const assert = require('assert');
-const {createDictionaryArchive, testMain} = require('../dev/util');
-const {DatabaseVM, DatabaseVMDictionaryImporterMediaLoader} = require('../dev/database-vm');
-
-
-const vm = new DatabaseVM();
-vm.execute([
- 'js/core.js',
- 'js/core/extension-error.js',
- 'js/general/cache-map.js',
- 'js/data/json-schema.js',
- 'js/media/media-util.js',
- 'js/language/dictionary-importer.js',
- 'js/data/database.js',
- 'js/language/dictionary-database.js'
-]);
-/** @type {typeof DictionaryImporter} */
-const DictionaryImporter2 = vm.getSingle('DictionaryImporter');
-/** @type {typeof DictionaryDatabase} */
-const DictionaryDatabase2 = vm.getSingle('DictionaryDatabase');
-
-
-/**
- * @param {string} dictionary
- * @param {string} [dictionaryName]
- * @returns {import('jszip')}
- */
-function createTestDictionaryArchive(dictionary, dictionaryName) {
- const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', dictionary);
- return createDictionaryArchive(dictionaryDirectory, dictionaryName);
-}
-
-
-/**
- * @param {import('dictionary-importer').OnProgressCallback} [onProgress]
- * @returns {DictionaryImporter}
- */
-function createDictionaryImporter(onProgress) {
- const dictionaryImporterMediaLoader = new DatabaseVMDictionaryImporterMediaLoader();
- return new DictionaryImporter2(dictionaryImporterMediaLoader, (...args) => {
- const {stepIndex, stepCount, index, count} = args[0];
- assert.ok(stepIndex < stepCount);
- assert.ok(index <= count);
- if (typeof onProgress === 'function') {
- onProgress(...args);
- }
- });
-}
-
-
-/**
- * @param {import('dictionary-database').TermEntry[]} dictionaryDatabaseEntries
- * @param {string} term
- * @returns {number}
- */
-function countDictionaryDatabaseEntriesWithTerm(dictionaryDatabaseEntries, term) {
- return dictionaryDatabaseEntries.reduce((i, v) => (i + (v.term === term ? 1 : 0)), 0);
-}
-
-/**
- * @param {import('dictionary-database').TermEntry[]} dictionaryDatabaseEntries
- * @param {string} reading
- * @returns {number}
- */
-function countDictionaryDatabaseEntriesWithReading(dictionaryDatabaseEntries, reading) {
- return dictionaryDatabaseEntries.reduce((i, v) => (i + (v.reading === reading ? 1 : 0)), 0);
-}
-
-/**
- * @param {import('dictionary-database').TermMeta[]|import('dictionary-database').KanjiMeta[]} metas
- * @param {import('dictionary-database').TermMetaType|import('dictionary-database').KanjiMetaType} mode
- * @returns {number}
- */
-function countMetasWithMode(metas, mode) {
- let i = 0;
- for (const item of metas) {
- if (item.mode === mode) { ++i; }
- }
- return i;
-}
-
-/**
- * @param {import('dictionary-database').KanjiEntry[]} kanji
- * @param {string} character
- * @returns {number}
- */
-function countKanjiWithCharacter(kanji, character) {
- let i = 0;
- for (const item of kanji) {
- if (item.character === character) { ++i; }
- }
- return i;
-}
-
-
-/**
- * @param {number} timeout
- * @returns {Promise<void>}
- */
-function clearDatabase(timeout) {
- return new Promise((resolve, reject) => {
- /** @type {?number} */
- let timer = setTimeout(() => {
- timer = null;
- reject(new Error(`clearDatabase failed to resolve after ${timeout}ms`));
- }, timeout);
-
- (async () => {
- const indexedDB = vm.indexedDB;
- for (const {name} of await indexedDB.databases()) {
- if (typeof name !== 'string') { continue; }
- /** @type {Promise<void>} */
- const promise2 = new Promise((resolve2, reject2) => {
- const request = indexedDB.deleteDatabase(name);
- request.onerror = (e) => reject2(e);
- request.onsuccess = () => resolve2();
- });
- await promise2;
- }
- if (timer !== null) {
- clearTimeout(timer);
- }
- resolve();
- })();
- });
-}
-
-
-/** */
-async function testDatabase1() {
- // Load dictionary data
- const testDictionary = createTestDictionaryArchive('valid-dictionary1');
- const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'});
- const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string'));
-
- const title = testDictionaryIndex.title;
- const titles = new Map([
- [title, {priority: 0, allowSecondarySearches: false}]
- ]);
-
- // Setup iteration data
- const iterations = [
- {
- cleanup: async () => {
- // Test purge
- await dictionaryDatabase.purge();
- await testDatabaseEmpty1(dictionaryDatabase);
- }
- },
- {
- cleanup: async () => {
- // Test deleteDictionary
- let progressEvent = false;
- await dictionaryDatabase.deleteDictionary(
- title,
- 1000,
- () => {
- progressEvent = true;
- }
- );
- assert.ok(progressEvent);
-
- await testDatabaseEmpty1(dictionaryDatabase);
- }
- },
- {
- cleanup: async () => {}
- }
- ];
-
- // Setup database
- const dictionaryDatabase = new DictionaryDatabase2();
- await dictionaryDatabase.prepare();
-
- for (const {cleanup} of iterations) {
- const expectedSummary = {
- title,
- revision: 'test',
- sequenced: true,
- version: 3,
- importDate: 0,
- prefixWildcardsSupported: true,
- counts: {
- kanji: {total: 2},
- kanjiMeta: {total: 6, freq: 6},
- media: {total: 4},
- tagMeta: {total: 15},
- termMeta: {total: 38, freq: 31, pitch: 7},
- terms: {total: 21}
- }
- };
-
- // Import data
- let progressEvent = false;
- const dictionaryImporter = createDictionaryImporter(() => { progressEvent = true; });
- const {result, errors} = await dictionaryImporter.importDictionary(
- dictionaryDatabase,
- testDictionarySource,
- {prefixWildcardsSupported: true}
- );
- expectedSummary.importDate = result.importDate;
- vm.assert.deepStrictEqual(errors, []);
- vm.assert.deepStrictEqual(result, expectedSummary);
- assert.ok(progressEvent);
-
- // Get info summary
- const info = await dictionaryDatabase.getDictionaryInfo();
- vm.assert.deepStrictEqual(info, [expectedSummary]);
-
- // Get counts
- const counts = await dictionaryDatabase.getDictionaryCounts(
- info.map((v) => v.title),
- true
- );
- vm.assert.deepStrictEqual(counts, {
- counts: [{kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 4}],
- total: {kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 4}
- });
-
- // Test find* functions
- await testFindTermsBulkTest1(dictionaryDatabase, titles);
- await testTindTermsExactBulk1(dictionaryDatabase, titles);
- await testFindTermsBySequenceBulk1(dictionaryDatabase, title);
- await testFindTermMetaBulk1(dictionaryDatabase, titles);
- await testFindKanjiBulk1(dictionaryDatabase, titles);
- await testFindKanjiMetaBulk1(dictionaryDatabase, titles);
- await testFindTagForTitle1(dictionaryDatabase, title);
-
- // Cleanup
- await cleanup();
- }
-
- await dictionaryDatabase.close();
-}
-
-/**
- * @param {DictionaryDatabase} database
- */
-async function testDatabaseEmpty1(database) {
- const info = await database.getDictionaryInfo();
- vm.assert.deepStrictEqual(info, []);
-
- const counts = await database.getDictionaryCounts([], true);
- vm.assert.deepStrictEqual(counts, {
- counts: [],
- total: {kanji: 0, kanjiMeta: 0, terms: 0, termMeta: 0, tagMeta: 0, media: 0}
- });
-}
-
-/**
- * @param {DictionaryDatabase} database
- * @param {import('dictionary-database').DictionarySet} titles
- */
-async function testFindTermsBulkTest1(database, titles) {
- /** @type {{inputs: {matchType: import('dictionary-database').MatchType, termList: string[]}[], expectedResults: {total: number, terms: [key: string, count: number][], readings: [key: string, count: number][]}}[]} */
- const data = [
- {
- inputs: [
- {
- matchType: 'exact',
- termList: ['打', '打つ', '打ち込む']
- },
- {
- matchType: 'exact',
- termList: ['だ', 'ダース', 'うつ', 'ぶつ', 'うちこむ', 'ぶちこむ']
- },
- {
- matchType: 'prefix',
- termList: ['打']
- }
- ],
- expectedResults: {
- total: 10,
- terms: [
- ['打', 2],
- ['打つ', 4],
- ['打ち込む', 4]
- ],
- readings: [
- ['だ', 1],
- ['ダース', 1],
- ['うつ', 2],
- ['ぶつ', 2],
- ['うちこむ', 2],
- ['ぶちこむ', 2]
- ]
- }
- },
- {
- inputs: [
- {
- matchType: 'exact',
- termList: ['込む']
- }
- ],
- expectedResults: {
- total: 0,
- terms: [],
- readings: []
- }
- },
- {
- inputs: [
- {
- matchType: 'suffix',
- termList: ['込む']
- }
- ],
- expectedResults: {
- total: 4,
- terms: [
- ['打ち込む', 4]
- ],
- readings: [
- ['うちこむ', 2],
- ['ぶちこむ', 2]
- ]
- }
- },
- {
- inputs: [
- {
- matchType: 'exact',
- termList: []
- }
- ],
- expectedResults: {
- total: 0,
- terms: [],
- readings: []
- }
- }
- ];
-
- for (const {inputs, expectedResults} of data) {
- for (const {termList, matchType} of inputs) {
- const results = await database.findTermsBulk(termList, titles, matchType);
- assert.strictEqual(results.length, expectedResults.total);
- for (const [term, count] of expectedResults.terms) {
- assert.strictEqual(countDictionaryDatabaseEntriesWithTerm(results, term), count);
- }
- for (const [reading, count] of expectedResults.readings) {
- assert.strictEqual(countDictionaryDatabaseEntriesWithReading(results, reading), count);
- }
- }
- }
-}
-
-/**
- * @param {DictionaryDatabase} database
- * @param {import('dictionary-database').DictionarySet} titles
- */
-async function testTindTermsExactBulk1(database, titles) {
- /** @type {{inputs: {termList: {term: string, reading: string}[]}[], expectedResults: {total: number, terms: [key: string, count: number][], readings: [key: string, count: number][]}}[]} */
- const data = [
- {
- inputs: [
- {
- termList: [
- {term: '打', reading: 'だ'},
- {term: '打つ', reading: 'うつ'},
- {term: '打ち込む', reading: 'うちこむ'}
- ]
- }
- ],
- expectedResults: {
- total: 5,
- terms: [
- ['打', 1],
- ['打つ', 2],
- ['打ち込む', 2]
- ],
- readings: [
- ['だ', 1],
- ['うつ', 2],
- ['うちこむ', 2]
- ]
- }
- },
- {
- inputs: [
- {
- termList: [
- {term: '打', reading: 'だ?'},
- {term: '打つ', reading: 'うつ?'},
- {term: '打ち込む', reading: 'うちこむ?'}
- ]
- }
- ],
- expectedResults: {
- total: 0,
- terms: [],
- readings: []
- }
- },
- {
- inputs: [
- {
- termList: [
- {term: '打つ', reading: 'うつ'},
- {term: '打つ', reading: 'ぶつ'}
- ]
- }
- ],
- expectedResults: {
- total: 4,
- terms: [
- ['打つ', 4]
- ],
- readings: [
- ['うつ', 2],
- ['ぶつ', 2]
- ]
- }
- },
- {
- inputs: [
- {
- termList: [
- {term: '打つ', reading: 'うちこむ'}
- ]
- }
- ],
- expectedResults: {
- total: 0,
- terms: [],
- readings: []
- }
- },
- {
- inputs: [
- {
- termList: []
- }
- ],
- expectedResults: {
- total: 0,
- terms: [],
- readings: []
- }
- }
- ];
-
- for (const {inputs, expectedResults} of data) {
- for (const {termList} of inputs) {
- const results = await database.findTermsExactBulk(termList, titles);
- assert.strictEqual(results.length, expectedResults.total);
- for (const [term, count] of expectedResults.terms) {
- assert.strictEqual(countDictionaryDatabaseEntriesWithTerm(results, term), count);
- }
- for (const [reading, count] of expectedResults.readings) {
- assert.strictEqual(countDictionaryDatabaseEntriesWithReading(results, reading), count);
- }
- }
- }
-}
-
-/**
- * @param {DictionaryDatabase} database
- * @param {string} mainDictionary
- */
-async function testFindTermsBySequenceBulk1(database, mainDictionary) {
- /** @type {{inputs: {sequenceList: number[]}[], expectedResults: {total: number, terms: [key: string, count: number][], readings: [key: string, count: number][]}}[]} */
- const data = [
- {
- inputs: [
- {
- sequenceList: [1, 2, 3, 4, 5]
- }
- ],
- expectedResults: {
- total: 11,
- terms: [
- ['打', 2],
- ['打つ', 4],
- ['打ち込む', 4],
- ['画像', 1]
- ],
- readings: [
- ['だ', 1],
- ['ダース', 1],
- ['うつ', 2],
- ['ぶつ', 2],
- ['うちこむ', 2],
- ['ぶちこむ', 2],
- ['がぞう', 1]
- ]
- }
- },
- {
- inputs: [
- {
- sequenceList: [1]
- }
- ],
- expectedResults: {
- total: 1,
- terms: [
- ['打', 1]
- ],
- readings: [
- ['だ', 1]
- ]
- }
- },
- {
- inputs: [
- {
- sequenceList: [2]
- }
- ],
- expectedResults: {
- total: 1,
- terms: [
- ['打', 1]
- ],
- readings: [
- ['ダース', 1]
- ]
- }
- },
- {
- inputs: [
- {
- sequenceList: [3]
- }
- ],
- expectedResults: {
- total: 4,
- terms: [
- ['打つ', 4]
- ],
- readings: [
- ['うつ', 2],
- ['ぶつ', 2]
- ]
- }
- },
- {
- inputs: [
- {
- sequenceList: [4]
- }
- ],
- expectedResults: {
- total: 4,
- terms: [
- ['打ち込む', 4]
- ],
- readings: [
- ['うちこむ', 2],
- ['ぶちこむ', 2]
- ]
- }
- },
- {
- inputs: [
- {
- sequenceList: [5]
- }
- ],
- expectedResults: {
- total: 1,
- terms: [
- ['画像', 1]
- ],
- readings: [
- ['がぞう', 1]
- ]
- }
- },
- {
- inputs: [
- {
- sequenceList: [-1]
- }
- ],
- expectedResults: {
- total: 0,
- terms: [],
- readings: []
- }
- },
- {
- inputs: [
- {
- sequenceList: []
- }
- ],
- expectedResults: {
- total: 0,
- terms: [],
- readings: []
- }
- }
- ];
-
- for (const {inputs, expectedResults} of data) {
- for (const {sequenceList} of inputs) {
- const results = await database.findTermsBySequenceBulk(sequenceList.map((query) => ({query, dictionary: mainDictionary})));
- assert.strictEqual(results.length, expectedResults.total);
- for (const [term, count] of expectedResults.terms) {
- assert.strictEqual(countDictionaryDatabaseEntriesWithTerm(results, term), count);
- }
- for (const [reading, count] of expectedResults.readings) {
- assert.strictEqual(countDictionaryDatabaseEntriesWithReading(results, reading), count);
- }
- }
- }
-}
-
-/**
- * @param {DictionaryDatabase} database
- * @param {import('dictionary-database').DictionarySet} titles
- */
-async function testFindTermMetaBulk1(database, titles) {
- /** @type {{inputs: {termList: string[]}[], expectedResults: {total: number, modes: [key: import('dictionary-database').TermMetaType, count: number][]}}[]} */
- const data = [
- {
- inputs: [
- {
- termList: ['打']
- }
- ],
- expectedResults: {
- total: 11,
- modes: [
- ['freq', 11]
- ]
- }
- },
- {
- inputs: [
- {
- termList: ['打つ']
- }
- ],
- expectedResults: {
- total: 10,
- modes: [
- ['freq', 10]
- ]
- }
- },
- {
- inputs: [
- {
- termList: ['打ち込む']
- }
- ],
- expectedResults: {
- total: 12,
- modes: [
- ['freq', 10],
- ['pitch', 2]
- ]
- }
- },
- {
- inputs: [
- {
- termList: ['?']
- }
- ],
- expectedResults: {
- total: 0,
- modes: []
- }
- }
- ];
-
- for (const {inputs, expectedResults} of data) {
- for (const {termList} of inputs) {
- const results = await database.findTermMetaBulk(termList, titles);
- assert.strictEqual(results.length, expectedResults.total);
- for (const [mode, count] of expectedResults.modes) {
- assert.strictEqual(countMetasWithMode(results, mode), count);
- }
- }
- }
-}
-
-/**
- * @param {DictionaryDatabase} database
- * @param {import('dictionary-database').DictionarySet} titles
- */
-async function testFindKanjiBulk1(database, titles) {
- /** @type {{inputs: {kanjiList: string[]}[], expectedResults: {total: number, kanji: [key: string, count: number][]}}[]} */
- const data = [
- {
- inputs: [
- {
- kanjiList: ['打']
- }
- ],
- expectedResults: {
- total: 1,
- kanji: [
- ['打', 1]
- ]
- }
- },
- {
- inputs: [
- {
- kanjiList: ['込']
- }
- ],
- expectedResults: {
- total: 1,
- kanji: [
- ['込', 1]
- ]
- }
- },
- {
- inputs: [
- {
- kanjiList: ['?']
- }
- ],
- expectedResults: {
- total: 0,
- kanji: []
- }
- }
- ];
-
- for (const {inputs, expectedResults} of data) {
- for (const {kanjiList} of inputs) {
- const results = await database.findKanjiBulk(kanjiList, titles);
- assert.strictEqual(results.length, expectedResults.total);
- for (const [kanji, count] of expectedResults.kanji) {
- assert.strictEqual(countKanjiWithCharacter(results, kanji), count);
- }
- }
- }
-}
-
-/**
- * @param {DictionaryDatabase} database
- * @param {import('dictionary-database').DictionarySet} titles
- */
-async function testFindKanjiMetaBulk1(database, titles) {
- /** @type {{inputs: {kanjiList: string[]}[], expectedResults: {total: number, modes: [key: import('dictionary-database').KanjiMetaType, count: number][]}}[]} */
- const data = [
- {
- inputs: [
- {
- kanjiList: ['打']
- }
- ],
- expectedResults: {
- total: 3,
- modes: [
- ['freq', 3]
- ]
- }
- },
- {
- inputs: [
- {
- kanjiList: ['込']
- }
- ],
- expectedResults: {
- total: 3,
- modes: [
- ['freq', 3]
- ]
- }
- },
- {
- inputs: [
- {
- kanjiList: ['?']
- }
- ],
- expectedResults: {
- total: 0,
- modes: []
- }
- }
- ];
-
- for (const {inputs, expectedResults} of data) {
- for (const {kanjiList} of inputs) {
- const results = await database.findKanjiMetaBulk(kanjiList, titles);
- assert.strictEqual(results.length, expectedResults.total);
- for (const [mode, count] of expectedResults.modes) {
- assert.strictEqual(countMetasWithMode(results, mode), count);
- }
- }
- }
-}
-
-/**
- * @param {DictionaryDatabase} database
- * @param {string} title
- */
-async function testFindTagForTitle1(database, title) {
- const data = [
- {
- inputs: [
- {
- name: 'E1'
- }
- ],
- expectedResults: {
- value: {category: 'default', dictionary: title, name: 'E1', notes: 'example tag 1', order: 0, score: 0}
- }
- },
- {
- inputs: [
- {
- name: 'K1'
- }
- ],
- expectedResults: {
- value: {category: 'default', dictionary: title, name: 'K1', notes: 'example kanji tag 1', order: 0, score: 0}
- }
- },
- {
- inputs: [
- {
- name: 'kstat1'
- }
- ],
- expectedResults: {
- value: {category: 'class', dictionary: title, name: 'kstat1', notes: 'kanji stat 1', order: 0, score: 0}
- }
- },
- {
- inputs: [
- {
- name: 'invalid'
- }
- ],
- expectedResults: {
- value: null
- }
- }
- ];
-
- for (const {inputs, expectedResults} of data) {
- for (const {name} of inputs) {
- const result = await database.findTagForTitle(name, title);
- vm.assert.deepStrictEqual(result, expectedResults.value);
- }
- }
-}
-
-
-/** */
-async function testDatabase2() {
- // Load dictionary data
- const testDictionary = createTestDictionaryArchive('valid-dictionary1');
- const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'});
- const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string'));
-
- const title = testDictionaryIndex.title;
- const titles = new Map([
- [title, {priority: 0, allowSecondarySearches: false}]
- ]);
-
- // Setup database
- const dictionaryDatabase = new DictionaryDatabase2();
- /** @type {import('dictionary-importer').ImportDetails} */
- const detaultImportDetails = {prefixWildcardsSupported: false};
-
- // Error: not prepared
- await assert.rejects(async () => await dictionaryDatabase.deleteDictionary(title, 1000, () => {}));
- await assert.rejects(async () => await dictionaryDatabase.findTermsBulk(['?'], titles, 'exact'));
- await assert.rejects(async () => await dictionaryDatabase.findTermsExactBulk([{term: '?', reading: '?'}], titles));
- await assert.rejects(async () => await dictionaryDatabase.findTermsBySequenceBulk([{query: 1, dictionary: title}]));
- await assert.rejects(async () => await dictionaryDatabase.findTermMetaBulk(['?'], titles));
- await assert.rejects(async () => await dictionaryDatabase.findTermMetaBulk(['?'], titles));
- await assert.rejects(async () => await dictionaryDatabase.findKanjiBulk(['?'], titles));
- await assert.rejects(async () => await dictionaryDatabase.findKanjiMetaBulk(['?'], titles));
- await assert.rejects(async () => await dictionaryDatabase.findTagForTitle('tag', title));
- await assert.rejects(async () => await dictionaryDatabase.getDictionaryInfo());
- await assert.rejects(async () => await dictionaryDatabase.getDictionaryCounts([...titles.keys()], true));
- await assert.rejects(async () => await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, detaultImportDetails));
-
- await dictionaryDatabase.prepare();
-
- // Error: already prepared
- await assert.rejects(async () => await dictionaryDatabase.prepare());
-
- await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, detaultImportDetails);
-
- // Error: dictionary already imported
- await assert.rejects(async () => await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, detaultImportDetails));
-
- await dictionaryDatabase.close();
-}
-
-
-/** */
-async function testDatabase3() {
- const invalidDictionaries = [
- 'invalid-dictionary1',
- 'invalid-dictionary2',
- 'invalid-dictionary3',
- 'invalid-dictionary4',
- 'invalid-dictionary5',
- 'invalid-dictionary6'
- ];
-
- // Setup database
- const dictionaryDatabase = new DictionaryDatabase2();
- /** @type {import('dictionary-importer').ImportDetails} */
- const detaultImportDetails = {prefixWildcardsSupported: false};
- await dictionaryDatabase.prepare();
-
- for (const invalidDictionary of invalidDictionaries) {
- const testDictionary = createTestDictionaryArchive(invalidDictionary);
- const testDictionarySource = await testDictionary.generateAsync({type: 'arraybuffer'});
-
- let error = null;
- try {
- await createDictionaryImporter().importDictionary(dictionaryDatabase, testDictionarySource, detaultImportDetails);
- } catch (e) {
- error = e;
- }
-
- if (error === null) {
- assert.ok(false, `Expected an error while importing ${invalidDictionary}`);
- } else {
- const prefix = 'Dictionary has invalid data';
- const message = /** @type {import('core').UnknownObject} */ (error).message;
- assert.ok(typeof message, 'string');
- if (typeof message === 'string') {
- assert.ok(message.startsWith(prefix), `Expected error message to start with '${prefix}': ${message}`);
- }
- }
- }
-
- await dictionaryDatabase.close();
-}
-
-
-/** */
-async function main() {
- const clearTimeout = 5000;
- try {
- await testDatabase1();
- await clearDatabase(clearTimeout);
-
- await testDatabase2();
- await clearDatabase(clearTimeout);
-
- await testDatabase3();
- await clearDatabase(clearTimeout);
- } catch (e) {
- console.log(e);
- process.exit(-1);
- throw e;
- }
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-document-util.js b/test/test-document-util.js
deleted file mode 100644
index 93ce1669..00000000
--- a/test/test-document-util.js
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * 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/>.
- */
-
-const fs = require('fs');
-const path = require('path');
-const assert = require('assert');
-const {JSDOM} = require('jsdom');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-
-// DOMRect class definition
-class DOMRect {
- /**
- * @param {number} x
- * @param {number} y
- * @param {number} width
- * @param {number} height
- */
- constructor(x, y, width, height) {
- /** @type {number} */
- this._x = x;
- /** @type {number} */
- this._y = y;
- /** @type {number} */
- this._width = width;
- /** @type {number} */
- this._height = height;
- }
-
- /** @type {number} */
- get x() { return this._x; }
- /** @type {number} */
- get y() { return this._y; }
- /** @type {number} */
- get width() { return this._width; }
- /** @type {number} */
- get height() { return this._height; }
- /** @type {number} */
- get left() { return this._x + Math.min(0, this._width); }
- /** @type {number} */
- get right() { return this._x + Math.max(0, this._width); }
- /** @type {number} */
- get top() { return this._y + Math.min(0, this._height); }
- /** @type {number} */
- get bottom() { return this._y + Math.max(0, this._height); }
- /** @returns {string} */
- toJSON() { return '<not implemented>'; }
-}
-
-
-/**
- * @param {string} fileName
- * @returns {JSDOM}
- */
-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;
-}
-
-/**
- * @param {Element} element
- * @param {string|undefined} selector
- * @returns {?Element}
- */
-function querySelectorChildOrSelf(element, selector) {
- return selector ? element.querySelector(selector) : element;
-}
-
-/**
- * @param {JSDOM} dom
- * @param {?Node} node
- * @returns {?Text|Node}
- */
-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);
-}
-
-/**
- * @param {unknown} value
- * @returns {unknown}
- */
-function getPrototypeOfOrNull(value) {
- try {
- return Object.getPrototypeOf(value);
- } catch (e) {
- return null;
- }
-}
-
-/**
- * @param {Document} document
- * @returns {?Element}
- */
-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 vm = new VM({document, window, Range, Node});
- vm.execute([
- 'js/data/sandbox/string-util.js',
- 'js/dom/dom-text-scanner.js',
- 'js/dom/text-source-range.js',
- 'js/dom/text-source-element.js',
- 'js/dom/document-util.js'
- ]);
- /** @type {[DOMTextScanner: typeof DOMTextScanner, TextSourceRange: typeof TextSourceRange, TextSourceElement: typeof TextSourceElement, DocumentUtil: typeof DocumentUtil]} */
- const [DOMTextScanner2, TextSourceRange2, TextSourceElement2, DocumentUtil2] = vm.get([
- 'DOMTextScanner',
- 'TextSourceRange',
- 'TextSourceElement',
- 'DocumentUtil'
- ]);
-
- try {
- await testDocumentTextScanningFunctions(dom, {DocumentUtil: DocumentUtil2, TextSourceRange: TextSourceRange2, TextSourceElement: TextSourceElement2});
- await testTextSourceRangeSeekFunctions(dom, {DOMTextScanner: DOMTextScanner2});
- } finally {
- window.close();
- }
-}
-
-/**
- * @param {JSDOM} dom
- * @param {{DocumentUtil: typeof DocumentUtil, TextSourceRange: typeof TextSourceRange, TextSourceElement: typeof TextSourceElement}} details
- */
-async function testDocumentTextScanningFunctions(dom, {DocumentUtil, TextSourceRange, TextSourceElement}) {
- const document = dom.window.document;
-
- for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('.test[data-test-type=scan]'))) {
- // Get test parameters
- const {
- elementFromPointSelector,
- caretRangeFromPointSelector,
- startNodeSelector,
- startOffset,
- endNodeSelector,
- endOffset,
- resultType,
- sentenceScanExtent,
- sentence,
- hasImposter,
- terminateAtNewlines
- } = 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));
-
- const startOffset2 = parseInt(/** @type {string} */ (startOffset), 10);
- const endOffset2 = parseInt(/** @type {string} */ (endOffset), 10);
- const sentenceScanExtent2 = parseInt(/** @type {string} */ (sentenceScanExtent), 10);
- const terminateAtNewlines2 = (terminateAtNewlines !== 'false');
-
- 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(/** @type {Node} */ (imposter ? imposter : startNode), startOffset2);
- range.setEnd(/** @type {Node} */ (imposter ? imposter : startNode), endOffset2);
-
- // 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;
- };
-
- // Test docRangeFromPoint
- const source = DocumentUtil.getRangeFromPoint(0, 0, {
- deepContentScan: false,
- normalizeCssZoom: true
- });
- 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; }
-
- // 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,
- sentenceScanExtent2,
- terminateAtNewlines2,
- terminatorMap,
- forwardQuoteMap,
- backwardQuoteMap
- ).text;
- assert.strictEqual(sentenceActual, sentence);
-
- // Clean
- source.cleanup();
- }
-}
-
-/**
- * @param {JSDOM} dom
- * @param {{DOMTextScanner: typeof DOMTextScanner}} details
- */
-async function testTextSourceRangeSeekFunctions(dom, {DOMTextScanner}) {
- const document = dom.window.document;
-
- for (const testElement of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('.test[data-test-type=text-source-range-seek]'))) {
- // Get test parameters
- const {
- seekNodeSelector,
- seekNodeIsText,
- seekOffset,
- seekLength,
- seekDirection,
- expectedResultNodeSelector,
- expectedResultNodeIsText,
- expectedResultOffset,
- expectedResultContent
- } = testElement.dataset;
-
- const seekOffset2 = parseInt(/** @type {string} */ (seekOffset), 10);
- const seekLength2 = parseInt(/** @type {string} */ (seekLength), 10);
- const expectedResultOffset2 = parseInt(/** @type {string} */ (expectedResultOffset), 10);
-
- /** @type {?Node} */
- let seekNode = testElement.querySelector(/** @type {string} */ (seekNodeSelector));
- if (seekNodeIsText === 'true' && seekNode !== null) {
- seekNode = seekNode.firstChild;
- }
-
- /** @type {?Node} */
- let expectedResultNode = testElement.querySelector(/** @type {string} */ (expectedResultNodeSelector));
- if (expectedResultNodeIsText === 'true' && expectedResultNode !== null) {
- expectedResultNode = expectedResultNode.firstChild;
- }
-
- const {node, offset, content} = (
- seekDirection === 'forward' ?
- new DOMTextScanner(/** @type {Node} */ (seekNode), seekOffset2, true, false).seek(seekLength2) :
- new DOMTextScanner(/** @type {Node} */ (seekNode), seekOffset2, true, false).seek(-seekLength2)
- );
-
- assert.strictEqual(node, expectedResultNode);
- assert.strictEqual(offset, expectedResultOffset2);
- assert.strictEqual(content, expectedResultContent);
- }
-}
-
-
-/** */
-async function main() {
- await testDocument1();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-hotkey-util.js b/test/test-hotkey-util.js
deleted file mode 100644
index 88f5a8d2..00000000
--- a/test/test-hotkey-util.js
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-
-/**
- * @template T
- * @param {T} value
- * @returns {T}
- */
-function clone(value) {
- return JSON.parse(JSON.stringify(value));
-}
-
-/**
- * @returns {HotkeyUtil}
- */
-function createHotkeyUtil() {
- const vm = new VM();
- vm.execute(['js/input/hotkey-util.js']);
- /** @type {typeof HotkeyUtil} */
- const HotkeyUtil2 = vm.getSingle('HotkeyUtil');
- return new HotkeyUtil2();
-}
-
-
-/** */
-function testCommandConversions() {
- /** @type {{os: import('environment').OperatingSystem, command: string, expectedCommand: string, expectedInput: {key: string, modifiers: import('input').Modifier[]}}[]} */
- const data = [
- {os: 'win', command: 'Alt+F', expectedCommand: 'Alt+F', expectedInput: {key: 'KeyF', modifiers: ['alt']}},
- {os: 'win', command: 'F1', expectedCommand: 'F1', expectedInput: {key: 'F1', modifiers: []}},
-
- {os: 'win', command: 'Ctrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}},
- {os: 'win', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}},
- {os: 'win', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}},
-
- {os: 'mac', command: 'Ctrl+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}},
- {os: 'mac', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'MacCtrl+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}},
- {os: 'mac', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}},
-
- {os: 'linux', command: 'Ctrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}},
- {os: 'linux', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}},
- {os: 'linux', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}
- ];
-
- const hotkeyUtil = createHotkeyUtil();
- for (const {command, os, expectedInput, expectedCommand} of data) {
- hotkeyUtil.os = os;
- const input = clone(hotkeyUtil.convertCommandToInput(command));
- assert.deepStrictEqual(input, expectedInput);
- const command2 = hotkeyUtil.convertInputToCommand(input.key, input.modifiers);
- assert.deepStrictEqual(command2, expectedCommand);
- }
-}
-
-/** */
-function testDisplayNames() {
- /** @type {{os: import('environment').OperatingSystem, key: ?string, modifiers: import('input').Modifier[], expected: string}[]} */
- const data = [
- {os: 'win', key: null, modifiers: [], expected: ''},
- {os: 'win', key: 'KeyF', modifiers: [], expected: 'F'},
- {os: 'win', key: 'F1', modifiers: [], expected: 'F1'},
- {os: 'win', key: null, modifiers: ['ctrl'], expected: 'Ctrl'},
- {os: 'win', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'},
- {os: 'win', key: 'F1', modifiers: ['ctrl'], expected: 'Ctrl + F1'},
- {os: 'win', key: null, modifiers: ['alt'], expected: 'Alt'},
- {os: 'win', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'},
- {os: 'win', key: 'F1', modifiers: ['alt'], expected: 'Alt + F1'},
- {os: 'win', key: null, modifiers: ['shift'], expected: 'Shift'},
- {os: 'win', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'},
- {os: 'win', key: 'F1', modifiers: ['shift'], expected: 'Shift + F1'},
- {os: 'win', key: null, modifiers: ['meta'], expected: 'Windows'},
- {os: 'win', key: 'KeyF', modifiers: ['meta'], expected: 'Windows + F'},
- {os: 'win', key: 'F1', modifiers: ['meta'], expected: 'Windows + F1'},
- {os: 'win', key: null, modifiers: ['mouse1'], expected: 'Mouse 1'},
- {os: 'win', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'},
- {os: 'win', key: 'F1', modifiers: ['mouse1'], expected: 'Mouse 1 + F1'},
-
- {os: 'mac', key: null, modifiers: [], expected: ''},
- {os: 'mac', key: 'KeyF', modifiers: [], expected: 'F'},
- {os: 'mac', key: 'F1', modifiers: [], expected: 'F1'},
- {os: 'mac', key: null, modifiers: ['ctrl'], expected: 'Ctrl'},
- {os: 'mac', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'},
- {os: 'mac', key: 'F1', modifiers: ['ctrl'], expected: 'Ctrl + F1'},
- {os: 'mac', key: null, modifiers: ['alt'], expected: 'Opt'},
- {os: 'mac', key: 'KeyF', modifiers: ['alt'], expected: 'Opt + F'},
- {os: 'mac', key: 'F1', modifiers: ['alt'], expected: 'Opt + F1'},
- {os: 'mac', key: null, modifiers: ['shift'], expected: 'Shift'},
- {os: 'mac', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'},
- {os: 'mac', key: 'F1', modifiers: ['shift'], expected: 'Shift + F1'},
- {os: 'mac', key: null, modifiers: ['meta'], expected: 'Cmd'},
- {os: 'mac', key: 'KeyF', modifiers: ['meta'], expected: 'Cmd + F'},
- {os: 'mac', key: 'F1', modifiers: ['meta'], expected: 'Cmd + F1'},
- {os: 'mac', key: null, modifiers: ['mouse1'], expected: 'Mouse 1'},
- {os: 'mac', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'},
- {os: 'mac', key: 'F1', modifiers: ['mouse1'], expected: 'Mouse 1 + F1'},
-
- {os: 'linux', key: null, modifiers: [], expected: ''},
- {os: 'linux', key: 'KeyF', modifiers: [], expected: 'F'},
- {os: 'linux', key: 'F1', modifiers: [], expected: 'F1'},
- {os: 'linux', key: null, modifiers: ['ctrl'], expected: 'Ctrl'},
- {os: 'linux', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'},
- {os: 'linux', key: 'F1', modifiers: ['ctrl'], expected: 'Ctrl + F1'},
- {os: 'linux', key: null, modifiers: ['alt'], expected: 'Alt'},
- {os: 'linux', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'},
- {os: 'linux', key: 'F1', modifiers: ['alt'], expected: 'Alt + F1'},
- {os: 'linux', key: null, modifiers: ['shift'], expected: 'Shift'},
- {os: 'linux', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'},
- {os: 'linux', key: 'F1', modifiers: ['shift'], expected: 'Shift + F1'},
- {os: 'linux', key: null, modifiers: ['meta'], expected: 'Super'},
- {os: 'linux', key: 'KeyF', modifiers: ['meta'], expected: 'Super + F'},
- {os: 'linux', key: 'F1', modifiers: ['meta'], expected: 'Super + F1'},
- {os: 'linux', key: null, modifiers: ['mouse1'], expected: 'Mouse 1'},
- {os: 'linux', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'},
- {os: 'linux', key: 'F1', modifiers: ['mouse1'], expected: 'Mouse 1 + F1'},
-
- {os: 'unknown', key: null, modifiers: [], expected: ''},
- {os: 'unknown', key: 'KeyF', modifiers: [], expected: 'F'},
- {os: 'unknown', key: 'F1', modifiers: [], expected: 'F1'},
- {os: 'unknown', key: null, modifiers: ['ctrl'], expected: 'Ctrl'},
- {os: 'unknown', key: 'KeyF', modifiers: ['ctrl'], expected: 'Ctrl + F'},
- {os: 'unknown', key: 'F1', modifiers: ['ctrl'], expected: 'Ctrl + F1'},
- {os: 'unknown', key: null, modifiers: ['alt'], expected: 'Alt'},
- {os: 'unknown', key: 'KeyF', modifiers: ['alt'], expected: 'Alt + F'},
- {os: 'unknown', key: 'F1', modifiers: ['alt'], expected: 'Alt + F1'},
- {os: 'unknown', key: null, modifiers: ['shift'], expected: 'Shift'},
- {os: 'unknown', key: 'KeyF', modifiers: ['shift'], expected: 'Shift + F'},
- {os: 'unknown', key: 'F1', modifiers: ['shift'], expected: 'Shift + F1'},
- {os: 'unknown', key: null, modifiers: ['meta'], expected: 'Meta'},
- {os: 'unknown', key: 'KeyF', modifiers: ['meta'], expected: 'Meta + F'},
- {os: 'unknown', key: 'F1', modifiers: ['meta'], expected: 'Meta + F1'},
- {os: 'unknown', key: null, modifiers: ['mouse1'], expected: 'Mouse 1'},
- {os: 'unknown', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'},
- {os: 'unknown', key: 'F1', modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}
- ];
-
- const hotkeyUtil = createHotkeyUtil();
- for (const {os, key, modifiers, expected} of data) {
- hotkeyUtil.os = os;
- const displayName = hotkeyUtil.getInputDisplayValue(key, modifiers);
- assert.deepStrictEqual(displayName, expected);
- }
-}
-
-/** */
-function testSortModifiers() {
- /** @type {{modifiers: import('input').Modifier[], expected: import('input').Modifier[]}[]} */
- const data = [
- {modifiers: [], expected: []},
- {modifiers: ['shift', 'alt', 'ctrl', 'mouse4', 'meta', 'mouse1', 'mouse0'], expected: ['meta', 'ctrl', 'alt', 'shift', 'mouse0', 'mouse1', 'mouse4']}
- ];
-
- const hotkeyUtil = createHotkeyUtil();
- for (const {modifiers, expected} of data) {
- const modifiers2 = hotkeyUtil.sortModifiers(modifiers);
- assert.strictEqual(modifiers2, modifiers);
- assert.deepStrictEqual(modifiers2, expected);
- }
-}
-
-
-/** */
-function main() {
- testCommandConversions();
- testDisplayNames();
- testSortModifiers();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-japanese-util.js b/test/test-japanese-util.js
deleted file mode 100644
index 0a95b858..00000000
--- a/test/test-japanese-util.js
+++ /dev/null
@@ -1,915 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-const vm = new VM();
-vm.execute([
- 'lib/wanakana.min.js',
- 'js/language/sandbox/japanese-util.js',
- 'js/general/text-source-map.js'
-]);
-/** @type {[JapaneseUtil: typeof JapaneseUtil, TextSourceMap: typeof TextSourceMap, wanakana: import('wanakana')]} */
-const [JapaneseUtil2, TextSourceMap2, wanakana] = vm.get(['JapaneseUtil', 'TextSourceMap', 'wanakana']);
-const jp = new JapaneseUtil2(wanakana);
-
-
-/** */
-function testIsCodePointKanji() {
- /** @type {[characters: string, expected: boolean][]} */
- const data = [
- ['力方', true],
- ['\u53f1\u{20b9f}', true],
- ['かたカタ々kata、。?,.?', false],
- ['逸逸', true]
- ];
-
- for (const [characters, expected] of data) {
- for (const character of characters) {
- const codePoint = /** @type {number} */ (character.codePointAt(0));
- const actual = jp.isCodePointKanji(codePoint);
- assert.strictEqual(actual, expected, `isCodePointKanji failed for ${character} (\\u{${codePoint.toString(16)}})`);
- }
- }
-}
-
-/** */
-function testIsCodePointKana() {
- /** @type {[characters: string, expected: boolean][]} */
- const data = [
- ['かたカタ', true],
- ['力方々kata、。?,.?', false],
- ['\u53f1\u{20b9f}', false]
- ];
-
- for (const [characters, expected] of data) {
- for (const character of characters) {
- const codePoint = /** @type {number} */ (character.codePointAt(0));
- const actual = jp.isCodePointKana(codePoint);
- assert.strictEqual(actual, expected, `isCodePointKana failed for ${character} (\\u{${codePoint.toString(16)}})`);
- }
- }
-}
-
-/** */
-function testIsCodePointJapanese() {
- /** @type {[characters: string, expected: boolean][]} */
- const data = [
- ['かたカタ力方々、。?', true],
- ['\u53f1\u{20b9f}', true],
- ['kata,.?', false],
- ['逸逸', true]
- ];
-
- for (const [characters, expected] of data) {
- for (const character of characters) {
- const codePoint = /** @type {number} */ (character.codePointAt(0));
- const actual = jp.isCodePointJapanese(codePoint);
- assert.strictEqual(actual, expected, `isCodePointJapanese failed for ${character} (\\u{${codePoint.toString(16)}})`);
- }
- }
-}
-
-/** */
-function testIsStringEntirelyKana() {
- /** @type {[string: string, expected: boolean][]} */
- const data = [
- ['かたかな', true],
- ['カタカナ', true],
- ['ひらがな', true],
- ['ヒラガナ', true],
- ['カタカナひらがな', true],
- ['かたカタ力方々、。?', false],
- ['\u53f1\u{20b9f}', false],
- ['kata,.?', false],
- ['かたカタ力方々、。?invalid', false],
- ['\u53f1\u{20b9f}invalid', false],
- ['kata,.?かた', false]
- ];
-
- for (const [string, expected] of data) {
- assert.strictEqual(jp.isStringEntirelyKana(string), expected);
- }
-}
-
-/** */
-function testIsStringPartiallyJapanese() {
- /** @type {[string: string, expected: boolean][]} */
- const data = [
- ['かたかな', true],
- ['カタカナ', true],
- ['ひらがな', true],
- ['ヒラガナ', true],
- ['カタカナひらがな', true],
- ['かたカタ力方々、。?', true],
- ['\u53f1\u{20b9f}', true],
- ['kata,.?', false],
- ['かたカタ力方々、。?invalid', true],
- ['\u53f1\u{20b9f}invalid', true],
- ['kata,.?かた', true],
- ['逸逸', true]
- ];
-
- for (const [string, expected] of data) {
- assert.strictEqual(jp.isStringPartiallyJapanese(string), expected);
- }
-}
-
-/** */
-function testConvertKatakanaToHiragana() {
- /** @type {[string: string, expected: string, keepProlongedSoundMarks?: boolean][]} */
- const data = [
- ['かたかな', 'かたかな'],
- ['ひらがな', 'ひらがな'],
- ['カタカナ', 'かたかな'],
- ['ヒラガナ', 'ひらがな'],
- ['カタカナかたかな', 'かたかなかたかな'],
- ['ヒラガナひらがな', 'ひらがなひらがな'],
- ['chikaraちからチカラ力', 'chikaraちからちから力'],
- ['katakana', 'katakana'],
- ['hiragana', 'hiragana'],
- ['カーナー', 'かあなあ'],
- ['カーナー', 'かーなー', true]
- ];
-
- for (const [string, expected, keepProlongedSoundMarks=false] of data) {
- assert.strictEqual(jp.convertKatakanaToHiragana(string, keepProlongedSoundMarks), expected);
- }
-}
-
-/** */
-function testConvertHiraganaToKatakana() {
- /** @type {[string: string, expected: string][]} */
- const data = [
- ['かたかな', 'カタカナ'],
- ['ひらがな', 'ヒラガナ'],
- ['カタカナ', 'カタカナ'],
- ['ヒラガナ', 'ヒラガナ'],
- ['カタカナかたかな', 'カタカナカタカナ'],
- ['ヒラガナひらがな', 'ヒラガナヒラガナ'],
- ['chikaraちからチカラ力', 'chikaraチカラチカラ力'],
- ['katakana', 'katakana'],
- ['hiragana', 'hiragana']
- ];
-
- for (const [string, expected] of data) {
- assert.strictEqual(jp.convertHiraganaToKatakana(string), expected);
- }
-}
-
-/** */
-function testConvertToRomaji() {
- /** @type {[string: string, expected: string][]} */
- const data = [
- ['かたかな', 'katakana'],
- ['ひらがな', 'hiragana'],
- ['カタカナ', 'katakana'],
- ['ヒラガナ', 'hiragana'],
- ['カタカナかたかな', 'katakanakatakana'],
- ['ヒラガナひらがな', 'hiraganahiragana'],
- ['chikaraちからチカラ力', 'chikarachikarachikara力'],
- ['katakana', 'katakana'],
- ['hiragana', 'hiragana']
- ];
-
- for (const [string, expected] of data) {
- assert.strictEqual(jp.convertToRomaji(string), expected);
- }
-}
-
-/** */
-function testConvertNumericToFullWidth() {
- /** @type {[string: string, expected: string][]} */
- const data = [
- ['0123456789', '0123456789'],
- ['abcdefghij', 'abcdefghij'],
- ['カタカナ', 'カタカナ'],
- ['ひらがな', 'ひらがな']
- ];
-
- for (const [string, expected] of data) {
- assert.strictEqual(jp.convertNumericToFullWidth(string), expected);
- }
-}
-
-/** */
-function testConvertHalfWidthKanaToFullWidth() {
- /** @type {[string: string, expected: string, expectedSourceMapping?: number[]][]} */
- const data = [
- ['0123456789', '0123456789'],
- ['abcdefghij', 'abcdefghij'],
- ['カタカナ', 'カタカナ'],
- ['ひらがな', 'ひらがな'],
- ['カキ', 'カキ', [1, 1]],
- ['ガキ', 'ガキ', [2, 1]],
- ['ニホン', 'ニホン', [1, 1, 1]],
- ['ニッポン', 'ニッポン', [1, 1, 2, 1]]
- ];
-
- for (const [string, expected, expectedSourceMapping] of data) {
- const sourceMap = new TextSourceMap2(string);
- const actual1 = jp.convertHalfWidthKanaToFullWidth(string, null);
- const actual2 = jp.convertHalfWidthKanaToFullWidth(string, sourceMap);
- assert.strictEqual(actual1, expected);
- assert.strictEqual(actual2, expected);
- if (typeof expectedSourceMapping !== 'undefined') {
- assert.ok(sourceMap.equals(new TextSourceMap2(string, expectedSourceMapping)));
- }
- }
-}
-
-/** */
-function testConvertAlphabeticToKana() {
- /** @type {[string: string, expected: string, expectedSourceMapping?: number[]][]} */
- const data = [
- ['0123456789', '0123456789'],
- ['abcdefghij', 'あbcでfgひj', [1, 1, 1, 2, 1, 1, 2, 1]],
- ['ABCDEFGHIJ', 'あbcでfgひj', [1, 1, 1, 2, 1, 1, 2, 1]], // wanakana.toHiragana converts text to lower case
- ['カタカナ', 'カタカナ'],
- ['ひらがな', 'ひらがな'],
- ['chikara', 'ちから', [3, 2, 2]],
- ['CHIKARA', 'ちから', [3, 2, 2]]
- ];
-
- for (const [string, expected, expectedSourceMapping] of data) {
- const sourceMap = new TextSourceMap2(string);
- const actual1 = jp.convertAlphabeticToKana(string, null);
- const actual2 = jp.convertAlphabeticToKana(string, sourceMap);
- assert.strictEqual(actual1, expected);
- assert.strictEqual(actual2, expected);
- if (typeof expectedSourceMapping !== 'undefined') {
- assert.ok(sourceMap.equals(new TextSourceMap2(string, expectedSourceMapping)));
- }
- }
-}
-
-/** */
-function testDistributeFurigana() {
- /** @type {[input: [term: string, reading: string], expected: {text: string, reading: string}[]][]} */
- const data = [
- ([
- ['有り難う', 'ありがとう'],
- [
- {text: '有', reading: 'あ'},
- {text: 'り', reading: ''},
- {text: '難', reading: 'がと'},
- {text: 'う', reading: ''}
- ]
- ]),
- [
- ['方々', 'かたがた'],
- [
- {text: '方々', reading: 'かたがた'}
- ]
- ],
- [
- ['お祝い', 'おいわい'],
- [
- {text: 'お', reading: ''},
- {text: '祝', reading: 'いわ'},
- {text: 'い', reading: ''}
- ]
- ],
- [
- ['美味しい', 'おいしい'],
- [
- {text: '美味', reading: 'おい'},
- {text: 'しい', reading: ''}
- ]
- ],
- [
- ['食べ物', 'たべもの'],
- [
- {text: '食', reading: 'た'},
- {text: 'べ', reading: ''},
- {text: '物', reading: 'もの'}
- ]
- ],
- [
- ['試し切り', 'ためしぎり'],
- [
- {text: '試', reading: 'ため'},
- {text: 'し', reading: ''},
- {text: '切', reading: 'ぎ'},
- {text: 'り', reading: ''}
- ]
- ],
- // Ambiguous
- [
- ['飼い犬', 'かいいぬ'],
- [
- {text: '飼い犬', reading: 'かいいぬ'}
- ]
- ],
- [
- ['長い間', 'ながいあいだ'],
- [
- {text: '長い間', reading: 'ながいあいだ'}
- ]
- ],
- // Same/empty reading
- [
- ['飼い犬', ''],
- [
- {text: '飼い犬', reading: ''}
- ]
- ],
- [
- ['かいいぬ', 'かいいぬ'],
- [
- {text: 'かいいぬ', reading: ''}
- ]
- ],
- [
- ['かいぬ', 'かいぬ'],
- [
- {text: 'かいぬ', reading: ''}
- ]
- ],
- // Misc
- [
- ['月', 'か'],
- [
- {text: '月', reading: 'か'}
- ]
- ],
- [
- ['月', 'カ'],
- [
- {text: '月', reading: 'カ'}
- ]
- ],
- // Mismatched kana readings
- [
- ['有り難う', 'アリガトウ'],
- [
- {text: '有', reading: 'ア'},
- {text: 'り', reading: 'リ'},
- {text: '難', reading: 'ガト'},
- {text: 'う', reading: 'ウ'}
- ]
- ],
- [
- ['ありがとう', 'アリガトウ'],
- [
- {text: 'ありがとう', reading: 'アリガトウ'}
- ]
- ],
- // Mismatched kana readings (real examples)
- [
- ['カ月', 'かげつ'],
- [
- {text: 'カ', reading: 'か'},
- {text: '月', reading: 'げつ'}
- ]
- ],
- [
- ['序ノ口', 'じょのくち'],
- [
- {text: '序', reading: 'じょ'},
- {text: 'ノ', reading: 'の'},
- {text: '口', reading: 'くち'}
- ]
- ],
- [
- ['スズメの涙', 'すずめのなみだ'],
- [
- {text: 'スズメ', reading: 'すずめ'},
- {text: 'の', reading: ''},
- {text: '涙', reading: 'なみだ'}
- ]
- ],
- [
- ['二カ所', 'にかしょ'],
- [
- {text: '二', reading: 'に'},
- {text: 'カ', reading: 'か'},
- {text: '所', reading: 'しょ'}
- ]
- ],
- [
- ['八ツ橋', 'やつはし'],
- [
- {text: '八', reading: 'や'},
- {text: 'ツ', reading: 'つ'},
- {text: '橋', reading: 'はし'}
- ]
- ],
- [
- ['八ツ橋', 'やつはし'],
- [
- {text: '八', reading: 'や'},
- {text: 'ツ', reading: 'つ'},
- {text: '橋', reading: 'はし'}
- ]
- ],
- [
- ['一カ月', 'いっかげつ'],
- [
- {text: '一', reading: 'いっ'},
- {text: 'カ', reading: 'か'},
- {text: '月', reading: 'げつ'}
- ]
- ],
- [
- ['一カ所', 'いっかしょ'],
- [
- {text: '一', reading: 'いっ'},
- {text: 'カ', reading: 'か'},
- {text: '所', reading: 'しょ'}
- ]
- ],
- [
- ['カ所', 'かしょ'],
- [
- {text: 'カ', reading: 'か'},
- {text: '所', reading: 'しょ'}
- ]
- ],
- [
- ['数カ月', 'すうかげつ'],
- [
- {text: '数', reading: 'すう'},
- {text: 'カ', reading: 'か'},
- {text: '月', reading: 'げつ'}
- ]
- ],
- [
- ['くノ一', 'くのいち'],
- [
- {text: 'く', reading: ''},
- {text: 'ノ', reading: 'の'},
- {text: '一', reading: 'いち'}
- ]
- ],
- [
- ['くノ一', 'くのいち'],
- [
- {text: 'く', reading: ''},
- {text: 'ノ', reading: 'の'},
- {text: '一', reading: 'いち'}
- ]
- ],
- [
- ['数カ国', 'すうかこく'],
- [
- {text: '数', reading: 'すう'},
- {text: 'カ', reading: 'か'},
- {text: '国', reading: 'こく'}
- ]
- ],
- [
- ['数カ所', 'すうかしょ'],
- [
- {text: '数', reading: 'すう'},
- {text: 'カ', reading: 'か'},
- {text: '所', reading: 'しょ'}
- ]
- ],
- [
- ['壇ノ浦の戦い', 'だんのうらのたたかい'],
- [
- {text: '壇', reading: 'だん'},
- {text: 'ノ', reading: 'の'},
- {text: '浦', reading: 'うら'},
- {text: 'の', reading: ''},
- {text: '戦', reading: 'たたか'},
- {text: 'い', reading: ''}
- ]
- ],
- [
- ['壇ノ浦の戦', 'だんのうらのたたかい'],
- [
- {text: '壇', reading: 'だん'},
- {text: 'ノ', reading: 'の'},
- {text: '浦', reading: 'うら'},
- {text: 'の', reading: ''},
- {text: '戦', reading: 'たたかい'}
- ]
- ],
- [
- ['序ノ口格', 'じょのくちかく'],
- [
- {text: '序', reading: 'じょ'},
- {text: 'ノ', reading: 'の'},
- {text: '口格', reading: 'くちかく'}
- ]
- ],
- [
- ['二カ国語', 'にかこくご'],
- [
- {text: '二', reading: 'に'},
- {text: 'カ', reading: 'か'},
- {text: '国語', reading: 'こくご'}
- ]
- ],
- [
- ['カ国', 'かこく'],
- [
- {text: 'カ', reading: 'か'},
- {text: '国', reading: 'こく'}
- ]
- ],
- [
- ['カ国語', 'かこくご'],
- [
- {text: 'カ', reading: 'か'},
- {text: '国語', reading: 'こくご'}
- ]
- ],
- [
- ['壇ノ浦の合戦', 'だんのうらのかっせん'],
- [
- {text: '壇', reading: 'だん'},
- {text: 'ノ', reading: 'の'},
- {text: '浦', reading: 'うら'},
- {text: 'の', reading: ''},
- {text: '合戦', reading: 'かっせん'}
- ]
- ],
- [
- ['一タ偏', 'いちたへん'],
- [
- {text: '一', reading: 'いち'},
- {text: 'タ', reading: 'た'},
- {text: '偏', reading: 'へん'}
- ]
- ],
- [
- ['ル又', 'るまた'],
- [
- {text: 'ル', reading: 'る'},
- {text: '又', reading: 'また'}
- ]
- ],
- [
- ['ノ木偏', 'のぎへん'],
- [
- {text: 'ノ', reading: 'の'},
- {text: '木偏', reading: 'ぎへん'}
- ]
- ],
- [
- ['一ノ貝', 'いちのかい'],
- [
- {text: '一', reading: 'いち'},
- {text: 'ノ', reading: 'の'},
- {text: '貝', reading: 'かい'}
- ]
- ],
- [
- ['虎ノ門事件', 'とらのもんじけん'],
- [
- {text: '虎', reading: 'とら'},
- {text: 'ノ', reading: 'の'},
- {text: '門事件', reading: 'もんじけん'}
- ]
- ],
- [
- ['教育ニ関スル勅語', 'きょういくにかんするちょくご'],
- [
- {text: '教育', reading: 'きょういく'},
- {text: 'ニ', reading: 'に'},
- {text: '関', reading: 'かん'},
- {text: 'スル', reading: 'する'},
- {text: '勅語', reading: 'ちょくご'}
- ]
- ],
- [
- ['二カ年', 'にかねん'],
- [
- {text: '二', reading: 'に'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['三カ年', 'さんかねん'],
- [
- {text: '三', reading: 'さん'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['四カ年', 'よんかねん'],
- [
- {text: '四', reading: 'よん'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['五カ年', 'ごかねん'],
- [
- {text: '五', reading: 'ご'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['六カ年', 'ろっかねん'],
- [
- {text: '六', reading: 'ろっ'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['七カ年', 'ななかねん'],
- [
- {text: '七', reading: 'なな'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['八カ年', 'はちかねん'],
- [
- {text: '八', reading: 'はち'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['九カ年', 'きゅうかねん'],
- [
- {text: '九', reading: 'きゅう'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['十カ年', 'じゅうかねん'],
- [
- {text: '十', reading: 'じゅう'},
- {text: 'カ', reading: 'か'},
- {text: '年', reading: 'ねん'}
- ]
- ],
- [
- ['鏡ノ間', 'かがみのま'],
- [
- {text: '鏡', reading: 'かがみ'},
- {text: 'ノ', reading: 'の'},
- {text: '間', reading: 'ま'}
- ]
- ],
- [
- ['鏡ノ間', 'かがみのま'],
- [
- {text: '鏡', reading: 'かがみ'},
- {text: 'ノ', reading: 'の'},
- {text: '間', reading: 'ま'}
- ]
- ],
- [
- ['ページ違反', 'ぺーじいはん'],
- [
- {text: 'ペ', reading: 'ぺ'},
- {text: 'ー', reading: ''},
- {text: 'ジ', reading: 'じ'},
- {text: '違反', reading: 'いはん'}
- ]
- ],
- // Mismatched kana
- [
- ['サボる', 'サボル'],
- [
- {text: 'サボ', reading: ''},
- {text: 'る', reading: 'ル'}
- ]
- ],
- // Reading starts with term, but has remainder characters
- [
- ['シック', 'シック・ビルしょうこうぐん'],
- [
- {text: 'シック', reading: 'シック・ビルしょうこうぐん'}
- ]
- ],
- // Kanji distribution tests
- [
- ['逸らす', 'そらす'],
- [
- {text: '逸', reading: 'そ'},
- {text: 'らす', reading: ''}
- ]
- ],
- [
- ['逸らす', 'そらす'],
- [
- {text: '逸', reading: 'そ'},
- {text: 'らす', reading: ''}
- ]
- ]
- ];
-
- for (const [[term, reading], expected] of data) {
- const actual = jp.distributeFurigana(term, reading);
- vm.assert.deepStrictEqual(actual, expected);
- }
-}
-
-/** */
-function testDistributeFuriganaInflected() {
- /** @type {[input: [term: string, reading: string, source: string], expected: {text: string, reading: string}[]][]} */
- const data = [
- [
- ['美味しい', 'おいしい', '美味しかた'],
- [
- {text: '美味', reading: 'おい'},
- {text: 'しかた', reading: ''}
- ]
- ],
- [
- ['食べる', 'たべる', '食べた'],
- [
- {text: '食', reading: 'た'},
- {text: 'べた', reading: ''}
- ]
- ],
- [
- ['迄に', 'までに', 'までに'],
- [
- {text: 'までに', reading: ''}
- ]
- ],
- [
- ['行う', 'おこなう', 'おこなわなかった'],
- [
- {text: 'おこなわなかった', reading: ''}
- ]
- ],
- [
- ['いい', 'いい', 'イイ'],
- [
- {text: 'イイ', reading: ''}
- ]
- ],
- [
- ['否か', 'いなか', '否カ'],
- [
- {text: '否', reading: 'いな'},
- {text: 'カ', reading: 'か'}
- ]
- ]
- ];
-
- for (const [[term, reading, source], expected] of data) {
- const actual = jp.distributeFuriganaInflected(term, reading, source);
- vm.assert.deepStrictEqual(actual, expected);
- }
-}
-
-/** */
-function testCollapseEmphaticSequences() {
- /** @type {[input: [text: string, fullCollapse: boolean], output: [expected: string, expectedSourceMapping: number[]]][]} */
- const data = [
- [['かこい', false], ['かこい', [1, 1, 1]]],
- [['かこい', true], ['かこい', [1, 1, 1]]],
- [['かっこい', false], ['かっこい', [1, 1, 1, 1]]],
- [['かっこい', true], ['かこい', [2, 1, 1]]],
- [['かっっこい', false], ['かっこい', [1, 2, 1, 1]]],
- [['かっっこい', true], ['かこい', [3, 1, 1]]],
- [['かっっっこい', false], ['かっこい', [1, 3, 1, 1]]],
- [['かっっっこい', true], ['かこい', [4, 1, 1]]],
-
- [['こい', false], ['こい', [1, 1]]],
- [['こい', true], ['こい', [1, 1]]],
- [['っこい', false], ['っこい', [1, 1, 1]]],
- [['っこい', true], ['こい', [2, 1]]],
- [['っっこい', false], ['っこい', [2, 1, 1]]],
- [['っっこい', true], ['こい', [3, 1]]],
- [['っっっこい', false], ['っこい', [3, 1, 1]]],
- [['っっっこい', true], ['こい', [4, 1]]],
-
- [['すごい', false], ['すごい', [1, 1, 1]]],
- [['すごい', true], ['すごい', [1, 1, 1]]],
- [['すごーい', false], ['すごーい', [1, 1, 1, 1]]],
- [['すごーい', true], ['すごい', [1, 2, 1]]],
- [['すごーーい', false], ['すごーい', [1, 1, 2, 1]]],
- [['すごーーい', true], ['すごい', [1, 3, 1]]],
- [['すっごーい', false], ['すっごーい', [1, 1, 1, 1, 1]]],
- [['すっごーい', true], ['すごい', [2, 2, 1]]],
- [['すっっごーーい', false], ['すっごーい', [1, 2, 1, 2, 1]]],
- [['すっっごーーい', true], ['すごい', [3, 3, 1]]],
-
- [['', false], ['', []]],
- [['', true], ['', []]],
- [['っ', false], ['っ', [1]]],
- [['っ', true], ['', [1]]],
- [['っっ', false], ['っ', [2]]],
- [['っっ', true], ['', [2]]],
- [['っっっ', false], ['っ', [3]]],
- [['っっっ', true], ['', [3]]]
- ];
-
- for (const [[text, fullCollapse], [expected, expectedSourceMapping]] of data) {
- const sourceMap = new TextSourceMap2(text);
- const actual1 = jp.collapseEmphaticSequences(text, fullCollapse, null);
- const actual2 = jp.collapseEmphaticSequences(text, fullCollapse, sourceMap);
- assert.strictEqual(actual1, expected);
- assert.strictEqual(actual2, expected);
- if (typeof expectedSourceMapping !== 'undefined') {
- assert.ok(sourceMap.equals(new TextSourceMap2(text, expectedSourceMapping)));
- }
- }
-}
-
-/** */
-function testIsMoraPitchHigh() {
- /** @type {[input: [moraIndex: number, pitchAccentDownstepPosition: number], expected: boolean][]} */
- const data = [
- [[0, 0], false],
- [[1, 0], true],
- [[2, 0], true],
- [[3, 0], true],
-
- [[0, 1], true],
- [[1, 1], false],
- [[2, 1], false],
- [[3, 1], false],
-
- [[0, 2], false],
- [[1, 2], true],
- [[2, 2], false],
- [[3, 2], false],
-
- [[0, 3], false],
- [[1, 3], true],
- [[2, 3], true],
- [[3, 3], false],
-
- [[0, 4], false],
- [[1, 4], true],
- [[2, 4], true],
- [[3, 4], true]
- ];
-
- for (const [[moraIndex, pitchAccentDownstepPosition], expected] of data) {
- const actual = jp.isMoraPitchHigh(moraIndex, pitchAccentDownstepPosition);
- assert.strictEqual(actual, expected);
- }
-}
-
-/** */
-function testGetKanaMorae() {
- /** @type {[text: string, expected: string[]][]} */
- const data = [
- ['かこ', ['か', 'こ']],
- ['かっこ', ['か', 'っ', 'こ']],
- ['カコ', ['カ', 'コ']],
- ['カッコ', ['カ', 'ッ', 'コ']],
- ['コート', ['コ', 'ー', 'ト']],
- ['ちゃんと', ['ちゃ', 'ん', 'と']],
- ['とうきょう', ['と', 'う', 'きょ', 'う']],
- ['ぎゅう', ['ぎゅ', 'う']],
- ['ディスコ', ['ディ', 'ス', 'コ']]
- ];
-
- for (const [text, expected] of data) {
- const actual = jp.getKanaMorae(text);
- vm.assert.deepStrictEqual(actual, expected);
- }
-}
-
-
-/** */
-function main() {
- testIsCodePointKanji();
- testIsCodePointKana();
- testIsCodePointJapanese();
- testIsStringEntirelyKana();
- testIsStringPartiallyJapanese();
- testConvertKatakanaToHiragana();
- testConvertHiraganaToKatakana();
- testConvertToRomaji();
- testConvertNumericToFullWidth();
- testConvertHalfWidthKanaToFullWidth();
- testConvertAlphabeticToKana();
- testDistributeFurigana();
- testDistributeFuriganaInflected();
- testCollapseEmphaticSequences();
- testIsMoraPitchHigh();
- testGetKanaMorae();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-json-schema.js b/test/test-json-schema.js
deleted file mode 100644
index f9cb023c..00000000
--- a/test/test-json-schema.js
+++ /dev/null
@@ -1,1048 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-const vm = new VM();
-vm.execute([
- 'js/core.js',
- 'js/core/extension-error.js',
- 'js/general/cache-map.js',
- 'js/data/json-schema.js'
-]);
-/** @type {typeof JsonSchema} */
-const JsonSchema2 = vm.getSingle('JsonSchema');
-
-
-/**
- * @param {import('json-schema').Schema} schema
- * @param {unknown} value
- * @returns {boolean}
- */
-function schemaValidate(schema, value) {
- return new JsonSchema2(schema).isValid(value);
-}
-
-/**
- * @param {import('json-schema').Schema} schema
- * @param {unknown} value
- * @returns {import('json-schema').Value}
- */
-function getValidValueOrDefault(schema, value) {
- return new JsonSchema2(schema).getValidValueOrDefault(value);
-}
-
-/**
- * @param {import('json-schema').Schema} schema
- * @param {import('json-schema').Value} value
- * @returns {import('json-schema').Value}
- */
-function createProxy(schema, value) {
- return new JsonSchema2(schema).createProxy(value);
-}
-
-/**
- * @template T
- * @param {T} value
- * @returns {T}
- */
-function clone(value) {
- return JSON.parse(JSON.stringify(value));
-}
-
-
-/** */
-function testValidate1() {
- /** @type {import('json-schema').Schema} */
- const schema = {
- allOf: [
- {
- type: 'number'
- },
- {
- anyOf: [
- {minimum: 10, maximum: 100},
- {minimum: -100, maximum: -10}
- ]
- },
- {
- oneOf: [
- {multipleOf: 3},
- {multipleOf: 5}
- ]
- },
- {
- not: {
- anyOf: [
- {multipleOf: 20}
- ]
- }
- }
- ]
- };
-
- /**
- * @param {number} value
- * @returns {boolean}
- */
- const jsValidate = (value) => {
- return (
- typeof value === 'number' &&
- (
- (value >= 10 && value <= 100) ||
- (value >= -100 && value <= -10)
- ) &&
- (
- (
- (value % 3) === 0 ||
- (value % 5) === 0
- ) &&
- (value % 15) !== 0
- ) &&
- (value % 20) !== 0
- );
- };
-
- for (let i = -111; i <= 111; i++) {
- const actual = schemaValidate(schema, i);
- const expected = jsValidate(i);
- assert.strictEqual(actual, expected, `i=${i}; expected=${expected}; actual=${actual}`);
- }
-}
-
-/** */
-function testValidate2() {
- /** @type {{schema: import('json-schema').Schema, inputs: {expected: boolean, value: unknown}[]}[]} */
- const data = [
- // String tests
- {
- schema: {
- type: 'string'
- },
- inputs: [
- {expected: false, value: null},
- {expected: false, value: void 0},
- {expected: false, value: 0},
- {expected: false, value: {}},
- {expected: false, value: []},
- {expected: true, value: ''}
- ]
- },
- {
- schema: {
- type: 'string',
- minLength: 2
- },
- inputs: [
- {expected: false, value: ''},
- {expected: false, value: '1'},
- {expected: true, value: '12'},
- {expected: true, value: '123'}
- ]
- },
- {
- schema: {
- type: 'string',
- maxLength: 2
- },
- inputs: [
- {expected: true, value: ''},
- {expected: true, value: '1'},
- {expected: true, value: '12'},
- {expected: false, value: '123'}
- ]
- },
- {
- schema: {
- type: 'string',
- pattern: 'test'
- },
- inputs: [
- {expected: false, value: ''},
- {expected: true, value: 'test'},
- {expected: false, value: 'TEST'},
- {expected: true, value: 'ABCtestDEF'},
- {expected: false, value: 'ABCTESTDEF'}
- ]
- },
- {
- schema: {
- type: 'string',
- pattern: '^test$'
- },
- inputs: [
- {expected: false, value: ''},
- {expected: true, value: 'test'},
- {expected: false, value: 'TEST'},
- {expected: false, value: 'ABCtestDEF'},
- {expected: false, value: 'ABCTESTDEF'}
- ]
- },
- {
- schema: {
- type: 'string',
- pattern: '^test$',
- patternFlags: 'i'
- },
- inputs: [
- {expected: false, value: ''},
- {expected: true, value: 'test'},
- {expected: true, value: 'TEST'},
- {expected: false, value: 'ABCtestDEF'},
- {expected: false, value: 'ABCTESTDEF'}
- ]
- },
- {
- schema: {
- type: 'string',
- pattern: '*'
- },
- inputs: [
- {expected: false, value: ''}
- ]
- },
- {
- schema: {
- type: 'string',
- pattern: '.',
- patternFlags: '?'
- },
- inputs: [
- {expected: false, value: ''}
- ]
- },
-
- // Const tests
- {
- schema: {
- const: 32
- },
- inputs: [
- {expected: true, value: 32},
- {expected: false, value: 0},
- {expected: false, value: '32'},
- {expected: false, value: null},
- {expected: false, value: {a: 'b'}},
- {expected: false, value: [1, 2, 3]}
- ]
- },
- {
- schema: {
- const: '32'
- },
- inputs: [
- {expected: false, value: 32},
- {expected: false, value: 0},
- {expected: true, value: '32'},
- {expected: false, value: null},
- {expected: false, value: {a: 'b'}},
- {expected: false, value: [1, 2, 3]}
- ]
- },
- {
- schema: {
- const: null
- },
- inputs: [
- {expected: false, value: 32},
- {expected: false, value: 0},
- {expected: false, value: '32'},
- {expected: true, value: null},
- {expected: false, value: {a: 'b'}},
- {expected: false, value: [1, 2, 3]}
- ]
- },
- {
- schema: {
- const: {a: 'b'}
- },
- inputs: [
- {expected: false, value: 32},
- {expected: false, value: 0},
- {expected: false, value: '32'},
- {expected: false, value: null},
- {expected: false, value: {a: 'b'}},
- {expected: false, value: [1, 2, 3]}
- ]
- },
- {
- schema: {
- const: [1, 2, 3]
- },
- inputs: [
- {expected: false, value: 32},
- {expected: false, value: 0},
- {expected: false, value: '32'},
- {expected: false, value: null},
- {expected: false, value: {a: 'b'}},
- {expected: false, value: [1, 2, 3]}
- ]
- },
-
- // Array contains tests
- {
- schema: {
- type: 'array',
- contains: {const: 32}
- },
- inputs: [
- {expected: false, value: []},
- {expected: true, value: [32]},
- {expected: true, value: [1, 32]},
- {expected: true, value: [1, 32, 1]},
- {expected: false, value: [33]},
- {expected: false, value: [1, 33]},
- {expected: false, value: [1, 33, 1]}
- ]
- },
-
- // Number limits tests
- {
- schema: {
- type: 'number',
- minimum: 0
- },
- inputs: [
- {expected: false, value: -1},
- {expected: true, value: 0},
- {expected: true, value: 1}
- ]
- },
- {
- schema: {
- type: 'number',
- exclusiveMinimum: 0
- },
- inputs: [
- {expected: false, value: -1},
- {expected: false, value: 0},
- {expected: true, value: 1}
- ]
- },
- {
- schema: {
- type: 'number',
- maximum: 0
- },
- inputs: [
- {expected: true, value: -1},
- {expected: true, value: 0},
- {expected: false, value: 1}
- ]
- },
- {
- schema: {
- type: 'number',
- exclusiveMaximum: 0
- },
- inputs: [
- {expected: true, value: -1},
- {expected: false, value: 0},
- {expected: false, value: 1}
- ]
- },
-
- // Integer limits tests
- {
- schema: {
- type: 'integer',
- minimum: 0
- },
- inputs: [
- {expected: false, value: -1},
- {expected: true, value: 0},
- {expected: true, value: 1}
- ]
- },
- {
- schema: {
- type: 'integer',
- exclusiveMinimum: 0
- },
- inputs: [
- {expected: false, value: -1},
- {expected: false, value: 0},
- {expected: true, value: 1}
- ]
- },
- {
- schema: {
- type: 'integer',
- maximum: 0
- },
- inputs: [
- {expected: true, value: -1},
- {expected: true, value: 0},
- {expected: false, value: 1}
- ]
- },
- {
- schema: {
- type: 'integer',
- exclusiveMaximum: 0
- },
- inputs: [
- {expected: true, value: -1},
- {expected: false, value: 0},
- {expected: false, value: 1}
- ]
- },
- {
- schema: {
- type: 'integer',
- multipleOf: 2
- },
- inputs: [
- {expected: true, value: -2},
- {expected: false, value: -1},
- {expected: true, value: 0},
- {expected: false, value: 1},
- {expected: true, value: 2}
- ]
- },
-
- // Numeric type tests
- {
- schema: {
- type: 'number'
- },
- inputs: [
- {expected: true, value: 0},
- {expected: true, value: 0.5},
- {expected: true, value: 1},
- {expected: false, value: '0'},
- {expected: false, value: null},
- {expected: false, value: []},
- {expected: false, value: {}}
- ]
- },
- {
- schema: {
- type: 'integer'
- },
- inputs: [
- {expected: true, value: 0},
- {expected: false, value: 0.5},
- {expected: true, value: 1},
- {expected: false, value: '0'},
- {expected: false, value: null},
- {expected: false, value: []},
- {expected: false, value: {}}
- ]
- },
-
- // Reference tests
- {
- schema: {
- definitions: {
- example: {
- type: 'number'
- }
- },
- $ref: '#/definitions/example'
- },
- inputs: [
- {expected: true, value: 0},
- {expected: true, value: 0.5},
- {expected: true, value: 1},
- {expected: false, value: '0'},
- {expected: false, value: null},
- {expected: false, value: []},
- {expected: false, value: {}}
- ]
- },
- {
- schema: {
- definitions: {
- example: {
- type: 'integer'
- }
- },
- $ref: '#/definitions/example'
- },
- inputs: [
- {expected: true, value: 0},
- {expected: false, value: 0.5},
- {expected: true, value: 1},
- {expected: false, value: '0'},
- {expected: false, value: null},
- {expected: false, value: []},
- {expected: false, value: {}}
- ]
- },
- {
- schema: {
- definitions: {
- example: {
- type: 'object',
- additionalProperties: false,
- properties: {
- test: {
- $ref: '#/definitions/example'
- }
- }
- }
- },
- $ref: '#/definitions/example'
- },
- inputs: [
- {expected: false, value: 0},
- {expected: false, value: 0.5},
- {expected: false, value: 1},
- {expected: false, value: '0'},
- {expected: false, value: null},
- {expected: false, value: []},
- {expected: true, value: {}},
- {expected: false, value: {test: 0}},
- {expected: false, value: {test: 0.5}},
- {expected: false, value: {test: 1}},
- {expected: false, value: {test: '0'}},
- {expected: false, value: {test: null}},
- {expected: false, value: {test: []}},
- {expected: true, value: {test: {}}},
- {expected: true, value: {test: {test: {}}}},
- {expected: true, value: {test: {test: {test: {}}}}}
- ]
- }
- ];
-
- for (const {schema, inputs} of data) {
- for (const {expected, value} of inputs) {
- const actual = schemaValidate(schema, value);
- assert.strictEqual(actual, expected);
- }
- }
-}
-
-
-/** */
-function testGetValidValueOrDefault1() {
- /** @type {{schema: import('json-schema').Schema, inputs: [value: unknown, expected: unknown][]}[]} */
- const data = [
- // Test value defaulting on objects with additionalProperties=false
- {
- schema: {
- type: 'object',
- required: ['test'],
- properties: {
- test: {
- type: 'string',
- default: 'default'
- }
- },
- additionalProperties: false
- },
- inputs: [
- [
- void 0,
- {test: 'default'}
- ],
- [
- null,
- {test: 'default'}
- ],
- [
- 0,
- {test: 'default'}
- ],
- [
- '',
- {test: 'default'}
- ],
- [
- [],
- {test: 'default'}
- ],
- [
- {},
- {test: 'default'}
- ],
- [
- {test: 'value'},
- {test: 'value'}
- ],
- [
- {test2: 'value2'},
- {test: 'default'}
- ],
- [
- {test: 'value', test2: 'value2'},
- {test: 'value'}
- ]
- ]
- },
-
- // Test value defaulting on objects with additionalProperties=true
- {
- schema: {
- type: 'object',
- required: ['test'],
- properties: {
- test: {
- type: 'string',
- default: 'default'
- }
- },
- additionalProperties: true
- },
- inputs: [
- [
- {},
- {test: 'default'}
- ],
- [
- {test: 'value'},
- {test: 'value'}
- ],
- [
- {test2: 'value2'},
- {test: 'default', test2: 'value2'}
- ],
- [
- {test: 'value', test2: 'value2'},
- {test: 'value', test2: 'value2'}
- ]
- ]
- },
-
- // Test value defaulting on objects with additionalProperties={schema}
- {
- schema: {
- type: 'object',
- required: ['test'],
- properties: {
- test: {
- type: 'string',
- default: 'default'
- }
- },
- additionalProperties: {
- type: 'number',
- default: 10
- }
- },
- inputs: [
- [
- {},
- {test: 'default'}
- ],
- [
- {test: 'value'},
- {test: 'value'}
- ],
- [
- {test2: 'value2'},
- {test: 'default', test2: 10}
- ],
- [
- {test: 'value', test2: 'value2'},
- {test: 'value', test2: 10}
- ],
- [
- {test2: 2},
- {test: 'default', test2: 2}
- ],
- [
- {test: 'value', test2: 2},
- {test: 'value', test2: 2}
- ],
- [
- {test: 'value', test2: 2, test3: null},
- {test: 'value', test2: 2, test3: 10}
- ],
- [
- {test: 'value', test2: 2, test3: void 0},
- {test: 'value', test2: 2, test3: 10}
- ]
- ]
- },
-
- // Test value defaulting where hasOwnProperty is false
- {
- schema: {
- type: 'object',
- required: ['test'],
- properties: {
- test: {
- type: 'string',
- default: 'default'
- }
- }
- },
- inputs: [
- [
- {},
- {test: 'default'}
- ],
- [
- {test: 'value'},
- {test: 'value'}
- ],
- [
- Object.create({test: 'value'}),
- {test: 'default'}
- ]
- ]
- },
- {
- schema: {
- type: 'object',
- required: ['toString'],
- properties: {
- toString: /** @type {import('json-schema').SchemaObject} */ ({
- type: 'string',
- default: 'default'
- })
- }
- },
- inputs: [
- [
- {},
- {toString: 'default'}
- ],
- [
- {toString: 'value'},
- {toString: 'value'}
- ],
- [
- Object.create({toString: 'value'}),
- {toString: 'default'}
- ]
- ]
- },
-
- // Test enum
- {
- schema: {
- type: 'object',
- required: ['test'],
- properties: {
- test: {
- type: 'string',
- default: 'value1',
- enum: ['value1', 'value2', 'value3']
- }
- }
- },
- inputs: [
- [
- {test: 'value1'},
- {test: 'value1'}
- ],
- [
- {test: 'value2'},
- {test: 'value2'}
- ],
- [
- {test: 'value3'},
- {test: 'value3'}
- ],
- [
- {test: 'value4'},
- {test: 'value1'}
- ]
- ]
- },
-
- // Test valid vs invalid default
- {
- schema: {
- type: 'object',
- required: ['test'],
- properties: {
- test: {
- type: 'integer',
- default: 2,
- minimum: 1
- }
- }
- },
- inputs: [
- [
- {test: -1},
- {test: 2}
- ]
- ]
- },
- {
- schema: {
- type: 'object',
- required: ['test'],
- properties: {
- test: {
- type: 'integer',
- default: 1,
- minimum: 2
- }
- }
- },
- inputs: [
- [
- {test: -1},
- {test: -1}
- ]
- ]
- },
-
- // Test references
- {
- schema: {
- definitions: {
- example: {
- type: 'number',
- default: 0
- }
- },
- $ref: '#/definitions/example'
- },
- inputs: [
- [
- 1,
- 1
- ],
- [
- null,
- 0
- ],
- [
- 'test',
- 0
- ],
- [
- {test: 'value'},
- 0
- ]
- ]
- },
- {
- schema: {
- definitions: {
- example: {
- type: 'object',
- additionalProperties: false,
- properties: {
- test: {
- $ref: '#/definitions/example'
- }
- }
- }
- },
- $ref: '#/definitions/example'
- },
- inputs: [
- [
- 1,
- {}
- ],
- [
- null,
- {}
- ],
- [
- 'test',
- {}
- ],
- [
- {},
- {}
- ],
- [
- {test: {}},
- {test: {}}
- ],
- [
- {test: 'value'},
- {test: {}}
- ],
- [
- {test: {test: {}}},
- {test: {test: {}}}
- ]
- ]
- }
- ];
-
- for (const {schema, inputs} of data) {
- for (const [value, expected] of inputs) {
- const actual = getValidValueOrDefault(schema, value);
- vm.assert.deepStrictEqual(actual, expected);
- }
- }
-}
-
-
-/** */
-function testProxy1() {
- /** @type {{schema: import('json-schema').Schema, tests: {error: boolean, value?: import('json-schema').Value, action: (value: import('core').SafeAny) => void}[]}[]} */
- const data = [
- // Object tests
- {
- schema: {
- type: 'object',
- required: ['test'],
- additionalProperties: false,
- properties: {
- test: {
- type: 'string',
- default: 'default'
- }
- }
- },
- tests: [
- {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }},
- {error: true, value: {test: 'default'}, action: (value) => { value.test = null; }},
- {error: true, value: {test: 'default'}, action: (value) => { delete value.test; }},
- {error: true, value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }},
- {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }}
- ]
- },
- {
- schema: {
- type: 'object',
- required: ['test'],
- additionalProperties: true,
- properties: {
- test: {
- type: 'string',
- default: 'default'
- }
- }
- },
- tests: [
- {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }},
- {error: true, value: {test: 'default'}, action: (value) => { value.test = null; }},
- {error: true, value: {test: 'default'}, action: (value) => { delete value.test; }},
- {error: false, value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }},
- {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }}
- ]
- },
- {
- schema: {
- type: 'object',
- required: ['test1'],
- additionalProperties: false,
- properties: {
- test1: {
- type: 'object',
- required: ['test2'],
- additionalProperties: false,
- properties: {
- test2: {
- type: 'object',
- required: ['test3'],
- additionalProperties: false,
- properties: {
- test3: {
- type: 'string',
- default: 'default'
- }
- }
- }
- }
- }
- }
- },
- tests: [
- {error: false, action: (value) => { value.test1.test2.test3 = 'string'; }},
- {error: true, action: (value) => { value.test1.test2.test3 = null; }},
- {error: true, action: (value) => { delete value.test1.test2.test3; }},
- {error: true, action: (value) => { value.test1.test2 = null; }},
- {error: true, action: (value) => { value.test1 = null; }},
- {error: true, action: (value) => { value.test4 = 'string'; }},
- {error: false, action: (value) => { delete value.test4; }}
- ]
- },
-
- // Array tests
- {
- schema: {
- type: 'array',
- items: {
- type: 'string',
- default: 'default'
- }
- },
- tests: [
- {error: false, value: ['default'], action: (value) => { value[0] = 'string'; }},
- {error: true, value: ['default'], action: (value) => { value[0] = null; }},
- {error: false, value: ['default'], action: (value) => { delete value[0]; }},
- {error: false, value: ['default'], action: (value) => { value[1] = 'string'; }},
- {error: false, value: ['default'], action: (value) => {
- value[1] = 'string';
- if (value.length !== 2) { throw new Error(`Invalid length; expected=2; actual=${value.length}`); }
- if (typeof value.push !== 'function') { throw new Error(`Invalid push; expected=function; actual=${typeof value.push}`); }
- }}
- ]
- },
-
- // Reference tests
- {
- schema: {
- definitions: {
- example: {
- type: 'object',
- additionalProperties: false,
- properties: {
- test: {
- $ref: '#/definitions/example'
- }
- }
- }
- },
- $ref: '#/definitions/example'
- },
- tests: [
- {error: false, value: {}, action: (value) => { value.test = {}; }},
- {error: false, value: {}, action: (value) => { value.test = {}; value.test.test = {}; }},
- {error: false, value: {}, action: (value) => { value.test = {test: {}}; }},
- {error: true, value: {}, action: (value) => { value.test = null; }},
- {error: true, value: {}, action: (value) => { value.test = 'string'; }},
- {error: true, value: {}, action: (value) => { value.test = {}; value.test.test = 'string'; }},
- {error: true, value: {}, action: (value) => { value.test = {test: 'string'}; }}
- ]
- }
- ];
-
- for (const {schema, tests} of data) {
- for (let {error, value, action} of tests) {
- if (typeof value === 'undefined') { value = getValidValueOrDefault(schema, void 0); }
- value = clone(value);
- assert.ok(schemaValidate(schema, value));
- const valueProxy = createProxy(schema, value);
- if (error) {
- assert.throws(() => action(valueProxy));
- } else {
- assert.doesNotThrow(() => action(valueProxy));
- }
- }
- }
-}
-
-
-/** */
-function main() {
- testValidate1();
- testValidate2();
- testGetValidValueOrDefault1();
- testProxy1();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-manifest.js b/test/test-manifest.js
deleted file mode 100644
index 32a498e1..00000000
--- a/test/test-manifest.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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/>.
- */
-
-const fs = require('fs');
-const path = require('path');
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {ManifestUtil} = require('../dev/manifest-util');
-
-
-/**
- * @returns {string}
- */
-function loadManifestString() {
- const manifestPath = path.join(__dirname, '..', 'ext', 'manifest.json');
- return fs.readFileSync(manifestPath, {encoding: 'utf8'});
-}
-
-/** */
-function validateManifest() {
- const manifestUtil = new ManifestUtil();
- const manifest1 = loadManifestString();
- const manifest2 = ManifestUtil.createManifestString(manifestUtil.getManifest());
- assert.strictEqual(manifest1, manifest2, 'Manifest data does not match.');
-}
-
-
-/** */
-function main() {
- validateManifest();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-object-property-accessor.js b/test/test-object-property-accessor.js
deleted file mode 100644
index 8826d6a9..00000000
--- a/test/test-object-property-accessor.js
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-const vm = new VM({});
-vm.execute('js/general/object-property-accessor.js');
-/** @type {typeof ObjectPropertyAccessor} */
-const ObjectPropertyAccessor2 = vm.getSingle('ObjectPropertyAccessor');
-
-
-/**
- * @returns {import('core').UnknownObject}
- */
-function createTestObject() {
- return {
- 0: null,
- value1: {
- value2: {},
- value3: [],
- value4: null
- },
- value5: [
- {},
- [],
- null
- ]
- };
-}
-
-
-/** */
-function testGet1() {
- /** @type {[pathArray: (string|number)[], getExpected: (object: import('core').SafeAny) => unknown][]} */
- const data = [
- [[], (object) => object],
- [['0'], (object) => object['0']],
- [['value1'], (object) => object.value1],
- [['value1', 'value2'], (object) => object.value1.value2],
- [['value1', 'value3'], (object) => object.value1.value3],
- [['value1', 'value4'], (object) => object.value1.value4],
- [['value5'], (object) => object.value5],
- [['value5', 0], (object) => object.value5[0]],
- [['value5', 1], (object) => object.value5[1]],
- [['value5', 2], (object) => object.value5[2]]
- ];
-
- for (const [pathArray, getExpected] of data) {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
- const expected = getExpected(object);
-
- assert.strictEqual(accessor.get(pathArray), expected);
- }
-}
-
-/** */
-function testGet2() {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
-
- /** @type {[pathArray: (string|number)[], message: string][]} */
- const data = [
- [[0], 'Invalid path: [0]'],
- [['0', 'invalid'], 'Invalid path: ["0"].invalid'],
- [['invalid'], 'Invalid path: invalid'],
- [['value1', 'invalid'], 'Invalid path: value1.invalid'],
- [['value1', 'value2', 'invalid'], 'Invalid path: value1.value2.invalid'],
- [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'],
- [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'],
- [['value1', 'value3', 0], 'Invalid path: value1.value3[0]'],
- [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'],
- [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'],
- [['value5', 'length'], 'Invalid path: value5.length'],
- [['value5', 0, 'invalid'], 'Invalid path: value5[0].invalid'],
- [['value5', 0, 0], 'Invalid path: value5[0][0]'],
- [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'],
- [['value5', 1, 0], 'Invalid path: value5[1][0]'],
- [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'],
- [['value5', 2, 0], 'Invalid path: value5[2][0]'],
- [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'],
- [['value5', 2.5], 'Invalid index']
- ];
-
- for (const [pathArray, message] of data) {
- assert.throws(() => accessor.get(pathArray), {message});
- }
-}
-
-
-/** */
-function testSet1() {
- const testValue = {};
- /** @type {(string|number)[][]} */
- const data = [
- ['0'],
- ['value1', 'value2'],
- ['value1', 'value3'],
- ['value1', 'value4'],
- ['value1'],
- ['value5', 0],
- ['value5', 1],
- ['value5', 2],
- ['value5']
- ];
-
- for (const pathArray of data) {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
-
- accessor.set(pathArray, testValue);
- assert.strictEqual(accessor.get(pathArray), testValue);
- }
-}
-
-/** */
-function testSet2() {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
-
- const testValue = {};
- /** @type {[pathArray: (string|number)[], message: string][]} */
- const data = [
- [[], 'Invalid path'],
- [[0], 'Invalid path: [0]'],
- [['0', 'invalid'], 'Invalid path: ["0"].invalid'],
- [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'],
- [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'],
- [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'],
- [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'],
- [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'],
- [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'],
- [['value5', 2, 0], 'Invalid path: value5[2][0]'],
- [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'],
- [['value5', 2.5], 'Invalid index']
- ];
-
- for (const [pathArray, message] of data) {
- assert.throws(() => accessor.set(pathArray, testValue), {message});
- }
-}
-
-
-/** */
-function testDelete1() {
- /**
- * @param {unknown} object
- * @param {string} property
- * @returns {boolean}
- */
- const hasOwn = (object, property) => Object.prototype.hasOwnProperty.call(object, property);
-
- /** @type {[pathArray: (string|number)[], validate: (object: import('core').SafeAny) => boolean][]} */
- const data = [
- [['0'], (object) => !hasOwn(object, '0')],
- [['value1', 'value2'], (object) => !hasOwn(object.value1, 'value2')],
- [['value1', 'value3'], (object) => !hasOwn(object.value1, 'value3')],
- [['value1', 'value4'], (object) => !hasOwn(object.value1, 'value4')],
- [['value1'], (object) => !hasOwn(object, 'value1')],
- [['value5'], (object) => !hasOwn(object, 'value5')]
- ];
-
- for (const [pathArray, validate] of data) {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
-
- accessor.delete(pathArray);
- assert.ok(validate(object));
- }
-}
-
-/** */
-function testDelete2() {
- /** @type {[pathArray: (string|number)[], message: string][]} */
- const data = [
- [[], 'Invalid path'],
- [[0], 'Invalid path: [0]'],
- [['0', 'invalid'], 'Invalid path: ["0"].invalid'],
- [['value1', 'value2', 0], 'Invalid path: value1.value2[0]'],
- [['value1', 'value3', 'invalid'], 'Invalid path: value1.value3.invalid'],
- [['value1', 'value4', 'invalid'], 'Invalid path: value1.value4.invalid'],
- [['value1', 'value4', 0], 'Invalid path: value1.value4[0]'],
- [['value5', 1, 'invalid'], 'Invalid path: value5[1].invalid'],
- [['value5', 2, 'invalid'], 'Invalid path: value5[2].invalid'],
- [['value5', 2, 0], 'Invalid path: value5[2][0]'],
- [['value5', 2, 0, 'invalid'], 'Invalid path: value5[2][0]'],
- [['value5', 2.5], 'Invalid index'],
- [['value5', 0], 'Invalid type'],
- [['value5', 1], 'Invalid type'],
- [['value5', 2], 'Invalid type']
- ];
-
- for (const [pathArray, message] of data) {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
-
- assert.throws(() => accessor.delete(pathArray), {message});
- }
-}
-
-
-/** */
-function testSwap1() {
- /** @type {[pathArray: (string|number)[], compareValues: boolean][]} */
- const data = [
- [['0'], true],
- [['value1', 'value2'], true],
- [['value1', 'value3'], true],
- [['value1', 'value4'], true],
- [['value1'], false],
- [['value5', 0], true],
- [['value5', 1], true],
- [['value5', 2], true],
- [['value5'], false]
- ];
-
- for (const [pathArray1, compareValues1] of data) {
- for (const [pathArray2, compareValues2] of data) {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
-
- const value1a = accessor.get(pathArray1);
- const value2a = accessor.get(pathArray2);
-
- accessor.swap(pathArray1, pathArray2);
-
- if (!compareValues1 || !compareValues2) { continue; }
-
- const value1b = accessor.get(pathArray1);
- const value2b = accessor.get(pathArray2);
-
- assert.deepStrictEqual(value1a, value2b);
- assert.deepStrictEqual(value2a, value1b);
- }
- }
-}
-
-/** */
-function testSwap2() {
- /** @type {[pathArray1: (string|number)[], pathArray2: (string|number)[], checkRevert: boolean, message: string][]} */
- const data = [
- [[], [], false, 'Invalid path 1'],
- [['0'], [], false, 'Invalid path 2'],
- [[], ['0'], false, 'Invalid path 1'],
- [[0], ['0'], false, 'Invalid path 1: [0]'],
- [['0'], [0], false, 'Invalid path 2: [0]']
- ];
-
- for (const [pathArray1, pathArray2, checkRevert, message] of data) {
- const object = createTestObject();
- const accessor = new ObjectPropertyAccessor2(object);
-
- let value1a;
- let value2a;
- if (checkRevert) {
- try {
- value1a = accessor.get(pathArray1);
- value2a = accessor.get(pathArray2);
- } catch (e) {
- // NOP
- }
- }
-
- assert.throws(() => accessor.swap(pathArray1, pathArray2), {message});
-
- if (!checkRevert) { continue; }
-
- const value1b = accessor.get(pathArray1);
- const value2b = accessor.get(pathArray2);
-
- assert.deepStrictEqual(value1a, value1b);
- assert.deepStrictEqual(value2a, value2b);
- }
-}
-
-
-/** */
-function testGetPathString1() {
- /** @type {[pathArray: (string|number)[], expected: string][]} */
- const data = [
- [[], ''],
- [[0], '[0]'],
- [['escape\\'], '["escape\\\\"]'],
- [['\'quote\''], '["\'quote\'"]'],
- [['"quote"'], '["\\"quote\\""]'],
- [['part1', 'part2'], 'part1.part2'],
- [['part1', 'part2', 3], 'part1.part2[3]'],
- [['part1', 'part2', '3'], 'part1.part2["3"]'],
- [['part1', 'part2', '3part'], 'part1.part2["3part"]'],
- [['part1', 'part2', '3part', 'part4'], 'part1.part2["3part"].part4'],
- [['part1', 'part2', '3part', '4part'], 'part1.part2["3part"]["4part"]']
- ];
-
- for (const [pathArray, expected] of data) {
- assert.strictEqual(ObjectPropertyAccessor2.getPathString(pathArray), expected);
- }
-}
-
-/** */
-function testGetPathString2() {
- /** @type {[pathArray: unknown[], message: string][]} */
- const data = [
- [[1.5], 'Invalid index'],
- [[null], 'Invalid type: object']
- ];
-
- for (const [pathArray, message] of data) {
- // @ts-ignore - Throwing is expected
- assert.throws(() => ObjectPropertyAccessor2.getPathString(pathArray), {message});
- }
-}
-
-
-/** */
-function testGetPathArray1() {
- /** @type {[pathString: string, pathArray: (string|number)[]][]} */
- const data = [
- ['', []],
- ['[0]', [0]],
- ['["escape\\\\"]', ['escape\\']],
- ['["\'quote\'"]', ['\'quote\'']],
- ['["\\"quote\\""]', ['"quote"']],
- ['part1.part2', ['part1', 'part2']],
- ['part1.part2[3]', ['part1', 'part2', 3]],
- ['part1.part2["3"]', ['part1', 'part2', '3']],
- ['part1.part2[\'3\']', ['part1', 'part2', '3']],
- ['part1.part2["3part"]', ['part1', 'part2', '3part']],
- ['part1.part2[\'3part\']', ['part1', 'part2', '3part']],
- ['part1.part2["3part"].part4', ['part1', 'part2', '3part', 'part4']],
- ['part1.part2[\'3part\'].part4', ['part1', 'part2', '3part', 'part4']],
- ['part1.part2["3part"]["4part"]', ['part1', 'part2', '3part', '4part']],
- ['part1.part2[\'3part\'][\'4part\']', ['part1', 'part2', '3part', '4part']]
- ];
-
- for (const [pathString, expected] of data) {
- // @ts-ignore
- vm.assert.deepStrictEqual(ObjectPropertyAccessor2.getPathArray(pathString), expected);
- }
-}
-
-/** */
-function testGetPathArray2() {
- /** @type {[pathString: string, message: string][]} */
- const data = [
- ['?', 'Unexpected character: ?'],
- ['.', 'Unexpected character: .'],
- ['0', 'Unexpected character: 0'],
- ['part1.[0]', 'Unexpected character: ['],
- ['part1?', 'Unexpected character: ?'],
- ['[part1]', 'Unexpected character: p'],
- ['[0a]', 'Unexpected character: a'],
- ['["part1"x]', 'Unexpected character: x'],
- ['[\'part1\'x]', 'Unexpected character: x'],
- ['["part1"]x', 'Unexpected character: x'],
- ['[\'part1\']x', 'Unexpected character: x'],
- ['part1..part2', 'Unexpected character: .'],
-
- ['[', 'Path not terminated correctly'],
- ['part1.', 'Path not terminated correctly'],
- ['part1[', 'Path not terminated correctly'],
- ['part1["', 'Path not terminated correctly'],
- ['part1[\'', 'Path not terminated correctly'],
- ['part1[""', 'Path not terminated correctly'],
- ['part1[\'\'', 'Path not terminated correctly'],
- ['part1[0', 'Path not terminated correctly'],
- ['part1[0].', 'Path not terminated correctly']
- ];
-
- for (const [pathString, message] of data) {
- assert.throws(() => ObjectPropertyAccessor2.getPathArray(pathString), {message});
- }
-}
-
-
-/** */
-function testHasProperty() {
- /** @type {[object: unknown, property: unknown, expected: boolean][]} */
- const data = [
- [{}, 'invalid', false],
- [{}, 0, false],
- [{valid: 0}, 'valid', true],
- [{null: 0}, null, false],
- [[], 'invalid', false],
- [[], 0, false],
- [[0], 0, true],
- [[0], null, false],
- ['string', 0, false],
- ['string', 'length', false],
- ['string', null, false]
- ];
-
- for (const [object, property, expected] of data) {
- // @ts-ignore
- assert.strictEqual(ObjectPropertyAccessor2.hasProperty(object, property), expected);
- }
-}
-
-/** */
-function testIsValidPropertyType() {
- /** @type {[object: unknown, property: unknown, expected: boolean][]} */
- const data = [
- [{}, 'invalid', true],
- [{}, 0, false],
- [{valid: 0}, 'valid', true],
- [{null: 0}, null, false],
- [[], 'invalid', false],
- [[], 0, true],
- [[0], 0, true],
- [[0], null, false],
- ['string', 0, false],
- ['string', 'length', false],
- ['string', null, false]
- ];
-
- for (const [object, property, expected] of data) {
- // @ts-ignore
- assert.strictEqual(ObjectPropertyAccessor2.isValidPropertyType(object, property), expected);
- }
-}
-
-
-/** */
-function main() {
- testGet1();
- testGet2();
- testSet1();
- testSet2();
- testDelete1();
- testDelete2();
- testSwap1();
- testSwap2();
- testGetPathString1();
- testGetPathString2();
- testGetPathArray1();
- testGetPathArray2();
- testHasProperty();
- testIsValidPropertyType();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-profile-conditions-util.js b/test/test-profile-conditions-util.js
deleted file mode 100644
index 2e6f751f..00000000
--- a/test/test-profile-conditions-util.js
+++ /dev/null
@@ -1,1136 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-
-const vm = new VM({});
-vm.execute([
- 'js/core.js',
- 'js/core/extension-error.js',
- 'js/general/cache-map.js',
- 'js/data/json-schema.js',
- 'js/background/profile-conditions-util.js'
-]);
-/** @type {typeof ProfileConditionsUtil} */
-const ProfileConditionsUtil2 = vm.getSingle('ProfileConditionsUtil');
-
-
-/** */
-function testNormalizeContext() {
- /** @type {{context: import('settings').OptionsContext, expected: import('profile-conditions-util').NormalizedOptionsContext}[]} */
- const data = [
- // Empty
- {
- context: {index: 0},
- expected: {index: 0, flags: []}
- },
-
- // Domain normalization
- {
- context: {depth: 0, url: ''},
- expected: {depth: 0, url: '', flags: []}
- },
- {
- context: {depth: 0, url: 'http://example.com/'},
- expected: {depth: 0, url: 'http://example.com/', domain: 'example.com', flags: []}
- },
- {
- context: {depth: 0, url: 'http://example.com:1234/'},
- expected: {depth: 0, url: 'http://example.com:1234/', domain: 'example.com', flags: []}
- },
- {
- context: {depth: 0, url: 'http://user@example.com:1234/'},
- expected: {depth: 0, url: 'http://user@example.com:1234/', domain: 'example.com', flags: []}
- }
- ];
-
- for (const {context, expected} of data) {
- const profileConditionsUtil = new ProfileConditionsUtil2();
- const actual = profileConditionsUtil.normalizeContext(context);
- vm.assert.deepStrictEqual(actual, expected);
- }
-}
-
-/** */
-function testSchemas() {
- /** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */
- const data = [
- // Empty
- {
- conditionGroups: [],
- expectedSchema: {},
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {conditions: []}
- ],
- expectedSchema: {},
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {conditions: []},
- {conditions: []}
- ],
- expectedSchema: {},
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}}
- ]
- },
-
- // popupLevel tests
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'equal',
- value: '0'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- depth: {const: 0}
- },
- required: ['depth']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: false, context: {depth: 1, url: 'http://example.com/'}},
- {expected: false, context: {depth: -1, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'notEqual',
- value: '0'
- }
- ]
- }
- ],
- expectedSchema: {
- not: {
- anyOf: [
- {
- properties: {
- depth: {const: 0}
- },
- required: ['depth']
- }
- ]
- }
- },
- inputs: [
- {expected: false, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 1, url: 'http://example.com/'}},
- {expected: true, context: {depth: -1, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'lessThan',
- value: '0'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- depth: {
- type: 'number',
- exclusiveMaximum: 0
- }
- },
- required: ['depth']
- },
- inputs: [
- {expected: false, context: {depth: 0, url: 'http://example.com/'}},
- {expected: false, context: {depth: 1, url: 'http://example.com/'}},
- {expected: true, context: {depth: -1, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'greaterThan',
- value: '0'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- depth: {
- type: 'number',
- exclusiveMinimum: 0
- }
- },
- required: ['depth']
- },
- inputs: [
- {expected: false, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 1, url: 'http://example.com/'}},
- {expected: false, context: {depth: -1, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'lessThanOrEqual',
- value: '0'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- depth: {
- type: 'number',
- maximum: 0
- }
- },
- required: ['depth']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: false, context: {depth: 1, url: 'http://example.com/'}},
- {expected: true, context: {depth: -1, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'greaterThanOrEqual',
- value: '0'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- depth: {
- type: 'number',
- minimum: 0
- }
- },
- required: ['depth']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 1, url: 'http://example.com/'}},
- {expected: false, context: {depth: -1, url: 'http://example.com/'}}
- ]
- },
-
- // url tests
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'url',
- operator: 'matchDomain',
- value: 'example.com'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- domain: {
- oneOf: [
- {const: 'example.com'}
- ]
- }
- },
- required: ['domain']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: false, context: {depth: 0, url: 'http://example1.com/'}},
- {expected: false, context: {depth: 0, url: 'http://example2.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example.com:1234/'}},
- {expected: true, context: {depth: 0, url: 'http://user@example.com:1234/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'url',
- operator: 'matchDomain',
- value: 'example.com, example1.com, example2.com'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- domain: {
- oneOf: [
- {const: 'example.com'},
- {const: 'example1.com'},
- {const: 'example2.com'}
- ]
- }
- },
- required: ['domain']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example1.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example2.com/'}},
- {expected: false, context: {depth: 0, url: 'http://example3.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example.com:1234/'}},
- {expected: true, context: {depth: 0, url: 'http://user@example.com:1234/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'url',
- operator: 'matchRegExp',
- value: '^http://example\\d?\\.com/[\\w\\W]*$'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- url: {
- type: 'string',
- pattern: '^http://example\\d?\\.com/[\\w\\W]*$',
- patternFlags: 'i'
- }
- },
- required: ['url']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example1.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example2.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example3.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example.com/example'}},
- {expected: false, context: {depth: 0, url: 'http://example.com:1234/'}},
- {expected: false, context: {depth: 0, url: 'http://user@example.com:1234/'}},
- {expected: false, context: {depth: 0, url: 'http://example-1.com/'}}
- ]
- },
-
- // modifierKeys tests
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'are',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- modifierKeys: {
- type: 'array',
- maxItems: 0,
- minItems: 0
- }
- },
- required: ['modifierKeys']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'are',
- value: 'alt, shift'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- modifierKeys: {
- type: 'array',
- maxItems: 2,
- minItems: 2,
- allOf: [
- {contains: {const: 'alt'}},
- {contains: {const: 'shift'}}
- ]
- }
- },
- required: ['modifierKeys']
- },
- inputs: [
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'areNot',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- not: {
- anyOf: [
- {
- properties: {
- modifierKeys: {
- type: 'array',
- maxItems: 0,
- minItems: 0
- }
- },
- required: ['modifierKeys']
- }
- ]
- }
- },
- inputs: [
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'areNot',
- value: 'alt, shift'
- }
- ]
- }
- ],
- expectedSchema: {
- not: {
- anyOf: [
- {
- properties: {
- modifierKeys: {
- type: 'array',
- maxItems: 2,
- minItems: 2,
- allOf: [
- {contains: {const: 'alt'}},
- {contains: {const: 'shift'}}
- ]
- }
- },
- required: ['modifierKeys']
- }
- ]
- }
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'include',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- modifierKeys: {
- type: 'array',
- minItems: 0
- }
- },
- required: ['modifierKeys']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'include',
- value: 'alt, shift'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- modifierKeys: {
- type: 'array',
- minItems: 2,
- allOf: [
- {contains: {const: 'alt'}},
- {contains: {const: 'shift'}}
- ]
- }
- },
- required: ['modifierKeys']
- },
- inputs: [
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'notInclude',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- modifierKeys: {
- type: 'array'
- }
- },
- required: ['modifierKeys']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'modifierKeys',
- operator: 'notInclude',
- value: 'alt, shift'
- }
- ]
- }
- ],
- expectedSchema: {
- properties: {
- modifierKeys: {
- type: 'array',
- not: {
- anyOf: [
- {contains: {const: 'alt'}},
- {contains: {const: 'shift'}}
- ]
- }
- }
- },
- required: ['modifierKeys']
- },
- inputs: [
- {expected: true, context: {depth: 0, url: 'http://example.com/', modifierKeys: []}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt']}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift']}},
- {expected: false, context: {depth: 0, url: 'http://example.com/', modifierKeys: ['alt', 'shift', 'ctrl']}}
- ]
- },
-
- // flags tests
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'are',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array',
- maxItems: 0,
- minItems: 0
- }
- }
- },
- inputs: [
- {expected: true, context: {depth: 0, url: ''}},
- {expected: true, context: {depth: 0, url: '', flags: []}},
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'are',
- value: 'clipboard, test2'
- }
- ]
- }
- ],
- expectedSchema: {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array',
- maxItems: 2,
- minItems: 2,
- allOf: [
- {contains: {const: 'clipboard'}},
- {contains: {const: 'test2'}}
- ]
- }
- }
- },
- inputs: [
- {expected: false, context: {depth: 0, url: ''}},
- {expected: false, context: {depth: 0, url: '', flags: []}},
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'areNot',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- not: {
- anyOf: [
- {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array',
- maxItems: 0,
- minItems: 0
- }
- }
- }
- ]
- }
- },
- inputs: [
- {expected: false, context: {depth: 0, url: ''}},
- {expected: false, context: {depth: 0, url: '', flags: []}},
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'areNot',
- value: 'clipboard, test2'
- }
- ]
- }
- ],
- expectedSchema: {
- not: {
- anyOf: [
- {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array',
- maxItems: 2,
- minItems: 2,
- allOf: [
- {contains: {const: 'clipboard'}},
- {contains: {const: 'test2'}}
- ]
- }
- }
- }
- ]
- }
- },
- inputs: [
- {expected: true, context: {depth: 0, url: ''}},
- {expected: true, context: {depth: 0, url: '', flags: []}},
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'include',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array',
- minItems: 0
- }
- }
- },
- inputs: [
- {expected: true, context: {depth: 0, url: ''}},
- {expected: true, context: {depth: 0, url: '', flags: []}},
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'include',
- value: 'clipboard, test2'
- }
- ]
- }
- ],
- expectedSchema: {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array',
- minItems: 2,
- allOf: [
- {contains: {const: 'clipboard'}},
- {contains: {const: 'test2'}}
- ]
- }
- }
- },
- inputs: [
- {expected: false, context: {depth: 0, url: ''}},
- {expected: false, context: {depth: 0, url: '', flags: []}},
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'notInclude',
- value: ''
- }
- ]
- }
- ],
- expectedSchema: {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array'
- }
- }
- },
- inputs: [
- {expected: true, context: {depth: 0, url: ''}},
- {expected: true, context: {depth: 0, url: '', flags: []}},
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: true, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'flags',
- operator: 'notInclude',
- value: 'clipboard, test2'
- }
- ]
- }
- ],
- expectedSchema: {
- required: ['flags'],
- properties: {
- flags: {
- type: 'array',
- not: {
- anyOf: [
- {contains: {const: 'clipboard'}},
- {contains: {const: 'test2'}}
- ]
- }
- }
- }
- },
- inputs: [
- {expected: true, context: {depth: 0, url: ''}},
- {expected: true, context: {depth: 0, url: '', flags: []}},
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2']}},
- // @ts-ignore - Ignore type for string flag for testing purposes
- {expected: false, context: {depth: 0, url: '', flags: ['clipboard', 'test2', 'test3']}}
- ]
- },
-
- // Multiple conditions tests
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'greaterThan',
- value: '0'
- },
- {
- type: 'popupLevel',
- operator: 'lessThan',
- value: '3'
- }
- ]
- }
- ],
- expectedSchema: {
- allOf: [
- {
- properties: {
- depth: {
- type: 'number',
- exclusiveMinimum: 0
- }
- },
- required: ['depth']
- },
- {
- properties: {
- depth: {
- type: 'number',
- exclusiveMaximum: 3
- }
- },
- required: ['depth']
- }
- ]
- },
- inputs: [
- {expected: false, context: {depth: -2, url: 'http://example.com/'}},
- {expected: false, context: {depth: -1, url: 'http://example.com/'}},
- {expected: false, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 1, url: 'http://example.com/'}},
- {expected: true, context: {depth: 2, url: 'http://example.com/'}},
- {expected: false, context: {depth: 3, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'greaterThan',
- value: '0'
- },
- {
- type: 'popupLevel',
- operator: 'lessThan',
- value: '3'
- }
- ]
- },
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'equal',
- value: '0'
- }
- ]
- }
- ],
- expectedSchema: {
- anyOf: [
- {
- allOf: [
- {
- properties: {
- depth: {
- type: 'number',
- exclusiveMinimum: 0
- }
- },
- required: ['depth']
- },
- {
- properties: {
- depth: {
- type: 'number',
- exclusiveMaximum: 3
- }
- },
- required: ['depth']
- }
- ]
- },
- {
- properties: {
- depth: {const: 0}
- },
- required: ['depth']
- }
- ]
- },
- inputs: [
- {expected: false, context: {depth: -2, url: 'http://example.com/'}},
- {expected: false, context: {depth: -1, url: 'http://example.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 1, url: 'http://example.com/'}},
- {expected: true, context: {depth: 2, url: 'http://example.com/'}},
- {expected: false, context: {depth: 3, url: 'http://example.com/'}}
- ]
- },
- {
- conditionGroups: [
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'greaterThan',
- value: '0'
- },
- {
- type: 'popupLevel',
- operator: 'lessThan',
- value: '3'
- }
- ]
- },
- {
- conditions: [
- {
- type: 'popupLevel',
- operator: 'lessThanOrEqual',
- value: '0'
- },
- {
- type: 'popupLevel',
- operator: 'greaterThanOrEqual',
- value: '-1'
- }
- ]
- }
- ],
- expectedSchema: {
- anyOf: [
- {
- allOf: [
- {
- properties: {
- depth: {
- type: 'number',
- exclusiveMinimum: 0
- }
- },
- required: ['depth']
- },
- {
- properties: {
- depth: {
- type: 'number',
- exclusiveMaximum: 3
- }
- },
- required: ['depth']
- }
- ]
- },
- {
- allOf: [
- {
- properties: {
- depth: {
- type: 'number',
- maximum: 0
- }
- },
- required: ['depth']
- },
- {
- properties: {
- depth: {
- type: 'number',
- minimum: -1
- }
- },
- required: ['depth']
- }
- ]
- }
- ]
- },
- inputs: [
- {expected: false, context: {depth: -2, url: 'http://example.com/'}},
- {expected: true, context: {depth: -1, url: 'http://example.com/'}},
- {expected: true, context: {depth: 0, url: 'http://example.com/'}},
- {expected: true, context: {depth: 1, url: 'http://example.com/'}},
- {expected: true, context: {depth: 2, url: 'http://example.com/'}},
- {expected: false, context: {depth: 3, url: 'http://example.com/'}}
- ]
- }
- ];
-
- for (const {conditionGroups, expectedSchema, inputs} of data) {
- const profileConditionsUtil = new ProfileConditionsUtil2();
- const schema = profileConditionsUtil.createSchema(conditionGroups);
- if (typeof expectedSchema !== 'undefined') {
- vm.assert.deepStrictEqual(schema.schema, expectedSchema);
- }
- if (Array.isArray(inputs)) {
- for (const {expected, context} of inputs) {
- const normalizedContext = profileConditionsUtil.normalizeContext(context);
- const actual = schema.isValid(normalizedContext);
- assert.strictEqual(actual, expected);
- }
- }
- }
-}
-
-
-/** */
-function main() {
- testNormalizeContext();
- testSchemas();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-text-source-map.js b/test/test-text-source-map.js
deleted file mode 100644
index 834a3d07..00000000
--- a/test/test-text-source-map.js
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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/>.
- */
-
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {VM} = require('../dev/vm');
-
-const vm = new VM();
-vm.execute(['js/general/text-source-map.js']);
-/** @type {typeof TextSourceMap} */
-const TextSourceMap2 = vm.getSingle('TextSourceMap');
-
-
-/** */
-function testSource() {
- const data = [
- ['source1'],
- ['source2'],
- ['source3']
- ];
-
- for (const [source] of data) {
- const sourceMap = new TextSourceMap2(source);
- assert.strictEqual(source, sourceMap.source);
- }
-}
-
-/** */
-function testEquals() {
- /** @type {[args1: [source1: string, mapping1: ?(number[])], args2: [source2: string, mapping2: ?(number[])], expectedEquals: boolean][]} */
- const data = [
- [['source1', null], ['source1', null], true],
- [['source2', null], ['source2', null], true],
- [['source3', null], ['source3', null], true],
-
- [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source1', null], true],
- [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source2', null], true],
- [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source3', null], true],
-
- [['source1', null], ['source1', [1, 1, 1, 1, 1, 1, 1]], true],
- [['source2', null], ['source2', [1, 1, 1, 1, 1, 1, 1]], true],
- [['source3', null], ['source3', [1, 1, 1, 1, 1, 1, 1]], true],
-
- [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source1', [1, 1, 1, 1, 1, 1, 1]], true],
- [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source2', [1, 1, 1, 1, 1, 1, 1]], true],
- [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source3', [1, 1, 1, 1, 1, 1, 1]], true],
-
- [['source1', [1, 2, 1, 3]], ['source1', [1, 2, 1, 3]], true],
- [['source2', [1, 2, 1, 3]], ['source2', [1, 2, 1, 3]], true],
- [['source3', [1, 2, 1, 3]], ['source3', [1, 2, 1, 3]], true],
-
- [['source1', [1, 3, 1, 2]], ['source1', [1, 2, 1, 3]], false],
- [['source2', [1, 3, 1, 2]], ['source2', [1, 2, 1, 3]], false],
- [['source3', [1, 3, 1, 2]], ['source3', [1, 2, 1, 3]], false],
-
- [['source1', [1, 1, 1, 1, 1, 1, 1]], ['source4', [1, 1, 1, 1, 1, 1, 1]], false],
- [['source2', [1, 1, 1, 1, 1, 1, 1]], ['source5', [1, 1, 1, 1, 1, 1, 1]], false],
- [['source3', [1, 1, 1, 1, 1, 1, 1]], ['source6', [1, 1, 1, 1, 1, 1, 1]], false]
- ];
-
- for (const [[source1, mapping1], [source2, mapping2], expectedEquals] of data) {
- const sourceMap1 = new TextSourceMap2(source1, mapping1);
- const sourceMap2 = new TextSourceMap2(source2, mapping2);
- assert.ok(sourceMap1.equals(sourceMap1));
- assert.ok(sourceMap2.equals(sourceMap2));
- assert.strictEqual(sourceMap1.equals(sourceMap2), expectedEquals);
- }
-}
-
-/** */
-function testGetSourceLength() {
- /** @type {[args: [source: string, mapping: number[]], finalLength: number, expectedValue: number][]} */
- const data = [
- [['source', [1, 1, 1, 1, 1, 1]], 1, 1],
- [['source', [1, 1, 1, 1, 1, 1]], 2, 2],
- [['source', [1, 1, 1, 1, 1, 1]], 3, 3],
- [['source', [1, 1, 1, 1, 1, 1]], 4, 4],
- [['source', [1, 1, 1, 1, 1, 1]], 5, 5],
- [['source', [1, 1, 1, 1, 1, 1]], 6, 6],
-
- [['source', [2, 2, 2]], 1, 2],
- [['source', [2, 2, 2]], 2, 4],
- [['source', [2, 2, 2]], 3, 6],
-
- [['source', [3, 3]], 1, 3],
- [['source', [3, 3]], 2, 6],
-
- [['source', [6, 6]], 1, 6]
- ];
-
- for (const [[source, mapping], finalLength, expectedValue] of data) {
- const sourceMap = new TextSourceMap2(source, mapping);
- assert.strictEqual(sourceMap.getSourceLength(finalLength), expectedValue);
- }
-}
-
-/** */
-function testCombineInsert() {
- /** @type {[args: [source: string, mapping: ?(number[])], expectedArgs: [expectedSource: string, expectedMapping: ?(number[])], operations: [operation: string, arg1: number, arg2: number][]][]} */
- const data = [
- // No operations
- [
- ['source', null],
- ['source', [1, 1, 1, 1, 1, 1]],
- []
- ],
-
- // Combine
- [
- ['source', null],
- ['source', [3, 1, 1, 1]],
- [
- ['combine', 0, 2]
- ]
- ],
- [
- ['source', null],
- ['source', [1, 1, 1, 3]],
- [
- ['combine', 3, 2]
- ]
- ],
- [
- ['source', null],
- ['source', [3, 3]],
- [
- ['combine', 0, 2],
- ['combine', 1, 2]
- ]
- ],
- [
- ['source', null],
- ['source', [3, 3]],
- [
- ['combine', 3, 2],
- ['combine', 0, 2]
- ]
- ],
-
- // Insert
- [
- ['source', null],
- ['source', [0, 1, 1, 1, 1, 1, 1]],
- [
- ['insert', 0, 0]
- ]
- ],
- [
- ['source', null],
- ['source', [1, 1, 1, 1, 1, 1, 0]],
- [
- ['insert', 6, 0]
- ]
- ],
- [
- ['source', null],
- ['source', [0, 1, 1, 1, 1, 1, 1, 0]],
- [
- ['insert', 0, 0],
- ['insert', 7, 0]
- ]
- ],
- [
- ['source', null],
- ['source', [0, 1, 1, 1, 1, 1, 1, 0]],
- [
- ['insert', 6, 0],
- ['insert', 0, 0]
- ]
- ],
-
- // Mixed
- [
- ['source', null],
- ['source', [3, 0, 3]],
- [
- ['combine', 0, 2],
- ['insert', 1, 0],
- ['combine', 2, 2]
- ]
- ],
- [
- ['source', null],
- ['source', [3, 0, 3]],
- [
- ['combine', 0, 2],
- ['combine', 1, 2],
- ['insert', 1, 0]
- ]
- ],
- [
- ['source', null],
- ['source', [3, 0, 3]],
- [
- ['insert', 3, 0],
- ['combine', 0, 2],
- ['combine', 2, 2]
- ]
- ]
- ];
-
- for (const [[source, mapping], [expectedSource, expectedMapping], operations] of data) {
- const sourceMap = new TextSourceMap2(source, mapping);
- const expectedSourceMap = new TextSourceMap2(expectedSource, expectedMapping);
- for (const [operation, ...args] of operations) {
- switch (operation) {
- case 'combine':
- sourceMap.combine(...args);
- break;
- case 'insert':
- sourceMap.insert(...args);
- break;
- }
- }
- assert.ok(sourceMap.equals(expectedSourceMap));
- }
-}
-
-
-/** */
-function main() {
- testSource();
- testEquals();
- testGetSourceLength();
- testCombineInsert();
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-translator.js b/test/test-translator.js
deleted file mode 100644
index 0c84e0be..00000000
--- a/test/test-translator.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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/>.
- */
-
-const fs = require('fs');
-const path = require('path');
-const assert = require('assert');
-const {testMain} = require('../dev/util');
-const {TranslatorVM} = require('../dev/translator-vm');
-
-
-/**
- * @template T
- * @param {T} value
- * @returns {T}
- */
-function clone(value) {
- return JSON.parse(JSON.stringify(value));
-}
-
-
-/** */
-async function main() {
- const write = (process.argv[2] === '--write');
-
- const translatorVM = new TranslatorVM();
- const dictionaryDirectory = path.join(__dirname, 'data', 'dictionaries', 'valid-dictionary1');
- await translatorVM.prepare(dictionaryDirectory, 'Test Dictionary 2');
-
- const testInputsFilePath = path.join(__dirname, 'data', 'translator-test-inputs.json');
- const {optionsPresets, tests} = JSON.parse(fs.readFileSync(testInputsFilePath, {encoding: 'utf8'}));
-
- const testResults1FilePath = path.join(__dirname, 'data', 'translator-test-results.json');
- const expectedResults1 = JSON.parse(fs.readFileSync(testResults1FilePath, {encoding: 'utf8'}));
- const actualResults1 = [];
-
- const testResults2FilePath = path.join(__dirname, 'data', 'translator-test-results-note-data1.json');
- const expectedResults2 = JSON.parse(fs.readFileSync(testResults2FilePath, {encoding: 'utf8'}));
- const actualResults2 = [];
-
- for (let i = 0, ii = tests.length; i < ii; ++i) {
- const test = tests[i];
- const expected1 = expectedResults1[i];
- const expected2 = expectedResults2[i];
- switch (test.func) {
- case 'findTerms':
- {
- const {name, mode, text} = test;
- /** @type {import('translation').FindTermsOptions} */
- const options = translatorVM.buildOptions(optionsPresets, test.options);
- const {dictionaryEntries, originalTextLength} = clone(await translatorVM.translator.findTerms(mode, text, options));
- const noteDataList = mode !== 'simple' ? clone(dictionaryEntries.map((dictionaryEntry) => translatorVM.createTestAnkiNoteData(clone(dictionaryEntry), mode))) : null;
- actualResults1.push({name, originalTextLength, dictionaryEntries});
- actualResults2.push({name, noteDataList});
- if (!write) {
- assert.deepStrictEqual(originalTextLength, expected1.originalTextLength);
- assert.deepStrictEqual(dictionaryEntries, expected1.dictionaryEntries);
- assert.deepStrictEqual(noteDataList, expected2.noteDataList);
- }
- }
- break;
- case 'findKanji':
- {
- const {name, text} = test;
- /** @type {import('translation').FindKanjiOptions} */
- const options = translatorVM.buildOptions(optionsPresets, test.options);
- const dictionaryEntries = clone(await translatorVM.translator.findKanji(text, options));
- const noteDataList = clone(dictionaryEntries.map((dictionaryEntry) => translatorVM.createTestAnkiNoteData(clone(dictionaryEntry), 'split')));
- actualResults1.push({name, dictionaryEntries});
- actualResults2.push({name, noteDataList});
- if (!write) {
- assert.deepStrictEqual(dictionaryEntries, expected1.dictionaryEntries);
- assert.deepStrictEqual(noteDataList, expected2.noteDataList);
- }
- }
- break;
- }
- }
-
- if (write) {
- // Use 2 indent instead of 4 to save a bit of file size
- fs.writeFileSync(testResults1FilePath, JSON.stringify(actualResults1, null, 2), {encoding: 'utf8'});
- fs.writeFileSync(testResults2FilePath, JSON.stringify(actualResults2, null, 2), {encoding: 'utf8'});
- }
-}
-
-
-if (require.main === module) { testMain(main); }
diff --git a/test/test-workers.js b/test/test-workers.js
deleted file mode 100644
index 3de7ac48..00000000
--- a/test/test-workers.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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/>.
- */
-
-const fs = require('fs');
-const path = require('path');
-const {JSDOM} = require('jsdom');
-const {VM} = require('../dev/vm');
-const assert = require('assert');
-
-
-class StubClass {
- /** */
- prepare() {
- // NOP
- }
-}
-
-
-/**
- * @returns {import('core').SafeAny}
- */
-function loadEslint() {
- return JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.eslintrc.json'), {encoding: 'utf8'}));
-}
-
-/**
- * @param {string[]} scriptPaths
- * @returns {string[]}
- */
-function filterScriptPaths(scriptPaths) {
- const extDirName = 'ext';
- return scriptPaths.filter((src) => !src.startsWith('/lib/')).map((src) => `${extDirName}${src}`);
-}
-
-/**
- * @param {string} fileName
- * @returns {string[]}
- */
-function getAllHtmlScriptPaths(fileName) {
- const domSource = fs.readFileSync(fileName, {encoding: 'utf8'});
- const dom = new JSDOM(domSource);
- const {window} = dom;
- const {document} = window;
- try {
- const scripts = document.querySelectorAll('script');
- return [...scripts].map(({src}) => src);
- } finally {
- window.close();
- }
-}
-
-/**
- * @param {string[]} scripts
- */
-function convertBackgroundScriptsToServiceWorkerScripts(scripts) {
- // Use parse5-based SimpleDOMParser
- scripts.splice(0, 0, '/lib/parse5.js');
- const index = scripts.indexOf('/js/dom/native-simple-dom-parser.js');
- assert.ok(index >= 0);
- scripts[index] = '/js/dom/simple-dom-parser.js';
-}
-
-/**
- * @param {string} scriptPath
- * @param {import('core').UnknownObject} fields
- * @returns {string[]}
- */
-function getImportedScripts(scriptPath, fields) {
- /** @type {string[]} */
- const importedScripts = [];
-
- /**
- * @param {...string} scripts
- */
- const importScripts = (...scripts) => {
- importedScripts.push(...scripts);
- };
-
- const vm = new VM(Object.assign({importScripts}, fields));
- vm.context.self = vm.context;
- vm.execute([scriptPath]);
-
- return importedScripts;
-}
-
-/** */
-function testServiceWorker() {
- // Verify that sw.js scripts match background.html scripts
- const extDir = path.join(__dirname, '..', 'ext');
- const scripts = getAllHtmlScriptPaths(path.join(extDir, 'background.html'));
- convertBackgroundScriptsToServiceWorkerScripts(scripts);
- const importedScripts = getImportedScripts('sw.js', {});
- assert.deepStrictEqual(scripts, importedScripts);
-
- // Verify that eslint config lists files correctly
- const expectedSwRulesFiles = filterScriptPaths(scripts);
- const eslintConfig = loadEslint();
- const swRules = /** @type {import('core').SafeAny[]} */ (eslintConfig.overrides).find((item) => (
- typeof item.env === 'object' &&
- item.env !== null &&
- item.env.serviceworker === true
- ));
- assert.ok(typeof swRules !== 'undefined');
- assert.ok(Array.isArray(swRules.files));
- assert.deepStrictEqual(swRules.files, expectedSwRulesFiles);
-}
-
-/** */
-function testWorkers() {
- testWorker(
- 'js/language/dictionary-worker-main.js',
- {DictionaryWorkerHandler: StubClass}
- );
-}
-
-/**
- * @param {string} scriptPath
- * @param {import('core').UnknownObject} fields
- */
-function testWorker(scriptPath, fields) {
- // Get script paths
- const scripts = getImportedScripts(scriptPath, fields);
-
- // Verify that eslint config lists files correctly
- const expectedRulesFiles = filterScriptPaths(scripts);
- const expectedRulesFilesSet = new Set(expectedRulesFiles);
- const eslintConfig = loadEslint();
- const rules = /** @type {import('core').SafeAny[]} */ (eslintConfig.overrides).find((item) => (
- typeof item.env === 'object' &&
- item.env !== null &&
- item.env.worker === true
- ));
- assert.ok(typeof rules !== 'undefined');
- assert.ok(Array.isArray(rules.files));
- assert.deepStrictEqual(/** @type {import('core').SafeAny[]} */ (rules.files).filter((v) => expectedRulesFilesSet.has(v)), expectedRulesFiles);
-}
-
-
-/** */
-function main() {
- try {
- testServiceWorker();
- testWorkers();
- } catch (e) {
- console.error(e);
- process.exit(-1);
- return;
- }
- process.exit(0);
-}
-
-
-if (require.main === module) { main(); }