spiegel-keyman/common/web/keyboard-processor/tests/node/engine/context.js
2023-03-04 09:49:54 +07:00

1102 lines
No EOL
51 KiB
JavaScript

import { assert } from 'chai';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
import { KeyboardInterface, KeyboardProcessor, MinimalKeymanGlobal, Mock } from '@keymanapp/keyboard-processor';
import { NodeKeyboardLoader } from '@keymanapp/keyboard-processor/node-keyboard-loader';
import { NodeProctor, RecordedKeystrokeSequence } from '@keymanapp/recorder-core';
/*
* ABOUT THIS TEST SUITE
* ---------------------
*
* This suite contains two types of tests, both designed to test all possible variations
* of behaviors that `KeyboardInterface.fullContextMatch` may be expected to handle.
*
* Type 1: White-box tests for validity of the generated context-cache
* - uses only the `baseSequence` of each test spec definition; does not
* use any `fullMatchDefs` entries.
* - uses the specified `contextCache` entry of each test spec in assertions
* - CTRL+F `Tests "stage 1" of fullContextMatch` for more details.
*
* Type 2: Black-box rule-matching tests
* - uses both `baseSequence` and `fullMatchDefs` entries of a test spec
* - tests that each simulation sequence's output either passes or fails against
* the specified rule, asserting against the specified `result` value.
* - Currently, `result` is always `true` for each spec's `baseSequence`.
* It should be a removable limitation, though.
*/
let device = {
formFactor: 'desktop',
OS: 'windows',
browser: 'native'
}
let keyboardWithHarness;
var toSupplementaryPairString = function(code){
var H = Math.floor((code - 0x10000) / 0x400) + 0xD800;
var L = (code - 0x10000) % 0x400 + 0xDC00;
return String.fromCharCode(H, L);
}
function runEngineRuleSet(ruleSet, defaultNoun) {
defaultNoun = defaultNoun ? defaultNoun : "Rule";
for(var i = 0; i < ruleSet.length; i++) {
var ruleDef = ruleSet[i]; // for example, DEADKEY_TEST_1.
var matchDefs = [{
sequence: ruleDef.baseSequence,
result: true,
msg: "Rule " + ruleDef.id + ": basic application of rule failed."}
].concat(ruleDef.fullMatchDefs ? ruleDef.fullMatchDefs : []);
for(var j = 0; j < matchDefs.length; j++) {
// Prepare the context!
var matchTest = matchDefs[j];
var ruleSeq = new RecordedKeystrokeSequence(matchTest.sequence);
let proctor = new NodeProctor(keyboardWithHarness, device, assert.equal);
// We want to specify the OutputTarget for this test; our actual concern is the resulting context.
var target = new Mock();
ruleSeq.test(proctor, target);
// Now for the real test!
let processor = new KeyboardProcessor(device);
processor.keyboardInterface = keyboardWithHarness;
var res = processor.keyboardInterface.fullContextMatch(ruleDef.n, target, ruleDef.rule);
var msg = matchTest.msg;
if(!msg) {
msg = defaultNoun + " incorrectly reported as " + (matchTest.result ? "unmatched!" : "matched!");
}
assert.equal(res, matchTest.result, msg);
}
}
}
//#region Test Spec Definitions
// Unfortunately, at present, this is all handwritten stuff crafted from partial Recorder use. Might should make
// a page that writes this format instead. Note that I've omitted the eventSpec part here, since these tests are
// Node-only.
/*
* Start definition of isolated rule tests for validity of `fullContextMatch` (KFCM) components.
*/
/* Keyman language equivalent:
*
* dk(1) > 'success'
*/
var DEADKEY_TEST_1 = {
id: 1,
// Match condition for rule
rule: [{t:'d', d: 1}],
// Start of context relative to cursor
n: 1,
ln: 1,
// Resulting context map
contextCache: [1],
baseSequence: { "output": "", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}
]},
fullMatchDefs: [{
sequence: { "output": "", "inputs": [
// Does it fail with a different deadkey in the position?
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}
]},
result: false,
msg: "Rule 1: did not fail when incorrect deadkey was present."
}, {
sequence: { "output": "", "inputs": [
// Slightly out-of-context deadkey shouldn't affect the match.
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true},
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}
]},
result: true,
msg: "Rule 1: failed when extra deadkey context exists in history."
}, {
sequence: { "output": "", "inputs": [
// Slightly out-of-context deadkey shouldn't affect the match.
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true},
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}
]},
result: false,
msg: "Rule 1: did not fail upon incorrect deadkey ordering at top of context."
}]
};
/* Keyman language equivalent:
*
* 'a' dk(0) dk(1) 'b' > 'success'
*/
var DEADKEY_TEST_2 = {
id: 2,
// Match condition for rule
rule: ['a', {t:'d', d: 0}, {t:'d', d: 1}, 'b'],
// Start of context relative to cursor
n: 5,
ln: 4,
// Resulting context map
contextCache: ['a', 0, 1, 'b'],
baseSequence: { "output": "abc", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
// The test has an extra character appended that's not part of the check.
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]},
fullMatchDefs: [{
sequence: { "output": "abc", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true},
// Should fail with inverted deadkey ordering at same KC_ position.
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true},
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true},
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true},
// The test has an extra character appended that's not part of the check.
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}
]},
result: false,
msg: "Rule 2: did not fail when deadkey ordering was inverted."
}, {
sequence: { "output": "cabc", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
// The test has an extra character appended that's not part of the check.
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]},
result: true,
msg: "Rule 2: failed when extra character context exists in history."
}, {
sequence: { "output": "ab", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
// The test has an extra deadkey appended that's not part of the check.
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //1
]},
result: true,
msg: "Rule 2: out-of-context deadkey caused rule match failure."
}, {
sequence: { "output": "abc", "inputs": [
// Should not fail with a deadkey prepended to the context.
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
// The test has an extra character appended that's not part of the check.
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]},
result: true,
msg: "Rule 2: prepended (out of context) deadkey caused rule match failure."
}]
};
/* Keyman language equivalent:
*
* dk(0) 'a' dk(0) dk(0) 'b' > 'success'
*/
var DEADKEY_TEST_3 = {
id: 3,
// Match condition for rule
rule: [{t:'d', d: 0}, 'a', {t:'d', d: 0}, {t:'d', d: 0}, 'b'],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: [0, 'a', 0, 0, 'b'],
baseSequence: { "output": "ab", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
fullMatchDefs: [{
sequence: { "output": "ab", "inputs": [
// Omission of the first deadkey should result in failure.
//{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: false,
msg: "Rule 3: required prepended deadkey omission did not prevent a rule match."
}, {
sequence: { "output": "ab", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
// Omission of the one of the duplicated deadkeys should result in failure.
//{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: false,
msg: "Rule 3: omitting one copy of a required duplicated deadkey did not prevent a rule match."
}, {
sequence: { "output": "ab", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
// Triplifying deadkeys in the center should result in failure.
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: false,
msg: "Rule 3: triple matching deadkeys where only two required did not prevent a rule match."
}, {
sequence: { "output": "ab", "inputs": [
// Should not fail with a duplicate deadkey prepended to the context.
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: true,
msg: "Rule 3: duplicate prepended deadkey prevented a rule match when only one was required."
}, {
sequence: { "output": "ab", "inputs": [
// Should not fail with an unrelated deadkey prepended to the context before the required one.
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: true,
msg: "Rule 3: prepended deadkey placed before rule's deadkey prevented a rule match."
}, {
sequence: { "output": "ab", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
// Should fail when unrelated, prepended deadkey actually comes after the required one.
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: false,
msg: "Rule 3: prepended deadkey after rule's required deadkey failed to prevent a rule match."
}]
};
/* Keyman language equivalent:
*
* 'a' dk(0) dk(0) 'b' dk(0) > 'success'
*/
var DEADKEY_TEST_4 = {
id: 4,
// Match condition for rule
rule: ['a', {t:'d', d: 0}, {t:'d', d: 0}, 'b', {t:'d', d: 0}],
// Start of context relative to cursor
n: 6,
ln: 5,
// Resulting context map
contextCache: ['a', 0, 0, 'b', 0],
baseSequence: { "output": "abc", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
// The test has an extra character appended that's not part of the check.
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]}
// No specialized fullMatchDefs here, as any appended deadkeys are automatically 'in context' for rules.
};
/* Keyman language equivalent:
*
* 'a' 'b' 'b' 'a' > 'success'
*/
var DEADKEY_TEST_5 = {
id: 5,
// Match condition for rule
rule: ['a', 'b', 'b', 'a'],
// Start of context relative to cursor
n: 5,
ln: 4,
// Resulting context map
contextCache: ['a', 'b', 'b', 'a'],
baseSequence: { "output": "abbac", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
// The test has an extra character appended that's not part of the check.
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]}
};
/* Keyman language equivalent:
*
* dk(1) dk(2) dk(0) dk(1) dk(2) > 'success'
*/
var DEADKEY_TEST_6 = {
id: 6,
// Match condition for rule
rule: [{t:'d', d: 1}, {t:'d', d: 2}, {t:'d', d: 0}, {t:'d', d: 1}, {t:'d', d: 2}],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: [1, 2, 0, 1, 2],
baseSequence: { "output": "", "inputs": [
// Testing with an extra deadkey at the start.
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":51,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //3
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":51,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //3
]}
};
/* Keyman language equivalent:
*
* 'c' 'a' 'b' context(3) context(2) > 'success'
*/
var ANY_CONTEXT_TEST_1 = {
id: 1,
// Match condition for rule
rule: ['c', "a", "b", {t:'c', c:3}, {t:'c', c:2}],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: ['c', 'a', 'b', 'b', 'a'],
baseSequence: { "output": "cabba", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "cacca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 1: Plain text mismatch with successful context() statements is matching the rule."
}, {
sequence: { "output": "cabaa", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 1: mismatched context() rule component is not failing the rule."
}]
};
/* Keyman language equivalent:
*
* store(bc) 'bc'
* 'c' 'a' any(bc) context(3) 'a' > 'success'
*/
var ANY_CONTEXT_TEST_2 = {
id: 2,
// Match condition for rule
rule: ['c', 'a', {t:'a', a: "bc"}, {t:'c', c:3}, 'a'],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: ['c', 'a', 'b', 'b', 'a'],
baseSequence: { "output": "cabba", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "cacca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: true,
msg: "Rule 2: Alternate 'any' store character failed to match the rule."
}, {
sequence: { "output": "cabca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 2: 'any' and 'context' correspond to mismatching characters, but matched the rule."
}]
};
/* Keyman language equivalent:
*
* store(ac) 'ac'
* store(bc) 'bc'
*
* 'c' any(ac) any(bc) context(3) context(2) > 'success'
*/
var ANY_CONTEXT_TEST_3 = {
id: 3,
// Match condition for rule
rule: ['c', {t:'a', a: "ac"}, {t:'a', a: "bc"}, {t:'c', c:3}, {t:'c', c:2}],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: ['c', 'a', 'b', 'b', 'a'],
baseSequence: { "output": "cabba", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "cacca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: true,
msg: "Rule 3: Alternate 'any' store character failed to match the rule."
}, {
sequence: { "output": "ccccc", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]},
result: true,
msg: "Rule 3: Alternate 'any' store character failed to match the rule."
}, {
sequence: { "output": "cabab", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: false,
msg: "Rule 3: context() rule component is matching the incorrect any() component."
}]
};
/* Keyman language equivalent:
*
* store(ab) 'ab'
* store(bc) 'bc'
*
* 'c' any(ab) index(bc, 2) 'a' > 'success'
*/
var ANY_INDEX_TEST_1 = {
id: 1,
// Match condition for rule
rule: ['c', 'a', {t:'a', a: "bc"}, {t:'i', i:"bc", o:3}, 'a'],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: ['c', 'a', 'b', 'b', 'a'],
baseSequence: { "output": "cabba", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "cacca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: true,
msg: "Rule 1: Alternate 'any' store character failed to match the rule."
}, {
sequence: { "output": "cabca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 1: 'any' and 'output' correspond to mismatching characters, but matched the rule."
}]
};
/* Keyman language equivalent:
*
* store(ab) 'ab'
* store(bc) 'bc'
*
* 'c' any(ab) index(bc, 2) index(bc, 2) index(ab, 2) > 'success'
*/
var ANY_INDEX_TEST_2 = {
id: 2,
// Match condition for rule
rule: ['c', {t:'a', a:"ab"}, {t:'i', i:"bc", o:2}, {t:'i', i:"bc", o:2}, {t:'i', i:"ab", o:2}],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: ['c', 'a', 'b', 'b', 'a'],
baseSequence: { "output": "cabba", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "cacca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 2: Mismatch with secondary output store did not fail the rule."
}, {
sequence: { "output": "cbccb", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: true,
msg: "Rule 2: Alternate 'any' store character failed to match the rule."
}]
};
/* Keyman language equivalent:
*
* store(ab) 'ab'
* store(bc) 'bc'
*
* 'c' any(ab) any(bc) index(bc, 3) index(ab, 2) > 'success'
*/
var ANY_INDEX_TEST_3 = {
id: 3,
// Match condition for rule
rule: ['c', {t:'a', a:"ab"}, {t:'a', a:"bc"}, {t:'i', i:"bc", o:3}, {t:'i', i:"ab", o:2}],
// Start of context relative to cursor
n: 5,
ln: 5,
// Resulting context map
contextCache: ['c', 'a', 'b', 'b', 'a'],
baseSequence: { "output": "cabba", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "cacca", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: true,
msg: "Rule 3a: Error with index() when a rule has multiple any() checks."
}, {
sequence: { "output": "cbccb", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: true,
msg: "Rule 3b: Error with index() when a rule has multiple any() checks."
}, {
sequence: { "output": "cbbbb", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: true,
msg: "Rule 3c: Error with index() when a rule has multiple any() checks."
}, {
sequence: { "output": "cbccc", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //c
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]},
result: false,
msg: "Rule 3: index() rule check matched the incorrect any()."
}]
};
/* Keyman language equivalent:
*
* store(match) dk(0) dk(1) dk(2)
* any(match) > 'success'
*/
var DEADKEY_STORE_TEST_1 = {
id: 1,
// Match condition for rule
rule: [{t:'a',a:[{d:0},{d:1},{d:2}]}],
// Start of context relative to cursor
n: 1,
ln: 1,
// Resulting context map
contextCache: [0],
baseSequence: { "output": "", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //1
]},
fullMatchDefs: [{
sequence: { "output": "", "inputs": [
{"type":"key","keyCode":51,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //3
]},
result: true,
msg: "Rule 1: Alternate 'any' store deadkey character failed to match the rule."
}, {
sequence: { "output": "c", "inputs": [
{"type":"key","keyCode":67,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //c
]},
result: false,
msg: "Rule 1: standard character mysteriously matched a deadkey from a store."
}]
};
/* Keyman language equivalent:
*
* store(match) dk(0) 'b' dk(2)
* any(match) > 'success'
*/
var DEADKEY_STORE_TEST_2 = {
id: 2,
// Match condition for rule
rule: [{t:'a',a:[{d:0},'b',{d:2}]}],
// Start of context relative to cursor
n: 1,
ln: 1,
// Resulting context map
contextCache: [0],
baseSequence: { "output": "", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //1
]},
fullMatchDefs: [{
sequence: { "output": "", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //2
]},
result: false,
msg: "Rule 2: deadkey not in store mysteriously matched within an any(store) op."
}, {
sequence: { "output": "b", "inputs": [
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: true,
msg: "Rule 2: standard character in store failed to match within an any(store) op."
}]
};
/* Keyman language equivalent:
*
* store(match) dk(0) dk(1) dk(2)
* store(abc) 'abc'
*
* any(match) index(abc, 1) > 'success'
*/
var DEADKEY_STORE_TEST_3 = {
id: 3,
// Match condition for rule
rule: [{t:'a',a:[{d:0},{d:1},{d:2}]},{t:'i',i:"abc", o:1}],
// Start of context relative to cursor
n: 2,
ln: 2,
// Resulting context map
contextCache: [0,'a'],
baseSequence: { "output": "a", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "a", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 3a: index in deadkey store not properly tracked by indexOutput."
}, {
sequence: { "output": "b", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: true,
msg: "Rule 3a: index in deadkey store not properly tracked by indexOutput."
}, {
sequence: { "output": "a", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //2
]},
result: false,
msg: "Rule 3: incorrectly matched rule when deadkey and character were incorrectly ordered."
}, {
sequence: { "output": "bb", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: false,
msg: "Rule 3: triggered when rule is one regular character out of context."
}, {
sequence: { "output": "b", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //2
]},
result: false,
msg: "Rule 3: triggered when rule is one deadkey out of context."
}]
};
/* Keyman language equivalent:
*
* store(match) dk(0) dk(1) dk(2)
* any(match) context(1) > 'success'
*/
var DEADKEY_STORE_TEST_4 = {
id: 4,
// Match condition for rule
rule: [{t:'a',a:[{d:0},{d:1},{d:2}]},{t:'c',c:1}],
// Start of context relative to cursor
n: 2,
ln: 2,
// Resulting context map
contextCache: [1,1],
baseSequence: { "output": "", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //2
]},
fullMatchDefs: [{
sequence: { "output": "", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //2
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //1
]},
result: false,
msg: "Rule 4: context matched to alternate (not selected) any(store) option erroneously."
}, {
sequence: { "output": "b", "inputs": [
{"type":"key","keyCode":51,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //3
{"type":"key","keyCode":51,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //3
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //b
]},
result: false,
msg: "Rule 4: triggered when rule is slightly out of context."
}]
};
/* Keyman language equivalent:
*
* store(match) 'abc'
* notany(match) any(match) > 'success'
*/
var NOTANY_NUL_TEST_1 = {
id: 1,
// Match condition for rule
rule: [{t: 'a', a:['a','b','c'], n: 1},{t:'a',a:['a', 'b', 'c']}],
// Start of context relative to cursor
n: 2,
ln: 2,
// Resulting context map
contextCache: [1, 'a'],
baseSequence: { "output": "a", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "aa", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 1: did not fail notany on matched character"
}, {
sequence: { "output": "a", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 1: did not fail notany on nul context"
}]
};
/* Keyman language equivalent:
*
* store(match) dk(0) dk(1) dk(2)
* store(abc) 'abc'
*
* notany(match) any(abc) > 'success'
*/
var NOTANY_NUL_TEST_2 = {
id: 2,
// Match condition for rule
rule: [{t: 'a', a:[{d:0},{d:1},{d:2}], n: 1},{t:'a',a:['a', 'b', 'c']}],
// Start of context relative to cursor
n: 2,
ln: 2,
// Resulting context map
contextCache: ['a', 'a'],
baseSequence: { "output": "aa", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "a", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 2: did not fail notany on matched deadkey"
}, {
sequence: { "output": "a", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 2: did not fail notany on nul context"
}]
};
/* Keyman language equivalent:
*
* store(first) dk(0) 'b' dk(2)
* store(second) 'a' dk(1) 'c'
*
* notany(first) any(second) > 'success'
*/
var NOTANY_NUL_TEST_3 = {
id: 3,
// Match condition for rule
rule: [{t: 'a', a:[{d:0},'b',{d:2}], n: 1},{t:'a',a:['a', 'b', 'c']}],
// Start of context relative to cursor
n: 2,
ln: 2,
// Resulting context map
contextCache: [1, 'a'],
baseSequence: { "output": "a", "inputs": [
{"type":"key","keyCode":50,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //1
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
fullMatchDefs: [{
sequence: { "output": "a", "inputs": [
{"type":"key","keyCode":49,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //0
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 3: did not properly match a deadkey within a mixed notany store"
}, {
sequence: { "output": "ba", "inputs": [
{"type":"key","keyCode":66,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //b
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 3: did not properly match a character within a mixed notany store"
}, {
sequence: { "output": "aa", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true}, //a
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: true,
msg: "Rule 3: context incorrectly matched a character within a mixed notany store"
}, {
sequence: { "output": "a", "inputs": [
{"type":"key","keyCode":65,"states":10752,"modifiers":0,"modifierChanged":false,"isVirtualKey":true} //a
]},
result: false,
msg: "Rule 3: did not fail notany on nul context"
}]
};
var DEADKEY_RULE_SET = [ DEADKEY_TEST_1, DEADKEY_TEST_2, DEADKEY_TEST_3, DEADKEY_TEST_4,
DEADKEY_TEST_5, DEADKEY_TEST_6
];
var ANY_CONTEXT_RULE_SET = [ ANY_CONTEXT_TEST_1, ANY_CONTEXT_TEST_2, ANY_CONTEXT_TEST_3 ];
var ANY_INDEX_RULE_SET = [ ANY_INDEX_TEST_1, ANY_INDEX_TEST_2, ANY_INDEX_TEST_3 ];
var DEADKEY_STORE_RULE_SET = [ DEADKEY_STORE_TEST_1, DEADKEY_STORE_TEST_2, DEADKEY_STORE_TEST_3,
DEADKEY_STORE_TEST_4 ];
var NOTANY_NUL_RULE_SET = [ NOTANY_NUL_TEST_1, NOTANY_NUL_TEST_2, NOTANY_NUL_TEST_3 ];
var FULL_RULE_SET = [].concat(DEADKEY_RULE_SET, ANY_CONTEXT_RULE_SET, ANY_INDEX_RULE_SET,
DEADKEY_STORE_RULE_SET, NOTANY_NUL_RULE_SET);
// -----------
//#endregion
describe('Engine - Context Matching', function() {
before(async function() {
let keyboardLoader = new NodeKeyboardLoader(new KeyboardInterface({}, MinimalKeymanGlobal));
const keyboard = await keyboardLoader.loadKeyboardFromPath(require.resolve('@keymanapp/common-test-resources/keyboards/test_simple_deadkeys.js'));
keyboardWithHarness = keyboardLoader.harness;
keyboardWithHarness.activeKeyboard = keyboard;
});
// Tests "stage 1" of fullContextMatch - ensuring that a proper context index map is built.
it('properly generates extended context data needed by context-matching checks below', function() {
let matchDefs = FULL_RULE_SET;
for(var j = 0; j < matchDefs.length; j++) {
// Prepare the context!
var ruleDef = matchDefs[j];
var ruleSeq = new RecordedKeystrokeSequence(ruleDef.baseSequence);
let proctor = new NodeProctor(keyboardWithHarness, device, assert.equal);
// We want to specify the OutputTarget for this test; our actual concern is the resulting context.
var target = new Mock();
ruleSeq.test(proctor, target);
// Now for the real test!
let processor = new KeyboardProcessor(device);
processor.keyboardInterface = keyboardWithHarness;
var res = processor.keyboardInterface._BuildExtendedContext(ruleDef.n, ruleDef.ln, target);
assert.sameOrderedMembers(res.valContext, ruleDef.contextCache);
}
});
describe('handles simple deadkey contexts', function() {
it('for the most basic cases: DEADKEY_TEST_1', function() {
runEngineRuleSet([DEADKEY_TEST_1], "Deadkeys");
});
it('with deadkey ordering: DEADKEY_TEST_2', function() {
runEngineRuleSet([DEADKEY_TEST_2], "Deadkeys");
});
it('with repeated deadkeys: DEADKEY_TEST_3', function() {
runEngineRuleSet([DEADKEY_TEST_3], "Deadkeys");
});
it('with caret after deadkey: DEADKEY_TEST_4', function() {
runEngineRuleSet([DEADKEY_TEST_4], "Deadkeys");
});
it('with no deadkeys, mid-text: DEADKEY_TEST_5', function() {
runEngineRuleSet([DEADKEY_TEST_5], "Deadkeys");
});
it('with long sequence of only deadkeys: DEADKEY_TEST_6', function() {
runEngineRuleSet([DEADKEY_TEST_6], "Deadkeys");
});
});
describe('handles any + context rule interactions', function() {
it('for basic context matches, no any: ANY_CONTEXT_TEST_1', function() {
runEngineRuleSet([ANY_CONTEXT_TEST_1]);
});
it('for any + context-of-any matches: ANY_CONTEXT_TEST_2', function() {
runEngineRuleSet([ANY_CONTEXT_TEST_2]);
});
it('for rules with multiple any + context-of-any pairs: ANY_CONTEXT_TEST_3', function() {
runEngineRuleSet([ANY_CONTEXT_TEST_3]);
});
});
describe('handles any + index context interactions', function() {
it('for basic any + index matches in context: ANY_INDEX_TEST_1', function() {
runEngineRuleSet([ANY_INDEX_TEST_1]);
});
it('for one-any to many-index matches in context: ANY_INDEX_TEST_2', function() {
runEngineRuleSet([ANY_INDEX_TEST_2]);
});
it('for two any + index pairs in rule: ANY_INDEX_TEST_3', function() {
runEngineRuleSet([ANY_INDEX_TEST_3]);
});
});
describe('handles interactions with deadkeys in stores', function() {
it('for any on pure deadkey store: DEADKEY_STORE_TEST_1', function() {
runEngineRuleSet([DEADKEY_STORE_TEST_1]);
});
it('for any on mixed char+deadkey store: DEADKEY_STORE_TEST_2', function() {
runEngineRuleSet([DEADKEY_STORE_TEST_2]);
});
it('for index matching any on pure deadkey store: DEADKEY_STORE_TEST_3', function() {
runEngineRuleSet([DEADKEY_STORE_TEST_3]);
});
it('for context matching any on pure deadkey store: DEADKEY_STORE_TEST_4', function() {
runEngineRuleSet([DEADKEY_STORE_TEST_4]);
});
});
describe('handles interactions between notany and nul in context', function() {
it('with notany against a store with pure characters: NOTANY_NUL_TEST_1', function() {
runEngineRuleSet([NOTANY_NUL_TEST_1]);
});
it('with notany against a store with pure deadkeys: NOTANY_NUL_TEST_2', function() {
runEngineRuleSet([NOTANY_NUL_TEST_2]);
});
it('with notany against a store with mixed characters and deadkeys: NOTANY_NUL_TEST_2', function() {
runEngineRuleSet([NOTANY_NUL_TEST_3]);
});
});
});