Merge pull request #15497 from keymanapp/maint/web/jstests
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

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.
- remove `node-keyboard-loader.ts` which was just a wrapper around `NodeKeyboardLoader`.
- some cleanup and removal of default exports and aliases.
- moved `specialized-backspace.tests.js` to `specialized-backspace.tests.ts` and fixed resulting issues/warnings.
- apply fix from #15477 if KEYMAN_ROOT is not set
- inline some 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

I'm not happy to put the compiled files for `test/auto/resources` 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`.

Follows: #15093
This commit is contained in:
Eberhard Beilharz 2026-03-23 18:15:53 +01:00 committed by GitHub
commit 66c5f124cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 369 additions and 362 deletions

80
package-lock.json generated
View file

@ -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,

View file

@ -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

View file

@ -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": {

View file

@ -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

View file

@ -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.";

View file

@ -1,2 +0,0 @@
export { CloudQueryResult, CloudQueryEngine as QueryEngine } from './queryEngine.js';
export { CloudRequesterInterface as RequesterInterface } from './requesterInterface.js';

View file

@ -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;

View file

@ -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';
export { DOMCloudRequester } from './domCloudRequester.js';

View file

@ -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);

View file

@ -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'));

View file

@ -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.

View file

@ -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 {

View file

@ -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'));

View file

@ -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'));

View file

@ -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';

View file

@ -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 = {

View file

@ -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',

View file

@ -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',

View file

@ -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');

View file

@ -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

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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']);

View file

@ -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);

View file

@ -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);

View file

@ -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';

View file

@ -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 () {

View file

@ -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');

View file

@ -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() {

View file

@ -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)

View file

@ -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) {

View file

@ -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;

View file

@ -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.

View file

@ -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,

View file

@ -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<Type> = gestures.matchers.MatcherSelection<Type>;
const getGestureModelSet = gestures.specs.getGestureModelSet;
const modelSetForAction = gestures.matchers.modelSetForAction;
import {
LongpressModel,
MultitapModel,
SimpleTapModel,
SubkeySelectModel
} from './isolatedGestureSpecs.js';
const TestGestureModelDefinitions: GestureModelDefs<string> = {
gestures: [
LongpressModel,

View file

@ -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]]);

View file

@ -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,

View file

@ -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,

View file

@ -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';
}

View file

@ -1 +0,0 @@
export * from './nodeKeyboardLoader.js';

View file

@ -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.'));
}
}

View file

@ -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"],
}

View file

@ -247,7 +247,7 @@ function simulateMultiSourceInput<OutputType, Type>(
// -------
let flattenedSpecs = processedSetup.map(
const flattenedSpecs = processedSetup.map(
(entry) => entry.sourceSpecs
).reduce((constructingArray, entries) => constructingArray.concat(entries), []);
@ -365,7 +365,7 @@ export function simulateSelectorInput<Type>(
selectorPromise: Promise<MatcherSelector<Type>>,
executor: () => Promise<void>
} {
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.

View file

@ -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" },
]
}

View file

@ -11,11 +11,12 @@
"dom/**/*.ts",
"headless/**/*.ts",
"integrated/**/*.ts",
"resources/**/*.ts",
"resources/@types/*.d.ts",
"predictive-text/**/*.ts",
],
"references": [
{ "path": "../../engine" },
{ "path": "resources" }
]
}