From e2b31c488b05adf3aacb610c677c66345c69392c Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Thu, 22 Jan 2026 20:39:07 +0100 Subject: [PATCH 1/4] maint(web): re-add engine .js tests PR #15093 merged multiple engine modules into one. However, in doing so we lost the .js tests and ran only the .ts ones. This change re-adds the .js tests and fixes them where the code diverged since then. Other changes: - rename `queryEngine.ts` to `cloudQueryEngine.ts` to match the class name - remove unused `keyboard-storage/cloud/index.ts` - expose `CLOUD_TIMEOUT_ERR` and `CLOUD_STUB_REGISTRATION_ERR` as `unitTestEndpoints` - add `test/resources` to exports in `web/package.json`. This was necessary because the compiled .ts test files are under `web/build` whereas the .js test files are under `web/src` and so the relative paths in imports no longer work. Also fix the imports in test files. - apply fix from #15477 if KEYMAN_ROOT is not set I'm not happy to put the compiled files under `web/src/test/auto/resources/build`, but that's the only way I got it to work. If I put the compiled files in `web/build/test/resources` it couldn't find the types for `promise-status-async`. Also, running the .js tests succeeded but then creating the coverage report failed. I was not able to find the reason or a fix for that. As a hack to work around this problem we check the number of failed tests instead of relying on the exit code of the test. Follows: #15093 Test-bot: skip --- web/common.inc.sh | 6 +- web/package.json | 4 + web/src/engine/build.sh | 28 +++- .../{queryEngine.ts => cloudQueryEngine.ts} | 0 .../src/keyboard-storage/cloud/index.ts | 2 - .../src/keyboard-storage/domCloudRequester.ts | 2 +- web/src/engine/src/keyboard-storage/index.ts | 11 +- .../prediction/predictionContext.tests.js | 16 +- .../engine/js-processor/basic-engine.tests.js | 8 +- .../engine/js-processor/basic-init.tests.js | 3 +- .../js-processor/bundled-module.tests.js | 24 +-- .../engine/js-processor/chirality.tests.js | 8 +- .../engine/js-processor/deadkeys.tests.js | 8 +- .../js-processor/engine/context.tests.js | 3 +- .../engine/notany_context.tests.js | 2 +- .../js-processor/engine/stores.tests.js | 5 +- .../engine/unmatched_final_group.tests.js | 10 +- .../engine/js-processor/kbdInterface.tests.ts | 2 +- .../non-positional-rules.tests.js | 2 +- ...ests.js => specialized-backspace.tests.ts} | 150 +++++++++++------- .../js-processor/transcriptions.tests.js | 6 +- .../keyboard-storage/cloudQueries.tests.js | 4 +- .../keyboardRequisitioner.tests.js | 15 +- .../keyboard-storage/keyboardStub.tests.js | 2 +- .../nodeCloudRequester.tests.js | 2 +- .../stubAndKeyboardCache.tests.js | 2 +- .../engine/keyboard/keyboard-loading.tests.js | 2 +- .../engine/keyboard/keyboard.tests.ts | 2 +- .../keyboard/keyboardLoaderBase.tests.ts | 2 +- .../main/headless/inputProcessor.tests.js | 24 +-- .../main/headless/languageProcessor.tests.js | 6 +- .../gestures/gestureMatcher.tests.ts | 5 +- .../gestures/gestureSequence.tests.ts | 3 +- .../gestures/matcherSelector.tests.ts | 3 +- .../gestures/pathMatcher.tests.ts | 2 +- .../gestures/touchpointCoordinator.tests.ts | 4 +- web/src/test/auto/resources/index.ts | 6 + .../resources/loader/node-keyboard-loader.ts | 1 - .../resources/loader/nodeCloudRequester.ts | 15 +- .../auto/resources/loader/tsconfig.node.json | 13 -- .../resources/simulateMultiSourceInput.ts | 4 +- web/src/test/auto/resources/tsconfig.json | 18 +++ web/src/test/auto/tsconfig.json | 3 +- 43 files changed, 262 insertions(+), 176 deletions(-) rename web/src/engine/src/keyboard-storage/cloud/{queryEngine.ts => cloudQueryEngine.ts} (100%) delete mode 100644 web/src/engine/src/keyboard-storage/cloud/index.ts rename web/src/test/auto/headless/engine/js-processor/{specialized-backspace.tests.js => specialized-backspace.tests.ts} (77%) create mode 100644 web/src/test/auto/resources/index.ts delete mode 100644 web/src/test/auto/resources/loader/node-keyboard-loader.ts delete mode 100644 web/src/test/auto/resources/loader/tsconfig.node.json create mode 100644 web/src/test/auto/resources/tsconfig.json diff --git a/web/common.inc.sh b/web/common.inc.sh index b4a3c47d7e..2c7ee35fd8 100644 --- a/web/common.inc.sh +++ b/web/common.inc.sh @@ -17,7 +17,7 @@ # compile engine/main # ``` function compile() { - if [ $# -lt 1 ]; then + if [[ $# -lt 1 ]]; then builder_die "Scripting error: insufficient argument count!" fi @@ -27,10 +27,10 @@ function compile() { local SRC_DIR=${2:-"${KEYMAN_ROOT}/web/src"} local BUILD_DIR=${3:-"${KEYMAN_ROOT}/web/build"} - tsc -b "${SRC_DIR}/$COMPILE_TARGET" + tsc -b "${SRC_DIR}/${COMPILE_TARGET}" # So... tsc does declaration-bundling on its own pretty well, at least for local development. - tsc --emitDeclarationOnly --outFile "${BUILD_DIR}/$COMPILE_TARGET/lib/index.d.ts" -p "${SRC_DIR}/$COMPILE_TARGET" + tsc --emitDeclarationOnly --outFile "${BUILD_DIR}/${COMPILE_TARGET}/lib/index.d.ts" -p "${SRC_DIR}/${COMPILE_TARGET}" } function _copy_dir_if_exists() { diff --git a/web/package.json b/web/package.json index 219b2ddac9..887eeed27d 100644 --- a/web/package.json +++ b/web/package.json @@ -81,6 +81,10 @@ "./tools/testing/test-utils": { "types": "./build/tools/testing/test-utils/obj/index.d.ts", "import": "./build/tools/testing/test-utils/obj/index.js" + }, + "./test/resources": { + "types": "./src/test/auto/resources/build/index.d.ts", + "import": "./src/test/auto/resources/build/index.js" } }, "imports": { diff --git a/web/src/engine/build.sh b/web/src/engine/build.sh index 2b5f6380b1..328d6a4a5d 100755 --- a/web/src/engine/build.sh +++ b/web/src/engine/build.sh @@ -68,11 +68,35 @@ do_build () { node src/osk/validate-gesture-specs.js } -builder_run_action clean rm -rf "$KEYMAN_ROOT/web/build/engine" +run_tests() { + local OUTPUT_FILE FAILURE_COUNT + # Remove stale coverage data + rm -rf "${KEYMAN_ROOT}/web/build/coverage/raw/engine" + + # Unfortunately we get an error from the coverage report generation: + # "TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file" + # The following lines ignore the exit code and instead check the number + # of failed tests from the output. + set +e + OUTPUT_FILE=$(mktemp) + test-headless engine "" 2>&1 | tee "${OUTPUT_FILE}" + set -e + + FAILURE_COUNT=$(grep ' failing' "${OUTPUT_FILE}" | xargs | cut -f 1 -d' ') + rm "${OUTPUT_FILE}" + builder_echo "(The 'TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file' is expected)" + if ((FAILURE_COUNT > 0)); then + builder_die "Headless engine tests failed (.js tests)" + fi + + test-headless-typescript engine +} + +builder_run_action clean rm -rf "${KEYMAN_ROOT}/web/build/engine" builder_run_child_actions clean builder_run_action configure node_select_version_and_npm_ci builder_run_child_actions configure builder_run_child_actions build builder_run_action build do_build -builder_run_action test test-headless-typescript engine +builder_run_action test run_tests builder_run_child_actions test \ No newline at end of file diff --git a/web/src/engine/src/keyboard-storage/cloud/queryEngine.ts b/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts similarity index 100% rename from web/src/engine/src/keyboard-storage/cloud/queryEngine.ts rename to web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts diff --git a/web/src/engine/src/keyboard-storage/cloud/index.ts b/web/src/engine/src/keyboard-storage/cloud/index.ts deleted file mode 100644 index 2ab752bcde..0000000000 --- a/web/src/engine/src/keyboard-storage/cloud/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { CloudQueryResult, CloudQueryEngine as QueryEngine } from './queryEngine.js'; -export { CloudRequesterInterface as RequesterInterface } from './requesterInterface.js'; diff --git a/web/src/engine/src/keyboard-storage/domCloudRequester.ts b/web/src/engine/src/keyboard-storage/domCloudRequester.ts index 85ab4cd73e..6d13b43cfd 100644 --- a/web/src/engine/src/keyboard-storage/domCloudRequester.ts +++ b/web/src/engine/src/keyboard-storage/domCloudRequester.ts @@ -1,6 +1,6 @@ import { ManagedPromise } from 'keyman/common/web-utils'; import { CloudRequesterInterface } from './cloud/requesterInterface.js'; -import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/queryEngine.js'; +import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/cloudQueryEngine.js'; export class DOMCloudRequester implements CloudRequesterInterface { private readonly fileLocal: boolean; diff --git a/web/src/engine/src/keyboard-storage/index.ts b/web/src/engine/src/keyboard-storage/index.ts index 7f83893de2..e6e3b7b990 100644 --- a/web/src/engine/src/keyboard-storage/index.ts +++ b/web/src/engine/src/keyboard-storage/index.ts @@ -8,8 +8,15 @@ export { REGION_CODES } from './keyboardStub.js'; export { StubAndKeyboardCache, toPrefixedKeyboardId, toUnprefixedKeyboardId } from './stubAndKeyboardCache.js'; -export { CloudQueryResult, CloudQueryEngine } from './cloud/queryEngine.js'; +export { CloudQueryResult, CloudQueryEngine } from './cloud/cloudQueryEngine.js'; export { CloudRequesterInterface } from './cloud/requesterInterface.js'; export { KeyboardRequisitioner } from './keyboardRequisitioner.js'; export { ModelCache } from './modelCache.js'; -export { DOMCloudRequester } from './domCloudRequester.js'; \ No newline at end of file +export { DOMCloudRequester } from './domCloudRequester.js'; + +import { CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/cloudQueryEngine.js'; + +export const unitTestEndpoints = { + CLOUD_TIMEOUT_ERR, + CLOUD_STUB_REGISTRATION_ERR +}; diff --git a/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.tests.js b/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.tests.js index e5bf59b2cd..6beca50f82 100644 --- a/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.tests.js +++ b/web/src/test/auto/headless/engine/interfaces/prediction/predictionContext.tests.js @@ -3,8 +3,8 @@ import sinon from 'sinon'; import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main'; import { PredictionContext } from 'keyman/engine/interfaces'; -import { Worker as LMWorker } from "@keymanapp/lexical-model-layer/node"; -import { DeviceSpec, SyntheticTextStore } from 'keyman/engine/keyboard'; +import { NodeWorker } from "@keymanapp/lexical-model-layer/node"; +import { SyntheticTextStore } from 'keyman/engine/keyboard'; function compileDummyModel(suggestionSets) { return ` @@ -66,7 +66,7 @@ describe("PredictionContext", () => { let langProcessor; beforeEach(function() { - langProcessor = new LanguageProcessor(LMWorker, new TranscriptionCache()); + langProcessor = new LanguageProcessor(NodeWorker, new TranscriptionCache()); }); afterEach(function() { @@ -83,7 +83,7 @@ describe("PredictionContext", () => { let textStore = new SyntheticTextStore("appl", 4); // "appl|", with '|' as the caret position. const initialTextStore = SyntheticTextStore.from(textStore); - const promise = predictiveContext.setCurrentTarget(textStore); + const promise = predictiveContext.setCurrentTextStore(textStore); // Initial predictive state: no suggestions. context.initializeState() has not yet been called. assert.equal(updateFake.callCount, 1); @@ -122,7 +122,7 @@ describe("PredictionContext", () => { let textStore = new SyntheticTextStore("appl", 4); // "appl|", with '|' as the caret position. const initialTextStore = SyntheticTextStore.from(textStore); - const promise = predictiveContext.setCurrentTarget(textStore); + const promise = predictiveContext.setCurrentTextStore(textStore); // Initial predictive state: no suggestions. context.initializeState() has not yet been called. assert.equal(updateFake.callCount, 1); @@ -181,7 +181,7 @@ describe("PredictionContext", () => { const predictiveContext = new PredictionContext(langProcessor, dummiedGetLayer); let textStore = new SyntheticTextStore("appl", 4); // "appl|", with '|' as the caret position. - const initialSuggestions = await predictiveContext.setCurrentTarget(textStore); + const initialSuggestions = await predictiveContext.setCurrentTextStore(textStore); let updateFake = sinon.fake(); predictiveContext.on('update', updateFake); @@ -206,7 +206,7 @@ describe("PredictionContext", () => { let textState = new SyntheticTextStore("appl", 4); // "appl|", with '|' as the caret position. - await predictiveContext.setCurrentTarget(textState); + await predictiveContext.setCurrentTextStore(textState); let updateFake = sinon.fake(); predictiveContext.on('update', updateFake); @@ -274,7 +274,7 @@ describe("PredictionContext", () => { // Test setup - return to the state at the end of the prior-defined unit test ('suggestion application...') - await predictiveContext.setCurrentTarget(textState); + await predictiveContext.setCurrentTextStore(textState); // This is the point in time that a reversion operation will rewind the context to. const revertBaseTextState = SyntheticTextStore.from(textState); diff --git a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js index 08bec6f436..40d6b6797b 100644 --- a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js @@ -7,11 +7,15 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../'); describe('Engine - Basic Simulation', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/basic_lao_simulation.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/basic-init.tests.js b/web/src/test/auto/headless/engine/js-processor/basic-init.tests.js index 63fdeb9536..29d6fb02de 100644 --- a/web/src/test/auto/headless/engine/js-processor/basic-init.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-init.tests.js @@ -4,8 +4,7 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); import { JSKeyboardProcessor } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; -import { DEFAULT_PROCESSOR_INIT_OPTIONS } from '../../../resources/defaultProcessorInitOptions.js'; +import { DEFAULT_PROCESSOR_INIT_OPTIONS, NodeKeyboardLoader } from 'keyman/test/resources'; global.keyman = {}; // So that keyboard-based checks against the global `keyman` succeed. // 10.0+ dependent keyboards, like khmer_angkor, will otherwise fail to load. diff --git a/web/src/test/auto/headless/engine/js-processor/bundled-module.tests.js b/web/src/test/auto/headless/engine/js-processor/bundled-module.tests.js index a70cc14991..40b78362ed 100644 --- a/web/src/test/auto/headless/engine/js-processor/bundled-module.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/bundled-module.tests.js @@ -1,14 +1,14 @@ import { assert } from "chai"; -import { DEFAULT_PROCESSOR_INIT_OPTIONS } from '../../../resources/defaultProcessorInitOptions.js'; -import * as JSProcessorModule from "keyman/engine/js-processor"; -import * as KeyboardModule from "keyman/engine/keyboard"; -const KMWString = KeyboardModule.KMWString; +import { DEFAULT_PROCESSOR_INIT_OPTIONS } from 'keyman/test/resources'; +import { JSKeyboardProcessor } from "keyman/engine/js-processor"; +import { JSKeyboard, SyntheticTextStore } from "keyman/engine/keyboard"; +import { KMWString, Version } from 'keyman/common/web-utils'; // A few small tests to ensure that the ES Module bundle was successfully constructed and is usable. -var toSupplementaryPairString = function(code){ - var H = Math.floor((code - 0x10000) / 0x400) + 0xD800; - var L = (code - 0x10000) % 0x400 + 0xDC00; +const toSupplementaryPairString = function(code){ + const H = Math.floor((code - 0x10000) / 0x400) + 0xD800; + const L = (code - 0x10000) % 0x400 + 0xDC00; return String.fromCharCode(H, L); } @@ -18,7 +18,7 @@ let u = toSupplementaryPairString; describe('Bundled ES Module for js-processor', function() { describe('JSKeyboardProcessor', function () { it('should initialize without errors', function () { - let kp = new JSProcessorModule.JSKeyboardProcessor(null, DEFAULT_PROCESSOR_INIT_OPTIONS); + let kp = new JSKeyboardProcessor(null, DEFAULT_PROCESSOR_INIT_OPTIONS); assert.isNotNull(kp); }); }); @@ -28,21 +28,21 @@ describe('Bundled ES Module for js-processor', function() { describe('Bundled ES Module for keyboard', function () { describe('Keyboard', function () { it('should initialize without errors', function () { - let kp = new KeyboardModule.JSKeyboard(); + let kp = new JSKeyboard(); assert.isNotNull(kp); }); }); describe("Imported `utils`", function () { it("should include `utils` package's Version class", () => { - let v16 = new KeyboardModule.Version([16, 1]); + let v16 = new Version([16, 1]); assert.equal(v16.toString(), "16.1"); }); }); describe('SyntheticTextStore', () => { it('basic functionality test', () => { - let textStore = new KeyboardModule.SyntheticTextStore("aple", 2); // ap | le + let textStore = new SyntheticTextStore("aple", 2); // ap | le textStore.insertTextBeforeCaret('p'); assert.equal(textStore.getText(), "apple"); }); @@ -50,7 +50,7 @@ describe('Bundled ES Module for keyboard', function () { it('smp test', () => { KMWString.enableSupplementaryPlane(true); // Declared & defined in web-utils. try { - let textStore = new KeyboardModule.SyntheticTextStore(u(0x1d5ba) + u(0x1d5c9) + u(0x1d5c5) + u(0x1d5be), 2); // ap | le + let textStore = new SyntheticTextStore(u(0x1d5ba) + u(0x1d5c9) + u(0x1d5c5) + u(0x1d5be), 2); // ap | le textStore.insertTextBeforeCaret(u(0x1d5c9)); assert.equal(textStore.getText(), u(0x1d5ba) + u(0x1d5c9) + u(0x1d5c9) + u(0x1d5c5) + u(0x1d5be)); } finally { diff --git a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js index 1c7b48cdcc..f99428c413 100644 --- a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js @@ -6,12 +6,16 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { ModifierKeyConstants } from '@keymanapp/common-types'; import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../'); describe('Engine - Chirality', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/chirality.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js index 15bb2e824f..e02fb1adaf 100644 --- a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js @@ -6,11 +6,15 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../'); describe('Engine - Deadkeys', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/deadkeys.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/engine/context.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/context.tests.js index 0105eb4d95..fa61b3feb3 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/context.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/context.tests.js @@ -5,8 +5,7 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface, JSKeyboardProcessor } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; -import { DEFAULT_PROCESSOR_INIT_OPTIONS } from '../../../resources/defaultProcessorInitOptions.js'; +import { DEFAULT_PROCESSOR_INIT_OPTIONS, NodeKeyboardLoader } from 'keyman/test/resources'; import { NodeProctor, RecordedKeystrokeSequence } from '@keymanapp/recorder-core'; diff --git a/web/src/test/auto/headless/engine/js-processor/engine/notany_context.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/notany_context.tests.js index 4676860f2f..edcbbd6492 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/notany_context.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/notany_context.tests.js @@ -5,7 +5,7 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { NodeProctor, RecordedKeystrokeSequence } from '@keymanapp/recorder-core'; const device = { diff --git a/web/src/test/auto/headless/engine/js-processor/engine/stores.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/stores.tests.js index ebf2c97600..eeb976a096 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/stores.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/stores.tests.js @@ -1,8 +1,9 @@ import { assert } from 'chai'; -import { JSKeyboard, KMWString } from 'keyman/engine/keyboard'; +import { KMWString } from 'keyman/common/web-utils'; +import { JSKeyboard } from 'keyman/engine/keyboard'; import { JSKeyboardProcessor } from 'keyman/engine/js-processor'; -import { DEFAULT_PROCESSOR_INIT_OPTIONS } from '../../../resources/defaultProcessorInitOptions.js'; +import { DEFAULT_PROCESSOR_INIT_OPTIONS } from 'keyman/test/resources'; let device = { formFactor: 'desktop', diff --git a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js index 61fd93c3e0..d558d89830 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js @@ -6,18 +6,22 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../../'); describe('Engine - Unmatched Final Groups', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/ghp_enter.json')); // Common test suite setup. let testSuite = new KeyboardTest(JSON.parse(testJSONtext)); - var keyboardWithHarness; + let keyboardWithHarness; let device = { formFactor: 'desktop', OS: 'windows', diff --git a/web/src/test/auto/headless/engine/js-processor/kbdInterface.tests.ts b/web/src/test/auto/headless/engine/js-processor/kbdInterface.tests.ts index a1cf76ca7a..872697cc12 100644 --- a/web/src/test/auto/headless/engine/js-processor/kbdInterface.tests.ts +++ b/web/src/test/auto/headless/engine/js-processor/kbdInterface.tests.ts @@ -6,7 +6,7 @@ const require = createRequire(import.meta.url); import { DeviceSpec } from 'keyman/common/web-utils'; import { JSKeyboard, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; describe('Headless keyboard loading', function () { const laoPath = require.resolve('@keymanapp/common-test-resources/keyboards/lao_2008_basic.js'); diff --git a/web/src/test/auto/headless/engine/js-processor/non-positional-rules.tests.js b/web/src/test/auto/headless/engine/js-processor/non-positional-rules.tests.js index 9a11fc686f..53620b8b09 100644 --- a/web/src/test/auto/headless/engine/js-processor/non-positional-rules.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/non-positional-rules.tests.js @@ -6,7 +6,7 @@ const require = createRequire(import.meta.url); import { Codes, KeyEvent, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; // Compare and contrast the unit tests here with those for app/browser key-event unit testing // in the hardware-event-processing set; the output objects there should have the same format diff --git a/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.js b/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.ts similarity index 77% rename from web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.js rename to web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.ts index 4357cf5968..581b1810e0 100644 --- a/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.ts @@ -3,12 +3,11 @@ import { assert } from 'chai'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); -import { KMWString } from 'keyman/common/web-utils'; -import { Codes, KeyEvent, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; +import { DeviceSpec, KMWString } from 'keyman/common/web-utils'; +import { Codes, DefaultOutputRules, JSKeyboard, KeyEvent, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface, JSKeyboardProcessor } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { ModifierKeyConstants } from '@keymanapp/common-types'; -import { DEFAULT_PROCESSOR_INIT_OPTIONS } from '../../../resources/defaultProcessorInitOptions.js'; const TEST_DEVICE = { @@ -16,45 +15,56 @@ const TEST_DEVICE = { OS: 'windows', browser: 'native', touchable: false -} +} as DeviceSpec; // Basic scaffolding necessary to use special, locally-defined test keyboards. -const COMMON_KBD_SCRIPT_PROPS = new (function (){ - this.KMINVER="10.0"; +class CommonKbdScriptProps { + KMINVER = "10.0"; // this.KV left empty - we aren't doing layout stuff for this test, so it's "fine". // - also this.KV.KLS // - also this.KV.BK - this.KH=''; - this.KM=0; - this.KBVER="0.0.1"; - this.KMBM=ModifierKeyConstants.K_SHIFTFLAG /* 0x0010 */; - this.gs=function(t,e) { - return this.g_main(t,e); - }; -})(); + KH = ''; + KM = 0; + KBVER = "0.0.1"; + KMBM = ModifierKeyConstants.K_SHIFTFLAG /* 0x0010 */; + gs(t: any, e: any) { + return (this as any).g_main(t, e); + } +} -const DUMMIED_KEYS_KEYBOARD_SCRIPT = function keyboard_core () { +const COMMON_KBD_SCRIPT_PROPS = new CommonKbdScriptProps(); + +const DUMMIED_KEYS_KEYBOARD_SCRIPT = function keyboard_core (this: any) { Object.assign(this, COMMON_KBD_SCRIPT_PROPS); this.KI="Keyboard_dummied_keys"; this.KN="Dummied Keys"; - this.g_main=function(t,e) { + this.g_main=function(t:any, e:any) { // All keys match but do nothing. return 1; } + this.gs=function(t:any, e:any) { + return this.g_main(t, e); + } } -const DOUBLED_BKSP_KEYBOARD_SCRIPT = function keyboard_core () { +const DOUBLED_BKSP_KEYBOARD_SCRIPT = function keyboard_core (this: any) { Object.assign(this, COMMON_KBD_SCRIPT_PROPS); this.KI="Keyboard_doubled_backspace"; this.KN="Doubled Backspace"; - this.g_main=function(t,e) { - var k=KeymanWeb; + this.KO=function(count: number, t: any, output: string) { + // Delete 'count' characters from the text store and output the replacement + t.deleteCharsBeforeCaret(count); + if(output) { + t.insertTextBeforeCaret(output); + } + }; + this.g_main=function(t: { getTextBeforeCaret: () => string; }, e: { Lcode: number; }) { // Standard compiled keyboards do not directly use any methods on `t`. if(e.Lcode == Codes.keyCodes.K_BKSP && KMWString.length(t.getTextBeforeCaret()) >= 2){ // If the context has at least two characters, delete two. - k.KO(2, t, '') + this.KO(2, t, '') // Our rule matched, so signal that. return 1; } else { @@ -62,22 +72,26 @@ const DOUBLED_BKSP_KEYBOARD_SCRIPT = function keyboard_core () { return 0; } } + this.gs=function(t:any, e:any) { + return this.g_main(t, e); + } } describe('Engine - specialized backspace handling', function() { const ipaPath = require.resolve('@keymanapp/common-test-resources/keyboards/sil_ipa.js'); const angkorPath = require.resolve('@keymanapp/common-test-resources/keyboards/khmer_angkor.js'); - let device = { + const device = { formFactor: 'desktop', OS: 'windows', - browser: 'native' - } + browser: 'native', + touchable: false + } as DeviceSpec; - let ipaWithHarness; - let angkorWithHarness; - let dummiedWithHarness; - let bksp2xWithHarness; + let ipaWithHarness: JSKeyboardInterface; + let angkorWithHarness: JSKeyboardInterface; + let dummiedWithHarness: JSKeyboardInterface; + let bksp2xWithHarness: JSKeyboardInterface; before(async () => { // -- START: Standard keyboard unit test loading boilerplate -- @@ -89,7 +103,7 @@ describe('Engine - specialized backspace handling', function() { // This part provides extra assurance that the keyboard properly loaded. assert.equal(keyboard.id, "Keyboard_sil_ipa"); - harness.activeKeyboard = keyboard; + harness.activeKeyboard = keyboard as JSKeyboard; ipaWithHarness = harness; // -------------- @@ -103,7 +117,7 @@ describe('Engine - specialized backspace handling', function() { // This part provides extra assurance that the keyboard properly loaded. assert.equal(keyboard.id, "Keyboard_khmer_angkor"); - harness.activeKeyboard = keyboard; + harness.activeKeyboard = keyboard as JSKeyboard; angkorWithHarness = harness; // -------------- @@ -111,7 +125,7 @@ describe('Engine - specialized backspace handling', function() { harness = new JSKeyboardInterface(globalThis, MinimalKeymanGlobal); harness.install(); // Sets the keyboard as the harness's "loaded" keyboard, but not "active". - harness.KR(new DUMMIED_KEYS_KEYBOARD_SCRIPT()); + harness.KR(new (DUMMIED_KEYS_KEYBOARD_SCRIPT as any)()); harness.activeKeyboard = harness.loadedKeyboard; assert.isOk(harness.activeKeyboard); dummiedWithHarness = harness; @@ -121,27 +135,29 @@ describe('Engine - specialized backspace handling', function() { harness = new JSKeyboardInterface(globalThis, MinimalKeymanGlobal); harness.install(); // Sets the keyboard as the harness's "loaded" keyboard, but not "active". - harness.KR(new DOUBLED_BKSP_KEYBOARD_SCRIPT()); + harness.KR(new (DOUBLED_BKSP_KEYBOARD_SCRIPT as any)()); harness.activeKeyboard = harness.loadedKeyboard; assert.isOk(harness.activeKeyboard); bksp2xWithHarness = harness; }); it('empty context, positional keyboard', () => { - let contextSource = new SyntheticTextStore(''); - let event = new KeyEvent({ + const contextSource = new SyntheticTextStore(''); + const event = new KeyEvent({ Lcode: Codes.keyCodes.K_BKSP, Lmodifiers: 0, Lstates: ModifierKeyConstants.NOTCAPITALFLAG | ModifierKeyConstants.NOTNUMLOCKFLAG | ModifierKeyConstants.NOTSCROLLFLAG, LisVirtualKey: true, kName: '', vkCode: Codes.keyCodes.K_BKSP, - device: device + device }); const processor = new JSKeyboardProcessor(TEST_DEVICE, { - keyboardInterface: angkorWithHarness - }, DEFAULT_PROCESSOR_INIT_OPTIONS); + baseLayout: 'us', + keyboardInterface: angkorWithHarness, + defaultOutputRules: new DefaultOutputRules() + }); const result = processor.processKeystroke(event, contextSource); assert.isTrue(result.triggerKeyDefault); @@ -156,8 +172,8 @@ describe('Engine - specialized backspace handling', function() { }); it("empty context, positional keyboard, outputless-key that's not BKSP", () => { - let contextSource = new SyntheticTextStore(''); - let event = new KeyEvent({ + const contextSource = new SyntheticTextStore(''); + const event = new KeyEvent({ Lcode: Codes.keyCodes.K_A, Lmodifiers: 0, Lstates: ModifierKeyConstants.NOTCAPITALFLAG | ModifierKeyConstants.NOTNUMLOCKFLAG | ModifierKeyConstants.NOTSCROLLFLAG, @@ -171,8 +187,10 @@ describe('Engine - specialized backspace handling', function() { // We want to ensure error cases without output, on null context, don't act // like backspaces. const processor = new JSKeyboardProcessor(TEST_DEVICE, { - keyboardInterface: dummiedWithHarness - }, DEFAULT_PROCESSOR_INIT_OPTIONS); + baseLayout: 'us', + keyboardInterface: dummiedWithHarness, + defaultOutputRules: new DefaultOutputRules() + }); const result = processor.processKeystroke(event, contextSource); // Did match a keyboard rule. @@ -189,10 +207,10 @@ describe('Engine - specialized backspace handling', function() { }); it('empty context, positional keyboard, but text is selected', () => { - let contextSource = new SyntheticTextStore('selected text', 0); + const contextSource = new SyntheticTextStore('selected text', 0); contextSource.setSelection(0, KMWString.length(contextSource.getText())); - let event = new KeyEvent({ + const event = new KeyEvent({ Lcode: Codes.keyCodes.K_BKSP, Lmodifiers: 0, Lstates: ModifierKeyConstants.NOTCAPITALFLAG | ModifierKeyConstants.NOTNUMLOCKFLAG | ModifierKeyConstants.NOTSCROLLFLAG, @@ -203,8 +221,10 @@ describe('Engine - specialized backspace handling', function() { }); const processor = new JSKeyboardProcessor(TEST_DEVICE, { - keyboardInterface: angkorWithHarness - }, DEFAULT_PROCESSOR_INIT_OPTIONS); + baseLayout: 'us', + keyboardInterface: angkorWithHarness, + defaultOutputRules: new DefaultOutputRules() + }); const result = processor.processKeystroke(event, contextSource); assert.isFalse(result.triggerKeyDefault); @@ -226,8 +246,8 @@ describe('Engine - specialized backspace handling', function() { }); it('empty left-context, positional keyboard', () => { - let contextSource = new SyntheticTextStore('post-caret text', 0); - let event = new KeyEvent({ + const contextSource = new SyntheticTextStore('post-caret text', 0); + const event = new KeyEvent({ Lcode: Codes.keyCodes.K_BKSP, Lmodifiers: 0, Lstates: ModifierKeyConstants.NOTCAPITALFLAG | ModifierKeyConstants.NOTNUMLOCKFLAG | ModifierKeyConstants.NOTSCROLLFLAG, @@ -238,8 +258,10 @@ describe('Engine - specialized backspace handling', function() { }); const processor = new JSKeyboardProcessor(TEST_DEVICE, { - keyboardInterface: angkorWithHarness - }, DEFAULT_PROCESSOR_INIT_OPTIONS); + baseLayout: 'us', + keyboardInterface: angkorWithHarness, + defaultOutputRules: new DefaultOutputRules() + }); const result = processor.processKeystroke(event, contextSource); assert.isTrue(result.triggerKeyDefault); @@ -254,8 +276,8 @@ describe('Engine - specialized backspace handling', function() { }); it('empty context, mnemonic keyboard', () => { - let contextSource = new SyntheticTextStore(''); - let event = new KeyEvent({ + const contextSource = new SyntheticTextStore(''); + const event = new KeyEvent({ Lcode: Codes.keyCodes.K_BKSP, Lmodifiers: 0, Lstates: ModifierKeyConstants.NOTCAPITALFLAG | ModifierKeyConstants.NOTNUMLOCKFLAG | ModifierKeyConstants.NOTSCROLLFLAG, @@ -266,8 +288,10 @@ describe('Engine - specialized backspace handling', function() { }); const processor = new JSKeyboardProcessor(TEST_DEVICE, { - keyboardInterface: ipaWithHarness - }, DEFAULT_PROCESSOR_INIT_OPTIONS); + baseLayout: 'us', + keyboardInterface: ipaWithHarness, + defaultOutputRules: new DefaultOutputRules() + }); const result = processor.processKeystroke(event, contextSource); assert.isTrue(result.triggerKeyDefault); @@ -282,8 +306,8 @@ describe('Engine - specialized backspace handling', function() { }); it('final empty context, positional keyboard, rule-handled BKSP', () => { - let contextSource = new SyntheticTextStore('abc', 2); - let event = new KeyEvent({ + const contextSource = new SyntheticTextStore('abc', 2); + const event = new KeyEvent({ Lcode: Codes.keyCodes.K_BKSP, Lmodifiers: 0, Lstates: ModifierKeyConstants.NOTCAPITALFLAG | ModifierKeyConstants.NOTNUMLOCKFLAG | ModifierKeyConstants.NOTSCROLLFLAG, @@ -297,8 +321,10 @@ describe('Engine - specialized backspace handling', function() { // context exists. const processor = new JSKeyboardProcessor(TEST_DEVICE, { - keyboardInterface: bksp2xWithHarness - }, DEFAULT_PROCESSOR_INIT_OPTIONS); + baseLayout: 'us', + keyboardInterface: bksp2xWithHarness, + defaultOutputRules: new DefaultOutputRules() + }); const result = processor.processKeystroke(event, contextSource); // Did match a keyboard rule. @@ -317,8 +343,8 @@ describe('Engine - specialized backspace handling', function() { // Special case: BKSP rule-matches with empty left-context. it("empty context, positional keyboard, outputless BKSP rule", () => { - let contextSource = new SyntheticTextStore(''); - let event = new KeyEvent({ + const contextSource = new SyntheticTextStore(''); + const event = new KeyEvent({ Lcode: Codes.keyCodes.K_BKSP, Lmodifiers: 0, Lstates: ModifierKeyConstants.NOTCAPITALFLAG | ModifierKeyConstants.NOTNUMLOCKFLAG | ModifierKeyConstants.NOTSCROLLFLAG, @@ -332,8 +358,10 @@ describe('Engine - specialized backspace handling', function() { // We want to ensure error cases without output, on null context, don't act // like backspaces. const processor = new JSKeyboardProcessor(TEST_DEVICE, { - keyboardInterface: dummiedWithHarness - }, DEFAULT_PROCESSOR_INIT_OPTIONS); + baseLayout: 'us', + keyboardInterface: dummiedWithHarness, + defaultOutputRules: new DefaultOutputRules() + }); const result = processor.processKeystroke(event, contextSource); assert.isFalse(result.triggerKeyDefault); diff --git a/web/src/test/auto/headless/engine/js-processor/transcriptions.tests.js b/web/src/test/auto/headless/engine/js-processor/transcriptions.tests.js index 6275d80061..ab1403f2fa 100644 --- a/web/src/test/auto/headless/engine/js-processor/transcriptions.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/transcriptions.tests.js @@ -175,8 +175,8 @@ describe("Transcriptions and Transforms", function() { it("handles context-free single-char output rules", function() { // We have other texts validating SyntheticTextStores; by using them as our base 'element', this unit test file // could eventually run in 'headless' mode. - const textStore = new SyntheticTextStore("apple"); - const originalTextStore = SyntheticTextStore.from(textStore); + let textStore = new SyntheticTextStore("apple"); + let originalTextStore = SyntheticTextStore.from(textStore); textStore.insertTextBeforeCaret("s"); /* It's not exactly black box, but presently we don't NEED the keyEvent object for the method to work. @@ -441,7 +441,7 @@ but not himself.`; // Sheev Palpatine, in the Star Wars prequels. const originalTextStore = SyntheticTextStore.from(textStore); textStore.clearSelection(); - const transform = textStore.textStore(originalTextStore); + const transform = textStore.buildTransformFrom(originalTextStore); assert.deepEqual(transform, { insert: '', deleteLeft: 0, diff --git a/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js index 7892178dfc..2033794572 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js @@ -2,9 +2,9 @@ import { assert } from 'chai'; import sinon from 'sinon'; import { ManagedPromise } from 'keyman/common/web-utils'; -import { CloudQueryEngine, StubAndKeyboardCache, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; +import { CloudQueryEngine, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; -import NodeCloudRequester from '../../../resources/loader/nodeCloudRequester.js'; +import { NodeCloudRequester } from 'keyman/test/resources'; import path from 'path'; import { fileURLToPath } from 'url'; diff --git a/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js index ee93c0eb99..5d740b7c1c 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js @@ -2,14 +2,11 @@ import { assert } from 'chai'; import sinon from 'sinon'; import fs from 'fs'; -import { KeyboardHarness, ManagedPromise, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; -import { - KeyboardRequisitioner, - toPrefixedKeyboardId as prefixed -} from 'keyman/engine/keyboard-storage'; +import { ManagedPromise } from 'keyman/common/web-utils'; +import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { NodeKeyboardLoader, NodeCloudRequester } from 'keyman/test/resources'; +import { KeyboardRequisitioner, toPrefixedKeyboardId } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; -import NodeCloudRequester from '../../../resources/loader/nodeCloudRequester.js'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -197,7 +194,7 @@ describe("KeyboardRequisitioner", () => { assert.equal(stubs.length, 2); for(let stub of stubs) { - assert.equal(stub.KI, prefixed('sil_euro_latin')); + assert.equal(stub.KI, toPrefixedKeyboardId('sil_euro_latin')); assert.equal(stub.KN, "EuroLatin (SIL)"); } @@ -216,7 +213,7 @@ describe("KeyboardRequisitioner", () => { assert.equal(stubs.length, 278); for(let stub of stubs) { - assert.equal(stub.KI, prefixed('sil_cameroon_azerty')); + assert.equal(stub.KI, toPrefixedKeyboardId('sil_cameroon_azerty')); assert.equal(stub.KN, "Cameroon AZERTY"); } diff --git a/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js index 96b6782892..40a3f17827 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js @@ -2,7 +2,7 @@ import { assert } from 'chai'; import sinon from 'sinon'; import { KeyboardStub } from 'keyman/engine/keyboard-storage'; -import NodeCloudRequester from '../../../resources/loader/nodeCloudRequester.js'; +import { NodeCloudRequester } from 'keyman/test/resources'; import path from 'path'; import { fileURLToPath } from 'url'; diff --git a/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js index 468906b1fc..7659495da3 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js @@ -1,7 +1,7 @@ import { assert } from 'chai'; import sinon from 'sinon'; -import NodeCloudRequester from '../../../resources/loader/nodeCloudRequester.js'; +import { NodeCloudRequester } from 'keyman/test/resources'; import path from 'path'; import { fileURLToPath } from 'url'; diff --git a/web/src/test/auto/headless/engine/keyboard-storage/stubAndKeyboardCache.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/stubAndKeyboardCache.tests.js index 9b120d01e6..106c76b202 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/stubAndKeyboardCache.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/stubAndKeyboardCache.tests.js @@ -4,7 +4,7 @@ import fs from 'fs'; import { KeyboardStub, StubAndKeyboardCache } from 'keyman/engine/keyboard-storage'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import path from 'path'; diff --git a/web/src/test/auto/headless/engine/keyboard/keyboard-loading.tests.js b/web/src/test/auto/headless/engine/keyboard/keyboard-loading.tests.js index e4c206d5f5..62a145f392 100644 --- a/web/src/test/auto/headless/engine/keyboard/keyboard-loading.tests.js +++ b/web/src/test/auto/headless/engine/keyboard/keyboard-loading.tests.js @@ -5,7 +5,7 @@ const require = createRequire(import.meta.url); import { KeyboardHarness, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; describe('Headless keyboard loading', function() { const laoPath = require.resolve('@keymanapp/common-test-resources/keyboards/lao_2008_basic.js'); diff --git a/web/src/test/auto/headless/engine/keyboard/keyboard.tests.ts b/web/src/test/auto/headless/engine/keyboard/keyboard.tests.ts index aae762bc70..ddd4e9e2e4 100644 --- a/web/src/test/auto/headless/engine/keyboard/keyboard.tests.ts +++ b/web/src/test/auto/headless/engine/keyboard/keyboard.tests.ts @@ -5,7 +5,7 @@ const require = createRequire(import.meta.url); import { DeviceSpec } from 'keyman/common/web-utils'; import { KeyboardHarness, MinimalKeymanGlobal, JSKeyboard } from 'keyman/engine/keyboard'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; describe('Keyboard tests', function () { diff --git a/web/src/test/auto/headless/engine/keyboard/keyboardLoaderBase.tests.ts b/web/src/test/auto/headless/engine/keyboard/keyboardLoaderBase.tests.ts index a5d4cad68d..62b0e7319b 100644 --- a/web/src/test/auto/headless/engine/keyboard/keyboardLoaderBase.tests.ts +++ b/web/src/test/auto/headless/engine/keyboard/keyboardLoaderBase.tests.ts @@ -6,7 +6,7 @@ const require = createRequire(import.meta.url); import { DeviceSpec } from 'keyman/common/web-utils'; import { KeyboardHarness, MinimalKeymanGlobal, KeyboardDownloadError, InvalidKeyboardError, JSKeyboard, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; import { assertThrowsAsync, assertThrows } from 'keyman/tools/testing/test-utils'; describe('Headless keyboard loading', function() { diff --git a/web/src/test/auto/headless/engine/main/headless/inputProcessor.tests.js b/web/src/test/auto/headless/engine/main/headless/inputProcessor.tests.js index c871be3b0f..85a5c26246 100644 --- a/web/src/test/auto/headless/engine/main/headless/inputProcessor.tests.js +++ b/web/src/test/auto/headless/engine/main/headless/inputProcessor.tests.js @@ -6,11 +6,11 @@ const require = createRequire(import.meta.url); import { InputProcessor } from 'keyman/engine/main'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { DefaultOutputRules, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; +import { DEFAULT_PROCESSOR_INIT_OPTIONS, NodeKeyboardLoader } from 'keyman/test/resources'; import { KeyboardTest } from '@keymanapp/recorder-core'; -import { Worker } from '@keymanapp/lexical-model-layer/node'; +import { NodeWorker } from '@keymanapp/lexical-model-layer/node'; import * as utils from 'keyman/common/web-utils'; const KMWString = utils.KMWString; @@ -31,7 +31,7 @@ KMWString.enableSupplementaryPlane(false); describe('InputProcessor', function() { describe('[[constructor]]', function () { it('should initialize without errors', function () { - let core = new InputProcessor(device); + let core = new InputProcessor(device, null, DEFAULT_PROCESSOR_INIT_OPTIONS); assert.isNotNull(core); }); @@ -40,7 +40,11 @@ describe('InputProcessor', function() { try { // Can construct without the second parameter; if so, the final assertion - .mayPredict // will be invalidated. (No worker, no ability to predict.) - core = new InputProcessor(device, Worker); + core = new InputProcessor(device, NodeWorker, { + baseLayout: 'us', + keyboardInterface: new JSKeyboardInterface({}, null, null), + defaultOutputRules: new DefaultOutputRules() + }); assert.isOk(core.keyboardProcessor); assert.isDefined(core.keyboardProcessor.contextDevice); @@ -103,7 +107,7 @@ describe('InputProcessor', function() { describe('without fat-fingering', function() { it('with minimal context (no fat-fingers)', function() { this.timeout(32); // ms - let core = new InputProcessor(device); + let core = new InputProcessor(device, null, DEFAULT_PROCESSOR_INIT_OPTIONS); let context = new SyntheticTextStore("", 0); core.keyboardProcessor.keyboardInterface = keyboardWithHarness; @@ -123,7 +127,7 @@ describe('InputProcessor', function() { this.timeout(500); // 500 ms, excluding text import. // These often run on VMs, so we'll be a bit generous. - let core = new InputProcessor(device); // I mean, it IS long context, and time + let core = new InputProcessor(device, null, DEFAULT_PROCESSOR_INIT_OPTIONS); // I mean, it IS long context, and time // thresholding is disabled within Node. core.keyboardProcessor.keyboardInterface = keyboardWithHarness; @@ -140,7 +144,7 @@ describe('InputProcessor', function() { describe('with fat-fingering', function() { it('with minimal context (with fat-fingers)', function() { this.timeout(32); // ms - let core = new InputProcessor(device); + let core = new InputProcessor(device, null, DEFAULT_PROCESSOR_INIT_OPTIONS); let context = new SyntheticTextStore("", 0); core.keyboardProcessor.keyboardInterface = keyboardWithHarness; @@ -164,7 +168,7 @@ describe('InputProcessor', function() { // Keep at the same 'order of magnitude' as the // 'without fat-fingers' test. - let core = new InputProcessor(device); // It IS long context, and time + let core = new InputProcessor(device, null, DEFAULT_PROCESSOR_INIT_OPTIONS); // It IS long context, and time // thresholding is disabled within Node. core.keyboardProcessor.keyboardInterface = keyboardWithHarness; @@ -201,7 +205,7 @@ describe('InputProcessor', function() { for (let testSet of testDefinitions.inputTestSets[0]['testSet']) { it(testSet.msg ?? 'test', function() { this.timeout(32); // ms - let core = new InputProcessor(device); + let core = new InputProcessor(device, null, DEFAULT_PROCESSOR_INIT_OPTIONS); let context = new SyntheticTextStore("", 0); core.keyboardProcessor.keyboardInterface = keyboardWithHarness; diff --git a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js index a8b524f6b2..13f6458c6c 100644 --- a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js +++ b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js @@ -13,7 +13,11 @@ import path from 'path'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../../'); // Required initialization setup. global.keyman = {}; // So that keyboard-based checks against the global `keyman` succeed. diff --git a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureMatcher.tests.ts b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureMatcher.tests.ts index a06ae76657..4211c4573a 100644 --- a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureMatcher.tests.ts +++ b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureMatcher.tests.ts @@ -2,14 +2,13 @@ import { assert } from 'chai' import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; -const PromiseStatuses = PromiseStatusModule.PromiseStatuses; -import { assertingPromiseStatus as promiseStatus } from '../../../../../resources/assertingPromiseStatus.js'; +const PromiseStatuses = PromiseStatusModule.PromiseStatuses; import { InputSample, gestures, GestureDebugPath } from 'keyman/engine/gesture-processor'; import { TouchpathTurtle } from '#gesture-tools'; -import { simulateMultiSourceMatcherInput } from "../../../../../resources/simulateMultiSourceInput.js"; +import { assertingPromiseStatus as promiseStatus, simulateMultiSourceMatcherInput } from 'keyman/test/resources'; import { FlickEndModel, diff --git a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureSequence.tests.ts b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureSequence.tests.ts index 2b0fbd32de..8460a640f6 100644 --- a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureSequence.tests.ts +++ b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureSequence.tests.ts @@ -2,7 +2,6 @@ import { assert } from 'chai' import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; -import { assertingPromiseStatus as promiseStatus } from '../../../../../resources/assertingPromiseStatus.js'; import { GestureModelDefs, buildGestureMatchInspector, gestures } from 'keyman/engine/gesture-processor'; const { matchers } = gestures; @@ -19,7 +18,7 @@ const modelSetForAction = gestures.matchers.modelSetForAction; import { HeadlessInputEngine, TouchpathTurtle } from '#gesture-tools'; import { ManagedPromise, timedPromise } from 'keyman/common/web-utils'; -import { assertGestureSequence, SequenceAssertion } from "../../../../../resources/sequenceAssertions.js"; +import { assertingPromiseStatus as promiseStatus, assertGestureSequence, SequenceAssertion } from 'keyman/test/resources'; import { LongpressModel, diff --git a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/matcherSelector.tests.ts b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/matcherSelector.tests.ts index 9ed3b846a0..51c69d8f5c 100644 --- a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/matcherSelector.tests.ts +++ b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/matcherSelector.tests.ts @@ -3,9 +3,8 @@ import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; const PromiseStatuses = PromiseStatusModule.PromiseStatuses; -import { assertingPromiseStatus as promiseStatus } from '../../../../../resources/assertingPromiseStatus.js'; -import { simulateMultiSourceMatcherInput, simulateSelectorInput } from "../../../../../resources/simulateMultiSourceInput.js"; +import { assertingPromiseStatus as promiseStatus, simulateMultiSourceMatcherInput, simulateSelectorInput } from 'keyman/test/resources'; import { timedPromise } from 'keyman/common/web-utils'; import { gestures } from 'keyman/engine/gesture-processor'; diff --git a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/pathMatcher.tests.ts b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/pathMatcher.tests.ts index 703057edb1..c9ad7bdef5 100644 --- a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/pathMatcher.tests.ts +++ b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/pathMatcher.tests.ts @@ -3,7 +3,7 @@ import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; const PromiseStatuses = PromiseStatusModule.PromiseStatuses; -import { assertingPromiseStatus as promiseStatus } from '../../../../../resources/assertingPromiseStatus.js'; +import { assertingPromiseStatus as promiseStatus } from 'keyman/test/resources'; import { InputSample, GestureSource, gestures, CumulativePathStats } from 'keyman/engine/gesture-processor'; import { timedPromise } from 'keyman/common/web-utils'; diff --git a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/touchpointCoordinator.tests.ts b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/touchpointCoordinator.tests.ts index 0deb01224c..a37a27871a 100644 --- a/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/touchpointCoordinator.tests.ts +++ b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/touchpointCoordinator.tests.ts @@ -2,7 +2,7 @@ import { assert } from 'chai' import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; -import { assertingPromiseStatus as promiseStatus } from '../../../../../resources/assertingPromiseStatus.js'; +import { assertingPromiseStatus as promiseStatus, assertGestureSequence, SequenceAssertion } from 'keyman/test/resources'; import { GestureModelDefs, GestureSource, gestures, TouchpointCoordinator } from 'keyman/engine/gesture-processor'; const { matchers } = gestures; @@ -14,8 +14,6 @@ type GestureSequence = gestures.matchers.GestureSequence; import { HeadlessInputEngine, TouchpathTurtle } from '#gesture-tools'; import { ManagedPromise, timedPromise } from 'keyman/common/web-utils'; -import { assertGestureSequence, SequenceAssertion } from "../../../../../resources/sequenceAssertions.js"; - import { LongpressModel, ModipressEndModel, diff --git a/web/src/test/auto/resources/index.ts b/web/src/test/auto/resources/index.ts new file mode 100644 index 0000000000..8c176d04b3 --- /dev/null +++ b/web/src/test/auto/resources/index.ts @@ -0,0 +1,6 @@ +export { assertingPromiseStatus } from './assertingPromiseStatus.js'; +export { DEFAULT_PROCESSOR_INIT_OPTIONS } from './defaultProcessorInitOptions.js'; +export { NodeKeyboardLoader } from './loader/nodeKeyboardLoader.js'; +export { NodeCloudRequester } from './loader/nodeCloudRequester.js'; +export { StageReportAssertion, SequenceAssertion, assertGestureSequence } from './sequenceAssertions.js'; +export { simulateMultiSourceMatcherInput, simulateSelectorInput } from './simulateMultiSourceInput.js'; \ No newline at end of file diff --git a/web/src/test/auto/resources/loader/node-keyboard-loader.ts b/web/src/test/auto/resources/loader/node-keyboard-loader.ts deleted file mode 100644 index 9630e113b0..0000000000 --- a/web/src/test/auto/resources/loader/node-keyboard-loader.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './nodeKeyboardLoader.js'; \ No newline at end of file diff --git a/web/src/test/auto/resources/loader/nodeCloudRequester.ts b/web/src/test/auto/resources/loader/nodeCloudRequester.ts index cd287eb407..2e654971f3 100644 --- a/web/src/test/auto/resources/loader/nodeCloudRequester.ts +++ b/web/src/test/auto/resources/loader/nodeCloudRequester.ts @@ -1,17 +1,16 @@ import { ManagedPromise } from 'keyman/common/web-utils'; -import { CloudRequesterInterface } from '../../../../engine/src/keyboard-storage/cloud/requesterInterface.js'; import { - CLOUD_TIMEOUT_ERR, - CLOUD_STUB_REGISTRATION_ERR, + CloudRequesterInterface, CloudQueryResult, - CloudQueryEngine -} from '../../../../engine/src/keyboard-storage/cloud/queryEngine.js'; + CloudQueryEngine, + unitTestEndpoints +} from 'keyman/engine/keyboard-storage'; import fs from 'node:fs'; import https from 'node:https'; import vm from 'node:vm'; -export default class NodeCloudRequester implements CloudRequesterInterface { +export class NodeCloudRequester implements CloudRequesterInterface { private static QUERY_SEED = 1; private readonly fileLocal: boolean; @@ -34,7 +33,7 @@ export default class NodeCloudRequester implements CloudRequesterInterface { // Set callback timer const timeoutObj = setTimeout(() => { - promise.reject(new Error(CLOUD_TIMEOUT_ERR)); + promise.reject(new Error(unitTestEndpoints.CLOUD_TIMEOUT_ERR)); }, 10000); const queryId = NodeCloudRequester.QUERY_SEED++; @@ -56,7 +55,7 @@ export default class NodeCloudRequester implements CloudRequesterInterface { }); if(!promise.isResolved) { - promise.reject(new Error(CLOUD_STUB_REGISTRATION_ERR)); + promise.reject(new Error(unitTestEndpoints.CLOUD_STUB_REGISTRATION_ERR)); } } diff --git a/web/src/test/auto/resources/loader/tsconfig.node.json b/web/src/test/auto/resources/loader/tsconfig.node.json deleted file mode 100644 index 0f657843ac..0000000000 --- a/web/src/test/auto/resources/loader/tsconfig.node.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../../../../../tsconfig.base.json", - "compilerOptions": { - "types": [ "node" ], - "outDir": "../../../../../../build/engine/keyboard/obj/keyboards/loaders/", - "tsBuildInfoFile": "../../../../../../build/engine/keyboard/obj/keyboards/loaders/tsconfig.node.tsbuildinfo", - "rootDir": "." - }, - "references": [ - { "path": "../../../tsconfig.json" } - ], - "include": ["node-keyboard-loader.ts", "nodeKeyboardLoader.ts"], -} diff --git a/web/src/test/auto/resources/simulateMultiSourceInput.ts b/web/src/test/auto/resources/simulateMultiSourceInput.ts index cf7c2516cd..452cec9285 100644 --- a/web/src/test/auto/resources/simulateMultiSourceInput.ts +++ b/web/src/test/auto/resources/simulateMultiSourceInput.ts @@ -247,7 +247,7 @@ function simulateMultiSourceInput( // ------- - let flattenedSpecs = processedSetup.map( + const flattenedSpecs = processedSetup.map( (entry) => entry.sourceSpecs ).reduce((constructingArray, entries) => constructingArray.concat(entries), []); @@ -365,7 +365,7 @@ export function simulateSelectorInput( selectorPromise: Promise>, executor: () => Promise } { - let inputClone = [].concat(input); + const inputClone = [].concat(input); // We NEED the sequences specified to be in chronological order of their start. // We'll just check if it's done properly out-of-the-gate - by sorting a clone, then comparing. diff --git a/web/src/test/auto/resources/tsconfig.json b/web/src/test/auto/resources/tsconfig.json new file mode 100644 index 0000000000..7ed8bfc57e --- /dev/null +++ b/web/src/test/auto/resources/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "build/", + "rootDir": ".", + "tsBuildInfoFile": "build/tsconfig.tsbuildinfo", + "skipLibCheck": true, + "noEmitOnError": false + }, + + "include": [ "*.ts", "loader/*.ts", "@types/*.d.ts" ], + "exclude": [ "node_modules" ], + + "references": [ + { "path": "../../../common/web-utils" }, + { "path": "../../../engine" }, + ] +} diff --git a/web/src/test/auto/tsconfig.json b/web/src/test/auto/tsconfig.json index 506b0610d4..75f6a5d17a 100644 --- a/web/src/test/auto/tsconfig.json +++ b/web/src/test/auto/tsconfig.json @@ -11,11 +11,12 @@ "dom/**/*.ts", "headless/**/*.ts", "integrated/**/*.ts", - "resources/**/*.ts", + "resources/@types/*.d.ts", "predictive-text/**/*.ts", ], "references": [ { "path": "../../engine" }, + { "path": "resources" } ] } From 1d9b8cc883026619a0a437b0991b2ab1fe729439 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Thu, 29 Jan 2026 15:41:58 +0100 Subject: [PATCH 2/4] maint(web): address code review comments - inline consts so that we don't have to export them for unit testing - create `getKeymanRoot` and `getWebTestResourcesPath` helper functions to DRY out the code - run `languageProcessortests.js` without coverage to prevent a failure creating the coverage report. --- package-lock.json | 155 ------------------ web/common.inc.sh | 20 ++- web/src/engine/build.sh | 31 ++-- .../cloud/cloudQueryEngine.ts | 13 +- .../src/keyboard-storage/domCloudRequester.ts | 8 +- web/src/engine/src/keyboard-storage/index.ts | 7 - .../engine/js-processor/basic-engine.tests.js | 9 +- .../engine/js-processor/chirality.tests.js | 9 +- .../engine/js-processor/deadkeys.tests.js | 9 +- .../engine/unmatched_final_group.tests.js | 8 +- .../keyboard-storage/cloudQueries.tests.js | 16 +- .../keyboardRequisitioner.tests.js | 22 +-- .../keyboard-storage/keyboardStub.tests.js | 10 +- .../nodeCloudRequester.tests.js | 14 +- .../headless/engine/loadKeyboardHelper.ts | 6 +- .../main/headless/languageProcessor.tests.js | 10 +- web/src/test/auto/resources/index.ts | 14 +- .../resources/loader/nodeCloudRequester.ts | 7 +- 18 files changed, 81 insertions(+), 287 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0c99d2e6a..514e17774a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1360,19 +1360,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/netbsd-arm64": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", @@ -1652,17 +1639,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@keymanapp/auto-history-action": { "resolved": "resources/build/version", "link": true @@ -3272,34 +3248,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/@types/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", @@ -4552,16 +4500,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { "version": "6.0.2", "dev": true, @@ -4741,13 +4679,6 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, - "node_modules/arg": { - "version": "4.1.3", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/argparse": { "version": "2.0.1", "dev": true, @@ -5911,13 +5842,6 @@ "version": "1.0.3", "license": "MIT" }, - "node_modules/create-require": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -9920,13 +9844,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true - }, "node_modules/make-fetch-happen": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", @@ -13020,61 +12937,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/tsc-watch": { "version": "4.6.2", "dev": true, @@ -13458,13 +13320,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "dev": true, @@ -13922,16 +13777,6 @@ "node": ">= 4.0.0" } }, - "node_modules/yn": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "dev": true, diff --git a/web/common.inc.sh b/web/common.inc.sh index 2c7ee35fd8..2d222bc34c 100644 --- a/web/common.inc.sh +++ b/web/common.inc.sh @@ -88,17 +88,19 @@ function prepare() { # test-headless engine/osk # ``` function test-headless() { - TEST_FOLDER=$1 - TEST_BASE="${KEYMAN_ROOT}/web/src/test/auto/headless/" - TEST_EXTENSIONS=${2:-} - if [[ ! -z "${2:-}" ]]; then + local TEST_FOLDER=$1 + local TEST_BASE="${KEYMAN_ROOT}/web/src/test/auto/headless/" + local TEST_EXTENSIONS=${2:-} + shift $(( $# < 2 ? $# : 2 )) + + if [[ ! -z "${TEST_EXTENSIONS}" ]]; then TEST_BASE="${KEYMAN_ROOT}/web/build/test/headless/" # Ensure the compiled tests are available. tsc --project "${KEYMAN_ROOT}/web/src/test/auto/tsconfig.json" fi - TEST_OPTS=() + local TEST_OPTS=() if builder_is_running_on_teamcity; then TEST_OPTS+=(--reporter "${KEYMAN_ROOT}/common/test/resources/mocha-teamcity-reporter/teamcity.cjs" --reporter-options parentFlowId="unit_tests") echo "##teamcity[flowStarted flowId='unit_tests']" @@ -108,10 +110,14 @@ function test-headless() { TEST_OPTS+=(--extension "${TEST_EXTENSIONS}") fi - builder_echo '> ' mocha --recursive "${TEST_BASE}${TEST_FOLDER}" "${TEST_OPTS[@]}" - if [[ -e .c8rc.json ]]; then + # Add any remaining arguments directly to Mocha. + TEST_OPTS+=("$@") + + if [[ -e .c8rc.json && -z "${SKIP_C8:-}" ]]; then + builder_echo '> ' c8 mocha --recursive "${TEST_BASE}${TEST_FOLDER}" "${TEST_OPTS[@]}" c8 mocha --recursive "${TEST_BASE}${TEST_FOLDER}" "${TEST_OPTS[@]}" else + builder_echo '> ' mocha --recursive "${TEST_BASE}${TEST_FOLDER}" "${TEST_OPTS[@]}" mocha --recursive "${TEST_BASE}${TEST_FOLDER}" "${TEST_OPTS[@]}" fi diff --git a/web/src/engine/build.sh b/web/src/engine/build.sh index 328d6a4a5d..aa496e3eb6 100755 --- a/web/src/engine/build.sh +++ b/web/src/engine/build.sh @@ -69,26 +69,21 @@ do_build () { } run_tests() { - local OUTPUT_FILE FAILURE_COUNT - # Remove stale coverage data - rm -rf "${KEYMAN_ROOT}/web/build/coverage/raw/engine" - - # Unfortunately we get an error from the coverage report generation: + # Run javascript tests + # + # Trying to run languageProcessor.tests.js with c8 coverage fails with: # "TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file" - # The following lines ignore the exit code and instead check the number - # of failed tests from the output. - set +e - OUTPUT_FILE=$(mktemp) - test-headless engine "" 2>&1 | tee "${OUTPUT_FILE}" - set -e - - FAILURE_COUNT=$(grep ' failing' "${OUTPUT_FILE}" | xargs | cut -f 1 -d' ') - rm "${OUTPUT_FILE}" - builder_echo "(The 'TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file' is expected)" - if ((FAILURE_COUNT > 0)); then - builder_die "Headless engine tests failed (.js tests)" - fi + # when c8 tries to create a report from the raw coverage data. The reason + # is a URL starting with `data:text/javascript` coming from + # `web/src/engine/predictive-text/worker-main/src/node/mappedWorker.ts`. + # + # So we first run all javascript tests except languageProcessor.tests.js + # with coverage, and then in a second step run languageProcessor.tests.js + # without coverage. + test-headless engine "" "--exclude" "**/languageProcessor.tests.js" + SKIP_C8=1 test-headless engine/main/headless/languageProcessor.tests.js + # Run typescript tests test-headless-typescript engine } diff --git a/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts b/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts index b6ec76df07..7f71681fb1 100644 --- a/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts +++ b/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts @@ -7,15 +7,6 @@ import { LanguageAPIPropertySpec } from 'keyman/engine/keyboard'; import { CloudRequesterInterface } from './requesterInterface.js'; import { ManagedPromise, Version } from 'keyman/common/web-utils'; -// For when the API call straight-up times out. -export const CLOUD_TIMEOUT_ERR = "The Cloud API request timed out."; -// Currently cannot distinguish between "no matching keyboard" and other script-load errors. -export const CLOUD_MALFORMED_OBJECT_ERR = "Could not find a keyboard with that ID."; -// Represents unspecified errors that occur when registering the results of a successful API call. -export const CLOUD_STUB_REGISTRATION_ERR = "The Cloud API failed to find an appropriate keyboard."; -// Represents custom, specified KMW errors that occur when registering the results of a successful API call. -export const CLOUD_REGISTRATION_ERR = "Error occurred while registering keyboards: "; - export const MISSING_KEYBOARD = function(kbdid: string) { return kbdid + ' keyboard not found.'; } @@ -139,7 +130,7 @@ export class CloudQueryEngine extends EventEmitter { try { result = this._registerCore(x); } catch(err) { - result = new Error(CLOUD_REGISTRATION_ERR + err); + result = new Error(`Error occurred while registering keyboards: ${err}`); } if(!promiseid) { @@ -199,7 +190,7 @@ export class CloudQueryEngine extends EventEmitter { // Ignore callback unless the context is defined if(typeof(options) == 'undefined' || typeof(options['context']) == 'undefined') { - return new Error(CLOUD_MALFORMED_OBJECT_ERR); + return new Error('Could not find a keyboard with that ID.'); } // Register each keyboard for the specified language codes diff --git a/web/src/engine/src/keyboard-storage/domCloudRequester.ts b/web/src/engine/src/keyboard-storage/domCloudRequester.ts index 6d13b43cfd..d97b31687a 100644 --- a/web/src/engine/src/keyboard-storage/domCloudRequester.ts +++ b/web/src/engine/src/keyboard-storage/domCloudRequester.ts @@ -1,6 +1,5 @@ import { ManagedPromise } from 'keyman/common/web-utils'; import { CloudRequesterInterface } from './cloud/requesterInterface.js'; -import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/cloudQueryEngine.js'; export class DOMCloudRequester implements CloudRequesterInterface { private readonly fileLocal: boolean; @@ -14,7 +13,7 @@ export class DOMCloudRequester implements CloudRequesterInterface { // Set callback timer const timeoutID = window.setTimeout(() => { - promise.reject(new Error(CLOUD_TIMEOUT_ERR)); + promise.reject(new Error('The Cloud API request timed out.')); }, 10000); const tFlag='&timerid='+ timeoutID; @@ -28,7 +27,7 @@ export class DOMCloudRequester implements CloudRequesterInterface { // script does not ever call `register`. Also provides default handling // should `register` fail to report results/failure correctly. if(!promise.isResolved) { - promise.reject(new Error(CLOUD_STUB_REGISTRATION_ERR)); + promise.reject(new Error('The Cloud API failed to find an appropriate keyboard.')); } }; @@ -42,7 +41,8 @@ export class DOMCloudRequester implements CloudRequesterInterface { lineno?: number, colno?: number, error?: Error) => { window.clearTimeout(timeoutID); - let msg = CLOUD_MALFORMED_OBJECT_ERR; + // Currently cannot distinguish between "no matching keyboard" and other script-load errors. + let msg = 'Could not find a keyboard with that ID.'; if(error) { msg = msg + ": " + error.message; } diff --git a/web/src/engine/src/keyboard-storage/index.ts b/web/src/engine/src/keyboard-storage/index.ts index e6e3b7b990..86d52c699d 100644 --- a/web/src/engine/src/keyboard-storage/index.ts +++ b/web/src/engine/src/keyboard-storage/index.ts @@ -13,10 +13,3 @@ export { CloudRequesterInterface } from './cloud/requesterInterface.js'; export { KeyboardRequisitioner } from './keyboardRequisitioner.js'; export { ModelCache } from './modelCache.js'; export { DOMCloudRequester } from './domCloudRequester.js'; - -import { CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/cloudQueryEngine.js'; - -export const unitTestEndpoints = { - CLOUD_TIMEOUT_ERR, - CLOUD_STUB_REGISTRATION_ERR -}; diff --git a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js index 40d6b6797b..8283cda218 100644 --- a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js @@ -7,15 +7,10 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from 'keyman/test/resources'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; -import { env } from 'node:process'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../'); +const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Basic Simulation', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/basic_lao_simulation.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js index f99428c413..9522d4411a 100644 --- a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js @@ -6,16 +6,11 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from 'keyman/test/resources'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { ModifierKeyConstants } from '@keymanapp/common-types'; -import { env } from 'node:process'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../'); +const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Chirality', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/chirality.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js index e02fb1adaf..3a244f69e2 100644 --- a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js @@ -6,15 +6,10 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from 'keyman/test/resources'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; -import { env } from 'node:process'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../'); +const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Deadkeys', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/deadkeys.json')); diff --git a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js index d558d89830..5cd9f4c51a 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js @@ -6,15 +6,11 @@ const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader } from 'keyman/test/resources'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; -import { env } from 'node:process'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; -const __dirname = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../../'); +const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Unmatched Final Groups', function() { let testJSONtext = fs.readFileSync(require.resolve('@keymanapp/common-test-resources/json/engine_tests/ghp_enter.json')); diff --git a/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js index 2033794572..84bd27b6e5 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js @@ -4,13 +4,7 @@ import sinon from 'sinon'; import { ManagedPromise } from 'keyman/common/web-utils'; import { CloudQueryEngine, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; -import { NodeCloudRequester } from 'keyman/test/resources'; - -import path from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { NodeCloudRequester, getWebTestResourcesPath } from 'keyman/test/resources'; const pathConfig = new PathConfiguration({ root: '', @@ -159,7 +153,7 @@ describe("Cloud-query interface", () => { describe('Stub fetching', () => { it('sil_euro_latin@no,sv', async () => { - const querier = mockQuery(`${__dirname}/../../../resources/query-mock-results/sil_euro_latin@no_sv.js.fixture`); + const querier = mockQuery(`${getWebTestResourcesPath()}/query-mock-results/sil_euro_latin@no_sv.js.fixture`); const promise = querier.fetchCloudStubs(['sil_euro_latin@no', 'sil_euro_latin@sv']); const stubs = await promise; @@ -176,7 +170,7 @@ describe("Cloud-query interface", () => { }); it('sil_cameroon_azerty', async () => { - const querier = mockQuery(`${__dirname}/../../../resources/query-mock-results/sil_cameroon_azerty.js.fixture`); + const querier = mockQuery(`${getWebTestResourcesPath()}/query-mock-results/sil_cameroon_azerty.js.fixture`); const promise = querier.fetchCloudStubs(['sil_cameroon_azerty']); const stubs = await promise; @@ -192,7 +186,7 @@ describe("Cloud-query interface", () => { }); it('@dz', async () => { - const querier = mockQuery(`${__dirname}/../../../resources/query-mock-results/@dz.js.fixture`); + const querier = mockQuery(`${getWebTestResourcesPath()}/query-mock-results/@dz.js.fixture`); const promise = querier.fetchCloudStubs(['@dz']); const stubs = await promise; @@ -209,7 +203,7 @@ describe("Cloud-query interface", () => { }); it('language list fetching', async () => { - const querier = mockQuery(`${__dirname}/../../../resources/query-mock-results/languages.js.fixture`); + const querier = mockQuery(`${getWebTestResourcesPath()}/query-mock-results/languages.js.fixture`); const promise = querier.languageListPromise; const langList = await promise; diff --git a/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js index 5d740b7c1c..8b408f7c16 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js @@ -4,15 +4,11 @@ import fs from 'fs'; import { ManagedPromise } from 'keyman/common/web-utils'; import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { NodeKeyboardLoader, NodeCloudRequester } from 'keyman/test/resources'; +import { NodeKeyboardLoader, NodeCloudRequester, getWebTestResourcesPath } from 'keyman/test/resources'; import { KeyboardRequisitioner, toPrefixedKeyboardId } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; import path from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); import { createRequire } from 'module'; const require = createRequire(import.meta.url); @@ -157,7 +153,7 @@ describe("KeyboardRequisitioner", () => { }); it('drops requests for already fetched stubs', async () => { - let setupDB = mockedSetup(`${__dirname}/../../../resources/query-mock-results/sil_euro_latin@no_sv.js.fixture`); + let setupDB = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/sil_euro_latin@no_sv.js.fixture`); let promise = setupDB.addKeyboardArray(['sil_euro_latin@no,sv']); const setupStubs = await promise; @@ -187,7 +183,7 @@ describe("KeyboardRequisitioner", () => { describe('Stub fetching', () => { it('sil_euro_latin@no,sv', async () => { - const keyboardRequisitioner = mockedSetup(`${__dirname}/../../../resources/query-mock-results/sil_euro_latin@no_sv.js.fixture`); + const keyboardRequisitioner = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/sil_euro_latin@no_sv.js.fixture`); const promise = keyboardRequisitioner.addKeyboardArray(['sil_euro_latin@no,sv']); const stubs = await promise; @@ -206,7 +202,7 @@ describe("KeyboardRequisitioner", () => { }); it('sil_cameroon_azerty', async () => { - const keyboardRequisitioner = mockedSetup(`${__dirname}/../../../resources/query-mock-results/sil_cameroon_azerty.js.fixture`); + const keyboardRequisitioner = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/sil_cameroon_azerty.js.fixture`); const promise = keyboardRequisitioner.addKeyboardArray(['sil_cameroon_azerty']); const stubs = await promise; @@ -224,7 +220,7 @@ describe("KeyboardRequisitioner", () => { }); it('@dz', async () => { - const keyboardRequisitioner = mockedSetup(`${__dirname}/../../../resources/query-mock-results/@dz.js.fixture`); + const keyboardRequisitioner = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/@dz.js.fixture`); const promise = keyboardRequisitioner.addKeyboardArray(['@dz']); const stubs = await promise; @@ -241,12 +237,12 @@ describe("KeyboardRequisitioner", () => { }); it('drops requests for already fetched stubs', async () => { - let setupDB = mockedSetup(`${__dirname}/../../../resources/query-mock-results/sil_euro_latin@no_sv.js.fixture`); + let setupDB = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/sil_euro_latin@no_sv.js.fixture`); let promise = setupDB.addKeyboardArray(['sil_euro_latin@no,sv']); const setupStubs = await promise; - const precachedRequisitioner = mockedSetup(`${__dirname}/../../../resources/query-mock-results/@dz.js.fixture`); + const precachedRequisitioner = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/@dz.js.fixture`); // Pre-load our 'setup' stubs into the query manager class before running the query. for(let stub of setupStubs) { precachedRequisitioner.cache.addStub(stub); @@ -268,7 +264,7 @@ describe("KeyboardRequisitioner", () => { describe('addLanguageKeyboards', function() { it('awaits the language list fetch + constructs a query for the requested language', async () => { - const keyboardRequisitioner = mockedSetup(`${__dirname}/../../../resources/query-mock-results/languages.js.fixture`); + const keyboardRequisitioner = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/languages.js.fixture`); const mockedRequester = keyboardRequisitioner.cloudQueryEngine.requestEngine; const originalRequest = mockedRequester.request; let swapFake = sinon.fake((query) => { @@ -304,7 +300,7 @@ describe("KeyboardRequisitioner", () => { // https://api.keyman.com/cloud/4.0/keyboards?jsonp=keyman.register&languageidtype=bcp47&version=17.0&keyboardid=khmer_angkor&timerid=49. // // The edits are minimal and notated within the fixture file. - const keyboardRequisitioner = mockedSetup(`${__dirname}/../../../resources/query-mock-results/khmer_angkor.hand-edited.js.fixture`); + const keyboardRequisitioner = mockedSetup(`${getWebTestResourcesPath()}/query-mock-results/khmer_angkor.hand-edited.js.fixture`); const cache = keyboardRequisitioner.cache; const [stub] = await keyboardRequisitioner.addKeyboardArray(['khmer_angkor']); diff --git a/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js index 40a3f17827..90af0886d5 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js @@ -2,13 +2,7 @@ import { assert } from 'chai'; import sinon from 'sinon'; import { KeyboardStub } from 'keyman/engine/keyboard-storage'; -import { NodeCloudRequester } from 'keyman/test/resources'; - -import path from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { NodeCloudRequester, getWebTestResourcesPath } from 'keyman/test/resources'; describe("KeyboardStub", () => { function performMockedRequest(mockedResultsFile) { @@ -81,7 +75,7 @@ describe("KeyboardStub", () => { }); it('merge(): barebones stub + fetched sil_euro_latin@no', async () => { - const query = performMockedRequest(`${__dirname}/../../../resources/query-mock-results/sil_euro_latin@no_sv.js.fixture`); + const query = performMockedRequest(`${getWebTestResourcesPath()}/query-mock-results/sil_euro_latin@no_sv.js.fixture`); await query.promise; assert.isTrue(query.mockedRegister.called); diff --git a/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js index 7659495da3..07172489bf 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js @@ -1,13 +1,7 @@ import { assert } from 'chai'; import sinon from 'sinon'; -import { NodeCloudRequester } from 'keyman/test/resources'; - -import path from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { NodeCloudRequester, getWebTestResourcesPath } from 'keyman/test/resources'; describe("Mocked cloud query results in headless mode ('canary' testing)", () => { function performMockedRequest(mockedResultsFile) { @@ -38,7 +32,7 @@ describe("Mocked cloud query results in headless mode ('canary' testing)", () => } it('sil_euro_latin@no,sv', async () => { - const query = performMockedRequest(`${__dirname}/../../../resources/query-mock-results/sil_euro_latin@no_sv.js.fixture`); + const query = performMockedRequest(`${getWebTestResourcesPath()}/query-mock-results/sil_euro_latin@no_sv.js.fixture`); await query.promise; assert.isTrue(query.mockedRegister.called); @@ -58,7 +52,7 @@ describe("Mocked cloud query results in headless mode ('canary' testing)", () => }); it('sil_cameroon_azerty', async () => { - const query = performMockedRequest(`${__dirname}/../../../resources/query-mock-results/sil_cameroon_azerty.js.fixture`); + const query = performMockedRequest(`${getWebTestResourcesPath()}/query-mock-results/sil_cameroon_azerty.js.fixture`); await query.promise; assert.isTrue(query.mockedRegister.called); @@ -75,7 +69,7 @@ describe("Mocked cloud query results in headless mode ('canary' testing)", () => }); it('@dz', async () => { - const query = performMockedRequest(`${__dirname}/../../../resources/query-mock-results/@dz.js.fixture`); + const query = performMockedRequest(`${getWebTestResourcesPath()}/query-mock-results/@dz.js.fixture`); await query.promise; assert.isTrue(query.mockedRegister.called); diff --git a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts index fa1be9f276..49dd8ea789 100644 --- a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts +++ b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts @@ -4,12 +4,10 @@ import fs from 'node:fs'; import { pathToFileURL } from 'node:url'; +import { getKeymanRoot } from 'keyman/test/resources'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; +const KEYMAN_ROOT = getKeymanRoot(); -const __dirname = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = process.env['KEYMAN_ROOT'] ?? (__dirname + '/../../../../../../'); export const coreurl = pathToFileURL(`${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core`).toString(); export function loadKeyboardBlob(filename: string) { diff --git a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js index 13f6458c6c..ff63e99fb2 100644 --- a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js +++ b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js @@ -1,5 +1,6 @@ import { assert } from 'chai'; +import path from 'path'; import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main'; import { SourcemappedWorker as LMWorker } from "@keymanapp/lexical-model-layer/node"; import { SyntheticTextStore } from 'keyman/engine/keyboard'; @@ -9,15 +10,10 @@ import { SyntheticTextStore } from 'keyman/engine/keyboard'; */ import { LexicalModelCompiler } from '@keymanapp/kmc-model'; -import path from 'path'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { getKeymanRoot } from 'keyman/test/resources'; -import { env } from 'node:process'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const KEYMAN_ROOT = env.KEYMAN_ROOT ?? (__dirname + '/../../../../../../../../'); +const KEYMAN_ROOT = getKeymanRoot(); // Required initialization setup. global.keyman = {}; // So that keyboard-based checks against the global `keyman` succeed. diff --git a/web/src/test/auto/resources/index.ts b/web/src/test/auto/resources/index.ts index 8c176d04b3..f95209c386 100644 --- a/web/src/test/auto/resources/index.ts +++ b/web/src/test/auto/resources/index.ts @@ -3,4 +3,16 @@ export { DEFAULT_PROCESSOR_INIT_OPTIONS } from './defaultProcessorInitOptions.js export { NodeKeyboardLoader } from './loader/nodeKeyboardLoader.js'; export { NodeCloudRequester } from './loader/nodeCloudRequester.js'; export { StageReportAssertion, SequenceAssertion, assertGestureSequence } from './sequenceAssertions.js'; -export { simulateMultiSourceMatcherInput, simulateSelectorInput } from './simulateMultiSourceInput.js'; \ No newline at end of file +export { simulateMultiSourceMatcherInput, simulateSelectorInput } from './simulateMultiSourceInput.js'; + +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; + +export function getKeymanRoot(): string { + const __dirname = dirname(fileURLToPath(import.meta.url)); + return process.env['KEYMAN_ROOT'] ?? (__dirname + '/../../../../../../'); +} + +export function getWebTestResourcesPath(): string { + return getKeymanRoot() + '/web/src/test/auto/resources'; +} diff --git a/web/src/test/auto/resources/loader/nodeCloudRequester.ts b/web/src/test/auto/resources/loader/nodeCloudRequester.ts index 2e654971f3..676749fea6 100644 --- a/web/src/test/auto/resources/loader/nodeCloudRequester.ts +++ b/web/src/test/auto/resources/loader/nodeCloudRequester.ts @@ -2,8 +2,7 @@ import { ManagedPromise } from 'keyman/common/web-utils'; import { CloudRequesterInterface, CloudQueryResult, - CloudQueryEngine, - unitTestEndpoints + CloudQueryEngine } from 'keyman/engine/keyboard-storage'; import fs from 'node:fs'; @@ -33,7 +32,7 @@ export class NodeCloudRequester implements CloudRequesterInterface { // Set callback timer const timeoutObj = setTimeout(() => { - promise.reject(new Error(unitTestEndpoints.CLOUD_TIMEOUT_ERR)); + promise.reject(new Error('The Cloud API request timed out.')); }, 10000); const queryId = NodeCloudRequester.QUERY_SEED++; @@ -55,7 +54,7 @@ export class NodeCloudRequester implements CloudRequesterInterface { }); if(!promise.isResolved) { - promise.reject(new Error(unitTestEndpoints.CLOUD_STUB_REGISTRATION_ERR)); + promise.reject(new Error('The Cloud API failed to find an appropriate keyboard.')); } } From 06f7f22cd0e2b2bd529a1341c26206841639cc62 Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Mon, 23 Mar 2026 15:08:30 +0100 Subject: [PATCH 3/4] maint(web): address more code review comments Co-authored-by: Marc Durdin --- .../src/keyboard-storage/cloud/cloudQueryEngine.ts | 13 +++++++++++-- .../src/keyboard-storage/domCloudRequester.ts | 8 ++++---- web/src/test/auto/resources/index.ts | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts b/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts index 7f71681fb1..b6ec76df07 100644 --- a/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts +++ b/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts @@ -7,6 +7,15 @@ import { LanguageAPIPropertySpec } from 'keyman/engine/keyboard'; import { CloudRequesterInterface } from './requesterInterface.js'; import { ManagedPromise, Version } from 'keyman/common/web-utils'; +// For when the API call straight-up times out. +export const CLOUD_TIMEOUT_ERR = "The Cloud API request timed out."; +// Currently cannot distinguish between "no matching keyboard" and other script-load errors. +export const CLOUD_MALFORMED_OBJECT_ERR = "Could not find a keyboard with that ID."; +// Represents unspecified errors that occur when registering the results of a successful API call. +export const CLOUD_STUB_REGISTRATION_ERR = "The Cloud API failed to find an appropriate keyboard."; +// Represents custom, specified KMW errors that occur when registering the results of a successful API call. +export const CLOUD_REGISTRATION_ERR = "Error occurred while registering keyboards: "; + export const MISSING_KEYBOARD = function(kbdid: string) { return kbdid + ' keyboard not found.'; } @@ -130,7 +139,7 @@ export class CloudQueryEngine extends EventEmitter { try { result = this._registerCore(x); } catch(err) { - result = new Error(`Error occurred while registering keyboards: ${err}`); + result = new Error(CLOUD_REGISTRATION_ERR + err); } if(!promiseid) { @@ -190,7 +199,7 @@ export class CloudQueryEngine extends EventEmitter { // Ignore callback unless the context is defined if(typeof(options) == 'undefined' || typeof(options['context']) == 'undefined') { - return new Error('Could not find a keyboard with that ID.'); + return new Error(CLOUD_MALFORMED_OBJECT_ERR); } // Register each keyboard for the specified language codes diff --git a/web/src/engine/src/keyboard-storage/domCloudRequester.ts b/web/src/engine/src/keyboard-storage/domCloudRequester.ts index d97b31687a..6d13b43cfd 100644 --- a/web/src/engine/src/keyboard-storage/domCloudRequester.ts +++ b/web/src/engine/src/keyboard-storage/domCloudRequester.ts @@ -1,5 +1,6 @@ import { ManagedPromise } from 'keyman/common/web-utils'; import { CloudRequesterInterface } from './cloud/requesterInterface.js'; +import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/cloudQueryEngine.js'; export class DOMCloudRequester implements CloudRequesterInterface { private readonly fileLocal: boolean; @@ -13,7 +14,7 @@ export class DOMCloudRequester implements CloudRequesterInterface { // Set callback timer const timeoutID = window.setTimeout(() => { - promise.reject(new Error('The Cloud API request timed out.')); + promise.reject(new Error(CLOUD_TIMEOUT_ERR)); }, 10000); const tFlag='&timerid='+ timeoutID; @@ -27,7 +28,7 @@ export class DOMCloudRequester implements CloudRequesterInterface { // script does not ever call `register`. Also provides default handling // should `register` fail to report results/failure correctly. if(!promise.isResolved) { - promise.reject(new Error('The Cloud API failed to find an appropriate keyboard.')); + promise.reject(new Error(CLOUD_STUB_REGISTRATION_ERR)); } }; @@ -41,8 +42,7 @@ export class DOMCloudRequester implements CloudRequesterInterface { lineno?: number, colno?: number, error?: Error) => { window.clearTimeout(timeoutID); - // Currently cannot distinguish between "no matching keyboard" and other script-load errors. - let msg = 'Could not find a keyboard with that ID.'; + let msg = CLOUD_MALFORMED_OBJECT_ERR; if(error) { msg = msg + ": " + error.message; } diff --git a/web/src/test/auto/resources/index.ts b/web/src/test/auto/resources/index.ts index f95209c386..2e2425b6c4 100644 --- a/web/src/test/auto/resources/index.ts +++ b/web/src/test/auto/resources/index.ts @@ -10,7 +10,7 @@ import { dirname } from 'node:path'; export function getKeymanRoot(): string { const __dirname = dirname(fileURLToPath(import.meta.url)); - return process.env['KEYMAN_ROOT'] ?? (__dirname + '/../../../../../../'); + return __dirname + '/../../../../../../'; } export function getWebTestResourcesPath(): string { From bbad49015491ea260a8458f4e0947709a51f24ac Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Mon, 23 Mar 2026 15:40:21 +0100 Subject: [PATCH 4/4] maint(web): cleanup imports For the files touched in this PR: - fix the order of the imports according to style guide - use `node:` prefix for node modules - add header --- .../keyboard-storage/cloud/cloudQueryEngine.ts | 13 ++++++++----- .../src/keyboard-storage/domCloudRequester.ts | 6 +++++- .../engine/js-processor/basic-engine.tests.js | 16 +++++++++------- .../engine/js-processor/chirality.tests.js | 17 ++++++++++------- .../engine/js-processor/deadkeys.tests.js | 9 ++++++--- .../engine/unmatched_final_group.tests.js | 16 +++++++++------- .../keyboard-storage/cloudQueries.tests.js | 5 ++++- .../keyboardRequisitioner.tests.js | 17 ++++++++++------- .../keyboard-storage/keyboardStub.tests.js | 3 +++ .../nodeCloudRequester.tests.js | 3 +++ .../auto/headless/engine/loadKeyboardHelper.ts | 1 + .../main/headless/languageProcessor.tests.js | 17 +++++++++-------- web/src/test/auto/resources/index.ts | 9 ++++++--- .../auto/resources/loader/nodeCloudRequester.ts | 17 ++++++++++------- 14 files changed, 93 insertions(+), 56 deletions(-) diff --git a/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts b/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts index b6ec76df07..f640a4046a 100644 --- a/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts +++ b/web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts @@ -1,11 +1,14 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import { EventEmitter } from 'eventemitter3'; -import { PathConfiguration } from 'keyman/engine/interfaces'; - -import { KeyboardStub, ErrorStub, KeyboardAPISpec } from '../keyboardStub.js'; -import { LanguageAPIPropertySpec } from 'keyman/engine/keyboard'; -import { CloudRequesterInterface } from './requesterInterface.js'; import { ManagedPromise, Version } from 'keyman/common/web-utils'; +import { PathConfiguration } from 'keyman/engine/interfaces'; +import { LanguageAPIPropertySpec } from 'keyman/engine/keyboard'; + +import { ErrorStub, KeyboardAPISpec, KeyboardStub } from '../keyboardStub.js'; +import { CloudRequesterInterface } from './requesterInterface.js'; // For when the API call straight-up times out. export const CLOUD_TIMEOUT_ERR = "The Cloud API request timed out."; diff --git a/web/src/engine/src/keyboard-storage/domCloudRequester.ts b/web/src/engine/src/keyboard-storage/domCloudRequester.ts index 6d13b43cfd..79d0fcd5bf 100644 --- a/web/src/engine/src/keyboard-storage/domCloudRequester.ts +++ b/web/src/engine/src/keyboard-storage/domCloudRequester.ts @@ -1,6 +1,10 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import { ManagedPromise } from 'keyman/common/web-utils'; + +import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_STUB_REGISTRATION_ERR, CLOUD_TIMEOUT_ERR } from './cloud/cloudQueryEngine.js'; import { CloudRequesterInterface } from './cloud/requesterInterface.js'; -import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/cloudQueryEngine.js'; export class DOMCloudRequester implements CloudRequesterInterface { private readonly fileLocal: boolean; diff --git a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js index 8283cda218..5e801458e9 100644 --- a/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/basic-engine.tests.js @@ -1,15 +1,17 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +import fs from 'node:fs'; + import { assert } from 'chai'; -import fs from 'fs'; - - import { createRequire } from 'module'; -const require = createRequire(import.meta.url); -import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; +import { JSKeyboardInterface } from 'keyman/engine/js-processor'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; +const require = createRequire(import.meta.url); const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Basic Simulation', function() { diff --git a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js index 9522d4411a..263b0d1d94 100644 --- a/web/src/test/auto/headless/engine/js-processor/chirality.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/chirality.tests.js @@ -1,15 +1,18 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +import fs from 'node:fs'; + import { assert } from 'chai'; -import fs from 'fs'; - import { createRequire } from 'module'; -const require = createRequire(import.meta.url); -import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; -import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; import { ModifierKeyConstants } from '@keymanapp/common-types'; +import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; +import { JSKeyboardInterface } from 'keyman/engine/js-processor'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; +const require = createRequire(import.meta.url); const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Chirality', function() { diff --git a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js index 3a244f69e2..367f0c9759 100644 --- a/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/deadkeys.tests.js @@ -1,14 +1,17 @@ -import { assert } from 'chai'; -import fs from 'fs'; +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +import fs from 'node:fs'; +import { assert } from 'chai'; import { createRequire } from 'module'; -const require = createRequire(import.meta.url); import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; import { JSKeyboardInterface } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; +const require = createRequire(import.meta.url); const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Deadkeys', function() { diff --git a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js index 5cd9f4c51a..57d4acc2a7 100644 --- a/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js +++ b/web/src/test/auto/headless/engine/js-processor/engine/unmatched_final_group.tests.js @@ -1,15 +1,17 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +import fs from 'node:fs'; + import { assert } from 'chai'; -import fs from 'fs'; - import { createRequire } from 'module'; -const require = createRequire(import.meta.url); -import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { JSKeyboardInterface } from 'keyman/engine/js-processor'; -import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; +import { JSKeyboardInterface } from 'keyman/engine/js-processor'; +import { MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; - +const require = createRequire(import.meta.url); const KEYMAN_ROOT = getKeymanRoot(); describe('Engine - Unmatched Final Groups', function() { diff --git a/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js index 84bd27b6e5..015ee3c3da 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/cloudQueries.tests.js @@ -1,9 +1,12 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import { assert } from 'chai'; import sinon from 'sinon'; import { ManagedPromise } from 'keyman/common/web-utils'; -import { CloudQueryEngine, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; +import { CloudQueryEngine, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; import { NodeCloudRequester, getWebTestResourcesPath } from 'keyman/test/resources'; const pathConfig = new PathConfiguration({ diff --git a/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js index 8b408f7c16..ddcaed3a82 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardRequisitioner.tests.js @@ -1,16 +1,19 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +import fs from 'node:fs'; +import path from 'node:path'; + import { assert } from 'chai'; +import { createRequire } from 'module'; import sinon from 'sinon'; -import fs from 'fs'; import { ManagedPromise } from 'keyman/common/web-utils'; -import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { NodeKeyboardLoader, NodeCloudRequester, getWebTestResourcesPath } from 'keyman/test/resources'; -import { KeyboardRequisitioner, toPrefixedKeyboardId } from 'keyman/engine/keyboard-storage'; import { PathConfiguration } from 'keyman/engine/interfaces'; +import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; +import { KeyboardRequisitioner, toPrefixedKeyboardId } from 'keyman/engine/keyboard-storage'; +import { NodeCloudRequester, NodeKeyboardLoader, getWebTestResourcesPath } from 'keyman/test/resources'; -import path from 'path'; - -import { createRequire } from 'module'; const require = createRequire(import.meta.url); let commonResourcesPackage = '@keymanapp/common-test-resources'; diff --git a/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js index 90af0886d5..8aa19fcfd4 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/keyboardStub.tests.js @@ -1,3 +1,6 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import { assert } from 'chai'; import sinon from 'sinon'; diff --git a/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js index 07172489bf..7f31ffe70a 100644 --- a/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js +++ b/web/src/test/auto/headless/engine/keyboard-storage/nodeCloudRequester.tests.js @@ -1,3 +1,6 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import { assert } from 'chai'; import sinon from 'sinon'; diff --git a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts index 49dd8ea789..5d1dc8b838 100644 --- a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts +++ b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts @@ -4,6 +4,7 @@ import fs from 'node:fs'; import { pathToFileURL } from 'node:url'; + import { getKeymanRoot } from 'keyman/test/resources'; const KEYMAN_ROOT = getKeymanRoot(); diff --git a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js index 05c4ffcb29..3375279649 100644 --- a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js +++ b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js @@ -1,16 +1,17 @@ -import { assert } from 'chai'; - -import path from 'path'; -import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main'; -import { SourcemappedWorker as LMWorker } from "@keymanapp/lexical-model-layer/node"; -import { SyntheticTextStore } from 'keyman/engine/keyboard'; - /* + * Keyman is copyright (C) SIL Global. MIT License. + * * Unit tests for the Dummy prediction model. */ +import path from 'node:path'; + +import { assert } from 'chai'; -import { LexicalModelCompiler } from '@keymanapp/kmc-model'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { LexicalModelCompiler } from '@keymanapp/kmc-model'; +import { SourcemappedWorker as LMWorker } from "@keymanapp/lexical-model-layer/node"; +import { SyntheticTextStore } from 'keyman/engine/keyboard'; +import { LanguageProcessor, TranscriptionCache } from 'keyman/engine/main'; import { getKeymanRoot } from 'keyman/test/resources'; const KEYMAN_ROOT = getKeymanRoot(); diff --git a/web/src/test/auto/resources/index.ts b/web/src/test/auto/resources/index.ts index 2e2425b6c4..7a770a9f3d 100644 --- a/web/src/test/auto/resources/index.ts +++ b/web/src/test/auto/resources/index.ts @@ -1,12 +1,15 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ export { assertingPromiseStatus } from './assertingPromiseStatus.js'; export { DEFAULT_PROCESSOR_INIT_OPTIONS } from './defaultProcessorInitOptions.js'; -export { NodeKeyboardLoader } from './loader/nodeKeyboardLoader.js'; export { NodeCloudRequester } from './loader/nodeCloudRequester.js'; -export { StageReportAssertion, SequenceAssertion, assertGestureSequence } from './sequenceAssertions.js'; +export { NodeKeyboardLoader } from './loader/nodeKeyboardLoader.js'; +export { assertGestureSequence, SequenceAssertion, StageReportAssertion } from './sequenceAssertions.js'; export { simulateMultiSourceMatcherInput, simulateSelectorInput } from './simulateMultiSourceInput.js'; -import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; export function getKeymanRoot(): string { const __dirname = dirname(fileURLToPath(import.meta.url)); diff --git a/web/src/test/auto/resources/loader/nodeCloudRequester.ts b/web/src/test/auto/resources/loader/nodeCloudRequester.ts index 676749fea6..d7f63b393d 100644 --- a/web/src/test/auto/resources/loader/nodeCloudRequester.ts +++ b/web/src/test/auto/resources/loader/nodeCloudRequester.ts @@ -1,14 +1,17 @@ -import { ManagedPromise } from 'keyman/common/web-utils'; -import { - CloudRequesterInterface, - CloudQueryResult, - CloudQueryEngine -} from 'keyman/engine/keyboard-storage'; - +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import fs from 'node:fs'; import https from 'node:https'; import vm from 'node:vm'; +import { ManagedPromise } from 'keyman/common/web-utils'; +import { + CloudQueryEngine, + CloudQueryResult, + CloudRequesterInterface +} from 'keyman/engine/keyboard-storage'; + export class NodeCloudRequester implements CloudRequesterInterface { private static QUERY_SEED = 1; private readonly fileLocal: boolean;