summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-12-20 00:47:15 -0500
committerGitHub <noreply@github.com>2023-12-20 05:47:15 +0000
commit8b943cc97fab890085448122e7c13dd035d0e238 (patch)
treea7a749a44771c6a82b1b72bb35cc0c81d57ddb54 /test
parentb13fbd47941fc20cf623871396e34a6dfe9b4dba (diff)
JSON validation (#394)
* Set up JSON testing * Add schema validation * Use parseJson * Finish types * Disambiguate ext/json-schema from node dependency with the same name * Add support for specifying the jsconfig file * Don't expose types * Update types * Use dictionary map type * Fix types * Fix AJV warnings * Move types * Move anb rename file * Move common mocks * Simplify types
Diffstat (limited to 'test')
-rw-r--r--test/data/json.json174
-rw-r--r--test/data/translator-test-inputs.json12
-rw-r--r--test/fixtures/translator-test.js36
-rw-r--r--test/jsconfig.json4
-rw-r--r--test/json-schema.test.js22
-rw-r--r--test/json.test.js189
-rw-r--r--test/mocks/common.js52
-rw-r--r--test/options-util.test.js29
-rw-r--r--test/profile-conditions-util.test.js2
-rw-r--r--test/utilities/translator.js5
10 files changed, 444 insertions, 81 deletions
diff --git a/test/data/json.json b/test/data/json.json
new file mode 100644
index 00000000..6d806263
--- /dev/null
+++ b/test/data/json.json
@@ -0,0 +1,174 @@
+{
+ "files": [
+ {"path": "package.json", "ignore": true},
+ {"path": "package-lock.json", "ignore": true},
+ {"path": "jsconfig.json", "ignore": true},
+ {"path": ".stylelintrc.json", "ignore": true},
+ {"path": ".htmlvalidate.json", "ignore": true},
+ {"path": ".eslintrc.json", "ignore": true},
+ {"path": ".vscode/settings.json", "ignore": true},
+ {"path": ".vscode/extensions.json", "ignore": true},
+ {"path": "dev/jsconfig.json", "ignore": true},
+ {"path": "ext/manifest.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary1/index.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary2/kanji_bank_1.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary2/index.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary3/kanji_meta_bank_1.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary3/index.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary4/tag_bank_1.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary4/index.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary5/term_bank_1.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary5/index.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary6/term_meta_bank_1.json", "ignore": true},
+ {"path": "test/data/dictionaries/invalid-dictionary6/index.json", "ignore": true},
+ {"path": "test/jsconfig.json", "ignore": true},
+
+ {
+ "path": "dev/data/manifest-variants.json",
+ "typeFile": "types/dev/manifest.d.ts",
+ "type": "ManifestConfig"
+ },
+ {
+ "path": "ext/data/deinflect.json",
+ "typeFile": "types/ext/deinflector.d.ts",
+ "type": "ReasonsRaw"
+ },
+ {
+ "path": "ext/data/pronunciation-style.json",
+ "typeFile": "types/ext/css-style-applier.d.ts",
+ "type": "RawStyleData"
+ },
+ {
+ "path": "ext/data/structured-content-style.json",
+ "typeFile": "types/ext/css-style-applier.d.ts",
+ "type": "RawStyleData"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-index-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-kanji-bank-v1-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-kanji-bank-v3-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-kanji-meta-bank-v3-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-tag-bank-v3-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-term-bank-v1-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-term-bank-v3-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/dictionary-term-meta-bank-v3-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/custom-audio-list-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "ext/data/schemas/options-schema.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "AjvSchema"
+ },
+ {
+ "path": "test/data/translator-test-inputs.json",
+ "typeFile": "types/test/translator.d.ts",
+ "type": "TranslatorTestInputs",
+ "jsconfig": "test"
+ },
+ {
+ "path": "test/data/translator-test-results.json",
+ "typeFile": "types/test/translator.d.ts",
+ "type": "TranslatorTestResults",
+ "jsconfig": "test"
+ },
+ {
+ "path": "test/data/translator-test-results-note-data1.json",
+ "typeFile": "types/test/translator.d.ts",
+ "type": "TranslatorTestNoteDataResults",
+ "jsconfig": "test"
+ },
+ {
+ "path": "test/data/anki-note-builder-test-results.json",
+ "typeFile": "types/test/translator.d.ts",
+ "type": "AnkiNoteBuilderTestResults",
+ "jsconfig": "test"
+ },
+ {
+ "path": "test/data/json.json",
+ "typeFile": "types/test/json.d.ts",
+ "type": "JsonInfo"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/index.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "Index",
+ "schema": "ext/data/schemas/dictionary-index-schema.json"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/kanji_bank_1.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "KanjiV3Array",
+ "schema": "ext/data/schemas/dictionary-kanji-bank-v3-schema.json"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/kanji_meta_bank_1.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "KanjiMetaArray",
+ "schema": "ext/data/schemas/dictionary-kanji-meta-bank-v3-schema.json"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/tag_bank_1.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "TagArray",
+ "schema": "ext/data/schemas/dictionary-tag-bank-v3-schema.json"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/tag_bank_2.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "TagArray",
+ "schema": "ext/data/schemas/dictionary-tag-bank-v3-schema.json"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/tag_bank_3.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "TagArray",
+ "schema": "ext/data/schemas/dictionary-tag-bank-v3-schema.json"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/term_bank_1.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "TermV3Array",
+ "schema": "ext/data/schemas/dictionary-term-bank-v3-schema.json"
+ },
+ {
+ "path": "test/data/dictionaries/valid-dictionary1/term_meta_bank_1.json",
+ "typeFile": "types/ext/dictionary-data.d.ts",
+ "type": "TermMetaArray",
+ "schema": "ext/data/schemas/dictionary-term-meta-bank-v3-schema.json"
+ }
+ ]
+} \ No newline at end of file
diff --git a/test/data/translator-test-inputs.json b/test/data/translator-test-inputs.json
index cf4b8f6a..5afb6a60 100644
--- a/test/data/translator-test-inputs.json
+++ b/test/data/translator-test-inputs.json
@@ -19,12 +19,12 @@
"sortFrequencyDictionary": null,
"sortFrequencyDictionaryOrder": "descending",
"removeNonJapaneseCharacters": true,
- "convertHalfWidthCharacters": false,
- "convertNumericCharacters": false,
- "convertAlphabeticCharacters": false,
- "convertHiraganaToKatakana": false,
- "convertKatakanaToHiragana": false,
- "collapseEmphaticSequences": false,
+ "convertHalfWidthCharacters": "false",
+ "convertNumericCharacters": "false",
+ "convertAlphabeticCharacters": "false",
+ "convertHiraganaToKatakana": "false",
+ "convertKatakanaToHiragana": "false",
+ "collapseEmphaticSequences": "false",
"textReplacements": [
null
],
diff --git a/test/fixtures/translator-test.js b/test/fixtures/translator-test.js
index cb1a3ef5..3304c587 100644
--- a/test/fixtures/translator-test.js
+++ b/test/fixtures/translator-test.js
@@ -18,8 +18,8 @@
import {IDBKeyRange, indexedDB} from 'fake-indexeddb';
import {readFileSync} from 'fs';
-import {fileURLToPath, pathToFileURL} from 'node:url';
-import {dirname, join, resolve} from 'path';
+import {fileURLToPath} from 'node:url';
+import {dirname, join} from 'path';
import {expect, vi} from 'vitest';
import {parseJson} from '../../dev/json.js';
import {createDictionaryArchive} from '../../dev/util.js';
@@ -28,43 +28,13 @@ import {DictionaryDatabase} from '../../ext/js/language/dictionary-database.js';
import {DictionaryImporter} from '../../ext/js/language/dictionary-importer.js';
import {JapaneseUtil} from '../../ext/js/language/sandbox/japanese-util.js';
import {Translator} from '../../ext/js/language/translator.js';
+import {chrome, fetch} from '../mocks/common.js';
import {DictionaryImporterMediaLoader} from '../mocks/dictionary-importer-media-loader.js';
import {createDomTest} from './dom-test.js';
const extDir = join(dirname(fileURLToPath(import.meta.url)), '../../ext');
const deinflectionReasonsPath = join(extDir, 'data/deinflect.json');
-/** @type {import('dev/vm').PseudoChrome} */
-const chrome = {
- runtime: {
- getURL: (path) => {
- return pathToFileURL(join(extDir, path.replace(/^\//, ''))).href;
- }
- }
-};
-
-/**
- * @param {string} url
- * @returns {Promise<import('dev/vm').PseudoFetchResponse>}
- */
-async function fetch(url) {
- let filePath;
- try {
- filePath = fileURLToPath(url);
- } catch (e) {
- filePath = resolve(extDir, url.replace(/^[/\\]/, ''));
- }
- await Promise.resolve();
- const content = readFileSync(filePath, {encoding: null});
- return {
- ok: true,
- status: 200,
- statusText: 'OK',
- text: async () => content.toString('utf8'),
- json: async () => parseJson(content.toString('utf8'))
- };
-}
-
vi.stubGlobal('indexedDB', indexedDB);
vi.stubGlobal('IDBKeyRange', IDBKeyRange);
vi.stubGlobal('fetch', fetch);
diff --git a/test/jsconfig.json b/test/jsconfig.json
index 9ab0c332..a9845861 100644
--- a/test/jsconfig.json
+++ b/test/jsconfig.json
@@ -15,7 +15,9 @@
"*": ["../types/ext/*"],
"dev/*": ["../types/dev/*"],
"test/*": ["../types/test/*"],
- "rollup/parseAst": ["../types/other/rollup-parse-ast"]
+ "rollup/parseAst": ["../types/other/rollup-parse-ast"],
+ "ext/json-schema": ["../types/ext/json-schema"],
+ "json-schema": ["json-schema"]
},
"types": [
"chrome",
diff --git a/test/json-schema.test.js b/test/json-schema.test.js
index fb7644de..0c1a483b 100644
--- a/test/json-schema.test.js
+++ b/test/json-schema.test.js
@@ -23,7 +23,7 @@ import {parseJson} from '../dev/json.js';
import {JsonSchema} from '../ext/js/data/json-schema.js';
/**
- * @param {import('json-schema').Schema} schema
+ * @param {import('ext/json-schema').Schema} schema
* @param {unknown} value
* @returns {boolean}
*/
@@ -32,18 +32,18 @@ function schemaValidate(schema, value) {
}
/**
- * @param {import('json-schema').Schema} schema
+ * @param {import('ext/json-schema').Schema} schema
* @param {unknown} value
- * @returns {import('json-schema').Value}
+ * @returns {import('ext/json-schema').Value}
*/
function getValidValueOrDefault(schema, value) {
return new JsonSchema(schema).getValidValueOrDefault(value);
}
/**
- * @param {import('json-schema').Schema} schema
- * @param {import('json-schema').Value} value
- * @returns {import('json-schema').Value}
+ * @param {import('ext/json-schema').Schema} schema
+ * @param {import('ext/json-schema').Value} value
+ * @returns {import('ext/json-schema').Value}
*/
function createProxy(schema, value) {
return new JsonSchema(schema).createProxy(value);
@@ -62,7 +62,7 @@ function clone(value) {
/** */
function testValidate1() {
test('Validate1', () => {
- /** @type {import('json-schema').Schema} */
+ /** @type {import('ext/json-schema').Schema} */
const schema = {
allOf: [
{
@@ -123,7 +123,7 @@ function testValidate1() {
/** */
function testValidate2() {
test('Validate2', () => {
- /** @type {{schema: import('json-schema').Schema, inputs: {expected: boolean, value: unknown}[]}[]} */
+ /** @type {{schema: import('ext/json-schema').Schema, inputs: {expected: boolean, value: unknown}[]}[]} */
const data = [
// String tests
{
@@ -530,7 +530,7 @@ function testValidate2() {
/** */
function testGetValidValueOrDefault1() {
test('GetValidValueOrDefault1', () => {
- /** @type {{schema: import('json-schema').Schema, inputs: [value: unknown, expected: unknown][]}[]} */
+ /** @type {{schema: import('ext/json-schema').Schema, inputs: [value: unknown, expected: unknown][]}[]} */
const data = [
// Test value defaulting on objects with additionalProperties=false
{
@@ -702,7 +702,7 @@ function testGetValidValueOrDefault1() {
type: 'object',
required: ['toString'],
properties: {
- toString: /** @type {import('json-schema').SchemaObject} */ ({
+ toString: /** @type {import('ext/json-schema').SchemaObject} */ ({
type: 'string',
default: 'default'
})
@@ -888,7 +888,7 @@ function testGetValidValueOrDefault1() {
/** */
function testProxy1() {
test('Proxy1', () => {
- /** @type {{schema: import('json-schema').Schema, tests: {error: boolean, value?: import('json-schema').Value, action: (value: import('core').SafeAny) => void}[]}[]} */
+ /** @type {{schema: import('ext/json-schema').Schema, tests: {error: boolean, value?: import('ext/json-schema').Value, action: (value: import('core').SafeAny) => void}[]}[]} */
const data = [
// Object tests
{
diff --git a/test/json.test.js b/test/json.test.js
new file mode 100644
index 00000000..8cf01491
--- /dev/null
+++ b/test/json.test.js
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2023 Yomitan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import Ajv from 'ajv';
+import {readFileSync} from 'fs';
+import {join, dirname as pathDirname} from 'path';
+import {createGenerator} from 'ts-json-schema-generator';
+import {fileURLToPath} from 'url';
+import {describe, expect, test} from 'vitest';
+import {parseJson} from '../dev/json.js';
+import {getAllFiles} from '../dev/util.js';
+
+const dirname = pathDirname(fileURLToPath(import.meta.url));
+const rootDir = join(dirname, '..');
+
+/**
+ * @param {import('test/json').JsconfigType|undefined} jsconfigType
+ * @returns {string}
+ */
+function getJsconfigPath(jsconfigType) {
+ let path;
+ switch (jsconfigType) {
+ case 'dev': path = '../dev/jsconfig.json'; break;
+ case 'test': path = '../test/jsconfig.json'; break;
+ default: path = '../jsconfig.json'; break;
+ }
+ return join(dirname, path);
+}
+
+/**
+ * @returns {Ajv}
+ */
+function createAjv() {
+ return new Ajv({
+ meta: true,
+ strictTuples: false,
+ allowUnionTypes: true
+ });
+}
+
+/**
+ * @param {string} path
+ * @param {string} type
+ * @param {import('test/json').JsconfigType|undefined} jsconfigType
+ * @returns {import('ajv').ValidateFunction<unknown>}
+ */
+function createValidatorFunctionFromTypeScript(path, type, jsconfigType) {
+ /** @type {import('ts-json-schema-generator/dist/src/Config').Config} */
+ const config = {
+ path,
+ tsconfig: getJsconfigPath(jsconfigType),
+ type,
+ jsDoc: 'none',
+ additionalProperties: false,
+ minify: false,
+ expose: 'none',
+ strictTuples: true
+ };
+ const schema = createGenerator(config).createSchema(config.type);
+ const ajv = createAjv();
+ return ajv.compile(schema);
+}
+
+/**
+ * @param {string} path
+ * @returns {import('ajv').ValidateFunction<unknown>}
+ */
+function createValidatorFunctionFromSchemaJson(path) {
+ /** @type {import('ajv').Schema} */
+ const schema = parseJson(readFileSync(path, {encoding: 'utf8'}));
+ const ajv = createAjv();
+ return ajv.compile(schema);
+}
+
+/**
+ * @param {string} value
+ * @returns {string}
+ */
+function normalizePathDirectorySeparators(value) {
+ return value.replace(/\\/g, '/');
+}
+
+
+describe.concurrent('JSON validation', () => {
+ const ignoreDirectories = new Set([
+ 'builds',
+ 'dictionaries',
+ 'node_modules',
+ 'playwright-report',
+ 'playwright',
+ 'test-results',
+ 'dev/lib',
+ 'test/playwright'
+ ]);
+
+ const existingJsonFiles = getAllFiles(rootDir, (path, isDirectory) => {
+ const fileNameNormalized = normalizePathDirectorySeparators(path);
+ if (isDirectory) {
+ return !ignoreDirectories.has(fileNameNormalized);
+ } else {
+ return /\.json$/i.test(fileNameNormalized);
+ }
+ });
+ /** @type {Set<string>} */
+ const existingJsonFileSet = new Set();
+ for (const path of existingJsonFiles) {
+ existingJsonFileSet.add(normalizePathDirectorySeparators(path));
+ }
+
+ const jsonFileName = 'json.json';
+
+ /** @type {import('test/json').JsonInfo} */
+ const jsonFileData = parseJson(readFileSync(join(dirname, `data/${jsonFileName}`), {encoding: 'utf8'}));
+
+ test(`Each item in ${jsonFileName} must have a unique path`, () => {
+ /** @type {Set<string>} */
+ const set = new Set();
+ for (const {path} of jsonFileData.files) {
+ set.add(path);
+ }
+ expect(set.size).toBe(jsonFileData.files.length);
+ });
+
+ /** @type {Map<string, import('test/json').JsonFileInfo>} */
+ const jsonFileMap = new Map();
+ for (const item of jsonFileData.files) {
+ jsonFileMap.set(item.path, item);
+ }
+
+ // Validate file existance
+ const requiredFiles = jsonFileData.files.filter((v) => !v.ignore);
+ test.each(requiredFiles)('File must exist in project: $path', ({path}) => {
+ expect(existingJsonFileSet.has(path)).toBe(true);
+ });
+
+ // Validate new files
+ const existingJsonFiles2 = existingJsonFiles.map((path) => ({path: normalizePathDirectorySeparators(path)}));
+ test.each(existingJsonFiles2)(`File must exist in ${jsonFileName}: $path`, ({path}) => {
+ expect(jsonFileMap.has(path)).toBe(true);
+ });
+
+ // Validate schemas 1
+ /** @type {import('test/json').JsonFileParseInfo[]} */
+ const schemaValidationTargets1 = [];
+ for (const info of jsonFileData.files) {
+ if (info.ignore || !existingJsonFileSet.has(info.path)) { continue; }
+ schemaValidationTargets1.push(info);
+ }
+ test.each(schemaValidationTargets1)('Validating file against TypeScript: $path', ({path, typeFile, type, jsconfig}) => {
+ const validate = createValidatorFunctionFromTypeScript(join(rootDir, typeFile), type, jsconfig);
+ const data = parseJson(readFileSync(join(rootDir, path), {encoding: 'utf8'}));
+ const valid = validate(data);
+ const {errors} = validate;
+ expect(errors).toBe(null);
+ expect(valid).toBe(true);
+ });
+
+ // Validate schemas 2
+ /** @type {{path: string, schema: string}[]} */
+ const schemaValidationTargets2 = [];
+ for (const info of jsonFileData.files) {
+ if (info.ignore || !existingJsonFileSet.has(info.path)) { continue; }
+ const {schema, path} = info;
+ if (typeof schema !== 'string') { continue; }
+ schemaValidationTargets2.push({schema, path});
+ }
+ test.each(schemaValidationTargets2)('Validating file against schema: $path', ({path, schema}) => {
+ const validate = createValidatorFunctionFromSchemaJson(join(rootDir, schema));
+ const data = parseJson(readFileSync(join(rootDir, path), {encoding: 'utf8'}));
+ const valid = validate(data);
+ const {errors} = validate;
+ expect(errors).toBe(null);
+ expect(valid).toBe(true);
+ });
+});
diff --git a/test/mocks/common.js b/test/mocks/common.js
new file mode 100644
index 00000000..7fe30a3e
--- /dev/null
+++ b/test/mocks/common.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 Yomitan Authors
+ * Copyright (C) 2020-2022 Yomichan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import {readFileSync} from 'fs';
+import {fileURLToPath, pathToFileURL} from 'node:url';
+import {dirname, join, resolve} from 'path';
+import {parseJson} from '../../dev/json.js';
+
+const extDir = join(dirname(fileURLToPath(import.meta.url)), '../../ext');
+
+/** @type {import('test/mocks').ChromeMock} */
+export const chrome = {
+ runtime: {
+ getURL: (path) => {
+ return pathToFileURL(join(extDir, path.replace(/^\//, ''))).href;
+ }
+ }
+};
+
+/** @type {import('test/mocks').FetchMock} */
+export async function fetch(url) {
+ let filePath;
+ try {
+ filePath = fileURLToPath(url);
+ } catch (e) {
+ filePath = resolve(extDir, url.replace(/^[/\\]/, ''));
+ }
+ await Promise.resolve();
+ const content = readFileSync(filePath, {encoding: null});
+ return {
+ ok: true,
+ status: 200,
+ statusText: 'OK',
+ text: async () => content.toString('utf8'),
+ json: async () => parseJson(content.toString('utf8'))
+ };
+}
diff --git a/test/options-util.test.js b/test/options-util.test.js
index 41185756..7bb9767a 100644
--- a/test/options-util.test.js
+++ b/test/options-util.test.js
@@ -19,40 +19,15 @@
/* eslint-disable no-multi-spaces */
import fs from 'fs';
-import url, {fileURLToPath} from 'node:url';
+import {fileURLToPath} from 'node:url';
import path from 'path';
import {expect, test, vi} from 'vitest';
-import {parseJson} from '../dev/json.js';
import {OptionsUtil} from '../ext/js/data/options-util.js';
import {TemplatePatcher} from '../ext/js/templates/template-patcher.js';
+import {chrome, fetch} from './mocks/common.js';
const dirname = path.dirname(fileURLToPath(import.meta.url));
-/**
- * @param {string} url2
- * @returns {Promise<import('dev/vm').PseudoFetchResponse>}
- */
-async function fetch(url2) {
- const filePath = url.fileURLToPath(url2);
- await Promise.resolve();
- const content = fs.readFileSync(filePath, {encoding: null});
- return {
- ok: true,
- status: 200,
- statusText: 'OK',
- text: async () => Promise.resolve(content.toString('utf8')),
- json: async () => Promise.resolve(parseJson(content.toString('utf8')))
- };
-}
-/** @type {import('dev/vm').PseudoChrome} */
-const chrome = {
- runtime: {
- getURL(path2) {
- return url.pathToFileURL(path.join(dirname, '..', 'ext', path2.replace(/^\//, ''))).href;
- }
- }
-};
-
vi.stubGlobal('fetch', fetch);
vi.stubGlobal('chrome', chrome);
diff --git a/test/profile-conditions-util.test.js b/test/profile-conditions-util.test.js
index f64ce79c..d5c8f8d2 100644
--- a/test/profile-conditions-util.test.js
+++ b/test/profile-conditions-util.test.js
@@ -62,7 +62,7 @@ function testNormalizeContext() {
/** */
function testSchemas() {
test('Schemas', () => {
- /** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */
+ /** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('ext/json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */
const data = [
// Empty
{
diff --git a/test/utilities/translator.js b/test/utilities/translator.js
index 9073b206..81081af6 100644
--- a/test/utilities/translator.js
+++ b/test/utilities/translator.js
@@ -18,10 +18,11 @@
/**
+ * TODO : This function is not very type safe at the moment, could be improved.
* @template {import('translation').FindTermsOptions|import('translation').FindKanjiOptions} T
* @param {string} dictionaryName
- * @param {import('dev/vm').OptionsPresetObject} optionsPresets
- * @param {string|import('dev/vm').OptionsPresetObject|(string|import('dev/vm').OptionsPresetObject)[]} optionsArray
+ * @param {import('test/translator').OptionsPresetObject} optionsPresets
+ * @param {import('test/translator').OptionsList} optionsArray
* @returns {T}
* @throws {Error}
*/