diff options
| -rw-r--r-- | test/test-all.js | 71 | ||||
| -rw-r--r-- | test/test-anki-note-builder.js | 322 | ||||
| -rw-r--r-- | test/test-cache-map.js | 137 | ||||
| -rw-r--r-- | test/test-core.js | 300 | ||||
| -rw-r--r-- | test/test-database.js | 982 | ||||
| -rw-r--r-- | test/test-document-util.js | 339 | ||||
| -rw-r--r-- | test/test-hotkey-util.js | 189 | ||||
| -rw-r--r-- | test/test-japanese-util.js | 915 | ||||
| -rw-r--r-- | test/test-json-schema.js | 1048 | ||||
| -rw-r--r-- | test/test-manifest.js | 49 | ||||
| -rw-r--r-- | test/test-object-property-accessor.js | 458 | ||||
| -rw-r--r-- | test/test-profile-conditions-util.js | 1136 | ||||
| -rw-r--r-- | test/test-text-source-map.js | 244 | ||||
| -rw-r--r-- | test/test-translator.js | 102 | ||||
| -rw-r--r-- | test/test-workers.js | 168 | 
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(); } |