mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-12 17:07:31 +00:00
Merge branch 'epic/web-core' into refactor/web/defaultRules
This commit is contained in:
commit
aeb8cbe39a
6 changed files with 194 additions and 15 deletions
|
|
@ -290,7 +290,8 @@ export class CoreKeyboardProcessor extends EventEmitter<EventMap> implements Key
|
|||
|
||||
/**
|
||||
* Processes post-keystroke actions for the given device and text store.
|
||||
* Handles any actions that should occur after a keystroke is processed.
|
||||
* Handles any actions that should occur after a keystroke is processed
|
||||
* or after applying suggestions.
|
||||
*
|
||||
* @param {DeviceSpec} device The device specification.
|
||||
* @param {TextStore} textStore The current text store context.
|
||||
|
|
@ -298,7 +299,10 @@ export class CoreKeyboardProcessor extends EventEmitter<EventMap> implements Key
|
|||
* @returns {ProcessorAction} The resulting processor action, or null if not applicable.
|
||||
*/
|
||||
public processPostKeystroke(device: DeviceSpec, textStore: TextStore): ProcessorAction {
|
||||
// TODO-web-core: Implement this method (#15286)
|
||||
// TODO-embed-osk-in-kmx: Implement this method (#15286)
|
||||
// This gets called after processing a keystroke to process the PostKeystroke group
|
||||
// (irrelevant for web-core since that is handled in Core), but also after
|
||||
// applying a suggestion, which we do need to handle.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +347,7 @@ export class CoreKeyboardProcessor extends EventEmitter<EventMap> implements Key
|
|||
* @returns {boolean} True if the keyboard layer changed, false otherwise.
|
||||
*/
|
||||
public selectLayer(keyEvent: KeyEvent): boolean {
|
||||
// TODO-web-core: Implement this method (#15284)
|
||||
// TODO-embed-osk-in-kmx: Implement this method (#15284)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export class KMXKeyboard {
|
|||
},{
|
||||
scope: KM_CORE_OPTION_SCOPE.OPT_ENVIRONMENT,
|
||||
key: KM_CORE_KMX_ENV.SIMULATEALTGR,
|
||||
value: "0" // TODO: We won't support simulating AltGr option in v19
|
||||
value: "0" // TODO: We won't support simulating AltGr option in v19 - see also emulatesAltGr
|
||||
}
|
||||
]
|
||||
const result = KM_Core.instance.state_create(_keyboard, environment_opts);
|
||||
|
|
@ -55,7 +55,7 @@ export class KMXKeyboard {
|
|||
}
|
||||
|
||||
public constructKeyEvent(key: ActiveKey | ActiveSubKey, device: DeviceSpec, stateKeys: StateKeyMap): KeyEvent {
|
||||
// TODO-web-core: Implement this method (#15290)
|
||||
// TODO-embed-osk-in-kmx: Implement this method (#15290)
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +86,8 @@ export class KMXKeyboard {
|
|||
}
|
||||
|
||||
public get isChiral(): boolean {
|
||||
// TODO-web-core: Implement this method (#15290)
|
||||
// TODO-embed-osk-in-kmx: Implement this method (#15290)
|
||||
// Only relevant for OSK
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ export class KMXKeyboard {
|
|||
* @return {boolean}
|
||||
*/
|
||||
public get emulatesAltGr(): boolean {
|
||||
// TODO-web-core: Implement this method (#15290)
|
||||
// TODO: We won't support simulating AltGr option in v19 - see also c'tor
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -108,8 +109,8 @@ export class KMXKeyboard {
|
|||
* @param {number} data 1 for KeyDown or FocusReceived,
|
||||
* 0 for KeyUp or FocusLost
|
||||
*/
|
||||
public notify(eventCode: NotifyEventCode, textStore: TextStore, data: number): void { // I2187
|
||||
// TODO-web-core: do we need to support this? (#15290)
|
||||
public notify(eventCode: NotifyEventCode, textStore: TextStore, data: number): void {
|
||||
// TODO: implement for IMX (cf #2239 and #7928)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,4 +13,5 @@ export type VariableStore = { [name: string]: string; };
|
|||
export interface VariableStoreSerializer {
|
||||
loadStore(keyboardID: string, storeName: string): VariableStore;
|
||||
saveStore(keyboardID: string, storeName: string, storeMap: VariableStore): void;
|
||||
findStores(keyboardID: string): VariableStore[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,27 +9,51 @@ import { CookieSerializer } from "keyman/engine/dom-utils";
|
|||
// dynamic property names; they'd have to be known at compile time to facilitate
|
||||
// strict type checking.
|
||||
class VarStoreSerializer extends CookieSerializer<VariableStore> {
|
||||
constructor(keyboardID: string, storeName: string) {
|
||||
public constructor(keyboardID: string, storeName: string) {
|
||||
super(`KeymanWeb_${keyboardID}_Option_${storeName}`);
|
||||
}
|
||||
|
||||
load() {
|
||||
public load(): VariableStore {
|
||||
return super.load(decodeURIComponent);
|
||||
}
|
||||
|
||||
save(storeMap: VariableStore) {
|
||||
public save(storeMap: VariableStore) {
|
||||
super.save(storeMap, encodeURIComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all variable stores associated with a given keyboard.
|
||||
*
|
||||
* @param {string} keyboardID The keyboard ID whose variable stores are to be found.
|
||||
*
|
||||
* @returns An array of VariableStore objects found for the keyboard.
|
||||
*/
|
||||
public static findStores(keyboardID: string): VariableStore[] {
|
||||
const pattern = new RegExp(`^KeymanWeb_${keyboardID}_Option_`);
|
||||
const matching = CookieSerializer.loadAllMatching<VariableStore>(pattern, decodeURIComponent);
|
||||
return matching.map(m => m.value);
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableStoreCookieSerializer implements VariableStoreSerializer {
|
||||
loadStore(keyboardID: string, storeName: string): VariableStore {
|
||||
public loadStore(keyboardID: string, storeName: string): VariableStore {
|
||||
const storeCookieSerializer = new VarStoreSerializer(keyboardID, storeName);
|
||||
return storeCookieSerializer.load();
|
||||
}
|
||||
|
||||
saveStore(keyboardID: string, storeName: string, storeMap: VariableStore) {
|
||||
public saveStore(keyboardID: string, storeName: string, storeMap: VariableStore) {
|
||||
const storeCookieSerializer = new VarStoreSerializer(keyboardID, storeName);
|
||||
storeCookieSerializer.save(storeMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all variable stores associated with a given keyboard.
|
||||
*
|
||||
* @param {string} keyboardID The keyboard ID whose variable stores are to be found.
|
||||
*
|
||||
* @returns An array of VariableStore objects found for the keyboard.
|
||||
*/
|
||||
public findStores(keyboardID: string): VariableStore[] {
|
||||
return VarStoreSerializer.findStores(keyboardID);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
import { assert } from 'chai';
|
||||
import { VariableStore } from 'keyman/engine/keyboard';
|
||||
import { VariableStoreCookieSerializer } from 'keyman/engine/main';
|
||||
|
||||
describe('VariableStoreCookieSerializer', () => {
|
||||
describe('saveStore and loadStore', () => {
|
||||
const keyboardID = 'test-keyboard';
|
||||
const storeName = 'testStore';
|
||||
const cookieName = `KeymanWeb_${keyboardID}_Option_${storeName}`;
|
||||
|
||||
afterEach(async () => {
|
||||
document.cookie = `${cookieName}=; max-age=0`;
|
||||
});
|
||||
|
||||
it('should save and load a simple store with string values', () => {
|
||||
// Arrange
|
||||
const serializer = new VariableStoreCookieSerializer();
|
||||
const storeData: VariableStore = {
|
||||
option1: 'value1',
|
||||
option2: 'value2',
|
||||
option3: 'value3'
|
||||
};
|
||||
|
||||
// Act
|
||||
serializer.saveStore(keyboardID, storeName, storeData);
|
||||
const loaded = serializer.loadStore(keyboardID, storeName);
|
||||
|
||||
// Assert
|
||||
assert.deepEqual(loaded, storeData, 'loaded store should match saved store');
|
||||
});
|
||||
|
||||
it('should save and load a store with empty values', () => {
|
||||
// Arrange
|
||||
const serializer = new VariableStoreCookieSerializer();
|
||||
const storeData: VariableStore = {
|
||||
option1: '',
|
||||
option2: 'value',
|
||||
option3: ''
|
||||
};
|
||||
|
||||
// Act
|
||||
serializer.saveStore(keyboardID, storeName, storeData);
|
||||
const loaded = serializer.loadStore(keyboardID, storeName);
|
||||
|
||||
// Assert
|
||||
assert.deepEqual(loaded, storeData, 'empty string values should be preserved');
|
||||
});
|
||||
|
||||
it('should save and load a store with special characters', () => {
|
||||
// Arrange
|
||||
const serializer = new VariableStoreCookieSerializer();
|
||||
const storeData: VariableStore = {
|
||||
encoded: 'value:with;special&chars=',
|
||||
emoji: '⭐'
|
||||
};
|
||||
|
||||
// Act
|
||||
serializer.saveStore(keyboardID, storeName, storeData);
|
||||
const loaded = serializer.loadStore(keyboardID, storeName);
|
||||
|
||||
// Assert
|
||||
assert.deepEqual(loaded, storeData, 'special characters should be properly encoded/decoded');
|
||||
});
|
||||
|
||||
it('should handle multiple stores for the same keyboard independently', () => {
|
||||
// Arrange
|
||||
const serializer = new VariableStoreCookieSerializer();
|
||||
const store1Data: VariableStore = { option: 'store1value' };
|
||||
const store2Data: VariableStore = { option: 'store2value' };
|
||||
|
||||
// Act
|
||||
serializer.saveStore(keyboardID, 'store1', store1Data);
|
||||
serializer.saveStore(keyboardID, 'store2', store2Data);
|
||||
const loaded1 = serializer.loadStore(keyboardID, 'store1');
|
||||
const loaded2 = serializer.loadStore(keyboardID, 'store2');
|
||||
|
||||
// Assert
|
||||
assert.deepEqual(loaded1, store1Data, 'first store should be independent');
|
||||
assert.deepEqual(loaded2, store2Data, 'second store should be independent');
|
||||
|
||||
// Cleanup
|
||||
document.cookie = `KeymanWeb_${keyboardID}_Option_store1=; max-age=0`;
|
||||
document.cookie = `KeymanWeb_${keyboardID}_Option_store2=; max-age=0`;
|
||||
});
|
||||
|
||||
it('should return empty object for non-existent store', () => {
|
||||
// Arrange
|
||||
const serializer = new VariableStoreCookieSerializer();
|
||||
|
||||
// Act
|
||||
const loaded = serializer.loadStore(keyboardID, 'nonexistent');
|
||||
|
||||
// Assert
|
||||
assert.deepEqual(loaded, {}, 'non-existent store should return empty object');
|
||||
});
|
||||
});
|
||||
|
||||
describe('findStores', () => {
|
||||
it('should return an empty array when no stores exist for a keyboardID', () => {
|
||||
|
||||
// Arrange
|
||||
const serializer = new VariableStoreCookieSerializer();
|
||||
const keyboardID = 'test-keyboard';
|
||||
const expected: VariableStore[] = [];
|
||||
|
||||
// Act
|
||||
const result = serializer.findStores(keyboardID);
|
||||
|
||||
// Assert
|
||||
assert.deepEqual(result, expected, 'result should be an empty array');
|
||||
assert.isTrue(Array.isArray(result), 'result should be an array');
|
||||
assert.strictEqual(result.length, 0, 'result array length should be 0');
|
||||
});
|
||||
|
||||
it('should return store for keyboard', () => {
|
||||
// Arrange
|
||||
const serializer = new VariableStoreCookieSerializer();
|
||||
const storeData1: VariableStore = {
|
||||
option1: 'value1',
|
||||
option2: 'value2',
|
||||
option3: 'value3'
|
||||
};
|
||||
serializer.saveStore('test-keyboard', 'storeName', storeData1);
|
||||
const storeData2: VariableStore = {
|
||||
settingA: 'A',
|
||||
settingB: 'B'
|
||||
};
|
||||
serializer.saveStore('another-keyboard', 'anotherStore', storeData2);
|
||||
|
||||
// Act
|
||||
const stores = serializer.findStores('test-keyboard');
|
||||
|
||||
// Assert
|
||||
assert.deepEqual(stores, [storeData1], 'stores should match saved store');
|
||||
|
||||
// Cleanup
|
||||
document.cookie = `KeymanWeb_test-keyboard_Option_storeName=; max-age=0`;
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -84,11 +84,16 @@ export default {
|
|||
// Relative, from the containing package.json
|
||||
files: ['web/build/test/dom/cases/keyboard-storage/**/*.tests.mjs']
|
||||
},
|
||||
{
|
||||
name: 'engine/main',
|
||||
// Relative, from the containing package.json
|
||||
files: ['web/build/test/dom/cases/main/**/*.tests.mjs']
|
||||
},
|
||||
{
|
||||
name: 'engine/osk',
|
||||
// Relative, from the containing package.json
|
||||
files: ['web/build/test/dom/cases/osk/**/*.tests.mjs']
|
||||
}
|
||||
},
|
||||
],
|
||||
middleware: [
|
||||
// Rewrites short-hand paths for test resources, making them fully relative to the repo root.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue