diff --git a/web/package.json b/web/package.json index 357126aae4d..79c54619ef7 100644 --- a/web/package.json +++ b/web/package.json @@ -68,6 +68,9 @@ "import": "./build/engine/osk/obj/test-index.js" } }, + "imports": { + "#recorder": "./build/tools/testing/recorder/obj/index.js" + }, "repository": { "type": "git", "url": "git+https://github.com/keymanapp/keyman.git" diff --git a/web/src/app/browser/src/contextManager.ts b/web/src/app/browser/src/contextManager.ts index 435c8d68dfc..b553bb50b7d 100644 --- a/web/src/app/browser/src/contextManager.ts +++ b/web/src/app/browser/src/contextManager.ts @@ -227,7 +227,7 @@ export default class ContextManager extends ContextManagerBase, sendEvents: boolean) { + public setActiveTarget(target: OutputTarget, sendEvents?: boolean) { const previousTarget = this.mostRecentTarget; const originalTarget = this.activeTarget; // may differ, depending on focus state. diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts index 189541dd53e..9555fea2f7b 100644 --- a/web/src/app/browser/src/keymanEngine.ts +++ b/web/src/app/browser/src/keymanEngine.ts @@ -546,7 +546,7 @@ export default class KeymanEngine extends KeymanEngineBase { - return loadKeyboardFromJSON("/keyboards/options_with_save.json", testconfig.timeouts.scriptLoad); + return loadKeyboardFromJSON("resources/json/keyboards/options_with_save.json", baseTimeout); }).then(() => { return keyman.setActiveKeyboard(keyboardID, 'en'); }).then(() => { // This requires proper storage to a cookie, as we'll be on a new instance of the same keyboard. - var value = KeymanWeb.loadStore(prefixedKeyboardID, storeName, 0); - assert.equal(value, 1, "Did not properly save and reload variable store setting"); + var value = KeymanWeb.loadStore(prefixedKeyboardID, storeName, '0'); + assert.equal(value, '1', "Did not properly save and reload variable store setting"); }).finally(() => { - KeymanWeb.saveStore(storeName, 0); + KeymanWeb.saveStore(storeName, '0'); }); }); it("Multiple-sequence check", function() { - this.timeout(testconfig.timeouts.standard + testconfig.timeouts.scriptLoad * 3); + this.timeout(baseTimeout + baseTimeout * 3); + const keyman: KeymanEngine = window['keyman']; + const KeymanWeb: KeyboardInterface = window['KeymanWeb']; + var keyboardID = "options_with_save"; var storeName = "foo"; return keyman.setActiveKeyboard(keyboardID, 'en').then(async function() { - KeymanWeb.saveStore(storeName, 1); + KeymanWeb.saveStore(storeName, '1'); keyman.removeKeyboards(keyboardID); await Promise.resolve(); }).then(() => { // First test: expects option to be "on" from cookie-init setting, emitting "foo.", then turning option "off". - return runKeyboardTestFromJSON('/engine_tests/options_with_save_1.json', + return runKeyboardTestFromJSON('resources/json/engine_tests/options_with_save_1.json', {usingOSK: false}, assert.equal, - testconfig.timeouts.scriptLoad) + baseTimeout) }).then(async () => { // Reset the keyboard... again. keyman.removeKeyboards(keyboardID); @@ -129,10 +144,10 @@ describe('Engine - Browser Interactions', function() { await Promise.resolve(); // Second test: expects option to still be "off" b/c cookies. - return runKeyboardTestFromJSON('/engine_tests/options_with_save_2.json', + return runKeyboardTestFromJSON('resources/json/engine_tests/options_with_save_2.json', {usingOSK: false}, assert.equal, - testconfig.timeouts.scriptLoad); + baseTimeout); }); }); }); @@ -140,22 +155,22 @@ describe('Engine - Browser Interactions', function() { // Performs basic processing system checks/tests to ensure the sequence testing // is based on correct assumptions about the code. describe('Integrated Simulation Checks', function() { - this.timeout(testconfig.timeouts.standard); + this.timeout(baseTimeout); before(function() { - this.timeout = testconfig.timeouts.scriptLoad; - return loadKeyboardFromJSON("/keyboards/lao_2008_basic.json", testconfig.timeouts.scriptLoad); + this.timeout(baseTimeout); + return loadKeyboardFromJSON("resources/json/keyboards/lao_2008_basic.json", baseTimeout); }); beforeEach(function() { - var inputElem = document.getElementById('singleton'); + const keyman: KeymanEngine = window['keyman']; keyman.setActiveElement(inputElem); inputElem.value = ""; }); after(function() { + const keyman: KeymanEngine = window['keyman']; keyman.removeKeyboards('lao_2008_basic'); - fixture.cleanup(); }); /** @@ -165,8 +180,6 @@ describe('Engine - Browser Interactions', function() { * See #8830. */ it('Simple Keypress', function() { - var inputElem = document.getElementById('singleton'); - var lao_s_key_json = {"type": "key", "key":"s", "code":"KeyS","keyCode":83,"modifierSet":0,"location":0}; var lao_s_event = new KMWRecorder.PhysicalInputEventSpec(lao_s_key_json); @@ -180,10 +193,8 @@ describe('Engine - Browser Interactions', function() { }); it('Simple OSK click', async function() { - var inputElem = document.getElementById('singleton'); - var lao_s_osk_json = {"type": "osk", "keyID": 'shift-K_S'}; - var lao_s_event = new KMWRecorder.OSKInputEventSpec(lao_s_osk_json); + var lao_s_event = new KMWRecorder.OSKInputEventSpec(lao_s_osk_json as OSKInputEventSpec); let eventDriver = new KMWRecorder.BrowserDriver(inputElem); await eventDriver.simulateEvent(lao_s_event); @@ -196,32 +207,35 @@ describe('Engine - Browser Interactions', function() { }); describe('Sequence Simulation Checks', function() { - this.timeout(testconfig.timeouts.scriptLoad); + this.timeout(baseTimeout); it('Keyboard simulation', async function() { - return await runKeyboardTestFromJSON('/engine_tests/basic_lao_simulation.json', {usingOSK: false}, assert.equal, testconfig.timeouts.scriptLoad); + return await runKeyboardTestFromJSON('resources/json/engine_tests/basic_lao_simulation.json', {usingOSK: false}, assert.equal, baseTimeout); }); it('OSK simulation', async function() { - return await runKeyboardTestFromJSON('/engine_tests/basic_lao_simulation.json', {usingOSK: true}, assert.equal, testconfig.timeouts.scriptLoad); + return await runKeyboardTestFromJSON('resources/json/engine_tests/basic_lao_simulation.json', {usingOSK: true}, assert.equal, baseTimeout); }) }); }); describe('Unmatched Final Groups', function() { - this.timeout(testconfig.timeouts.scriptLoad); + this.timeout(baseTimeout); before(function() { - fixture.setBase('fixtures'); - return setupKMW(null, testconfig.timeouts.scriptLoad + testconfig.timeouts.eventDelay); + return setupKMW(null, baseTimeout + eventTimeout); }); beforeEach(function(done) { - fixture.load("singleTextArea.html"); + // Do NOT use an here, as that has special return-char handling that + // complicates matters. + const textArea = document.createElement('textarea'); + textArea.id = 'singleton'; + host.appendChild(textArea); window.setTimeout(function() { done() - }, testconfig.timeouts.eventDelay); + }, eventTimeout); }); after(function() { @@ -229,40 +243,41 @@ describe('Unmatched Final Groups', function() { }); afterEach(function() { - fixture.cleanup(); + host.innerHTML = ''; }); it('matches rule from early group AND performs default behavior', async function() { // While a TAB-oriented version would be nice, it's much harder to write the test // to detect change in last input element. - return await runKeyboardTestFromJSON('/engine_tests/ghp_enter.json', {usingOSK: true}, assert.equal, testconfig.timeouts.scriptLoad); + return await runKeyboardTestFromJSON('resources/json/engine_tests/ghp_enter.json', {usingOSK: true}, assert.equal, baseTimeout); }); }); // Kept separate to maintain an extra-clean setup for this test. describe('Engine - Browser Interactions', function() { - this.timeout(testconfig.timeouts.scriptLoad); - - before(function() { - fixture.setBase('fixtures'); - }); + this.timeout(baseTimeout); beforeEach(function() { - fixture.load("singleInput.html"); - return setupKMW(null, testconfig.timeouts.scriptLoad); + inputElem = document.createElement('input'); + inputElem.id = 'singleton'; + host.appendChild(inputElem); + + return setupKMW(null, baseTimeout); }); afterEach(function() { - fixture.cleanup(); + host.innerHTML = ''; teardownKMW(); }); describe('Keyboard Loading', function() { it('Local', function() { - this.timeout(testconfig.timeouts.scriptLoad); + this.timeout(baseTimeout); + const keyman: KeymanEngine = window['keyman']; + const KeymanWeb: KeyboardInterface = window['KeymanWeb']; - return loadKeyboardFromJSON("/keyboards/lao_2008_basic.json", - testconfig.timeouts.scriptLoad).then(async function() { + return loadKeyboardFromJSON("resources/json/keyboards/lao_2008_basic.json", + baseTimeout).then(async function() { assert.isNotNull(keyman.getKeyboard("lao_2008_basic", "lo"), "Keyboard stub was not registered!"); assert.equal(keyman.getActiveKeyboard(), "Keyboard_lao_2008_basic", "Keyboard not set automatically!"); keyman.removeKeyboards('lao_2008_basic'); @@ -273,15 +288,17 @@ describe('Engine - Browser Interactions', function() { }); it('Automatically sets first available keyboard', function() { - this.timeout(2 * testconfig.timeouts.scriptLoad); + this.timeout(2 * baseTimeout); + const keyman: KeymanEngine = window['keyman']; + const KeymanWeb: KeyboardInterface = window['KeymanWeb']; - return loadKeyboardFromJSON("/keyboards/lao_2008_basic.json", - testconfig.timeouts.scriptLoad, + return loadKeyboardFromJSON("resources/json/keyboards/lao_2008_basic.json", + baseTimeout, {passive: true}).then(() => { // Because we're loading the keyboard 'passively', KMW's setActiveKeyboard function is auto-called // on the stub-add. That specific call (for first keyboard auto-activation) is outside of KMW's // current Promise chain, so we can't _directly_ rely on a KMW Promise to test it. - return new Promise((resolve) => { + return new Promise((resolve) => { let hasResolved = false; // So, we give KMW the time needed for auto-activation to happen, polling a bit actively so that we don't // wait unnecessarily long after it occurs. @@ -292,7 +309,7 @@ describe('Engine - Browser Interactions', function() { } window.clearTimeout(intervalTimer); - }, testconfig.timeouts.scriptLoad); + }, baseTimeout); let intervalTimer = window.setInterval(() => { if(keyman.getActiveKeyboard() != '') { diff --git a/web/src/test/auto/integrated/cases/engine_chirality.js b/web/src/test/auto/integrated/cases/engine_chirality.spec.ts similarity index 54% rename from web/src/test/auto/integrated/cases/engine_chirality.js rename to web/src/test/auto/integrated/cases/engine_chirality.spec.ts index 43e664f7e5f..2969fe5d3e0 100644 --- a/web/src/test/auto/integrated/cases/engine_chirality.js +++ b/web/src/test/auto/integrated/cases/engine_chirality.spec.ts @@ -1,26 +1,28 @@ -import { assert } from '/node_modules/chai/chai.js'; +import { assert } from 'chai'; import { - loadKeyboardFromJSON, runKeyboardTestFromJSON, setupKMW, teardownKMW } from "../test_utils.js"; +const baseTimeout = 5000; +const eventTimeout = 500; + +const host = document.createElement('div'); +document.body.appendChild(host); + describe('Engine - Chirality', function() { - this.timeout(testconfig.timeouts.scriptLoad); + this.timeout(baseTimeout); before(function() { - fixture.setBase('fixtures'); - return setupKMW(null, testconfig.timeouts.scriptLoad); + return setupKMW(null, baseTimeout); }); - beforeEach(function(done) { - fixture.load("singleInput.html"); - - window.setTimeout(function() { - done() - }, testconfig.timeouts.eventDelay); + beforeEach(async function() { + const singleton = document.createElement('input'); + singleton.id = 'singleton'; + host.appendChild(singleton); }); after(function() { @@ -28,27 +30,24 @@ describe('Engine - Chirality', function() { }); afterEach(function() { - fixture.cleanup(); + host.innerHTML = ''; }); it('Keyboard + OSK simulation', async function() { - this.timeout(testconfig.timeouts.scriptLoad * (testconfig.mobile ? 1 : 2)); /* Interestingly, this still works on iOS, probably because we're able to force-set * the 'location' property in the simulated event on mobile devices, even when iOS neglects to * set it for real events. */ - return await runKeyboardTestFromJSON('/engine_tests/chirality.json', + return await runKeyboardTestFromJSON('resources/json/engine_tests/chirality.json', {usingOSK: false}, assert.equal, - testconfig.timeouts.scriptLoad).then(async () => { + baseTimeout).then(async () => { /* We only really care to test the 'desktop' OSK because of how it directly models the modifier keys. * * The 'phone' and 'layout' versions take shortcuts that bypass any tricky chiral logic; * a better test for those would be to ensure the touch OSK is constructed properly. */ - if(!testconfig.mobile) { - return await runKeyboardTestFromJSON('/engine_tests/chirality.json', {usingOSK: true}, assert.equal, testconfig.timeouts.scriptLoad); - } + return await runKeyboardTestFromJSON('resources/json/engine_tests/chirality.json', {usingOSK: true}, assert.equal, baseTimeout); }); }); }); \ No newline at end of file diff --git a/web/src/test/auto/integrated/cases/events.js b/web/src/test/auto/integrated/cases/events.spec.ts similarity index 68% rename from web/src/test/auto/integrated/cases/events.js rename to web/src/test/auto/integrated/cases/events.spec.ts index 41eb42bcfe6..f4f5566ce66 100644 --- a/web/src/test/auto/integrated/cases/events.js +++ b/web/src/test/auto/integrated/cases/events.spec.ts @@ -1,4 +1,4 @@ -import { assert } from '/node_modules/chai/chai.js'; +import { assert } from 'chai'; import { loadKeyboardFromJSON, @@ -6,32 +6,56 @@ import { setupKMW, teardownKMW } from "../test_utils.js"; -import * as KMWRecorder from '/@keymanapp/keyman/build/tools/testing/recorder/lib/index.mjs'; +import * as KMWRecorder from '#recorder'; +import OSKInputEventSpec = KMWRecorder.OSKInputEventSpec; + +import { timedPromise } from '@keymanapp/web-utils'; +import { type KeymanEngine } from 'keyman/app/browser'; + +const host = document.createElement('div'); +document.body.appendChild(host); + +const attachmentTimeout = 500; describe('Event Management', function() { - this.timeout(testconfig.timeouts.standard); + this.timeout(5000); before(function() { - this.timeout(testconfig.timeouts.scriptLoad * 2); - fixture.setBase('fixtures'); - fixture.load("eventTestConfig.html"); - - return setupKMW(null, testconfig.timeouts.scriptLoad).then(() => { + return setupKMW(null, 5000).then(() => { // We use this keyboard since we only need minimal input functionality for these tests. // Smaller is better when dealing with net latency. - return loadKeyboardFromJSON("/keyboards/test_simple_deadkeys.json", testconfig.timeouts.scriptLoad); + return loadKeyboardFromJSON("resources/json/keyboards/test_simple_deadkeys.json", 5000); }); }); + // Async, to give the mutation observers a chance to fire first. + beforeEach(async function() { + const input = document.createElement('input'); + input.id = 'input'; + const textarea = document.createElement('textarea'); + textarea.id = 'textarea'; + + host.appendChild(input); + host.appendChild(textarea); + + await timedPromise(attachmentTimeout); + }); + + afterEach(function() { + host.innerHTML = '' + }); + after(function() { teardownKMW(); }); it('Keystroke-based onChange event generation', async function() { + const keyman: KeymanEngine = window['keyman']; + var simple_A = {"type":"key","key":"a","code":"KeyA","keyCode":65,"modifierSet":0,"location":0}; var event = new KMWRecorder.PhysicalInputEventSpec(simple_A); - var ele = document.getElementById("input"); + var ele = document.getElementById('input'); ele.onchange = function() { ele.onchange = null; @@ -51,10 +75,11 @@ describe('Event Management', function() { }); it('OSK-based onChange event generation', async function() { + const keyman: KeymanEngine = window['keyman']; var simple_A = {"type":"osk","keyID":"default-K_A"}; - var event = new KMWRecorder.OSKInputEventSpec(simple_A); + var event = new KMWRecorder.OSKInputEventSpec(simple_A as OSKInputEventSpec); - var ele = document.getElementById("input"); + var ele = document.getElementById('input'); ele.onchange = function() { ele.onchange = null; @@ -79,10 +104,11 @@ describe('Event Management', function() { }); it('Keystroke-based onInput event generation', async function() { + const keyman: KeymanEngine = window['keyman']; var simple_A = {"type":"key","key":"a","code":"KeyA","keyCode":65,"modifierSet":0,"location":0}; var event = new KMWRecorder.PhysicalInputEventSpec(simple_A); - var ele = document.getElementById("input"); + var ele = document.getElementById('input'); keyman.setActiveElement(ele); var counterObj = {i:0}; @@ -101,10 +127,11 @@ describe('Event Management', function() { }); it('OSK-based onInput event generation', async function() { + const keyman: KeymanEngine = window['keyman']; var simple_A = {"type":"osk","keyID":"default-K_A"}; - var event = new KMWRecorder.OSKInputEventSpec(simple_A); + var event = new KMWRecorder.OSKInputEventSpec(simple_A as OSKInputEventSpec); - var ele = document.getElementById("input"); + var ele = document.getElementById('input'); keyman.setActiveElement(ele); var counterObj = {i:0}; diff --git a/web/src/test/auto/integrated/cases/test_config.js b/web/src/test/auto/integrated/cases/test_config.js deleted file mode 100644 index 18b95c672a7..00000000000 --- a/web/src/test/auto/integrated/cases/test_config.js +++ /dev/null @@ -1,29 +0,0 @@ -import { assert } from '/node_modules/chai/chai.js'; - -/* - * A canary test to ensure our timeout configurations are working correctly for our CI. - */ -describe('Test configuration', function () { - this.timeout(testconfig.timeouts.standard); - - it('Correctly retrieves timeout configurations', function() { - let configArgs = __karma__.config.args; - assert.isNotNull(configArgs, "Cannot find the custom arguments object from our Karma config."); - - var timeoutConfig; - - for(var i=0; i < configArgs.length; i++) { - if(configArgs[i].type == 'timeouts') { - timeoutConfig = configArgs[i]; - } - } - - assert.isNotNull(timeoutConfig, "Cannot find the timeout configuration object."); - - // Now, were they processed correctly? - assert.isNotNull(window['testconfig'], "Test global 'config' is missing!"); - let timeouts = testconfig.timeouts; - // May be longer if an extra factor (such as a mobile device multiplier) is added. - assert.isAtLeast(timeouts.standard, timeoutConfig.standard, "Timeouts were not parsed properly for test configuration."); - }) -}); \ No newline at end of file diff --git a/web/src/test/auto/integrated/cases/text_selection.js b/web/src/test/auto/integrated/cases/text_selection.spec.ts similarity index 88% rename from web/src/test/auto/integrated/cases/text_selection.js rename to web/src/test/auto/integrated/cases/text_selection.spec.ts index 377bb359167..2b641a04249 100644 --- a/web/src/test/auto/integrated/cases/text_selection.js +++ b/web/src/test/auto/integrated/cases/text_selection.spec.ts @@ -1,4 +1,4 @@ -import { assert } from '/node_modules/chai/chai.js'; +import { assert } from 'chai'; import { DEVICE_DETECT_FAILURE, @@ -6,20 +6,28 @@ import { setupKMW, teardownKMW } from "../test_utils.js"; -import * as KMWRecorder from '/@keymanapp/keyman/build/tools/testing/recorder/lib/index.mjs'; +import * as KMWRecorder from '#recorder'; +import { timedPromise } from '@keymanapp/web-utils'; +import { type KeymanEngine } from 'keyman/app/browser'; + +const baseTimeout = 5000; +const attachmentTimeout = 500; +const host = document.createElement('div'); +document.body.appendChild(host); describe('Text Selection', function() { - this.timeout(testconfig.timeouts.standard); + this.timeout(baseTimeout); /* Utility functions */ function setupElement(ele) { + const keyman: KeymanEngine = window['keyman']; keyman.setActiveElement(ele); return ele; } function assertInputSteps(ele, count, setup) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { var i = 0; var listener = function() { i++; @@ -39,7 +47,7 @@ describe('Text Selection', function() { /* Define key event specs */ - var keys = {}; + let keys: {[id: string]: KMWRecorder.PhysicalInputEventSpec} = {}; for (var i = 0; i < 26; i++) { var simple = {"type":"key","key":String.fromCharCode(i+97),"code":"Key"+String.fromCharCode(i+65),"keyCode":i+65,"modifierSet":0,"location":0}; var key = new KMWRecorder.PhysicalInputEventSpec(simple); @@ -55,28 +63,33 @@ describe('Text Selection', function() { before(function() { // These tests require use of KMW's device-detection functionality. assert.isFalse(DEVICE_DETECT_FAILURE, "Cannot run due to device detection failure."); - fixture.setBase('fixtures'); - fixture.load("single"+inputType+".html"); - this.timeout(testconfig.timeouts.scriptLoad*2); - return setupKMW(null, testconfig.timeouts.scriptLoad).then(() => { - return loadKeyboardFromJSON("/keyboards/web_context_tests.json", testconfig.timeouts.scriptLoad).then(() => { + return setupKMW(null, baseTimeout).then(() => { + return loadKeyboardFromJSON("resources/json/keyboards/web_context_tests.json", baseTimeout).then(() => { + const keyman: KeymanEngine = window['keyman']; return keyman.setActiveKeyboard("web_context_tests"); }); }); }); + beforeEach(async function() { + const ele = document.createElement(inputType); + ele.id = 'singleton'; + host.appendChild(ele); + + await timedPromise(attachmentTimeout); + }); + after(function() { - fixture.cleanup(); - window.keyman?.removeKeyboards('web_context_tests'); + const keyman: KeymanEngine = window['keyman']; + keyman?.removeKeyboards('web_context_tests'); teardownKMW(); }); - afterEach(function(done) { - fixture.load("single"+inputType+".html"); - window.setTimeout(function(){ - done(); - }, testconfig.timeouts.eventDelay); + afterEach(async function() { + host.innerHTML = ''; + + Promise.resolve(); }); /* @@ -94,7 +107,7 @@ describe('Text Selection', function() { * Using the web_context_tests keyboard, type abcd. The output after the final key should be '!'. */ it('Should do a basic transform without selection involved', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); await assertInputSteps(ele, 5, () => { @@ -110,7 +123,7 @@ describe('Text Selection', function() { }); it('Should do a basic transform without selection involved - SMP', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); await assertInputSteps(ele, 5, () => { @@ -125,7 +138,8 @@ describe('Text Selection', function() { assert.deepEqual([ele.selectionStart, ele.selectionEnd], [4,4], "Expected caret to be at end of text"); }); - for(var direction of ['forward', 'backward']) { + // `as const` allows TS to lock in on the specific strings, rather than just saying 'string' as the type. + for(let direction of ['forward', 'backward'] as const) { /** * TEST_SELECTION @@ -134,7 +148,7 @@ describe('Text Selection', function() { * The output after the final key should be aqx. */ it('Should do a basic selection replacement, in '+direction+' direction', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'abcx' @@ -164,7 +178,7 @@ describe('Text Selection', function() { }); it('Should do a basic selection replacement, with a matching rule, in '+direction+' direction', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'abcx' @@ -194,7 +208,7 @@ describe('Text Selection', function() { }); it('Should do a basic selection replacement, in '+direction+' direction - SMP', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'fghj' @@ -230,7 +244,7 @@ describe('Text Selection', function() { * The output after the final key should be 'abcd'. */ it('Should ignore context when a selection, in '+direction+' direction, is made', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'abcx' @@ -260,7 +274,7 @@ describe('Text Selection', function() { }); it('Should ignore context when a selection, in '+direction+' direction, is made - SMP', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'fghj' @@ -296,7 +310,7 @@ describe('Text Selection', function() { * delete it with Backspace. Type d. The output after the final key should be !. */ it('Should correctly delete selection, in '+direction+' direction, with backspace and not lose sync', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'abcx' @@ -336,7 +350,7 @@ describe('Text Selection', function() { }); it('Should correctly delete selection, in '+direction+' direction, with backspace and not lose sync - SMP', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'abcx' @@ -382,7 +396,7 @@ describe('Text Selection', function() { * Press Backspace, and type d. The output after the final key should be !. */ it('Should correctly replace selection, in '+direction+' direction, and not lose sync', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'abcx' @@ -430,7 +444,7 @@ describe('Text Selection', function() { }); it('Should correctly replace selection, in '+direction+' direction, and not lose sync - SMP', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'abcx' @@ -484,7 +498,7 @@ describe('Text Selection', function() { * Select the abc characters, and type d. The output after the final key should be xdx. */ it('Should not treat the selection, in '+direction+' direction, as context', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: 'xabcx' @@ -515,7 +529,7 @@ describe('Text Selection', function() { }); it('Should not treat the selection, in '+direction+' direction, as context - SMP', async () => { - var ele = document.getElementById("singleton"); + var ele = document.getElementById("singleton") as HTMLInputElement | HTMLTextAreaElement; var eventDriver = instantiateBrowserDriver(ele); // Step 1: [k][f][g][h][k] diff --git a/web/src/test/auto/integrated/manual.conf.cjs b/web/src/test/auto/integrated/manual.conf.cjs deleted file mode 100644 index 1969c400503..00000000000 --- a/web/src/test/auto/integrated/manual.conf.cjs +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = function(config) { - var base = require("./base.conf.cjs"); - - var specifics = { - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['mocha'], - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['Firefox', 'Chrome'], // Can be specified at run-time instead! - // Future note for us: https://www.npmjs.com/package/karma-browserstack-launcher - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity, - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - // Can't set in base.conf.cjs b/c of constant's definition style. - logLevel: config.LOG_INFO - }; - - config.set(Object.assign(specifics, base)); -} diff --git a/web/src/test/auto/integrated/test_init_check.js b/web/src/test/auto/integrated/test_init_check.spec.ts similarity index 87% rename from web/src/test/auto/integrated/test_init_check.js rename to web/src/test/auto/integrated/test_init_check.spec.ts index 7eb95c1e21b..f51a280f560 100644 --- a/web/src/test/auto/integrated/test_init_check.js +++ b/web/src/test/auto/integrated/test_init_check.spec.ts @@ -1,8 +1,8 @@ // A simple "does device detection work properly" unit test. // We'll let things break after this reports, since this will likely signal a LOT of other failures. -import { assert } from '/node_modules/chai/chai.js'; +import { assert } from 'chai'; -import Device from '/@keymanapp/keyman/build/engine/device-detect/lib/index.mjs'; +import Device from 'keyman/engine/device-detect'; /* Note - we still have to prevent errors setting up test resources; * Karma will fail to report errors for affected browsers otherwise. @@ -16,7 +16,7 @@ describe('Test Initialization', function() { this.timeout(15000); it("Detects device without JS errors", function() { - var device; + let device: Device; try { device = new Device(); device.detect(); diff --git a/web/src/test/auto/integrated/test_utils.js b/web/src/test/auto/integrated/test_utils.ts similarity index 58% rename from web/src/test/auto/integrated/test_utils.js rename to web/src/test/auto/integrated/test_utils.ts index 9d0227bae52..769705216d6 100644 --- a/web/src/test/auto/integrated/test_utils.js +++ b/web/src/test/auto/integrated/test_utils.ts @@ -1,10 +1,17 @@ // // KeymanWeb test suite - processing of the Karma configuration's client.args parameter. -import Device from '/@keymanapp/keyman/build/engine/device-detect/lib/index.mjs'; -import * as KMWRecorder from '/@keymanapp/keyman/build/tools/testing/recorder/lib/index.mjs'; +import Device from 'keyman/engine/device-detect'; +import * as KMWRecorder from '#recorder'; +import { type BrowserInitOptionSpec, type KeymanEngine } from 'keyman/app/browser'; +import { ErrorStub, type KeyboardAPISpec, type KeyboardStub } from 'keyman/engine/package-cache'; export let DEVICE_DETECT_FAILURE = false; +const loc = document.location; +// config.testFile generally starts with a '/', with the path resembling the actual full local +// filesystem for the drive. +const domain = `${loc.protocol}/${loc.host}` + // If we've set things up to support Device dection without loading KMW... try { let device = new Device(); @@ -16,16 +23,17 @@ try { // // Keyman test suite utility methods -export function setupKMW(kmwOptions, timeout) { - let ui; +export function setupKMW(kmwOptions: BrowserInitOptionSpec | string, timeout: number) { + let ui: string; if(typeof(kmwOptions) == 'string' || typeof(kmwOptions) == 'undefined' || kmwOptions == null) { - ui = kmwOptions; + ui = kmwOptions as string; - var kmwOptions = { + kmwOptions = { attachType:'auto', root:'/', - resources:'/build/app/resources' + // up from 'browser/debug' + resources:'../../resources' }; if(ui) { @@ -33,27 +41,27 @@ export function setupKMW(kmwOptions, timeout) { } } - const kmwPromise = setupScript('build/app/browser/keymanweb.js', timeout); + const kmwPromise = setupScript('web/build/app/browser/debug/keymanweb.js', timeout); ui = kmwOptions.ui; kmwOptions.attachType = kmwOptions.attachType ? kmwOptions.attachType : 'auto'; if(!kmwOptions.root) { - kmwOptions.root = '/build/app/browser'; + kmwOptions.root = 'web/build/app/browser/debug'; } if(!kmwOptions.resources) { - kmwOptions.resources = '/build/resources'; + kmwOptions.resources = 'web/build/app/resources'; } let uiPromise; if(ui) { - uiPromise = setupScript('build/app/ui/kmwui' + ui + '.js', timeout); + uiPromise = setupScript('web/build/app/ui/debug/kmwui' + ui + '.js', timeout); kmwOptions.ui=ui; } - let compositePromise = kmwPromise; + let compositePromise: Promise = kmwPromise; if(uiPromise) { compositePromise = Promise.all([kmwPromise, uiPromise]); } @@ -76,17 +84,17 @@ export function setupKMW(kmwOptions, timeout) { * @param {*} timeout * @returns */ -export function setupScript(src, timeout) { +export function setupScript(src: string, timeout: number) { return setupScriptInternal(src, timeout); } -function setupScriptInternal(src, timeout, attemptCount, existingTimer) { +function setupScriptInternal(src: string, timeout: number, attemptCount?: number, existingTimer?: number) { attemptCount = attemptCount || 1; if(attemptCount > 1) { console.log("Re-attempting load of script '" + src + "': retry #" + attemptCount); } - let promise = new Promise((resolve, reject) => { + let promise = new Promise((resolve, reject) => { const Lscript = document.createElement('script'); let hasResolved = false; Lscript.charset="UTF-8"; // KMEW-89 @@ -100,9 +108,11 @@ function setupScriptInternal(src, timeout, attemptCount, existingTimer) { reject(new Error("Script load attempt for '" + src + "' timed out.")); }, timeout); + // @ts-ignore // TS does not recognize that