Merge pull request #16073 from keymanapp/feat/developer/kmc-convert-warningMessages
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

feat(developer): reviewRules: return object instead of string[] 😎
This commit is contained in:
SabineSIL 2026-08-12 20:10:19 +02:00 committed by GitHub
commit 786887b053
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 817 additions and 2471 deletions

View file

@ -18,9 +18,9 @@ const SevError = CompilerErrorSeverity.Error | Namespace;
export class ConverterMessages {
static ERROR_FileNotFound = SevError | 0x0003;
static Error_FileNotFound = (o: { inputFilename: string; }) => m(
static Error_FileNotFound = (o?: { inputFilename: string | null; }) => m(
this.ERROR_FileNotFound,
`Input filename '${def(o.inputFilename)}' does not exist or could not be loaded.`
`Input filename '${def(o?.inputFilename)}' does not exist or could not be loaded.`
);
static ERROR_InvalidFile = SevError | 0x0004;

View file

@ -68,9 +68,9 @@ export class Converter implements KeymanCompiler {
return null;
}
const ConverterClass = ConverterClassFactory.find(inputFilename, outputFilename);
const ConverterClass = ConverterClassFactory.find(inputFilename, outputFilename ?? '.kmn');
if (!ConverterClass) {
this.callbacks.reportMessage(ConverterMessages.Error_NoConverterFound({ inputFilename, outputFilename }));
this.callbacks.reportMessage(ConverterMessages.Error_NoConverterFound({ inputFilename, outputFilename: outputFilename ?? '' }));
return null;
}

View file

@ -17,9 +17,10 @@ export class KeylayoutFileReader {
constructor(private callbacks: CompilerCallbacks /*,private options: CompilerOptions*/) { };
/**
* @brief helper function to find a specific keyMap index in a keyMapSet
* @brief Helper function to check if a specific keyMap index exists in a keyMapSet.
* This is neccessary because the amount of <keyMap index> must correspond to
* the amount of <keyMapSelect mapIndex>.
* @param jsonObj the read keylayout data to be checked
* @param keyMapSelect the keyMapSelect element to find in keyMapSet
* @return true if the keyMapSet element is found, false if not
@ -36,7 +37,9 @@ export class KeylayoutFileReader {
}
/**
* @brief helper function to find a specific keyMapSelect index in a modifierMap
* @brief Helper function to check if a specific keyMapSelect index exists in a modifierMap.
* This is neccessary because the amount of <keyMap index> must correspond to
* the amount of <keyMapSelect mapIndex>.
* @param jsonObj the read keylayout data to be checked
* @param keyMap the keyMap element to find in modifierMap
* @return true if the keyMap element is found, false if not
@ -53,8 +56,8 @@ export class KeylayoutFileReader {
}
/**
* @brief member function checking if all keyMapSelect elements have a corresponding keyMap
* element in the .keylayout file (if not, the .keylayout file is invalid and will not be converted)
* @brief member function checking if all keyMapSelect elements have exact one corresponding keyMap element (per keyMapSet)
* in the .keylayout file (if not, the .keylayout file is invalid and will not be converted)
* see TN2056 (https://developer.apple.com/library/archive/technotes/tn2056/_index.html#//apple_ref/doc/uid/DTS10003085-CH1-SUBSECTION7)
* @param jsonObj the read keylayout data to be checked
* @return true if all keyMapSelect elements have a corresponding keyMap element, false if not
@ -82,6 +85,10 @@ export class KeylayoutFileReader {
* @returns true if valid, false if invalid
*/
public validate(source: Keylayout.KeylayoutXMLSourceFile, inputFilename: string): boolean {
if (!source) {
this.callbacks.reportMessage(ConverterMessages.Error_UnableToReadFile({ inputFilename: inputFilename }));
return false;
}
if (!SchemaValidators.default.keylayout(source)) {
for (const err of (<any>SchemaValidators.default.keylayout).errors) {
this.callbacks.reportMessage(DeveloperUtilsMessages.Error_InvalidXml({
@ -148,7 +155,7 @@ export class KeylayoutFileReader {
* @param inputFilename the ukelele .keylayout-file to be parsed
* @return in case of success: json object containing data of the .keylayout file; else null
*/
public read(source: Uint8Array): Keylayout.KeylayoutXMLSourceFile {
public read(source: Uint8Array): Keylayout.KeylayoutXMLSourceFile | null {
try {
const data = new TextDecoder().decode(source);

View file

@ -109,7 +109,7 @@ export class KeylayoutToKmnConverter {
* @param outputFilename the resulting keyman .kmn-file
* @return null on success
*/
async run(inputFilename: string, outputFilename?: string): Promise<ConverterToKmnResult> {
async run(inputFilename: string, outputFilename?: string): Promise<ConverterToKmnResult | null> {
if (!inputFilename) {
throw new Error('Input filename is required');
@ -118,7 +118,7 @@ export class KeylayoutToKmnConverter {
const KeylayoutReader = new KeylayoutFileReader(this.callbacks/*, this.options*/);
const binaryData = this.callbacks.loadFile(inputFilename);
const jsonO: Keylayout.KeylayoutXMLSourceFile = KeylayoutReader.read(binaryData);
const jsonO: Keylayout.KeylayoutXMLSourceFile | null = KeylayoutReader.read(binaryData);
if (!jsonO) {
this.callbacks.reportMessage(ConverterMessages.Error_UnableToReadFile({ inputFilename: inputFilename }));
@ -128,7 +128,7 @@ export class KeylayoutToKmnConverter {
if (!KeylayoutReader.validate(jsonO, inputFilename)) {
return null;
}
} catch (e) {
} catch (e: any) {
this.callbacks.reportMessage(ConverterMessages.Error_InvalidFile({ errorText: e.toString() }));
return null;
}
@ -137,10 +137,16 @@ export class KeylayoutToKmnConverter {
const kmnFileWriter = new KmnFileWriter(this.callbacks, this.options);
// write to object/ConverterToKmnResult
const outputKmn = kmnFileWriter.write(processedData);
const outputKmn = processedData ? kmnFileWriter.write(processedData) : null;
if (!processedData || !outputKmn) {
return null;
}
const result: ConverterToKmnResult = {
artifacts: {
kmn: { data: outputKmn, filename: processedData.kmnFilename }
kmn: {
data: outputKmn, filename: processedData.kmnFilename
}
}
};
return result;
@ -151,7 +157,7 @@ export class KeylayoutToKmnConverter {
* @param jsonObj containing filename, behaviorand rules of a json object
* @return an ProcessedData containing all data ready to print out
*/
private convert(jsonObj: Keylayout.KeylayoutXMLSourceFile, inputfilename: string, outputFilename?: string): ProcessedData {
private convert(jsonObj: Keylayout.KeylayoutXMLSourceFile, inputfilename: string, outputFilename?: string): ProcessedData | null {
// modifiers for each behavior
const modifierBehavior: string[][] = [];
@ -197,7 +203,7 @@ export class KeylayoutToKmnConverter {
* @param jsonObj: json Object containing all data read from a keylayout file
* @return an object containing the name of the input file, an array of behaviors and a populated array of Rules[]
*/
public createRuleData(dataUkelele: ProcessedData, jsonObj: Keylayout.KeylayoutXMLSourceFile): ProcessedData {
public createRuleData(dataUkelele: ProcessedData, jsonObj: Keylayout.KeylayoutXMLSourceFile): ProcessedData | null {
const rules: Rule[] = [];
let dkCounterC3: number = 0;
@ -228,7 +234,7 @@ export class KeylayoutToKmnConverter {
// ...............e. g. <key code="1" output="s"/> ...............................................................................
// ...............................................................................................................................
if (jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['output'] !== undefined) {
if (jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['output'] !== undefined) {
// loop modifiers
for (let l = 0; l < dataUkelele.modifiers[i].length; l++) {
@ -292,8 +298,8 @@ export class KeylayoutToKmnConverter {
/* dk for C2*/ 0,
/* unique B */ 0,
/* modifierKey*/ b1ModifierKeyObj[m].modifier,
/* key */ b1ModifierKeyObj[m].key,
/* modifierKey*/ b1ModifierKeyObj[m].modifier ?? "",
/* key */ b1ModifierKeyObj[m].key ?? "",
/* output */ new TextEncoder().encode(outputchar)
);
if ((outputchar !== undefined) && (outputchar !== "undefined") && (outputchar !== "")) {
@ -338,7 +344,7 @@ export class KeylayoutToKmnConverter {
// with present actionId (a18) find all keycode-behavior-pairs that use this action (a18) => (keymapIndex 0/keycode 24 and keymapIndex 3/keycode 24) ....................................
// from these create an array of modifier combinations e.g. [['','caps?'], ['Caps']] .....................................................................................................
/* eg: [['24', 0], ['24', 3]] */ const b4DeadkeyObj: KeylayoutFileData[] = this.getKeyModifierArrayFromActionID(jsonObj, actionId);
/* e.g. [['','caps?'], ['Caps']]*/ const b4DeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj);
/* e.g. [['','caps?'], ['Caps']]*/ const b4DeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj) as string[][];
// ........................................................................................................................................................................................
@ -372,8 +378,8 @@ export class KeylayoutToKmnConverter {
/* dk for C2*/ dkCounterC2++,
/* unique B */ 0,
/* modifierKey*/ b1ModifierKeyObj[n4].modifier,
/* key */ b1ModifierKeyObj[n4].key,
/* modifierKey*/ b1ModifierKeyObj[n4].modifier ?? "",
/* key */ b1ModifierKeyObj[n4].key ?? "",
/* output */ new TextEncoder().encode(b1ModifierKeyObj[n4].outchar),
);
if ((b1ModifierKeyObj[n4].outchar !== undefined)
@ -402,21 +408,22 @@ export class KeylayoutToKmnConverter {
// with actionId from above loop all 'action' and search for a state-next-pair ...................................................................................................................
// e.g. in Block 5: find <when state="3" next="1"/> for action id a16 .............................................................................................................................
for (let l = 0; l < jsonObj.keyboard.actions.action[b1ActionIndex].when.length; l++) {
if ((jsonObj.keyboard.actions.action[b1ActionIndex].when[l]['state'] !== "none")
&& (jsonObj.keyboard.actions.action[b1ActionIndex].when[l]['next'] !== undefined)) {
if (jsonObj.keyboard.actions?.action?.[b1ActionIndex]?.when) {
for (const when of jsonObj.keyboard.actions.action[b1ActionIndex].when) {
if ((when['state'] !== "none")
&& (when['next'] !== undefined)) {
// Data of Block Nr 5 ........................................................................................................................................................................
// of this state-next-pair get value of next (next="1") and state="3" ........................................................................................................................
/* e.g. state = 3 */ const b5ValueState: string = jsonObj.keyboard.actions.action[b1ActionIndex].when[l]['state'];
/* e.g. next = 1 */ const b5ValueNext: string = jsonObj.keyboard.actions.action[b1ActionIndex].when[l]['next'];
/* e.g. state = 3 */ const b5ValueState: string = when['state'] as string;
/* e.g. next = 1 */ const b5ValueNext: string = when['next'];
// ...........................................................................................................................................................................................
// Data of Block Nr 4 ........................................................................................................................................................................
// with present actionId (a16) find all keycode-behavior-pairs that use this action (a16) => (keymapIndex 3/keycode 32) ....................................................................
// from these create an array of modifier combinations e.g. [ [ 'anyOption', 'Caps' ] ] .....................................................................................................
/* e.g. [['32', 3]] */ const b4DeadkeyObj: KeylayoutFileData[] = this.getKeyModifierArrayFromActionID(jsonObj, actionId);
/* e.g. [ [ 'anyOption', 'Caps' ] ]*/ const b4DeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj);
/* e.g. [ [ 'anyOption', 'Caps' ] ]*/ const b4DeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj) as string[][];
// ...........................................................................................................................................................................................
// Data of Block Nr 3 ........................................................................................................................................................................
@ -428,7 +435,7 @@ export class KeylayoutToKmnConverter {
// with present actionId (a17) find all key names and behaviors that use this action (a17) => (keymapIndex 3/keycode 28) ....................................................................
// from these create an array of modifier combinations e.g. [ [ 'anyOption', 'Caps' ] ] .....................................................................................................
/* eg: index=3 */ const b2PrevDeadkeyObj: KeylayoutFileData[] = this.getKeyModifierArrayFromActionID(jsonObj, b3ActionId);
/* e.g. [ [ 'anyOption', 'Caps' ] ] */ const b2PrevDeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b2PrevDeadkeyObj);
/* e.g. [ [ 'anyOption', 'Caps' ] ] */ const b2PrevDeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b2PrevDeadkeyObj) as string[][];
// ...........................................................................................................................................................................................
// Data of Block Nr 6 ........................................................................................................................................................................
// create an array[action id,state,output] from all state-output-pairs that use state = b5ValueNext (e.g. use 1 in <when state="1" output="â"/> ) .........................................
@ -439,17 +446,17 @@ export class KeylayoutToKmnConverter {
// create array[Keycode,Keyname,action id,actionIndex,output] and array[Keyname,action id,behavior,modifier,output] .........................................................................
/* eg: ['49','K_SPACE','a0','0','Â'] */ const b1KeycodeObj: KeylayoutFileData[] = this.getKeyActionOutputArrayFromActionStateOutputArray(jsonObj, b6ActionIdObj);
/* eg: ['K_SPACE','a0','0','NCAPS','Â'] */ const b1ModifierKeyObj: KeylayoutFileData[] = this.getKeyBehaviorModOutputArrayFromKeyActionBehaviorOutputArray(jsonObj, b1KeycodeObj, isCapsused);
// ...........................................................................................................................................................................................
// ...........................................................................................................................................................................................
for (let n1 = 0; n1 < b2PrevDeadkeyModifierObj.length; n1++) {
for (let n2 = 0; n2 < b2PrevDeadkeyModifierObj[n1].length; n2++) {
for (let n3 = 0; n3 < b2PrevDeadkeyObj.length; n3++) {
for (let n4 = 0; n4 < b4DeadkeyModifierObj.length; n4++) {
for (let n5 = 0; n5 < b4DeadkeyModifierObj[n4].length; n5++) {
for (let n6 = 0; n6 < b4DeadkeyObj.length; n6++) {
for (let n7 = 0; n7 < b1ModifierKeyObj.length; n7++) {
for (let n1 = 0; n1 < b2PrevDeadkeyModifierObj.length; n1++) {
for (let n2 = 0; n2 < b2PrevDeadkeyModifierObj[n1].length; n2++) {
for (let n3 = 0; n3 < b2PrevDeadkeyObj.length; n3++) {
for (let n4 = 0; n4 < b4DeadkeyModifierObj.length; n4++) {
for (let n5 = 0; n5 < b4DeadkeyModifierObj[n4].length; n5++) {
for (let n6 = 0; n6 < b4DeadkeyObj.length; n6++) {
for (let n7 = 0; n7 < b1ModifierKeyObj.length; n7++) {
ruleObj = new Rule(
ruleObj = new Rule(
/* ruleType */ "C3",
/* modifierPrevDeadkey*/ this.createKmnModifier(b2PrevDeadkeyModifierObj[n1][n2], isCapsused),
/* prevDeadkey */ this.mapUkeleleKeycodeToVK(Number(b2PrevDeadkeyObj[n3].key)),
@ -461,14 +468,15 @@ export class KeylayoutToKmnConverter {
/* dk for C2*/ 0,
/* unique B */ 0,
/* modifierKey*/ b1ModifierKeyObj[n7].modifier,
/* key */ b1ModifierKeyObj[n7].key,
/* modifierKey*/ b1ModifierKeyObj[n7].modifier ?? "",
/* key */ b1ModifierKeyObj[n7].key ?? "",
/* output */ new TextEncoder().encode(b1ModifierKeyObj[n7].outchar),
);
if ((b1ModifierKeyObj[n7].outchar !== undefined)
&& (b1ModifierKeyObj[n7].outchar !== "undefined")
&& (b1ModifierKeyObj[n7].outchar !== "")) {
rules.push(ruleObj);
);
if ((b1ModifierKeyObj[n7].outchar !== undefined)
&& (b1ModifierKeyObj[n7].outchar !== "undefined")
&& (b1ModifierKeyObj[n7].outchar !== "")) {
rules.push(ruleObj);
}
}
}
}
@ -930,6 +938,7 @@ export class KeylayoutToKmnConverter {
* @param isCAPSused : boolean flag to indicate if CAPS is used in a keylayout file or not
* @return an array: KeylayoutFileData[] containing [{KeyName,actionId,behavior,modifier,output}]
*/
public getKeyBehaviorModOutputArrayFromKeyActionBehaviorOutputArray(data: Keylayout.KeylayoutXMLSourceFile, search: KeylayoutFileData[], isCAPSused: boolean): KeylayoutFileData[] {
const keyBehaviorModOutput = [];
if (!((search === undefined) || (search === null) || (search.length === 0))) {
@ -948,7 +957,7 @@ export class KeylayoutToKmnConverter {
}
}
// remove duplicates
const uniquekeyBehaviorModOutput = keyBehaviorModOutput.reduce((unique, o) => {
const uniquekeyBehaviorModOutput = keyBehaviorModOutput.reduce<KeylayoutFileData[]>((unique, o) => {
if (!unique.some(obj =>
obj.actionId === o.actionId &&
obj.key === o.key &&
@ -999,7 +1008,7 @@ export class KeylayoutToKmnConverter {
//.............................................................................
// remove duplicates
const uniqueactionOutputBehaviorKey = actionOutputBehaviorKeyModi.reduce((unique, o) => {
const uniqueactionOutputBehaviorKey = actionOutputBehaviorKeyModi.reduce<KeylayoutFileData[]>((unique, o) => {
if (!unique.some(obj =>
obj.outchar === o.outchar &&
obj.actionId === o.actionId &&

File diff suppressed because it is too large Load diff

View file

@ -24,6 +24,9 @@
<key code="1" output="U+1F603"/>
<key code="2" output="&#128514;"/>
<key code="3" output="😀"/>
<key code="3" output="U+0001"/>
<key code="4" output="&#x0004;"/><!--should give unexpected entity as fast xml parser reads it to '' -->
</keyMap>
</keyMapSet>
<actions>

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboard SYSTEM "../../../../../resources/standards-data/Keylayout/Keylayout.dtd">
<!--
Data generated August 1st 2025
Generated by S. Schmitt
more keyMapSelect than KeyMap
-->
<keyboard group="126" id="-1272" name="Test_DifferentAmountOfMapSelectInKeyMapERROR" maxout="1">
<layouts>
<layout first="0" last="0" mapSet="138" modifiers="30"/>
</layouts>
<!-- different amount of tags MapSelect(2) vs. KeyMap(3) -->
<modifierMap id="30" defaultIndex="0">
<keyMapSelect mapIndex="0">
<modifier keys=""/>
<modifier keys="shift? caps? "/>
</keyMapSelect>
<keyMapSelect mapIndex="1">
<modifier keys="caps"/>
</keyMapSelect>
</modifierMap>
<!-- different amount of tags MapSelect(2) vs. KeyMap(3) -->
<keyMapSet id="ANSI">
<keyMap index="0">
<key code="0" action="A_9"/>
<key code="1" output="s"/>
<key code="2" output="d"/>
<key code="3" output="f"/>
<key code="4" output="h"/>
<key code="9" output="v"/>
<key code="10" output="\"/>
</keyMap>
<keyMap index="1">
<key code="0" action="A_1"/>
<key code="1" output="S"/>
<key code="2" output="D"/>
<key code="3" output="F"/>
<key code="4" output="@"/>
<key code="5" output="G"/>
<key code="6" output="Z"/>
<key code="7" output="X"/>
<key code="8" output="C"/>
<key code="9" output="V"/>
<key code="10" output="\"/>
</keyMap>
</keyMapSet>
<keyMapSet id="JIS">
<keyMap index="0">
<key code="0" action="A_9"/>
<key code="1" output="s"/>
<key code="2" output="d"/>
<key code="3" output="f"/>
<key code="4" output="h"/>
<key code="9" output="v"/>
<key code="10" output="\"/>
</keyMap>
<keyMap index="1">
<key code="0" action="A_1"/>
<key code="1" output="ሴ"/>
<key code="2" output="😎"/>
</keyMap>
</keyMapSet>
<actions>
<action id="A_1">
<when state="none" output="A"/>
</action>
<action id="A_9">
<when state="none" output="A"/>
</action>
</actions>
<terminators>
<when state="1" output="ˆ"/>
</terminators>
</keyboard>

View file

@ -12,6 +12,7 @@ import { assert } from 'chai';
import { Keylayout } from "@keymanapp/developer-utils";
import { compilerTestCallbacks, makePathToFixture } from './helpers/index.js';
import { KeylayoutFileReader } from '../src/keylayout-to-kmn/keylayout-file-reader.js';
import { KL_KeyMap, KL_KeyMapSelect } from "../../common/web/utils/src/types/keylayout/keylayout-xml.js";
describe('KeylayoutFileReader', function () {
@ -27,6 +28,50 @@ describe('KeylayoutFileReader', function () {
const validated = sutR.validate(result as Keylayout.KeylayoutXMLSourceFile, inputFilename);
assert.isTrue(validated);
});
it('validate() should return false on inputfile with unknown tags', async function () {
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test_unknownTags.keylayout');
const result: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
const validated = sutR.validate(result as Keylayout.KeylayoutXMLSourceFile, inputFilename);
assert.isFalse(validated);
});
it('validate() should return false on inputfile with additional tags', async function () {
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test_additionalTags.keylayout');
const result: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
const validated = sutR.validate(result as Keylayout.KeylayoutXMLSourceFile, inputFilename);
assert.isFalse(validated);
});
it('validate() should return false on inputfile with missing tags', async function () {
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test_missingTags.keylayout');
const result: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
const validated = sutR.validate(result as Keylayout.KeylayoutXMLSourceFile, inputFilename);
assert.isFalse(validated);
});
it('validate() should return false on no entries in action-when', async function () {
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test_noActionWhen.keylayout');
const result: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
const validated = sutR.validate(result as Keylayout.KeylayoutXMLSourceFile, inputFilename);
assert.isFalse(validated);
});
it('validate() should return false on null as input', async function () {
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test_noActionWhen.keylayout');
//const result: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
const validated = sutR.validate(null, inputFilename);
assert.isFalse(validated);
});
it('validate() should return false on undefined as input', async function () {
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test_noActionWhen.keylayout');
//const result: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
const validated = sutR.validate(undefined, inputFilename);
assert.isFalse(validated);
});
});
describe('validate() should return false on inputfiles with errors ', function () {
@ -86,6 +131,84 @@ describe('KeylayoutFileReader', function () {
});
});
describe('findMapIndexinKeymap ', function () {
const keyMapSelect: KL_KeyMapSelect = {
mapIndex: '',
modifier: []
};
keyMapSelect.modifier.push({ keys: 'caps' });
keyMapSelect.modifier.push({ keys: 'rightOption' });
keyMapSelect.modifier.push({ keys: 'rightShift caps' });
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test.keylayout');
const jsonO: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
[
['0', true],
['7', true],
['999', false],
['A', false],
[123, false],
['', false],
[null, false],
[undefined, false],
].forEach(function (values) {
it(("findMapIndexinKeymap(keyMapSelect.mapIndex = '" + values[0] + "')").padEnd(40, " ") + "should return " + "'" + values[1] + "'", async function () {
keyMapSelect.mapIndex = values[0] as string;
const result = sutR.findMapIndexinKeymap(jsonO as Keylayout.KeylayoutXMLSourceFile, keyMapSelect);
assert.equal(result, values[1]);
});
});
});
describe('findIndexinKeymapSelect ', function () {
const keyMap: KL_KeyMap = {
index: '',
key: []
};
keyMap.key.push({ code: '0', output: 'A' });
keyMap.key.push({ code: '1', action: 'S' });
keyMap.key.push({ code: '2', output: 'D' });
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
const inputFilename = makePathToFixture('../data/Test.keylayout');
const jsonO: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(inputFilename));
[
['0', true],
['7', true],
['999', false],
['A', false],
[123, false],
['', false],
[null, false],
[undefined, false],
].forEach(function (values) {
it(("findIndexinKeymapSelect(keyMap.index = '" + values[0] + "')").padEnd(40, " ") + "should return " + "'" + values[1] + "'", async function () {
keyMap.index = values[0] as string;
const result = sutR.findIndexinKeymapSelect(jsonO as Keylayout.KeylayoutXMLSourceFile, keyMap);
assert.equal(result, values[1]);
});
});
});
describe('checkForCorrespondingElements ', function () {
const sutR = new KeylayoutFileReader(compilerTestCallbacks);
[
['../data/Test.keylayout', true],
['../data/Test_sameKeyMapAndKeyMapselectAndJisERROR.keylayout', true],
['../data/Test_moreKeymapSelectThanKeymapERROR.keylayout', false],
['../data/Test_moreKeyMapThanKeyMapselectERROR.keylayout', false],
['../data/Test_moreKeyMapThanKeyMapselectAndJisERROR.keylayout', false],
].forEach(function (values) {
it(("checkForCorrespondingElements in " + values[0]).padEnd(40, " ") + "should return " + "'" + values[1] + "'", async function () {
const jsonO: Keylayout.KeylayoutXMLSourceFile | null = sutR.read(compilerTestCallbacks.loadFile(makePathToFixture(values[0] as string)));
const result = sutR.checkForCorrespondingElements(jsonO as Keylayout.KeylayoutXMLSourceFile);
assert.equal(result, values[1]);
});
});
});
describe("read() check structure of returned JSON", function () {
it('read() should have the correct JSON structure', async function () {
@ -96,7 +219,7 @@ describe('KeylayoutFileReader', function () {
const result: Keylayout.KeylayoutXMLSourceFile = sutR.read(binaryData);
assert.isNotNull(result);
assert.isTrue(result.keyboard !== null);
assert.notEqual(result.keyboard, null);
for (let i = 0; i < result.keyboard.layouts.length; i++) {
assert.isTrue(result.keyboard.layouts[i].layout.length > 0);
@ -108,8 +231,8 @@ describe('KeylayoutFileReader', function () {
for (let j = 0; j < result.keyboard.keyMapSet[i].keyMap.length; j++) {
assert.isTrue(result.keyboard.keyMapSet[i].keyMap[j].key.length > 0);
for (let k = 0; k < result.keyboard.keyMapSet[i].keyMap[j].key.length; k++) {
assert.isTrue(result.keyboard.keyMapSet[i].keyMap[j].key[k]['action'] !== null
|| result.keyboard.keyMapSet[i].keyMap[j].key[k]['output'] !== null);
assert.isNotNull(result.keyboard.keyMapSet[i].keyMap[j].key[k]['action']);
assert.isNotNull(result.keyboard.keyMapSet[i].keyMap[j].key[k]['output']);
}
}
}
@ -124,8 +247,8 @@ describe('KeylayoutFileReader', function () {
for (let i = 0; i < result.keyboard.modifierMap.length; i++) {
assert.isTrue(result.keyboard.modifierMap[i].keyMapSelect.length > 0);
for (let j = 0; j < result.keyboard.keyMapSet.length; j++) {
assert.isTrue(result.keyboard.keyMapSet[j].keyMap.length
=== result.keyboard.modifierMap[i].keyMapSelect.length);
assert.equal(result.keyboard.keyMapSet[j].keyMap.length,
result.keyboard.modifierMap[i].keyMapSelect.length);
}
}
});

View file

@ -60,14 +60,13 @@ describe('KeylayoutToKmnConverter', function () {
['../data/Test.keylayout'],
].forEach(function (files) {
it(files + " should give no errors ", async function () {
sut.run(makePathToFixture(files[0]));
// assert.isTrue(compilerTestCallbacks.messages.length === 1 && compilerTestCallbacks.messages[0].code === 5292037);
await sut.run(makePathToFixture(files[0]));
assert.isTrue(compilerTestCallbacks.messages.length === 0);
await sut.run(makePathToFixture(files[0]));
assert.equal(compilerTestCallbacks.messages.length, 0);
});
});
});
});
});
});
describe('RunTestFiles resulting in errors ', function () {
const sut = new KeylayoutToKmnConverter(compilerTestCallbacks, compilerTestOptions);
[
@ -97,7 +96,7 @@ describe('KeylayoutToKmnConverter', function () {
['../data/Test_undefinedAction.keylayout'],
].forEach(function (files) {
it(files + " should give Error: undefined action detected", async function () {
sut.run(makePathToFixture(files[0]));
await sut.run(makePathToFixture(files[0]));
assert.equal(compilerTestCallbacks.messages.length, 1);
assert.equal(compilerTestCallbacks.messages[0].code, 5292040);
});

View file

@ -63,7 +63,7 @@ describe('KmnFileWriter', function () {
it(('writeKmnFileHeader should return store text with filename ').padEnd(62, " ") + 'on correct input', async function () {
const writtenCorrectName = sutW.writeKmnFileHeader(converted);
assert.equal(writtenCorrectName, (outExpectedFirst + converted.keylayoutFilename + outExpectedLast));
assert.equal(writtenCorrectName, (outExpectedFirst + (converted?.keylayoutFilename ?? "") + outExpectedLast));
});
});
@ -105,57 +105,48 @@ describe('KmnFileWriter', function () {
});
});
describe('reviewRules messages', function () {
const sutW = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions);
[/*
[
[[new Rule("C0", '', '', 0, 0, '', '', 0, 0, 'UNAVAILABLE', 'K_A', new TextEncoder().encode('A'))],
[''],
[''],
['c WARNING: unavailable modifier : here: ']],
['c WARNING: unavailable modifier here: ']],
[[new Rule("C1", '', '', 0, 0, 'CAPS', 'K_EQUAL', 0, 0, 'UNAVAILABLE', 'K_B', new TextEncoder().encode('B'))],
[''],
[''],
['c WARNING: unavailable modifier : here: ']],
['c WARNING: unavailable modifier here: ']],
[[new Rule("C2", '', '', 0, 0, 'CAPS', 'K_EQUAL', 0, 0, 'UNAVAILABLE', 'K_C', new TextEncoder().encode('C'),)],
[''],
[''],
['c WARNING: unavailable modifier : here: ']],
['c WARNING: unavailable modifier here: ']],
[[new Rule("C2", '', '', 0, 0, 'UNAVAILABLE_dk', 'K_EQUAL', 0, 0, 'UNAVAILABLE', 'K_C', new TextEncoder().encode('C'),)],
[[new Rule("C2", '', '', 1, 1, 'UNAVAILABLE_dk', 'K_EQUAL', 2, 2, 'UNAVAILABLE', 'K_C', new TextEncoder().encode('C'),)],
[''],
['c WARNING: unavailable modifier : here: '],
['c WARNING: unavailable modifier : here: ']],
['c WARNING: unavailable modifier here: '],
['c WARNING: unavailable superior rule ( [UNAVAILABLE_dk K_EQUAL] > dk(A2) ) : unavailable modifier here: ']],
[[new Rule("C3", 'UNAVAILABLE_prev_dk', 'K_D', 0, 0, 'UNAVAILABLE_dk', 'K_EQUAL', 0, 0, 'SHIFT', 'K_C', new TextEncoder().encode('D'),)],
['c WARNING: unavailable modifier : here: '],
['c WARNING: unavailable modifier : here: '],
['c WARNING: unavailable superior rule ( [UNAVAILABLE_dk K_EQUAL] > dk(B0) ) : here: ']],
[[new Rule("C3", 'UNAVAILABLE_prev_dk', 'K_D', 1, 1, 'UNAVAILABLE_dk', 'K_EQUAL', 0, 0, 'SHIFT', 'K_C', new TextEncoder().encode('D'),)],
['c WARNING: unavailable modifier here: '],
['c WARNING: unavailable superior rule ( [UNAVAILABLE_prev_dk K_D] > dk(A1) ) : unavailable modifier here: '],
['c WARNING: unavailable superior rule ( [UNAVAILABLE_dk K_EQUAL] > dk(B0) ) : here: ']],
*/
[[new Rule("C3", 'UNAVAILABLE_prev_dk', 'K_D', 0, 0, 'UNAVAILABLE_dk', 'K_EQUAL', 0, 0, 'UNAVAIL', 'K_C', new TextEncoder().encode('D'),)],
['c WARNING: unavailable modifier : here: '],
['c WARNING: unavailable modifier : here: '],
['c WARNING: unavailable superior rule ( [UNAVAILABLE_dk K_EQUAL] > dk(B0) ) : unavailable modifier : here: ']],
[[new Rule("C3", 'CAPS', 'K_D', 0, 0, 'RALT', 'K_EQUAL', 0, 0, 'SHIFT', 'K_C', new TextEncoder().encode('D'),)],
[[new Rule("C3", 'CAPS', 'K_D', 1, 1, 'RALT', 'K_EQUAL', 0, 0, 'SHIFT', 'K_C', new TextEncoder().encode('D'),)],
[''],
[''],
['']],
[[new Rule("C3", 'X', 'K_X', 0, 0, 'Y', 'K_Y', 0, 0, 'SHIFT', 'K_Z', new TextEncoder().encode('D'),)],
['c WARNING: unavailable modifier : here: '],
['c WARNING: unavailable modifier : here: '],
['c WARNING: unavailable superior rule ( [Y K_Y] > dk(B0) ) : here: ']],
[[new Rule("C3", 'X', 'K_X', 1, 1, 'Y', 'K_Y', 0, 0, 'SHIFT', 'K_Z', new TextEncoder().encode('D'),)],
['c WARNING: unavailable modifier here: '],
['c WARNING: unavailable superior rule ( [X K_X] > dk(A1) ) : unavailable modifier here: '],
['c WARNING: unavailable superior rule ( [Y K_Y] > dk(B0) ) : here: ']],
].forEach(function (values: (string[] | Rule[])[], index: number) {
it(('rule " ' + (values[0][0] as Rule).ruleType as string + ' "') + 'should create "' + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () {
const result: string[] = sutW.unitTestEndpoints.reviewRules(values[0] as Rule[], 0);
const result: string[] = sutW.unitTestEndpoints.reviewRules(values[0] as Rule[], 0).warningMessages;
assert.equal(result[0], values[1][0]);
assert.equal(result[1], values[2][0]);
assert.equal(result[2], values[3][0]);
@ -163,6 +154,7 @@ describe('KmnFileWriter', function () {
});
});
describe('reviewRules messages duplicate and ambiguous', function () {
const sutW = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions);
[
@ -170,9 +162,9 @@ describe('KmnFileWriter', function () {
[[
new Rule("C3", 'LALT', 'K_A', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'LALT', 'K_A', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
["c WARNING: duplicate rule: earlier: dk(B0) + [SHIFT K_B] > dk(B0) here: "],
["c WARNING: duplicate rule: earlier: dk(B0) + [CAPS K_C] > 'X' here: "]],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
["c WARNING: duplicate rule: earlier: dk(C0) + [SHIFT K_B] > dk(B0) here: "],
["c WARNING: duplicate rule: earlier: dk(B0) + [CAPS K_C] > 'X' here: "]],
//6-6 dup
[[
@ -180,7 +172,7 @@ describe('KmnFileWriter', function () {
new Rule("C3", 'CTRL', 'K_D', 0, 0, 'NCAPS', 'K_E', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),],
[''],
[""],
["c WARNING: duplicate rule: earlier: dk(B0) + [CAPS K_C] > 'X' here: "]],
["c WARNING: duplicate rule: earlier: dk(B0) + [CAPS K_C] > 'X' here: "]],
//6-6 amb
[[
@ -188,29 +180,29 @@ describe('KmnFileWriter', function () {
new Rule("C3", 'CTRL', 'K_D', 0, 0, 'NCAPS', 'K_E', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('Y')),],
[''],
[""],
["c WARNING: ambiguous rule: earlier: dk(B0) + [CAPS K_C] > 'X' here: "]],
["c WARNING: ambiguous rule: earlier: dk(B0) + [CAPS K_C] > 'X' here: "]],
// 5-5 amb
[[
new Rule("C3", 'LALT', 'K_A', 0, 0, 'NCAPS', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'LALT', 'K_A', 0, 0, 'NCAPS', 'K_B', 0, 1, 'RALT', 'K_F', new TextEncoder().encode('X')),],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
["c WARNING: ambiguous rule: earlier: dk(B0) + [NCAPS K_B] > dk(B0) here: "], [''],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
["c WARNING: ambiguous rule: earlier: dk(C0) + [NCAPS K_B] > dk(B0) here: "], [''],
],
// 5-5 dup
[[
new Rule("C3", 'LALT', 'K_A', 0, 0, 'NCAPS', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'LALT', 'K_A', 0, 0, 'NCAPS', 'K_B', 0, 0, 'RALT', 'K_F', new TextEncoder().encode('X')),],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
["c WARNING: duplicate rule: earlier: dk(B0) + [NCAPS K_B] > dk(B0) here: "],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
["c WARNING: duplicate rule: earlier: dk(C0) + [NCAPS K_B] > dk(B0) here: "],
['']],
// 4-2 amb
[[
new Rule("C3", 'LALT', 'K_A', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C2", '', '', 0, 0, 'LALT', 'K_A', 0, 0, 'RALT', 'K_F', new TextEncoder().encode('X')),],
['c WARNING: ambiguous rule: later: [LALT K_A] > dk(C0) here: '],
['c WARNING: ambiguous rule: later: [LALT K_A] > dk(C0) here: '],
[''],
['']],
@ -218,7 +210,7 @@ describe('KmnFileWriter', function () {
[[
new Rule("C3", 'LALT', 'K_A', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'LALT', 'K_A', 1, 1, 'NCAPS', 'K_E', 0, 0, 'RALT', 'K_F', new TextEncoder().encode('Y')),],
['c WARNING: ambiguous rule: earlier: [LALT K_A] > dk(C0) here: '],
['c WARNING: ambiguous rule: earlier: [LALT K_A] > dk(C0) here: '],
[""],
[''],],
@ -226,7 +218,7 @@ describe('KmnFileWriter', function () {
[[
new Rule("C3", 'LALT', 'K_A', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'LALT', 'K_A', 0, 0, 'NCAPS', 'K_E', 0, 0, 'RALT', 'K_F', new TextEncoder().encode('X')),],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
['c WARNING: duplicate rule: earlier: [LALT K_A] > dk(C0) here: '],
[''],
['']],
@ -234,7 +226,7 @@ describe('KmnFileWriter', function () {
[[
new Rule("C3", 'LALT', 'K_A', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C2", '', '', 0, 0, 'LALT', 'K_A', 0, 0, 'RALT', 'K_F', new TextEncoder().encode('Y')),],
['c WARNING: ambiguous rule: later: [LALT K_A] > dk(C0) here: '],
['c WARNING: ambiguous rule: later: [LALT K_A] > dk(C0) here: '],
[''],
['']],
@ -243,7 +235,7 @@ describe('KmnFileWriter', function () {
new Rule("C2", '', '', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'CTRL', 'K_D', 0, 0, 'NCAPS', 'K_E', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),],
[''],
["c WARNING: duplicate rule: earlier: dk(C0) + [CAPS K_C] > 'X' here: "],
["c WARNING: duplicate rule: earlier: dk(C0) + [CAPS K_C] > 'X' here: "],
[''],],
// 6-3 amb
@ -251,14 +243,14 @@ describe('KmnFileWriter', function () {
new Rule("C2", '', '', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'CTRL', 'K_D', 0, 0, 'NCAPS', 'K_E', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('Y')),],
[''],
["c WARNING: ambiguous rule: earlier: dk(C0) + [CAPS K_C] > 'X' here: "],
["c WARNING: ambiguous rule: earlier: dk(C0) + [CAPS K_C] > 'X' here: "],
[''],],
// 2-4 amb
[[
new Rule("C2", '', '', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C3", 'SHIFT', 'K_B', 0, 0, 'NCAPS', 'K_E', 0, 0, 'RALT', 'K_F', new TextEncoder().encode('Y')),],
['c WARNING: ambiguous rule: earlier: [SHIFT K_B] > dk(A0) here: '],
['c WARNING: ambiguous rule: earlier: [SHIFT K_B] > dk(A0) here: '],
[''],
['']],
@ -267,7 +259,7 @@ describe('KmnFileWriter', function () {
new Rule("C2", '', '', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C2", '', '', 0, 0, 'SHIFT', 'K_B', 1, 1, 'RALT', 'K_F', new TextEncoder().encode('Y')),],
[''],
['c WARNING: ambiguous rule: earlier: [SHIFT K_B] > dk(C0) here: '],
['c WARNING: ambiguous rule: earlier: [SHIFT K_B] > dk(C0) here: '],
['']],
// 2-2 dup
@ -275,7 +267,7 @@ describe('KmnFileWriter', function () {
new Rule("C2", '', '', 0, 0, 'SHIFT', 'K_B', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),
new Rule("C2", '', '', 0, 0, 'SHIFT', 'K_B', 0, 0, 'RALT', 'K_F', new TextEncoder().encode('Y')),],
[''],
['c WARNING: duplicate rule: earlier: [SHIFT K_B] > dk(C0) here: '],
['c WARNING: duplicate rule: earlier: [SHIFT K_B] > dk(C0) here: '],
['']],
// 3-3 dup
@ -284,7 +276,7 @@ describe('KmnFileWriter', function () {
new Rule("C2", '', '', 0, 0, 'NCAPS', 'K_E', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X')),],
[''],
[''],
["c WARNING: duplicate rule: earlier: dk(A0) + [CAPS K_C] > 'X' here: "]],
["c WARNING: duplicate rule: earlier: dk(A0) + [CAPS K_C] > 'X' here: "]],
// 3-3 amb
[[
@ -292,7 +284,7 @@ describe('KmnFileWriter', function () {
new Rule("C2", '', '', 0, 0, 'NCAPS', 'K_E', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('Y')),],
[''],
[''],
["c WARNING: ambiguous rule: earlier: dk(A0) + [CAPS K_C] > 'X' here: "]],
["c WARNING: ambiguous rule: earlier: dk(A0) + [CAPS K_C] > 'X' here: "]],
// 2-1 amb
[[
@ -300,7 +292,7 @@ describe('KmnFileWriter', function () {
new Rule("C0", '', '', 0, 0, '', '', 0, 0, 'RALT', 'K_B', new TextEncoder().encode('Y'))],
[''],
[''],
['c WARNING: ambiguous rule: later: [RALT K_B] > dk(A0) here: ']],
['c WARNING: ambiguous rule: later: [RALT K_B] > dk(A0) here: ']],
// 1-1 amb
[[
@ -308,7 +300,7 @@ describe('KmnFileWriter', function () {
new Rule("C0", '', '', 0, 0, '', '', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('Y'))],
[''],
[''],
["c WARNING: ambiguous rule: earlier: [CAPS K_C] > 'X' here: "]],
["c WARNING: ambiguous rule: earlier: [CAPS K_C] > 'X' here: "]],
// 1-1 amb
[[
@ -316,11 +308,11 @@ describe('KmnFileWriter', function () {
new Rule("C0", '', '', 0, 0, '', '', 0, 0, 'CAPS', 'K_C', new TextEncoder().encode('X'))],
[''],
[''],
["c WARNING: duplicate rule: earlier: [CAPS K_C] > 'X' here: "]],
["c WARNING: duplicate rule: earlier: [CAPS K_C] > 'X' here: "]],
].forEach(function (values: (string[] | Rule[])[], index: number) {
it('rule ' + (values[0][0] as Rule).ruleType as string + ' should create " ' + ' "' + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () {
const result: string[] = sutW.unitTestEndpoints.reviewRules(values[0] as Rule[], 1);
const result: string[] = sutW.unitTestEndpoints.reviewRules(values[0] as Rule[], 1).warningMessages;
assert.equal(result[0], values[1][0]);
assert.equal(result[1], values[2][0]);
assert.equal(result[2], values[3][0]);
@ -337,10 +329,10 @@ describe('KmnFileWriter', function () {
],
[''],
[''],
["c WARNING: ambiguous rule: later: [RALT K_B] > dk(A0) ambiguous rule: earlier: [RALT K_B] > 'X' here: PLEASE CHECK THE FOLLOWING RULE AS IT WILL NOT BE WRITTEN ! "]],
["c WARNING: ambiguous rule: later: [RALT K_B] > dk(A0) ambiguous rule: earlier: [RALT K_B] > 'X' PLEASE CHECK THAT RULE AS IT WILL NOT BE WRITTEN ! here: "]],
].forEach(function (values: (string[] | Rule[])[], index: number) {
it(('rule ' + (values[0][0] as Rule).ruleType as string + ' should create " ' + ' "') + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () {
const result: string[] = sutW.unitTestEndpoints.reviewRules(values[0] as Rule[], 2);
const result: string[] = sutW.unitTestEndpoints.reviewRules(values[0] as Rule[], 2).warningMessages;
assert.equal(result[0], values[1][0]);
assert.equal(result[1], values[2][0]);
assert.equal(result[2], values[3][0]);
@ -450,5 +442,42 @@ describe('KmnFileWriter', function () {
});
});
});
describe('writeCharacterOrUnicode and return values', function () {
const sutW = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions);
[
["A", "A", "Msg "],
["ሴ", "ሴ", "Msg "],
["😀", "😀", "Msg "],
["ẘ", "ẘ", "Msg "],
["U+0001", "U+0001", "Msg Use of a control character "],
["U+0061", "a", "Msg "],
["&#x0002;", "U+0002", "Msg Use of a control character "],
["&#x1234;", 'ሴ', "Msg "],
["&#0003;", "U+0003", "Msg Use of a control character "],
["&#4666;", "ሺ", "Msg "],
["", "U+0006", "Msg Use of a control character "],
['', '', 'Msg empty output or unsupported numerical html entity: '],
].forEach(function (values) {
it(('should convert "' + values[0] + '"').padEnd(25, " ") + 'to "' + values[1] + '"', async function () {
const result = sutW.writeCharacterOrUnicode(values[0] as string, "Msg ");
assert.isNotNull(result);
assert.equal(result.character, values[1]);
assert.equal(result.message, values[2]);
});
});
});
describe('writeCharacterOrUnicode and return null for result.message and result.character', function () {
const sutW = new KmnFileWriter(compilerTestCallbacks, compilerTestOptions);
[
[null, null],
[undefined, null],
].forEach(function (values) {
it(('should convert "' + values[0] + '"').padEnd(25, " ") + 'to "' + values[1] + '"', async function () {
const result = sutW.writeCharacterOrUnicode(values[0], "");
assert.isNull(result);
});
});
});
});