diff --git a/package-lock.json b/package-lock.json index de8f8c7aaf..6f3f9a24ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4811,15 +4811,6 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/argparse": { "version": "2.0.1", "dev": true, @@ -5943,15 +5934,6 @@ "version": "1.0.3", "license": "MIT" }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "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", @@ -9870,15 +9852,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "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", @@ -12883,47 +12856,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "typescript": ">=2.7" - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "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, @@ -13760,18 +13692,6 @@ "node": ">= 4.0.0" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "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 b4a3c47d7e..2d222bc34c 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() { @@ -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/package.json b/web/package.json index f60aaef7c0..4c2ea67d84 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..cdf39ca569 100755 --- a/web/src/engine/build.sh +++ b/web/src/engine/build.sh @@ -14,9 +14,11 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" # ################################ Main script ################################ +# dependency on kmc required for tests builder_describe "Builds Keyman Engine for Web (KMW)" \ "@/core:wasm build" \ "@/common/tools/es-bundling build" \ + "@/developer/src/kmc build" \ "@/web/src/common/web-utils build" \ "clean" \ "configure" \ @@ -68,11 +70,30 @@ do_build () { node src/osk/validate-gesture-specs.js } -builder_run_action clean rm -rf "$KEYMAN_ROOT/web/build/engine" +run_tests() { + # 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" + # 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 +} + +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 99% rename from web/src/engine/src/keyboard-storage/cloud/queryEngine.ts rename to web/src/engine/src/keyboard-storage/cloud/cloudQueryEngine.ts index b6ec76df07..f640a4046a 100644 --- a/web/src/engine/src/keyboard-storage/cloud/queryEngine.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/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..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/queryEngine.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..86d52c699d 100644 --- a/web/src/engine/src/keyboard-storage/index.ts +++ b/web/src/engine/src/keyboard-storage/index.ts @@ -8,8 +8,8 @@ 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'; 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..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,17 +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 } from '../../../resources/loader/nodeKeyboardLoader.js'; 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'; -import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +const require = createRequire(import.meta.url); +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/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..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,17 +1,19 @@ +/* + * 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 } from '../../../resources/loader/nodeKeyboardLoader.js'; -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'; -import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +const require = createRequire(import.meta.url); +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 15bb2e824f..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,16 +1,18 @@ -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 } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader, getKeymanRoot } from 'keyman/test/resources'; import { KeyboardTest, NodeProctor } from '@keymanapp/recorder-core'; -import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +const require = createRequire(import.meta.url); +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/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..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,23 +1,25 @@ +/* + * 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 } from '../../../resources/loader/nodeKeyboardLoader.js'; 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'; -import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +const require = createRequire(import.meta.url); +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')); // 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..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,16 +1,13 @@ +/* + * 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, StubAndKeyboardCache, toPrefixedKeyboardId as prefixed } 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'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { CloudQueryEngine, toPrefixedKeyboardId as prefixed } from 'keyman/engine/keyboard-storage'; +import { NodeCloudRequester, getWebTestResourcesPath } from 'keyman/test/resources'; const pathConfig = new PathConfiguration({ root: '', @@ -159,7 +156,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 +173,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 +189,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 +206,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 ee93c0eb99..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,23 +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 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 { PathConfiguration } from 'keyman/engine/interfaces'; -import NodeCloudRequester from '../../../resources/loader/nodeCloudRequester.js'; - -import path from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - import { createRequire } from 'module'; +import sinon from 'sinon'; + +import { ManagedPromise } from 'keyman/common/web-utils'; +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'; + const require = createRequire(import.meta.url); let commonResourcesPackage = '@keymanapp/common-test-resources'; @@ -160,7 +156,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; @@ -190,14 +186,14 @@ 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; 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)"); } @@ -209,14 +205,14 @@ 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; 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"); } @@ -227,7 +223,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; @@ -244,12 +240,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); @@ -271,7 +267,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) => { @@ -307,7 +303,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 96b6782892..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,14 +1,11 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import { assert } from 'chai'; import sinon from 'sinon'; import { KeyboardStub } from 'keyman/engine/keyboard-storage'; -import NodeCloudRequester from '../../../resources/loader/nodeCloudRequester.js'; - -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 +78,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 468906b1fc..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,13 +1,10 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import { assert } from 'chai'; import sinon from 'sinon'; -import NodeCloudRequester from '../../../resources/loader/nodeCloudRequester.js'; - -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 +35,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 +55,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 +72,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/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/jskeyboard.tests.ts b/web/src/test/auto/headless/engine/keyboard/jskeyboard.tests.ts index 3d498a4787..dcf2de6b85 100644 --- a/web/src/test/auto/headless/engine/keyboard/jskeyboard.tests.ts +++ b/web/src/test/auto/headless/engine/keyboard/jskeyboard.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('JSKeyboard tests', function () { 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/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/keyboard/kmxkeyboard.tests.ts b/web/src/test/auto/headless/engine/keyboard/kmxkeyboard.tests.ts index 47be8ee0ec..9754e17977 100644 --- a/web/src/test/auto/headless/engine/keyboard/kmxkeyboard.tests.ts +++ b/web/src/test/auto/headless/engine/keyboard/kmxkeyboard.tests.ts @@ -6,7 +6,7 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { NodeKeyboardLoader } from '../../../resources/loader/nodeKeyboardLoader.js'; +import { NodeKeyboardLoader } from 'keyman/test/resources'; describe('KMXKeyboard tests', function () { // TODO-embed-osk-in-kmx: Enable when hooking up with RTL Core API (#15482) diff --git a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts index fa1be9f276..5d1dc8b838 100644 --- a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts +++ b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts @@ -5,11 +5,10 @@ import fs from 'node:fs'; import { pathToFileURL } from 'node:url'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; +import { getKeymanRoot } from 'keyman/test/resources'; + +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/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 f81583b527..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,19 +1,20 @@ -import { assert } from 'chai'; - -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 path from 'path'; 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'; -import { env } from 'node:process'; -const KEYMAN_ROOT = env.KEYMAN_ROOT; +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/headless/engine/osk/gesture-processor/gestures/gestureMatcher.tests.ts b/web/src/test/auto/headless/engine/osk/gesture-processor/gestures/gestureMatcher.tests.ts index 539eaea279..52f1dea656 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 @@ -4,9 +4,9 @@ import * as PromiseStatusModule from 'promise-status-async'; import { InputSample, gestures, GestureDebugPath } from 'keyman/engine/gesture-processor'; -import { assertingPromiseStatus as promiseStatus } from '#test-resources/assertingPromiseStatus.js'; import { TouchpathTurtle } from '#gesture-tools'; -import { simulateMultiSourceMatcherInput } from "#test-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 a8c44bc068..94b5f597f7 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 @@ -3,10 +3,18 @@ import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; import { GestureModelDefs, buildGestureMatchInspector, gestures } from 'keyman/engine/gesture-processor'; -import { ManagedPromise, timedPromise } from 'keyman/common/web-utils'; + import { HeadlessInputEngine, TouchpathTurtle } from '#gesture-tools'; -import { assertingPromiseStatus as promiseStatus } from '#test-resources/assertingPromiseStatus.js'; -import { assertGestureSequence, SequenceAssertion } from "#test-resources/sequenceAssertions.js"; +import { ManagedPromise, timedPromise } from 'keyman/common/web-utils'; + +import { assertingPromiseStatus as promiseStatus, assertGestureSequence, SequenceAssertion } from 'keyman/test/resources'; + +import { + LongpressModel, + MultitapModel, + SimpleTapModel, + SubkeySelectModel +} from './isolatedGestureSpecs.js'; const { matchers } = gestures; @@ -19,13 +27,6 @@ type MatcherSelection = gestures.matchers.MatcherSelection; const getGestureModelSet = gestures.specs.getGestureModelSet; const modelSetForAction = gestures.matchers.modelSetForAction; -import { - LongpressModel, - MultitapModel, - SimpleTapModel, - SubkeySelectModel -} from './isolatedGestureSpecs.js'; - const TestGestureModelDefinitions: GestureModelDefs = { gestures: [ 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 9df88ec45e..d727cf5923 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 @@ -2,13 +2,12 @@ import { assert } from 'chai' import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; +import { assertingPromiseStatus as promiseStatus, simulateMultiSourceMatcherInput, simulateSelectorInput } from 'keyman/test/resources'; import { timedPromise } from 'keyman/common/web-utils'; import { gestures } from 'keyman/engine/gesture-processor'; import { TouchpathTurtle } from '#gesture-tools'; -import { assertingPromiseStatus as promiseStatus } from '#test-resources/assertingPromiseStatus.js'; -import { simulateMultiSourceMatcherInput, simulateSelectorInput } from "#test-resources/simulateMultiSourceInput.js"; import { LongpressModel, @@ -56,7 +55,7 @@ describe("MatcherSelector", function () { specSet: [LongpressModel] }, this.fakeClock); - let completion = executor(); + const completion = executor(); await selectorPromise; await Promise.race([completion, selectionPromises[0]]); 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 5132af9892..64702cca86 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 @@ -1,11 +1,11 @@ import { assert } from 'chai' import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; +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'; -import { assertingPromiseStatus as promiseStatus } from '#test-resources/assertingPromiseStatus.js'; import { InstantRejectionModel, 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 ff1584ddb5..812a0e5651 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,12 +2,11 @@ import { assert } from 'chai' import sinon from 'sinon'; import * as PromiseStatusModule from 'promise-status-async'; import { PROMISE_PENDING } from 'promise-status-async'; +import { assertingPromiseStatus as promiseStatus, assertGestureSequence, SequenceAssertion } from 'keyman/test/resources'; import { GestureModelDefs, GestureSource, gestures, TouchpointCoordinator } from 'keyman/engine/gesture-processor'; import { ManagedPromise, timedPromise } from 'keyman/common/web-utils'; import { HeadlessInputEngine, TouchpathTurtle } from '#gesture-tools'; -import { assertingPromiseStatus as promiseStatus } from '#test-resources/assertingPromiseStatus.js'; -import { assertGestureSequence, SequenceAssertion } from "#test-resources/sequenceAssertions.js"; import { LongpressModel, diff --git a/web/src/test/auto/resources/index.ts b/web/src/test/auto/resources/index.ts new file mode 100644 index 0000000000..7a770a9f3d --- /dev/null +++ b/web/src/test/auto/resources/index.ts @@ -0,0 +1,21 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +export { assertingPromiseStatus } from './assertingPromiseStatus.js'; +export { DEFAULT_PROCESSOR_INIT_OPTIONS } from './defaultProcessorInitOptions.js'; +export { NodeCloudRequester } from './loader/nodeCloudRequester.js'; +export { NodeKeyboardLoader } from './loader/nodeKeyboardLoader.js'; +export { assertGestureSequence, SequenceAssertion, StageReportAssertion } from './sequenceAssertions.js'; +export { simulateMultiSourceMatcherInput, simulateSelectorInput } from './simulateMultiSourceInput.js'; + +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export function getKeymanRoot(): string { + const __dirname = dirname(fileURLToPath(import.meta.url)); + return __dirname + '/../../../../../../'; +} + +export function getWebTestResourcesPath(): string { + return getKeymanRoot() + '/web/src/test/auto/resources'; +} 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..d7f63b393d 100644 --- a/web/src/test/auto/resources/loader/nodeCloudRequester.ts +++ b/web/src/test/auto/resources/loader/nodeCloudRequester.ts @@ -1,17 +1,18 @@ -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, - CloudQueryResult, - CloudQueryEngine -} from '../../../../engine/src/keyboard-storage/cloud/queryEngine.js'; - +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ import fs from 'node:fs'; import https from 'node:https'; import vm from 'node:vm'; -export default class NodeCloudRequester implements CloudRequesterInterface { +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; @@ -34,7 +35,7 @@ export default class NodeCloudRequester implements CloudRequesterInterface { // Set callback timer const timeoutObj = setTimeout(() => { - promise.reject(new Error(CLOUD_TIMEOUT_ERR)); + promise.reject(new Error('The Cloud API request timed out.')); }, 10000); const queryId = NodeCloudRequester.QUERY_SEED++; @@ -56,7 +57,7 @@ export default class NodeCloudRequester implements CloudRequesterInterface { }); if(!promise.isResolved) { - promise.reject(new Error(CLOUD_STUB_REGISTRATION_ERR)); + promise.reject(new Error('The Cloud API failed to find an appropriate keyboard.')); } } 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" } ] }