Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/controller/textAreaInput.test.ts
5241 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { Emitter, Event } from '../../../../base/common/event.js';
8
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
9
import { OperatingSystem } from '../../../../base/common/platform.js';
10
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
11
import { Position } from '../../../common/core/position.js';
12
import { IRecorded, IRecordedEvent, IRecordedTextareaState } from './imeRecordedTypes.js';
13
import { TestAccessibilityService } from '../../../../platform/accessibility/test/common/testAccessibilityService.js';
14
import { NullLogService } from '../../../../platform/log/common/log.js';
15
import { IBrowser, ICompleteTextAreaWrapper, ITextAreaInputHost, TextAreaInput } from '../../../browser/controller/editContext/textArea/textAreaEditContextInput.js';
16
import { TextAreaState } from '../../../browser/controller/editContext/textArea/textAreaEditContextState.js';
17
18
suite('TextAreaInput', () => {
19
20
ensureNoDisposablesAreLeakedInTestSuite();
21
22
interface OutgoingType {
23
type: 'type';
24
text: string;
25
replacePrevCharCnt: number;
26
replaceNextCharCnt: number;
27
positionDelta: number;
28
}
29
interface OutgoingCompositionStart {
30
type: 'compositionStart';
31
data: string;
32
}
33
interface OutgoingCompositionUpdate {
34
type: 'compositionUpdate';
35
data: string;
36
}
37
interface OutgoingCompositionEnd {
38
type: 'compositionEnd';
39
}
40
type OutoingEvent = OutgoingType | OutgoingCompositionStart | OutgoingCompositionUpdate | OutgoingCompositionEnd;
41
42
function yieldNow(): Promise<void> {
43
return new Promise((resolve, reject) => {
44
queueMicrotask(resolve);
45
});
46
}
47
48
async function simulateInteraction(recorded: IRecorded): Promise<OutoingEvent[]> {
49
const disposables = new DisposableStore();
50
const host: ITextAreaInputHost = {
51
context: null!,
52
getScreenReaderContent: function (): TextAreaState {
53
return new TextAreaState('', 0, 0, null, undefined);
54
},
55
deduceModelPosition: function (viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position {
56
throw new Error('Function not implemented.');
57
}
58
};
59
const wrapper = disposables.add(new class extends Disposable implements ICompleteTextAreaWrapper {
60
private _onKeyDown = this._register(new Emitter<KeyboardEvent>());
61
readonly onKeyDown = this._onKeyDown.event;
62
63
private _onKeyPress = this._register(new Emitter<KeyboardEvent>());
64
readonly onKeyPress = this._onKeyPress.event;
65
66
private _onKeyUp = this._register(new Emitter<KeyboardEvent>());
67
readonly onKeyUp = this._onKeyUp.event;
68
69
private _onCompositionStart = this._register(new Emitter<CompositionEvent>());
70
readonly onCompositionStart = this._onCompositionStart.event;
71
72
private _onCompositionUpdate = this._register(new Emitter<CompositionEvent>());
73
readonly onCompositionUpdate = this._onCompositionUpdate.event;
74
75
private _onCompositionEnd = this._register(new Emitter<CompositionEvent>());
76
readonly onCompositionEnd = this._onCompositionEnd.event;
77
78
private _onBeforeInput = this._register(new Emitter<InputEvent>());
79
readonly onBeforeInput = this._onBeforeInput.event;
80
81
private _onInput = this._register(new Emitter<InputEvent>());
82
readonly onInput = this._onInput.event;
83
84
readonly onCut = Event.None;
85
readonly onCopy = Event.None;
86
readonly onPaste = Event.None;
87
readonly onFocus = Event.None;
88
readonly onBlur = Event.None;
89
readonly onSyntheticTap = Event.None;
90
91
private _state: IRecordedTextareaState;
92
private _currDispatchingEvent: IRecordedEvent | null;
93
94
public ownerDocument = document;
95
96
constructor() {
97
super();
98
this._state = {
99
selectionDirection: 'none',
100
selectionEnd: 0,
101
selectionStart: 0,
102
value: ''
103
};
104
this._currDispatchingEvent = null;
105
}
106
107
public _initialize(state: IRecordedTextareaState): void {
108
this._state.value = state.value;
109
this._state.selectionStart = state.selectionStart;
110
this._state.selectionEnd = state.selectionEnd;
111
}
112
113
public _dispatchRecordedEvent(event: IRecordedEvent): void {
114
this._currDispatchingEvent = event;
115
this._state.value = event.state.value;
116
this._state.selectionStart = event.state.selectionStart;
117
this._state.selectionEnd = event.state.selectionEnd;
118
this._state.selectionDirection = event.state.selectionDirection;
119
120
if (event.type === 'keydown' || event.type === 'keypress' || event.type === 'keyup') {
121
const mockEvent = <KeyboardEvent>{
122
timeStamp: event.timeStamp,
123
type: event.type,
124
altKey: event.altKey,
125
charCode: event.charCode,
126
code: event.code,
127
ctrlKey: event.ctrlKey,
128
isComposing: event.isComposing,
129
key: event.key,
130
keyCode: event.keyCode,
131
location: event.location,
132
metaKey: event.metaKey,
133
repeat: event.repeat,
134
shiftKey: event.shiftKey,
135
getModifierState: (keyArg: string) => false
136
};
137
if (event.type === 'keydown') {
138
this._onKeyDown.fire(mockEvent);
139
} else if (event.type === 'keypress') {
140
this._onKeyPress.fire(mockEvent);
141
} else {
142
this._onKeyUp.fire(mockEvent);
143
}
144
} else if (event.type === 'compositionstart' || event.type === 'compositionupdate' || event.type === 'compositionend') {
145
const mockEvent = <CompositionEvent>{
146
timeStamp: event.timeStamp,
147
type: event.type,
148
data: event.data
149
};
150
if (event.type === 'compositionstart') {
151
this._onCompositionStart.fire(mockEvent);
152
} else if (event.type === 'compositionupdate') {
153
this._onCompositionUpdate.fire(mockEvent);
154
} else {
155
this._onCompositionEnd.fire(mockEvent);
156
}
157
} else if (event.type === 'beforeinput' || event.type === 'input') {
158
const mockEvent = <InputEvent>{
159
timeStamp: event.timeStamp,
160
type: event.type,
161
data: event.data,
162
inputType: event.inputType,
163
isComposing: event.isComposing,
164
};
165
if (event.type === 'beforeinput') {
166
this._onBeforeInput.fire(mockEvent);
167
} else {
168
this._onInput.fire(mockEvent);
169
}
170
} else {
171
throw new Error(`Not Implemented`);
172
}
173
this._currDispatchingEvent = null;
174
}
175
176
getValue(): string {
177
return this._state.value;
178
}
179
setValue(reason: string, value: string): void {
180
if (this._currDispatchingEvent?.type === 'compositionstart') {
181
assert.fail('should not change the state of the textarea in a compositionstart');
182
}
183
this._state.value = value;
184
}
185
getSelectionStart(): number {
186
return this._state.selectionDirection === 'backward' ? this._state.selectionEnd : this._state.selectionStart;
187
}
188
getSelectionEnd(): number {
189
return this._state.selectionDirection === 'backward' ? this._state.selectionStart : this._state.selectionEnd;
190
}
191
setSelectionRange(reason: string, selectionStart: number, selectionEnd: number): void {
192
if (this._currDispatchingEvent?.type === 'compositionstart') {
193
assert.fail('should not change the state of the textarea in a compositionstart');
194
}
195
this._state.selectionStart = selectionStart;
196
this._state.selectionEnd = selectionEnd;
197
this._state.selectionDirection = (selectionStart !== selectionEnd ? 'forward' : 'none');
198
}
199
200
public setIgnoreSelectionChangeTime(reason: string): void { }
201
public getIgnoreSelectionChangeTime(): number { return Date.now(); }
202
public resetSelectionChangeTime(): void { }
203
204
public hasFocus(): boolean { return true; }
205
});
206
const input = disposables.add(new TextAreaInput(host, wrapper, recorded.env.OS, recorded.env.browser, new TestAccessibilityService(), new NullLogService()));
207
208
wrapper._initialize(recorded.initial);
209
input._initializeFromTest();
210
211
const outgoingEvents: OutoingEvent[] = [];
212
213
disposables.add(input.onType((e) => outgoingEvents.push({
214
type: 'type',
215
text: e.text,
216
replacePrevCharCnt: e.replacePrevCharCnt,
217
replaceNextCharCnt: e.replaceNextCharCnt,
218
positionDelta: e.positionDelta,
219
})));
220
disposables.add(input.onCompositionStart((e) => outgoingEvents.push({
221
type: 'compositionStart',
222
data: e.data,
223
})));
224
disposables.add(input.onCompositionUpdate((e) => outgoingEvents.push({
225
type: 'compositionUpdate',
226
data: e.data,
227
})));
228
disposables.add(input.onCompositionEnd((e) => outgoingEvents.push({
229
type: 'compositionEnd'
230
})));
231
232
for (const event of recorded.events) {
233
wrapper._dispatchRecordedEvent(event);
234
await yieldNow();
235
}
236
237
disposables.dispose();
238
239
return outgoingEvents;
240
}
241
242
function interpretTypeEvents(OS: OperatingSystem, browser: IBrowser, initialState: IRecordedTextareaState, events: OutoingEvent[]): IRecordedTextareaState {
243
let text = initialState.value;
244
let selectionStart = initialState.selectionStart;
245
let selectionEnd = initialState.selectionEnd;
246
for (const event of events) {
247
if (event.type === 'type') {
248
text = (
249
text.substring(0, selectionStart - event.replacePrevCharCnt)
250
+ event.text
251
+ text.substring(selectionEnd + event.replaceNextCharCnt)
252
);
253
selectionStart = selectionStart - event.replacePrevCharCnt + event.text.length;
254
selectionEnd = selectionStart;
255
256
if (event.positionDelta) {
257
selectionStart += event.positionDelta;
258
selectionEnd += event.positionDelta;
259
}
260
}
261
}
262
return {
263
value: text,
264
selectionStart: selectionStart,
265
selectionEnd: selectionEnd,
266
selectionDirection: (browser.isFirefox || OS === OperatingSystem.Windows || OS === OperatingSystem.Linux) ? 'forward' : 'none'
267
};
268
}
269
270
test('macOS - Chrome - Korean using 2-Set Korean (1)', async () => {
271
// macOS, 2-Set Korean, type 'dkrk' and click
272
const recorded: IRecorded = {
273
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
274
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
275
events: [
276
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: false, key: 'ㅇ', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
277
{ timeStamp: 6.20, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
278
{ timeStamp: 6.40, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: 'ㅇ', inputType: 'insertCompositionText', isComposing: true },
279
{ timeStamp: 6.50, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionupdate', data: 'ㅇ' },
280
{ timeStamp: 6.90, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'ㅇ', inputType: 'insertCompositionText', isComposing: true },
281
{ timeStamp: 136.10, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: true, key: 'ㅇ', keyCode: 68, location: 0, metaKey: false, repeat: false, shiftKey: false },
282
{ timeStamp: 288.10, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'ㅏ', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
283
{ timeStamp: 296.00, state: { value: 'aaㅇaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '아', inputType: 'insertCompositionText', isComposing: true },
284
{ timeStamp: 296.00, state: { value: 'aaㅇaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '아' },
285
{ timeStamp: 296.40, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '아', inputType: 'insertCompositionText', isComposing: true },
286
{ timeStamp: 368.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'ㅏ', keyCode: 75, location: 0, metaKey: false, repeat: false, shiftKey: false },
287
{ timeStamp: 536.10, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'ㄱ', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
288
{ timeStamp: 543.20, state: { value: 'aa아aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '악', inputType: 'insertCompositionText', isComposing: true },
289
{ timeStamp: 543.30, state: { value: 'aa아aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '악' },
290
{ timeStamp: 543.60, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '악', inputType: 'insertCompositionText', isComposing: true },
291
{ timeStamp: 632.00, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'ㄱ', keyCode: 82, location: 0, metaKey: false, repeat: false, shiftKey: false },
292
{ timeStamp: 783.90, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'ㅏ', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
293
{ timeStamp: 790.70, state: { value: 'aa악aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '아', inputType: 'insertCompositionText', isComposing: true },
294
{ timeStamp: 790.80, state: { value: 'aa악aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '아' },
295
{ timeStamp: 791.20, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '아', inputType: 'insertCompositionText', isComposing: true },
296
{ timeStamp: 791.20, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionend', data: '아' },
297
{ timeStamp: 791.30, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
298
{ timeStamp: 791.30, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '가', inputType: 'insertCompositionText', isComposing: true },
299
{ timeStamp: 791.30, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '가' },
300
{ timeStamp: 791.50, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'input', data: '가', inputType: 'insertCompositionText', isComposing: true },
301
{ timeStamp: 880.10, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'ㅏ', keyCode: 75, location: 0, metaKey: false, repeat: false, shiftKey: false },
302
{ timeStamp: 2209.00, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'compositionend', data: '가' }
303
],
304
final: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' },
305
};
306
307
const actualOutgoingEvents = await simulateInteraction(recorded);
308
assert.deepStrictEqual(actualOutgoingEvents, [
309
{ type: 'compositionStart', data: '' },
310
{ type: 'type', text: 'ㅇ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
311
{ type: 'compositionUpdate', data: 'ㅇ' },
312
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
313
{ type: 'compositionUpdate', data: '아' },
314
{ type: 'type', text: '악', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
315
{ type: 'compositionUpdate', data: '악' },
316
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
317
{ type: 'compositionUpdate', data: '아' },
318
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
319
{ type: 'compositionEnd' },
320
{ type: 'compositionStart', data: '' },
321
{ type: 'type', text: '가', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
322
{ type: 'compositionUpdate', data: '가' },
323
{ type: 'type', text: '가', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
324
{ type: 'compositionEnd' }
325
]);
326
327
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
328
assert.deepStrictEqual(actualResultingState, recorded.final);
329
});
330
331
test('macOS - Chrome - Korean using 2-Set Korean (2)', async () => {
332
// macOS, 2-Set Korean, type 'qud' and click
333
// See https://github.com/microsoft/vscode/issues/134254
334
const recorded: IRecorded = {
335
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
336
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
337
events: [
338
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyQ', ctrlKey: false, isComposing: false, key: 'ㅂ', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
339
{ timeStamp: 7.40, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
340
{ timeStamp: 7.60, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: 'ㅂ', inputType: 'insertCompositionText', isComposing: true },
341
{ timeStamp: 7.60, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionupdate', data: 'ㅂ' },
342
{ timeStamp: 8.20, state: { value: 'aaㅂaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'ㅂ', inputType: 'insertCompositionText', isComposing: true },
343
{ timeStamp: 136.10, state: { value: 'aaㅂaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyQ', ctrlKey: false, isComposing: true, key: 'ㅂ', keyCode: 81, location: 0, metaKey: false, repeat: false, shiftKey: false },
344
{ timeStamp: 680.10, state: { value: 'aaㅂaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyU', ctrlKey: false, isComposing: true, key: 'ㅕ', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
345
{ timeStamp: 687.20, state: { value: 'aaㅂaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '벼', inputType: 'insertCompositionText', isComposing: true },
346
{ timeStamp: 687.40, state: { value: 'aaㅂaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '벼' },
347
{ timeStamp: 688.80, state: { value: 'aa벼aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '벼', inputType: 'insertCompositionText', isComposing: true },
348
{ timeStamp: 768.10, state: { value: 'aa벼aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyU', ctrlKey: false, isComposing: true, key: 'ㅕ', keyCode: 85, location: 0, metaKey: false, repeat: false, shiftKey: false },
349
{ timeStamp: 1768.00, state: { value: 'aa벼aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: true, key: 'ㅇ', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
350
{ timeStamp: 1775.00, state: { value: 'aa벼aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '병', inputType: 'insertCompositionText', isComposing: true },
351
{ timeStamp: 1775.10, state: { value: 'aa벼aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '병' },
352
{ timeStamp: 1775.60, state: { value: 'aa병aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '병', inputType: 'insertCompositionText', isComposing: true },
353
{ timeStamp: 1928.10, state: { value: 'aa병aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: true, key: 'ㅇ', keyCode: 68, location: 0, metaKey: false, repeat: false, shiftKey: false },
354
{ timeStamp: 6565.70, state: { value: 'aa병aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionend', data: '병' }
355
],
356
final: { value: 'aa병aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' },
357
};
358
359
const actualOutgoingEvents = await simulateInteraction(recorded);
360
assert.deepStrictEqual(actualOutgoingEvents, [
361
{ type: 'compositionStart', data: '' },
362
{ type: 'type', text: 'ㅂ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
363
{ type: 'compositionUpdate', data: 'ㅂ' },
364
{ type: 'type', text: '벼', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
365
{ type: 'compositionUpdate', data: '벼' },
366
{ type: 'type', text: '병', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
367
{ type: 'compositionUpdate', data: '병' },
368
{ type: 'type', text: '병', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
369
{ type: 'compositionEnd' }
370
]);
371
372
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
373
assert.deepStrictEqual(actualResultingState, recorded.final);
374
});
375
376
test('macOS - Chrome - Japanese using Hiragana (Google)', async () => {
377
// macOS, Hiragana (Google), type 'sennsei' and Enter
378
const recorded: IRecorded = {
379
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
380
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
381
events: [
382
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: false, key: 's', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
383
{ timeStamp: 8.50, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
384
{ timeStamp: 8.70, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: 's', inputType: 'insertCompositionText', isComposing: true },
385
{ timeStamp: 8.70, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionupdate', data: 's' },
386
{ timeStamp: 9.30, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 's', inputType: 'insertCompositionText', isComposing: true },
387
{ timeStamp: 111.70, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 83, location: 0, metaKey: false, repeat: false, shiftKey: false },
388
{ timeStamp: 439.80, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
389
{ timeStamp: 444.50, state: { value: 'aasaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: 'せ', inputType: 'insertCompositionText', isComposing: true },
390
{ timeStamp: 444.60, state: { value: 'aasaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: 'せ' },
391
{ timeStamp: 445.20, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'せ', inputType: 'insertCompositionText', isComposing: true },
392
{ timeStamp: 559.90, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
393
{ timeStamp: 1943.90, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
394
{ timeStamp: 1949.30, state: { value: 'aaせaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: 'せn', inputType: 'insertCompositionText', isComposing: true },
395
{ timeStamp: 1949.40, state: { value: 'aaせaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: 'せn' },
396
{ timeStamp: 1949.90, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'input', data: 'せn', inputType: 'insertCompositionText', isComposing: true },
397
{ timeStamp: 2039.90, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
398
{ timeStamp: 2207.80, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
399
{ timeStamp: 2215.70, state: { value: 'aaせnaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'none' }, type: 'beforeinput', data: 'せん', inputType: 'insertCompositionText', isComposing: true },
400
{ timeStamp: 2215.80, state: { value: 'aaせnaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'none' }, type: 'compositionupdate', data: 'せん' },
401
{ timeStamp: 2216.10, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'input', data: 'せん', inputType: 'insertCompositionText', isComposing: true },
402
{ timeStamp: 2311.90, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
403
{ timeStamp: 2551.90, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
404
{ timeStamp: 2557.00, state: { value: 'aaせんaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'none' }, type: 'beforeinput', data: 'せんs', inputType: 'insertCompositionText', isComposing: true },
405
{ timeStamp: 2557.00, state: { value: 'aaせんaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'none' }, type: 'compositionupdate', data: 'せんs' },
406
{ timeStamp: 2557.40, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'input', data: 'せんs', inputType: 'insertCompositionText', isComposing: true },
407
{ timeStamp: 2671.70, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 83, location: 0, metaKey: false, repeat: false, shiftKey: false },
408
{ timeStamp: 2903.80, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
409
{ timeStamp: 2912.30, state: { value: 'aaせんsaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'none' }, type: 'beforeinput', data: 'せんせ', inputType: 'insertCompositionText', isComposing: true },
410
{ timeStamp: 2912.50, state: { value: 'aaせんsaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'none' }, type: 'compositionupdate', data: 'せんせ' },
411
{ timeStamp: 2912.90, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'input', data: 'せんせ', inputType: 'insertCompositionText', isComposing: true },
412
{ timeStamp: 3023.90, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
413
{ timeStamp: 3519.90, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'i', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
414
{ timeStamp: 3537.10, state: { value: 'aaせんせaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'none' }, type: 'beforeinput', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
415
{ timeStamp: 3537.10, state: { value: 'aaせんせaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'none' }, type: 'compositionupdate', data: 'せんせい' },
416
{ timeStamp: 3537.60, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'none' }, type: 'input', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
417
{ timeStamp: 3639.90, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'i', keyCode: 73, location: 0, metaKey: false, repeat: false, shiftKey: false },
418
{ timeStamp: 4887.80, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: true, key: 'Enter', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
419
{ timeStamp: 4892.80, state: { value: 'aaせんせいaa', selectionStart: 2, selectionEnd: 6, selectionDirection: 'none' }, type: 'beforeinput', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
420
{ timeStamp: 4892.90, state: { value: 'aaせんせいaa', selectionStart: 2, selectionEnd: 6, selectionDirection: 'none' }, type: 'compositionupdate', data: 'せんせい' },
421
{ timeStamp: 4893.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'none' }, type: 'input', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
422
{ timeStamp: 4893.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'none' }, type: 'compositionend', data: 'せんせい' },
423
{ timeStamp: 4967.80, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Enter', keyCode: 13, location: 0, metaKey: false, repeat: false, shiftKey: false }
424
],
425
final: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'none' },
426
};
427
428
const actualOutgoingEvents = await simulateInteraction(recorded);
429
assert.deepStrictEqual(actualOutgoingEvents, [
430
{ type: 'compositionStart', data: '' },
431
{ type: 'type', text: 's', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
432
{ type: 'compositionUpdate', data: 's' },
433
{ type: 'type', text: 'せ', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
434
{ type: 'compositionUpdate', data: 'せ' },
435
{ type: 'type', text: 'せn', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
436
{ type: 'compositionUpdate', data: 'せn' },
437
{ type: 'type', text: 'せん', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
438
{ type: 'compositionUpdate', data: 'せん' },
439
{ type: 'type', text: 'せんs', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
440
{ type: 'compositionUpdate', data: 'せんs' },
441
{ type: 'type', text: 'せんせ', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
442
{ type: 'compositionUpdate', data: 'せんせ' },
443
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
444
{ type: 'compositionUpdate', data: 'せんせい' },
445
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 4, replaceNextCharCnt: 0, positionDelta: 0 },
446
{ type: 'compositionUpdate', data: 'せんせい' },
447
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 4, replaceNextCharCnt: 0, positionDelta: 0 },
448
{ type: 'compositionEnd' }
449
]);
450
451
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
452
assert.deepStrictEqual(actualResultingState, recorded.final);
453
});
454
455
test('macOS - Chrome - Chinese using Pinyin - Traditional', async () => {
456
// macOS, Pinyin - Traditional, type 'xu' and '1'
457
const recorded: IRecorded = {
458
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
459
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
460
events: [
461
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyX', ctrlKey: false, isComposing: false, key: 'x', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
462
{ timeStamp: 48.70, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
463
{ timeStamp: 48.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: 'x', inputType: 'insertCompositionText', isComposing: true },
464
{ timeStamp: 48.90, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionupdate', data: 'x' },
465
{ timeStamp: 49.20, state: { value: 'aaxaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'x', inputType: 'insertCompositionText', isComposing: true },
466
{ timeStamp: 127.80, state: { value: 'aaxaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyX', ctrlKey: false, isComposing: true, key: 'x', keyCode: 88, location: 0, metaKey: false, repeat: false, shiftKey: false },
467
{ timeStamp: 480.00, state: { value: 'aaxaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyU', ctrlKey: false, isComposing: true, key: 'u', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
468
{ timeStamp: 535.60, state: { value: 'aaxaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: 'xu', inputType: 'insertCompositionText', isComposing: true },
469
{ timeStamp: 535.70, state: { value: 'aaxaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: 'xu' },
470
{ timeStamp: 535.90, state: { value: 'aaxuaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'input', data: 'xu', inputType: 'insertCompositionText', isComposing: true },
471
{ timeStamp: 575.80, state: { value: 'aaxuaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyU', ctrlKey: false, isComposing: true, key: 'u', keyCode: 85, location: 0, metaKey: false, repeat: false, shiftKey: false },
472
{ timeStamp: 1055.90, state: { value: 'aaxuaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Digit1', ctrlKey: false, isComposing: true, key: '1', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
473
{ timeStamp: 1061.70, state: { value: 'aaxuaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'none' }, type: 'beforeinput', data: '需', inputType: 'insertCompositionText', isComposing: true },
474
{ timeStamp: 1061.80, state: { value: 'aaxuaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'none' }, type: 'compositionupdate', data: '需' },
475
{ timeStamp: 1063.20, state: { value: 'aa需aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '需', inputType: 'insertCompositionText', isComposing: true },
476
{ timeStamp: 1063.30, state: { value: 'aa需aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionend', data: '需' },
477
{ timeStamp: 1207.90, state: { value: 'aa需aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Digit1', ctrlKey: false, isComposing: false, key: '1', keyCode: 49, location: 0, metaKey: false, repeat: false, shiftKey: false }
478
],
479
final: { value: 'aa需aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' },
480
};
481
482
const actualOutgoingEvents = await simulateInteraction(recorded);
483
assert.deepStrictEqual(actualOutgoingEvents, [
484
{ type: 'compositionStart', data: '' },
485
{ type: 'type', text: 'x', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
486
{ type: 'compositionUpdate', data: 'x' },
487
{ type: 'type', text: 'xu', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
488
{ type: 'compositionUpdate', data: 'xu' },
489
{ type: 'type', text: '需', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
490
{ type: 'compositionUpdate', data: '需' },
491
{ type: 'type', text: '需', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
492
{ type: 'compositionEnd' }
493
]);
494
495
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
496
assert.deepStrictEqual(actualResultingState, recorded.final);
497
});
498
499
test('macOS - Chrome - long press with arrow keys', async () => {
500
// macOS, English, long press o, press arrow right twice and then press Enter
501
// See https://github.com/microsoft/vscode/issues/67739
502
const recorded: IRecorded = {
503
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
504
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
505
events: [
506
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: false, shiftKey: false },
507
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keypress', altKey: false, charCode: 111, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 111, location: 0, metaKey: false, repeat: false, shiftKey: false },
508
{ timeStamp: 2.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: 'o', inputType: 'insertText', isComposing: false },
509
{ timeStamp: 3.40, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'o', inputType: 'insertText', isComposing: false },
510
{ timeStamp: 500.50, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: true, shiftKey: false },
511
{ timeStamp: 583.90, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: true, shiftKey: false },
512
{ timeStamp: 667.60, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: true, shiftKey: false },
513
{ timeStamp: 750.90, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: true, shiftKey: false },
514
{ timeStamp: 835.00, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: true, shiftKey: false },
515
{ timeStamp: 856.10, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: false, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: false, shiftKey: false },
516
{ timeStamp: 1952.10, state: { value: 'aaoaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'ArrowRight', ctrlKey: false, isComposing: false, key: 'ArrowRight', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
517
{ timeStamp: 1956.50, state: { value: 'aaoaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionstart', data: 'o' },
518
{ timeStamp: 1956.80, state: { value: 'aaoaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: 'ô', inputType: 'insertCompositionText', isComposing: true },
519
{ timeStamp: 1956.90, state: { value: 'aaoaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: 'ô' },
520
{ timeStamp: 1960.60, state: { value: 'aaôaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'ô', inputType: 'insertCompositionText', isComposing: true },
521
{ timeStamp: 2088.10, state: { value: 'aaôaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'ArrowRight', ctrlKey: false, isComposing: true, key: 'ArrowRight', keyCode: 39, location: 0, metaKey: false, repeat: false, shiftKey: false },
522
{ timeStamp: 2480.10, state: { value: 'aaôaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'ArrowRight', ctrlKey: false, isComposing: true, key: 'ArrowRight', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
523
{ timeStamp: 2484.30, state: { value: 'aaôaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: 'ö', inputType: 'insertCompositionText', isComposing: true },
524
{ timeStamp: 2484.40, state: { value: 'aaôaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: 'ö' },
525
{ timeStamp: 2484.70, state: { value: 'aaöaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'ö', inputType: 'insertCompositionText', isComposing: true },
526
{ timeStamp: 2584.20, state: { value: 'aaöaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'ArrowRight', ctrlKey: false, isComposing: true, key: 'ArrowRight', keyCode: 39, location: 0, metaKey: false, repeat: false, shiftKey: false },
527
{ timeStamp: 6424.20, state: { value: 'aaöaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: true, key: 'Enter', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
528
{ timeStamp: 6431.70, state: { value: 'aaöaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: 'ö', inputType: 'insertCompositionText', isComposing: true },
529
{ timeStamp: 6431.70, state: { value: 'aaöaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: 'ö' },
530
{ timeStamp: 6431.80, state: { value: 'aaöaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'ö', inputType: 'insertCompositionText', isComposing: true },
531
{ timeStamp: 6431.90, state: { value: 'aaöaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionend', data: 'ö' },
532
{ timeStamp: 6496.20, state: { value: 'aaöaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Enter', keyCode: 13, location: 0, metaKey: false, repeat: false, shiftKey: false }
533
],
534
final: { value: 'aaöaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' },
535
};
536
537
const actualOutgoingEvents = await simulateInteraction(recorded);
538
assert.deepStrictEqual(actualOutgoingEvents, [
539
{ type: 'type', text: 'o', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
540
{ type: 'compositionStart', data: 'o' },
541
{ type: 'type', text: 'ô', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
542
{ type: 'compositionUpdate', data: 'ô' },
543
{ type: 'type', text: 'ö', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
544
{ type: 'compositionUpdate', data: 'ö' },
545
{ type: 'type', text: 'ö', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
546
{ type: 'compositionUpdate', data: 'ö' },
547
{ type: 'type', text: 'ö', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
548
{ type: 'compositionEnd' }
549
]);
550
551
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
552
assert.deepStrictEqual(actualResultingState, recorded.final);
553
});
554
555
test('macOS - Chrome - pressing quotes on US Intl', async () => {
556
// macOS, US International - PC, press ', ', ;
557
const recorded: IRecorded = {
558
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
559
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
560
events: [
561
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Quote', ctrlKey: false, isComposing: false, key: 'Dead', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
562
{ timeStamp: 2.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
563
{ timeStamp: 3.10, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: '\'', inputType: 'insertCompositionText', isComposing: true },
564
{ timeStamp: 3.20, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionupdate', data: '\'' },
565
{ timeStamp: 3.70, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '\'', inputType: 'insertCompositionText', isComposing: true },
566
{ timeStamp: 71.90, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Quote', ctrlKey: false, isComposing: true, key: 'Dead', keyCode: 222, location: 0, metaKey: false, repeat: false, shiftKey: false },
567
{ timeStamp: 144.00, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Quote', ctrlKey: false, isComposing: true, key: 'Dead', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
568
{ timeStamp: 146.20, state: { value: 'aa\'aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '\'', inputType: 'insertCompositionText', isComposing: true },
569
{ timeStamp: 146.40, state: { value: 'aa\'aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '\'' },
570
{ timeStamp: 146.70, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: '\'', inputType: 'insertCompositionText', isComposing: true },
571
{ timeStamp: 146.80, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionend', data: '\'' },
572
{ timeStamp: 147.20, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
573
{ timeStamp: 147.20, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: '\'', inputType: 'insertCompositionText', isComposing: true },
574
{ timeStamp: 147.70, state: { value: 'aa\'aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionupdate', data: '\'' },
575
{ timeStamp: 148.20, state: { value: 'aa\'\'aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'input', data: '\'', inputType: 'insertCompositionText', isComposing: true },
576
{ timeStamp: 208.10, state: { value: 'aa\'\'aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Quote', ctrlKey: false, isComposing: true, key: 'Dead', keyCode: 222, location: 0, metaKey: false, repeat: false, shiftKey: false },
577
{ timeStamp: 323.70, state: { value: 'aa\'\'aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Semicolon', ctrlKey: false, isComposing: true, key: ';', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
578
{ timeStamp: 325.70, state: { value: 'aa\'\'aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'none' }, type: 'beforeinput', data: '\';', inputType: 'insertCompositionText', isComposing: true },
579
{ timeStamp: 325.80, state: { value: 'aa\'\'aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'none' }, type: 'compositionupdate', data: '\';' },
580
{ timeStamp: 326.30, state: { value: 'aa\'\';aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'input', data: '\';', inputType: 'insertCompositionText', isComposing: true },
581
{ timeStamp: 326.30, state: { value: 'aa\'\';aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'compositionend', data: '\';' },
582
{ timeStamp: 428.00, state: { value: 'aa\'\';aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Semicolon', ctrlKey: false, isComposing: false, key: ';', keyCode: 186, location: 0, metaKey: false, repeat: false, shiftKey: false }
583
],
584
final: { value: 'aa\'\';aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'none' },
585
};
586
587
const actualOutgoingEvents = await simulateInteraction(recorded);
588
assert.deepStrictEqual(actualOutgoingEvents, ([
589
{ type: 'compositionStart', data: '' },
590
{ type: 'type', text: `'`, replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
591
{ type: 'compositionUpdate', data: `'` },
592
{ type: 'type', text: `'`, replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
593
{ type: 'compositionUpdate', data: `'` },
594
{ type: 'type', text: `'`, replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
595
{ type: 'compositionEnd' },
596
{ type: 'compositionStart', data: '' },
597
{ type: 'type', text: `'`, replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
598
{ type: 'compositionUpdate', data: `'` },
599
{ type: 'type', text: `';`, replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
600
{ type: 'compositionUpdate', data: `';` },
601
{ type: 'type', text: `';`, replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
602
{ type: 'compositionEnd' }
603
]));
604
605
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
606
assert.deepStrictEqual(actualResultingState, recorded.final);
607
});
608
609
test('macOS - Chrome - inserting emoji using ctrl+cmd+space', async () => {
610
// macOS, English, press ctrl+cmd+space, and then pick an emoji using the mouse
611
// See https://github.com/microsoft/vscode/issues/4271
612
const recorded: IRecorded = {
613
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
614
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
615
events: [
616
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'ControlLeft', ctrlKey: true, isComposing: false, key: 'Control', keyCode: 17, location: 1, metaKey: false, repeat: false, shiftKey: false },
617
{ timeStamp: 600.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'MetaLeft', ctrlKey: true, isComposing: false, key: 'Meta', keyCode: 91, location: 1, metaKey: true, repeat: false, shiftKey: false },
618
{ timeStamp: 1080.10, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Space', ctrlKey: true, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: true, repeat: false, shiftKey: false },
619
{ timeStamp: 1247.90, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'MetaLeft', ctrlKey: true, isComposing: false, key: 'Meta', keyCode: 91, location: 1, metaKey: false, repeat: false, shiftKey: false },
620
{ timeStamp: 1263.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: true, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false },
621
{ timeStamp: 1367.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'ControlLeft', ctrlKey: false, isComposing: false, key: 'Control', keyCode: 17, location: 1, metaKey: false, repeat: false, shiftKey: false },
622
{ timeStamp: 17962.90, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: '🥳', inputType: 'insertText', isComposing: false },
623
{ timeStamp: 17966.60, state: { value: 'aa🥳aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'input', data: '🥳', inputType: 'insertText', isComposing: false }
624
],
625
final: { value: 'aa🥳aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' },
626
};
627
628
const actualOutgoingEvents = await simulateInteraction(recorded);
629
assert.deepStrictEqual(actualOutgoingEvents, ([
630
{ type: 'type', text: '🥳', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }
631
]));
632
633
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
634
assert.deepStrictEqual(actualResultingState, recorded.final);
635
});
636
637
test('macOS - Firefox - long press with mouse', async () => {
638
// macOS, English, long press e and choose using mouse
639
// See https://github.com/microsoft/monaco-editor/issues/2358
640
const recorded: IRecorded = {
641
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: true, isChrome: false, isSafari: false } },
642
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
643
events: [
644
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
645
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keypress', altKey: false, charCode: 101, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 101, location: 0, metaKey: false, repeat: false, shiftKey: false },
646
{ timeStamp: 7.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'e', inputType: 'insertText', isComposing: false },
647
{ timeStamp: 7.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'e', inputType: 'insertText', isComposing: false },
648
{ timeStamp: 500.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: true, shiftKey: false },
649
{ timeStamp: 667.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: true, shiftKey: false },
650
{ timeStamp: 750.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: true, shiftKey: false },
651
{ timeStamp: 834.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: true, shiftKey: false },
652
{ timeStamp: 917.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: true, shiftKey: false },
653
{ timeStamp: 1001.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: true, shiftKey: false },
654
{ timeStamp: 1024.00, state: { value: 'aaeaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: false, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
655
{ timeStamp: 2988.00, state: { value: 'aaeaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'è', inputType: 'insertText', isComposing: false },
656
{ timeStamp: 2988.00, state: { value: 'aaèaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'è', inputType: 'insertText', isComposing: false }
657
],
658
final: { value: 'aaèaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' },
659
};
660
661
const actualOutgoingEvents = await simulateInteraction(recorded);
662
assert.deepStrictEqual(actualOutgoingEvents, [
663
{ type: 'type', text: 'e', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
664
{ type: 'type', text: 'è', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 }
665
]);
666
667
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
668
assert.deepStrictEqual(actualResultingState, recorded.final);
669
});
670
671
test('macOS - Firefox - inserting emojis', async () => {
672
// macOS, English, from the edit menu, click Emoji & Symbols, select an emoji
673
// See https://github.com/microsoft/vscode/issues/106392
674
const recorded: IRecorded = {
675
env: { OS: OperatingSystem.Macintosh, browser: { isAndroid: false, isFirefox: true, isChrome: false, isSafari: false } },
676
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
677
events: [
678
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: '😍', inputType: 'insertText', isComposing: false },
679
{ timeStamp: 1.00, state: { value: 'aa😍aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '😍', inputType: 'insertText', isComposing: false }
680
],
681
final: { value: 'aa😍aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' },
682
};
683
684
const actualOutgoingEvents = await simulateInteraction(recorded);
685
assert.deepStrictEqual(actualOutgoingEvents, [
686
{ type: 'type', text: '😍', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }
687
]);
688
689
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
690
assert.deepStrictEqual(actualResultingState, recorded.final);
691
});
692
693
test('macOS - Safari - Chinese - issue #119469', async () => {
694
const recorded: IRecorded = {
695
env: { 'OS': OperatingSystem.Macintosh, 'browser': { 'isAndroid': false, 'isFirefox': false, 'isChrome': false, 'isSafari': true } },
696
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' },
697
events: [
698
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionstart', data: '' },
699
{ timeStamp: 1.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'compositionupdate', data: 'f' },
700
{ timeStamp: 1.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: 'f', inputType: 'insertCompositionText', isComposing: undefined },
701
{ timeStamp: 2.00, state: { value: 'aafaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'f', inputType: 'insertCompositionText', isComposing: undefined },
702
{ timeStamp: -30.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyF', ctrlKey: false, isComposing: true, key: 'f', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
703
{ timeStamp: 106.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyF', ctrlKey: false, isComposing: true, key: 'f', keyCode: 70, location: 0, metaKey: false, repeat: false, shiftKey: false },
704
{ timeStamp: 721.00, state: { value: 'aafaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: null, inputType: 'deleteCompositionText', isComposing: undefined },
705
{ timeStamp: 723.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'input', data: null, inputType: 'deleteCompositionText', isComposing: undefined },
706
{ timeStamp: 723.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'none' }, type: 'beforeinput', data: 'f', inputType: 'insertFromComposition', isComposing: undefined },
707
{ timeStamp: 723.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'input', data: 'f', inputType: 'insertFromComposition', isComposing: undefined },
708
{ timeStamp: 723.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'compositionend', data: 'f' },
709
{ timeStamp: 698.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Enter', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
710
{ timeStamp: 826.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Enter', keyCode: 13, location: 0, metaKey: false, repeat: false, shiftKey: false },
711
{ timeStamp: 1114.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keydown', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Enter', keyCode: 13, location: 0, metaKey: false, repeat: false, shiftKey: false },
712
{ timeStamp: 1114.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'keypress', altKey: false, charCode: 13, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Enter', keyCode: 13, location: 0, metaKey: false, repeat: false, shiftKey: false },
713
{ timeStamp: 1137.00, state: { value: 'aafaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'none' }, type: 'beforeinput', data: null, inputType: 'insertLineBreak', isComposing: undefined },
714
{ timeStamp: 1138.00, state: { value: 'aaf\naa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'input', data: null, inputType: 'insertLineBreak', isComposing: undefined },
715
{ timeStamp: 1250.00, state: { value: 'aaf\naa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none' }, type: 'keyup', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Enter', keyCode: 13, location: 0, metaKey: false, repeat: false, shiftKey: false }
716
],
717
final: {
718
value: 'aaf\naa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'none'
719
},
720
};
721
722
const actualOutgoingEvents = await simulateInteraction(recorded);
723
assert.deepStrictEqual(actualOutgoingEvents, ([
724
{ type: 'compositionStart', data: '' },
725
{ type: 'type', text: 'f', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
726
{ type: 'compositionUpdate', data: 'f' },
727
{ type: 'type', text: 'f', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
728
{ type: 'compositionEnd' },
729
{ type: 'type', text: '\n', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }
730
]));
731
732
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
733
assert.deepStrictEqual(actualResultingState, recorded.final);
734
});
735
736
test('Windows - Chrome - Japanese using Hiragana', async () => {
737
// Windows, Japanese/Hiragana, type 'sennsei' and Enter
738
const recorded: IRecorded = {
739
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
740
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
741
events: [
742
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
743
{ timeStamp: 0.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
744
{ timeStamp: 0.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 's', inputType: 'insertCompositionText', isComposing: true },
745
{ timeStamp: 0.90, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 's' },
746
{ timeStamp: 9.30, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 's', inputType: 'insertCompositionText', isComposing: true },
747
{ timeStamp: 97.50, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
748
{ timeStamp: 99.10, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 83, location: 0, metaKey: false, repeat: false, shiftKey: false },
749
{ timeStamp: 615.90, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
750
{ timeStamp: 619.80, state: { value: 'aasaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せ', inputType: 'insertCompositionText', isComposing: true },
751
{ timeStamp: 619.80, state: { value: 'aasaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せ' },
752
{ timeStamp: 627.70, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'せ', inputType: 'insertCompositionText', isComposing: true },
753
{ timeStamp: 719.90, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
754
{ timeStamp: 723.60, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
755
{ timeStamp: 1816.10, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
756
{ timeStamp: 1828.30, state: { value: 'aaせaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せn', inputType: 'insertCompositionText', isComposing: true },
757
{ timeStamp: 1828.40, state: { value: 'aaせaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せn' },
758
{ timeStamp: 1828.70, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'せn', inputType: 'insertCompositionText', isComposing: true },
759
{ timeStamp: 1903.70, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
760
{ timeStamp: 1904.70, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
761
{ timeStamp: 2111.70, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
762
{ timeStamp: 2123.40, state: { value: 'aaせnaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せん', inputType: 'insertCompositionText', isComposing: true },
763
{ timeStamp: 2123.40, state: { value: 'aaせnaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せん' },
764
{ timeStamp: 2123.70, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'せん', inputType: 'insertCompositionText', isComposing: true },
765
{ timeStamp: 2215.80, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
766
{ timeStamp: 2217.10, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
767
{ timeStamp: 2968.00, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
768
{ timeStamp: 2970.00, state: { value: 'aaせんaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんs', inputType: 'insertCompositionText', isComposing: true },
769
{ timeStamp: 2970.00, state: { value: 'aaせんaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんs' },
770
{ timeStamp: 2970.20, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'input', data: 'せんs', inputType: 'insertCompositionText', isComposing: true },
771
{ timeStamp: 3079.70, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
772
{ timeStamp: 3080.70, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 83, location: 0, metaKey: false, repeat: false, shiftKey: false },
773
{ timeStamp: 3295.20, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
774
{ timeStamp: 3297.10, state: { value: 'aaせんsaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんせ', inputType: 'insertCompositionText', isComposing: true },
775
{ timeStamp: 3297.20, state: { value: 'aaせんsaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんせ' },
776
{ timeStamp: 3297.40, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'input', data: 'せんせ', inputType: 'insertCompositionText', isComposing: true },
777
{ timeStamp: 3408.00, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
778
{ timeStamp: 3409.00, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
779
{ timeStamp: 3880.80, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
780
{ timeStamp: 3882.80, state: { value: 'aaせんせaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
781
{ timeStamp: 3882.90, state: { value: 'aaせんせaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんせい' },
782
{ timeStamp: 3883.30, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'input', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
783
{ timeStamp: 3976.30, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
784
{ timeStamp: 3977.50, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'i', keyCode: 73, location: 0, metaKey: false, repeat: false, shiftKey: false },
785
{ timeStamp: 6364.90, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
786
{ timeStamp: 6367.40, state: { value: 'aaせんせいaa', selectionStart: 2, selectionEnd: 6, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
787
{ timeStamp: 6367.40, state: { value: 'aaせんせいaa', selectionStart: 2, selectionEnd: 6, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんせい' },
788
{ timeStamp: 6367.60, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'input', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
789
{ timeStamp: 6367.60, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'compositionend', data: 'せんせい' },
790
{ timeStamp: 6479.60, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false }
791
],
792
final: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' },
793
};
794
795
const actualOutgoingEvents = await simulateInteraction(recorded);
796
assert.deepStrictEqual(actualOutgoingEvents, [
797
{ type: 'compositionStart', data: '' },
798
{ type: 'type', text: 's', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
799
{ type: 'compositionUpdate', data: 's' },
800
{ type: 'type', text: 'せ', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
801
{ type: 'compositionUpdate', data: 'せ' },
802
{ type: 'type', text: 'せn', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
803
{ type: 'compositionUpdate', data: 'せn' },
804
{ type: 'type', text: 'せん', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
805
{ type: 'compositionUpdate', data: 'せん' },
806
{ type: 'type', text: 'せんs', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
807
{ type: 'compositionUpdate', data: 'せんs' },
808
{ type: 'type', text: 'せんせ', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
809
{ type: 'compositionUpdate', data: 'せんせ' },
810
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
811
{ type: 'compositionUpdate', data: 'せんせい' },
812
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 4, replaceNextCharCnt: 0, positionDelta: 0 },
813
{ type: 'compositionUpdate', data: 'せんせい' },
814
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 4, replaceNextCharCnt: 0, positionDelta: 0 },
815
{ type: 'compositionEnd' }
816
]);
817
818
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
819
assert.deepStrictEqual(actualResultingState, recorded.final);
820
});
821
822
test('Windows 11 - Chrome - Japanese using Hiragana', async () => {
823
// Windows, Japanese/Hiragana, type 'sennsei' and Enter
824
const recorded: IRecorded = {
825
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
826
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
827
events: [
828
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
829
{ timeStamp: 15.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
830
{ timeStamp: 15.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 's', inputType: 'insertCompositionText', isComposing: true },
831
{ timeStamp: 15.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 's' },
832
{ timeStamp: 20.00, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 's', inputType: 'insertCompositionText', isComposing: true },
833
{ timeStamp: 111.00, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
834
{ timeStamp: 111.00, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 83, location: 0, metaKey: false, repeat: false, shiftKey: false },
835
{ timeStamp: 832.00, state: { value: 'aasaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
836
{ timeStamp: 839.00, state: { value: 'aasaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せ', inputType: 'insertCompositionText', isComposing: true },
837
{ timeStamp: 839.00, state: { value: 'aasaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せ' },
838
{ timeStamp: 890.00, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'せ', inputType: 'insertCompositionText', isComposing: true },
839
{ timeStamp: 936.00, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
840
{ timeStamp: 937.00, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
841
{ timeStamp: 1456.00, state: { value: 'aaせaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
842
{ timeStamp: 1460.00, state: { value: 'aaせaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せn', inputType: 'insertCompositionText', isComposing: true },
843
{ timeStamp: 1460.00, state: { value: 'aaせaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せn' },
844
{ timeStamp: 1461.00, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'せn', inputType: 'insertCompositionText', isComposing: true },
845
{ timeStamp: 1522.00, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
846
{ timeStamp: 1522.00, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
847
{ timeStamp: 1684.00, state: { value: 'aaせnaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
848
{ timeStamp: 1694.00, state: { value: 'aaせnaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せん', inputType: 'insertCompositionText', isComposing: true },
849
{ timeStamp: 1694.00, state: { value: 'aaせnaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せん' },
850
{ timeStamp: 1694.00, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'せん', inputType: 'insertCompositionText', isComposing: true },
851
{ timeStamp: 1763.00, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
852
{ timeStamp: 1763.00, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
853
{ timeStamp: 1873.00, state: { value: 'aaせんaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
854
{ timeStamp: 1878.00, state: { value: 'aaせんaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんs', inputType: 'insertCompositionText', isComposing: true },
855
{ timeStamp: 1878.00, state: { value: 'aaせんaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんs' },
856
{ timeStamp: 1878.00, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'input', data: 'せんs', inputType: 'insertCompositionText', isComposing: true },
857
{ timeStamp: 1969.00, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
858
{ timeStamp: 1969.00, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 83, location: 0, metaKey: false, repeat: false, shiftKey: false },
859
{ timeStamp: 2094.00, state: { value: 'aaせんsaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
860
{ timeStamp: 2111.00, state: { value: 'aaせんsaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんせ', inputType: 'insertCompositionText', isComposing: true },
861
{ timeStamp: 2111.00, state: { value: 'aaせんsaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんせ' },
862
{ timeStamp: 2111.00, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'input', data: 'せんせ', inputType: 'insertCompositionText', isComposing: true },
863
{ timeStamp: 2222.00, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
864
{ timeStamp: 2222.00, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyE', ctrlKey: false, isComposing: true, key: 'e', keyCode: 69, location: 0, metaKey: false, repeat: false, shiftKey: false },
865
{ timeStamp: 2356.00, state: { value: 'aaせんせaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
866
{ timeStamp: 2367.00, state: { value: 'aaせんせaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
867
{ timeStamp: 2367.00, state: { value: 'aaせんせaa', selectionStart: 2, selectionEnd: 5, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんせい' },
868
{ timeStamp: 2367.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'input', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
869
{ timeStamp: 2456.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
870
{ timeStamp: 2456.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'i', keyCode: 73, location: 0, metaKey: false, repeat: false, shiftKey: false },
871
{ timeStamp: 3776.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
872
{ timeStamp: 3776.00, state: { value: 'aaせんせいaa', selectionStart: 2, selectionEnd: 6, selectionDirection: 'forward' }, type: 'beforeinput', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
873
{ timeStamp: 3776.00, state: { value: 'aaせんせいaa', selectionStart: 2, selectionEnd: 6, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'せんせい' },
874
{ timeStamp: 3785.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'input', data: 'せんせい', inputType: 'insertCompositionText', isComposing: true },
875
{ timeStamp: 3785.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'compositionend', data: 'せんせい' },
876
{ timeStamp: 3886.00, state: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Enter', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false }
877
],
878
final: { value: 'aaせんせいaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' },
879
};
880
881
const actualOutgoingEvents = await simulateInteraction(recorded);
882
assert.deepStrictEqual(actualOutgoingEvents, ([
883
{ type: 'compositionStart', data: '' },
884
{ type: 'type', text: 's', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
885
{ type: 'compositionUpdate', data: 's' },
886
{ type: 'type', text: 'せ', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
887
{ type: 'compositionUpdate', data: 'せ' },
888
{ type: 'type', text: 'せn', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
889
{ type: 'compositionUpdate', data: 'せn' },
890
{ type: 'type', text: 'せん', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
891
{ type: 'compositionUpdate', data: 'せん' },
892
{ type: 'type', text: 'せんs', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
893
{ type: 'compositionUpdate', data: 'せんs' },
894
{ type: 'type', text: 'せんせ', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
895
{ type: 'compositionUpdate', data: 'せんせ' },
896
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
897
{ type: 'compositionUpdate', data: 'せんせい' },
898
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 4, replaceNextCharCnt: 0, positionDelta: 0 },
899
{ type: 'compositionUpdate', data: 'せんせい' },
900
{ type: 'type', text: 'せんせい', replacePrevCharCnt: 4, replaceNextCharCnt: 0, positionDelta: 0 },
901
{ type: 'compositionEnd' }
902
]));
903
904
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
905
assert.deepStrictEqual(actualResultingState, recorded.final);
906
});
907
908
test('Windows - Chrome - Korean (1)', async () => {
909
// Windows, Korean, type 'dkrk' and click
910
const recorded: IRecorded = {
911
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
912
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
913
events: [
914
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
915
{ timeStamp: 23.10, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
916
{ timeStamp: 23.10, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ㅇ', inputType: 'insertCompositionText', isComposing: true },
917
{ timeStamp: 23.20, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ㅇ' },
918
{ timeStamp: 23.60, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'ㅇ', inputType: 'insertCompositionText', isComposing: true },
919
{ timeStamp: 119.30, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
920
{ timeStamp: 215.00, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
921
{ timeStamp: 215.40, state: { value: 'aaㅇaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '아', inputType: 'insertCompositionText', isComposing: true },
922
{ timeStamp: 215.40, state: { value: 'aaㅇaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '아' },
923
{ timeStamp: 215.90, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '아', inputType: 'insertCompositionText', isComposing: true },
924
{ timeStamp: 303.20, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
925
{ timeStamp: 511.10, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
926
{ timeStamp: 511.70, state: { value: 'aa아aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '악', inputType: 'insertCompositionText', isComposing: true },
927
{ timeStamp: 511.70, state: { value: 'aa아aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '악' },
928
{ timeStamp: 512.10, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '악', inputType: 'insertCompositionText', isComposing: true },
929
{ timeStamp: 598.20, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
930
{ timeStamp: 791.00, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
931
{ timeStamp: 791.50, state: { value: 'aa악aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '아', inputType: 'insertCompositionText', isComposing: true },
932
{ timeStamp: 791.50, state: { value: 'aa악aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '아' },
933
{ timeStamp: 791.80, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '아', inputType: 'insertCompositionText', isComposing: true },
934
{ timeStamp: 791.90, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionend', data: '아' },
935
{ timeStamp: 792.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
936
{ timeStamp: 792.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '가', inputType: 'insertCompositionText', isComposing: true },
937
{ timeStamp: 792.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '가' },
938
{ timeStamp: 792.30, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '가', inputType: 'insertCompositionText', isComposing: true },
939
{ timeStamp: 919.00, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
940
{ timeStamp: 2721.50, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionend', data: '가' }
941
],
942
final: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' },
943
};
944
945
const actualOutgoingEvents = await simulateInteraction(recorded);
946
assert.deepStrictEqual(actualOutgoingEvents, [
947
{ type: 'compositionStart', data: '' },
948
{ type: 'type', text: 'ㅇ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
949
{ type: 'compositionUpdate', data: 'ㅇ' },
950
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
951
{ type: 'compositionUpdate', data: '아' },
952
{ type: 'type', text: '악', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
953
{ type: 'compositionUpdate', data: '악' },
954
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
955
{ type: 'compositionUpdate', data: '아' },
956
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
957
{ type: 'compositionEnd' },
958
{ type: 'compositionStart', data: '' },
959
{ type: 'type', text: '가', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
960
{ type: 'compositionUpdate', data: '가' },
961
{ type: 'type', text: '가', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
962
{ type: 'compositionEnd' }
963
]);
964
965
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
966
assert.deepStrictEqual(actualResultingState, recorded.final);
967
});
968
969
test('Windows 11 - Chrome - Korean (1)', async () => {
970
// Windows, Korean, type 'dkrk' and Space
971
const recorded: IRecorded = {
972
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
973
974
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
975
events: [
976
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
977
{ timeStamp: 9.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
978
{ timeStamp: 10.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ㅇ', inputType: 'insertCompositionText', isComposing: true },
979
{ timeStamp: 10.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ㅇ' },
980
{ timeStamp: 26.00, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'ㅇ', inputType: 'insertCompositionText', isComposing: true },
981
{ timeStamp: 119.00, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
982
{ timeStamp: 134.00, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyD', ctrlKey: false, isComposing: true, key: 'd', keyCode: 68, location: 0, metaKey: false, repeat: false, shiftKey: false },
983
{ timeStamp: 442.00, state: { value: 'aaㅇaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
984
{ timeStamp: 442.00, state: { value: 'aaㅇaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '아', inputType: 'insertCompositionText', isComposing: true },
985
{ timeStamp: 442.00, state: { value: 'aaㅇaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '아' },
986
{ timeStamp: 451.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '아', inputType: 'insertCompositionText', isComposing: true },
987
{ timeStamp: 535.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
988
{ timeStamp: 535.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'k', keyCode: 75, location: 0, metaKey: false, repeat: false, shiftKey: false },
989
{ timeStamp: 879.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
990
{ timeStamp: 879.00, state: { value: 'aa아aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '악', inputType: 'insertCompositionText', isComposing: true },
991
{ timeStamp: 879.00, state: { value: 'aa아aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '악' },
992
{ timeStamp: 881.00, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '악', inputType: 'insertCompositionText', isComposing: true },
993
{ timeStamp: 980.00, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
994
{ timeStamp: 992.00, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'r', keyCode: 82, location: 0, metaKey: false, repeat: false, shiftKey: false },
995
{ timeStamp: 1230.00, state: { value: 'aa악aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
996
{ timeStamp: 1230.00, state: { value: 'aa악aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '아', inputType: 'insertCompositionText', isComposing: true },
997
{ timeStamp: 1230.00, state: { value: 'aa악aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '아' },
998
{ timeStamp: 1242.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '아', inputType: 'insertCompositionText', isComposing: true },
999
{ timeStamp: 1242.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionend', data: '아' },
1000
{ timeStamp: 1242.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1001
{ timeStamp: 1242.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '가', inputType: 'insertCompositionText', isComposing: true },
1002
{ timeStamp: 1242.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '가' },
1003
{ timeStamp: 1243.00, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '가', inputType: 'insertCompositionText', isComposing: true },
1004
{ timeStamp: 1375.00, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1005
{ timeStamp: 1375.00, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'k', keyCode: 75, location: 0, metaKey: false, repeat: false, shiftKey: false },
1006
{ timeStamp: 3412.00, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionend', data: '가' },
1007
{ timeStamp: 3412.00, state: { value: 'aa아가aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: null, inputType: 'deleteContentBackward', isComposing: false },
1008
{ timeStamp: 3413.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: null, inputType: 'deleteContentBackward', isComposing: false },
1009
{ timeStamp: 3413.00, state: { value: 'aa아aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '가', inputType: 'insertText', isComposing: false },
1010
{ timeStamp: 3414.00, state: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '가', inputType: 'insertText', isComposing: false }
1011
],
1012
final: { value: 'aa아가aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' },
1013
};
1014
1015
const actualOutgoingEvents = await simulateInteraction(recorded);
1016
assert.deepStrictEqual(actualOutgoingEvents, [
1017
{ type: 'compositionStart', data: '' },
1018
{ type: 'type', text: 'ㅇ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1019
{ type: 'compositionUpdate', data: 'ㅇ' },
1020
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1021
{ type: 'compositionUpdate', data: '아' },
1022
{ type: 'type', text: '악', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1023
{ type: 'compositionUpdate', data: '악' },
1024
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1025
{ type: 'compositionUpdate', data: '아' },
1026
{ type: 'type', text: '아', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1027
{ type: 'compositionEnd' },
1028
{ type: 'compositionStart', data: '' },
1029
{ type: 'type', text: '가', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1030
{ type: 'compositionUpdate', data: '가' },
1031
{ type: 'type', text: '가', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1032
{ type: 'compositionEnd' },
1033
{ type: 'type', text: '', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1034
{ type: 'type', text: '가', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }
1035
]);
1036
1037
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
1038
assert.deepStrictEqual(actualResultingState, recorded.final);
1039
});
1040
1041
test('Windows - Chrome - Korean (2)', async () => {
1042
// Windows, Korean, type 'gksrmf' and Space
1043
const recorded: IRecorded = {
1044
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
1045
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
1046
events: [
1047
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyG', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1048
{ timeStamp: 23.30, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1049
{ timeStamp: 23.50, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ㅎ', inputType: 'insertCompositionText', isComposing: true },
1050
{ timeStamp: 23.50, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ㅎ' },
1051
{ timeStamp: 27.30, state: { value: 'aaㅎaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'ㅎ', inputType: 'insertCompositionText', isComposing: true },
1052
{ timeStamp: 111.80, state: { value: 'aaㅎaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyG', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1053
{ timeStamp: 606.80, state: { value: 'aaㅎaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1054
{ timeStamp: 607.40, state: { value: 'aaㅎaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '하', inputType: 'insertCompositionText', isComposing: true },
1055
{ timeStamp: 607.40, state: { value: 'aaㅎaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '하' },
1056
{ timeStamp: 607.80, state: { value: 'aa하aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '하', inputType: 'insertCompositionText', isComposing: true },
1057
{ timeStamp: 705.20, state: { value: 'aa하aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1058
{ timeStamp: 1455.80, state: { value: 'aa하aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1059
{ timeStamp: 1456.40, state: { value: 'aa하aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '한', inputType: 'insertCompositionText', isComposing: true },
1060
{ timeStamp: 1456.50, state: { value: 'aa하aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '한' },
1061
{ timeStamp: 1456.90, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '한', inputType: 'insertCompositionText', isComposing: true },
1062
{ timeStamp: 1567.40, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1063
{ timeStamp: 1963.10, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1064
{ timeStamp: 1963.70, state: { value: 'aa한aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '한', inputType: 'insertCompositionText', isComposing: true },
1065
{ timeStamp: 1963.80, state: { value: 'aa한aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '한' },
1066
{ timeStamp: 1963.80, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '한', inputType: 'insertCompositionText', isComposing: true },
1067
{ timeStamp: 1963.90, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionend', data: '한' },
1068
{ timeStamp: 1964.10, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1069
{ timeStamp: 1964.10, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ㄱ', inputType: 'insertCompositionText', isComposing: true },
1070
{ timeStamp: 1964.10, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ㄱ' },
1071
{ timeStamp: 1964.40, state: { value: 'aa한ㄱaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'ㄱ', inputType: 'insertCompositionText', isComposing: true },
1072
{ timeStamp: 2063.60, state: { value: 'aa한ㄱaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1073
{ timeStamp: 2823.60, state: { value: 'aa한ㄱaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyM', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1074
{ timeStamp: 2824.00, state: { value: 'aa한ㄱaa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: '그', inputType: 'insertCompositionText', isComposing: true },
1075
{ timeStamp: 2824.10, state: { value: 'aa한ㄱaa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: '그' },
1076
{ timeStamp: 2824.40, state: { value: 'aa한그aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '그', inputType: 'insertCompositionText', isComposing: true },
1077
{ timeStamp: 2935.30, state: { value: 'aa한그aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyM', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1078
{ timeStamp: 3187.50, state: { value: 'aa한그aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyF', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1079
{ timeStamp: 3188.00, state: { value: 'aa한그aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: '글', inputType: 'insertCompositionText', isComposing: true },
1080
{ timeStamp: 3188.00, state: { value: 'aa한그aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: '글' },
1081
{ timeStamp: 3188.40, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '글', inputType: 'insertCompositionText', isComposing: true },
1082
{ timeStamp: 3319.20, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyF', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1083
{ timeStamp: 3847.30, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1084
{ timeStamp: 3847.80, state: { value: 'aa한글aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: '글', inputType: 'insertCompositionText', isComposing: true },
1085
{ timeStamp: 3847.80, state: { value: 'aa한글aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: '글' },
1086
{ timeStamp: 3847.90, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '글', inputType: 'insertCompositionText', isComposing: true },
1087
{ timeStamp: 3848.10, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionend', data: '글' },
1088
{ timeStamp: 3847.70, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false },
1089
{ timeStamp: 3847.80, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keypress', altKey: false, charCode: 32, code: 'Space', ctrlKey: false, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false },
1090
{ timeStamp: 3848.30, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: ' ', inputType: 'insertText', isComposing: false },
1091
{ timeStamp: 3848.60, state: { value: 'aa한글 aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'input', data: ' ', inputType: 'insertText', isComposing: false },
1092
{ timeStamp: 3919.20, state: { value: 'aa한글 aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1093
{ timeStamp: 3919.50, state: { value: 'aa한글 aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false }
1094
],
1095
final: { value: 'aa한글 aa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' },
1096
};
1097
1098
const actualOutgoingEvents = await simulateInteraction(recorded);
1099
assert.deepStrictEqual(actualOutgoingEvents, [
1100
{ type: 'compositionStart', data: '' },
1101
{ type: 'type', text: 'ㅎ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1102
{ type: 'compositionUpdate', data: 'ㅎ' },
1103
{ type: 'type', text: '하', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1104
{ type: 'compositionUpdate', data: '하' },
1105
{ type: 'type', text: '한', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1106
{ type: 'compositionUpdate', data: '한' },
1107
{ type: 'type', text: '한', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1108
{ type: 'compositionUpdate', data: '한' },
1109
{ type: 'type', text: '한', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1110
{ type: 'compositionEnd' },
1111
{ type: 'compositionStart', data: '' },
1112
{ type: 'type', text: 'ㄱ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1113
{ type: 'compositionUpdate', data: 'ㄱ' },
1114
{ type: 'type', text: '그', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1115
{ type: 'compositionUpdate', data: '그' },
1116
{ type: 'type', text: '글', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1117
{ type: 'compositionUpdate', data: '글' },
1118
{ type: 'type', text: '글', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1119
{ type: 'compositionUpdate', data: '글' },
1120
{ type: 'type', text: '글', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1121
{ type: 'compositionEnd' },
1122
{ type: 'type', text: ' ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }
1123
]);
1124
1125
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
1126
assert.deepStrictEqual(actualResultingState, recorded.final);
1127
});
1128
1129
test('Windows 11 - Chrome - Korean (2)', async () => {
1130
// Windows, Korean, type 'gksrmf' and Space
1131
const recorded: IRecorded = {
1132
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
1133
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
1134
events: [
1135
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'ControlLeft', ctrlKey: false, isComposing: false, key: 'Control', keyCode: 17, location: 1, metaKey: false, repeat: false, shiftKey: false },
1136
{ timeStamp: 1561.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyG', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1137
{ timeStamp: 1566.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1138
{ timeStamp: 1566.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ㅎ', inputType: 'insertCompositionText', isComposing: true },
1139
{ timeStamp: 1566.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ㅎ' },
1140
{ timeStamp: 1567.00, state: { value: 'aaㅎaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'ㅎ', inputType: 'insertCompositionText', isComposing: true },
1141
{ timeStamp: 1681.00, state: { value: 'aaㅎaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyG', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1142
{ timeStamp: 1681.00, state: { value: 'aaㅎaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyG', ctrlKey: false, isComposing: true, key: 'g', keyCode: 71, location: 0, metaKey: false, repeat: false, shiftKey: false },
1143
{ timeStamp: 2013.00, state: { value: 'aaㅎaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1144
{ timeStamp: 2013.00, state: { value: 'aaㅎaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '하', inputType: 'insertCompositionText', isComposing: true },
1145
{ timeStamp: 2013.00, state: { value: 'aaㅎaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '하' },
1146
{ timeStamp: 2013.00, state: { value: 'aa하aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '하', inputType: 'insertCompositionText', isComposing: true },
1147
{ timeStamp: 2096.00, state: { value: 'aa하aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1148
{ timeStamp: 2096.00, state: { value: 'aa하aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'k', keyCode: 75, location: 0, metaKey: false, repeat: false, shiftKey: false },
1149
{ timeStamp: 2457.00, state: { value: 'aa하aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1150
{ timeStamp: 2457.00, state: { value: 'aa하aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '한', inputType: 'insertCompositionText', isComposing: true },
1151
{ timeStamp: 2457.00, state: { value: 'aa하aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '한' },
1152
{ timeStamp: 2457.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '한', inputType: 'insertCompositionText', isComposing: true },
1153
{ timeStamp: 2568.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1154
{ timeStamp: 2568.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyS', ctrlKey: false, isComposing: true, key: 's', keyCode: 83, location: 0, metaKey: false, repeat: false, shiftKey: false },
1155
{ timeStamp: 3066.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1156
{ timeStamp: 3066.00, state: { value: 'aa한aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '한', inputType: 'insertCompositionText', isComposing: true },
1157
{ timeStamp: 3066.00, state: { value: 'aa한aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '한' },
1158
{ timeStamp: 3066.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '한', inputType: 'insertCompositionText', isComposing: true },
1159
{ timeStamp: 3066.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionend', data: '한' },
1160
{ timeStamp: 3070.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1161
{ timeStamp: 3070.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ㄱ', inputType: 'insertCompositionText', isComposing: true },
1162
{ timeStamp: 3070.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ㄱ' },
1163
{ timeStamp: 3071.00, state: { value: 'aa한ㄱaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'ㄱ', inputType: 'insertCompositionText', isComposing: true },
1164
{ timeStamp: 3180.00, state: { value: 'aa한ㄱaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1165
{ timeStamp: 3180.00, state: { value: 'aa한ㄱaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'r', keyCode: 82, location: 0, metaKey: false, repeat: false, shiftKey: false },
1166
{ timeStamp: 3650.00, state: { value: 'aa한ㄱaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyM', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1167
{ timeStamp: 3650.00, state: { value: 'aa한ㄱaa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: '그', inputType: 'insertCompositionText', isComposing: true },
1168
{ timeStamp: 3650.00, state: { value: 'aa한ㄱaa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: '그' },
1169
{ timeStamp: 3650.00, state: { value: 'aa한그aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '그', inputType: 'insertCompositionText', isComposing: true },
1170
{ timeStamp: 3753.00, state: { value: 'aa한그aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyM', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1171
{ timeStamp: 3768.00, state: { value: 'aa한그aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyM', ctrlKey: false, isComposing: true, key: 'm', keyCode: 77, location: 0, metaKey: false, repeat: false, shiftKey: false },
1172
{ timeStamp: 4554.00, state: { value: 'aa한그aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyF', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1173
{ timeStamp: 4554.00, state: { value: 'aa한그aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: '글', inputType: 'insertCompositionText', isComposing: true },
1174
{ timeStamp: 4554.00, state: { value: 'aa한그aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: '글' },
1175
{ timeStamp: 4558.00, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '글', inputType: 'insertCompositionText', isComposing: true },
1176
{ timeStamp: 4685.00, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyF', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1177
{ timeStamp: 4685.00, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyF', ctrlKey: false, isComposing: true, key: 'f', keyCode: 70, location: 0, metaKey: false, repeat: false, shiftKey: false },
1178
{ timeStamp: 6632.00, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionend', data: '글' },
1179
{ timeStamp: 6634.00, state: { value: 'aa한글aa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: null, inputType: 'deleteContentBackward', isComposing: false },
1180
{ timeStamp: 6634.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: null, inputType: 'deleteContentBackward', isComposing: false },
1181
{ timeStamp: 6634.00, state: { value: 'aa한aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '글', inputType: 'insertText', isComposing: false },
1182
{ timeStamp: 6634.00, state: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '글', inputType: 'insertText', isComposing: false }
1183
],
1184
final: { value: 'aa한글aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' },
1185
1186
};
1187
1188
const actualOutgoingEvents = await simulateInteraction(recorded);
1189
assert.deepStrictEqual(actualOutgoingEvents, [
1190
{ type: 'compositionStart', data: '' },
1191
{ type: 'type', text: 'ㅎ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1192
{ type: 'compositionUpdate', data: 'ㅎ' },
1193
{ type: 'type', text: '하', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1194
{ type: 'compositionUpdate', data: '하' },
1195
{ type: 'type', text: '한', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1196
{ type: 'compositionUpdate', data: '한' },
1197
{ type: 'type', text: '한', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1198
{ type: 'compositionUpdate', data: '한' },
1199
{ type: 'type', text: '한', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1200
{ type: 'compositionEnd' },
1201
{ type: 'compositionStart', data: '' },
1202
{ type: 'type', text: 'ㄱ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1203
{ type: 'compositionUpdate', data: 'ㄱ' },
1204
{ type: 'type', text: '그', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1205
{ type: 'compositionUpdate', data: '그' },
1206
{ type: 'type', text: '글', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1207
{ type: 'compositionUpdate', data: '글' },
1208
{ type: 'type', text: '글', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1209
{ type: 'compositionEnd' },
1210
{ type: 'type', text: '', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1211
{ type: 'type', text: '글', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }
1212
]);
1213
1214
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
1215
assert.deepStrictEqual(actualResultingState, recorded.final);
1216
});
1217
1218
test('Windows - Chrome - Chinese', async () => {
1219
// Windows, Chinese, Type 'ni' press Space and then 'hao' and press Space.
1220
const recorded: IRecorded = {
1221
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
1222
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
1223
events: [
1224
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1225
{ timeStamp: 0.80, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1226
{ timeStamp: 0.90, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'n', inputType: 'insertCompositionText', isComposing: true },
1227
{ timeStamp: 1.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'n' },
1228
{ timeStamp: 1.20, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'n', inputType: 'insertCompositionText', isComposing: true },
1229
{ timeStamp: 66.80, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1230
{ timeStamp: 67.90, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
1231
{ timeStamp: 466.70, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1232
{ timeStamp: 470.10, state: { value: 'aanaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ni', inputType: 'insertCompositionText', isComposing: true },
1233
{ timeStamp: 470.20, state: { value: 'aanaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ni' },
1234
{ timeStamp: 470.50, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'ni', inputType: 'insertCompositionText', isComposing: true },
1235
{ timeStamp: 563.20, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1236
{ timeStamp: 564.20, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'i', keyCode: 73, location: 0, metaKey: false, repeat: false, shiftKey: false },
1237
{ timeStamp: 1835.00, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1238
{ timeStamp: 1837.20, state: { value: 'aaniaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: '你', inputType: 'insertCompositionText', isComposing: true },
1239
{ timeStamp: 1837.30, state: { value: 'aaniaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: '你' },
1240
{ timeStamp: 1837.70, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '你', inputType: 'insertCompositionText', isComposing: true },
1241
{ timeStamp: 1837.80, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionend', data: '你' },
1242
{ timeStamp: 1914.90, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1243
{ timeStamp: 1916.10, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false },
1244
{ timeStamp: 3000.40, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyH', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1245
{ timeStamp: 3000.80, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1246
{ timeStamp: 3000.80, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'h', inputType: 'insertCompositionText', isComposing: true },
1247
{ timeStamp: 3000.90, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'h' },
1248
{ timeStamp: 3001.30, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'h', inputType: 'insertCompositionText', isComposing: true },
1249
{ timeStamp: 3091.60, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyH', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1250
{ timeStamp: 3092.60, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyH', ctrlKey: false, isComposing: true, key: 'h', keyCode: 72, location: 0, metaKey: false, repeat: false, shiftKey: false },
1251
{ timeStamp: 3131.50, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyA', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1252
{ timeStamp: 3134.80, state: { value: 'aa你haa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ha', inputType: 'insertCompositionText', isComposing: true },
1253
{ timeStamp: 3134.80, state: { value: 'aa你haa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ha' },
1254
{ timeStamp: 3135.10, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'input', data: 'ha', inputType: 'insertCompositionText', isComposing: true },
1255
{ timeStamp: 3234.90, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyA', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1256
{ timeStamp: 3236.20, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyA', ctrlKey: false, isComposing: true, key: 'a', keyCode: 65, location: 0, metaKey: false, repeat: false, shiftKey: false },
1257
{ timeStamp: 3491.70, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1258
{ timeStamp: 3494.80, state: { value: 'aa你haaa', selectionStart: 3, selectionEnd: 5, selectionDirection: 'forward' }, type: 'beforeinput', data: 'hao', inputType: 'insertCompositionText', isComposing: true },
1259
{ timeStamp: 3495.00, state: { value: 'aa你haaa', selectionStart: 3, selectionEnd: 5, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'hao' },
1260
{ timeStamp: 3495.40, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'input', data: 'hao', inputType: 'insertCompositionText', isComposing: true },
1261
{ timeStamp: 3570.70, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1262
{ timeStamp: 3572.40, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: true, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: false, shiftKey: false },
1263
{ timeStamp: 4739.00, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1264
{ timeStamp: 4742.10, state: { value: 'aa你haoaa', selectionStart: 3, selectionEnd: 6, selectionDirection: 'forward' }, type: 'beforeinput', data: '好', inputType: 'insertCompositionText', isComposing: true },
1265
{ timeStamp: 4742.10, state: { value: 'aa你haoaa', selectionStart: 3, selectionEnd: 6, selectionDirection: 'forward' }, type: 'compositionupdate', data: '好' },
1266
{ timeStamp: 4742.50, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '好', inputType: 'insertCompositionText', isComposing: true },
1267
{ timeStamp: 4742.60, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionend', data: '好' },
1268
{ timeStamp: 4834.70, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1269
{ timeStamp: 4836.00, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false }
1270
],
1271
final: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' },
1272
};
1273
1274
const actualOutgoingEvents = await simulateInteraction(recorded);
1275
assert.deepStrictEqual(actualOutgoingEvents, [
1276
{ type: 'compositionStart', data: '' },
1277
{ type: 'type', text: 'n', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1278
{ type: 'compositionUpdate', data: 'n' },
1279
{ type: 'type', text: 'ni', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1280
{ type: 'compositionUpdate', data: 'ni' },
1281
{ type: 'type', text: '你', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
1282
{ type: 'compositionUpdate', data: '你' },
1283
{ type: 'type', text: '你', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1284
{ type: 'compositionEnd' },
1285
{ type: 'compositionStart', data: '' },
1286
{ type: 'type', text: 'h', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1287
{ type: 'compositionUpdate', data: 'h' },
1288
{ type: 'type', text: 'ha', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1289
{ type: 'compositionUpdate', data: 'ha' },
1290
{ type: 'type', text: 'hao', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
1291
{ type: 'compositionUpdate', data: 'hao' },
1292
{ type: 'type', text: '好', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
1293
{ type: 'compositionUpdate', data: '好' },
1294
{ type: 'type', text: '好', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1295
{ type: 'compositionEnd' }
1296
]);
1297
1298
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
1299
assert.deepStrictEqual(actualResultingState, recorded.final);
1300
});
1301
1302
test('Windows 11 - Chrome - Chinese', async () => {
1303
// Windows, Chinese, Type 'ni' press Space and then 'hao' and press Space.
1304
const recorded: IRecorded = {
1305
env: { OS: OperatingSystem.Windows, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
1306
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
1307
events: [
1308
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1309
{ timeStamp: 1.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1310
{ timeStamp: 1.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'n', inputType: 'insertCompositionText', isComposing: true },
1311
{ timeStamp: 1.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'n' },
1312
{ timeStamp: 1.00, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'n', inputType: 'insertCompositionText', isComposing: true },
1313
{ timeStamp: 63.00, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1314
{ timeStamp: 63.00, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyN', ctrlKey: false, isComposing: true, key: 'n', keyCode: 78, location: 0, metaKey: false, repeat: false, shiftKey: false },
1315
{ timeStamp: 331.00, state: { value: 'aanaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1316
{ timeStamp: 331.00, state: { value: 'aanaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ni', inputType: 'insertCompositionText', isComposing: true },
1317
{ timeStamp: 331.00, state: { value: 'aanaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ni' },
1318
{ timeStamp: 342.00, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'ni', inputType: 'insertCompositionText', isComposing: true },
1319
{ timeStamp: 403.00, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1320
{ timeStamp: 403.00, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyI', ctrlKey: false, isComposing: true, key: 'i', keyCode: 73, location: 0, metaKey: false, repeat: false, shiftKey: false },
1321
{ timeStamp: 614.00, state: { value: 'aaniaa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1322
{ timeStamp: 617.00, state: { value: 'aaniaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: '你', inputType: 'insertCompositionText', isComposing: true },
1323
{ timeStamp: 617.00, state: { value: 'aaniaa', selectionStart: 2, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: '你' },
1324
{ timeStamp: 657.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '你', inputType: 'insertCompositionText', isComposing: true },
1325
{ timeStamp: 658.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionend', data: '你' },
1326
{ timeStamp: 715.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1327
{ timeStamp: 715.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false },
1328
{ timeStamp: 1117.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyH', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1329
{ timeStamp: 1117.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1330
{ timeStamp: 1117.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: 'h', inputType: 'insertCompositionText', isComposing: true },
1331
{ timeStamp: 1117.00, state: { value: 'aa你aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'h' },
1332
{ timeStamp: 1117.00, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: 'h', inputType: 'insertCompositionText', isComposing: true },
1333
{ timeStamp: 1199.00, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyH', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1334
{ timeStamp: 1199.00, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyH', ctrlKey: false, isComposing: true, key: 'h', keyCode: 72, location: 0, metaKey: false, repeat: false, shiftKey: false },
1335
{ timeStamp: 1317.00, state: { value: 'aa你haa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyA', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1336
{ timeStamp: 1322.00, state: { value: 'aa你haa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ha', inputType: 'insertCompositionText', isComposing: true },
1337
{ timeStamp: 1322.00, state: { value: 'aa你haa', selectionStart: 3, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ha' },
1338
{ timeStamp: 1328.00, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'input', data: 'ha', inputType: 'insertCompositionText', isComposing: true },
1339
{ timeStamp: 1419.00, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyA', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1340
{ timeStamp: 1419.00, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyA', ctrlKey: false, isComposing: true, key: 'a', keyCode: 65, location: 0, metaKey: false, repeat: false, shiftKey: false },
1341
{ timeStamp: 1592.00, state: { value: 'aa你haaa', selectionStart: 5, selectionEnd: 5, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1342
{ timeStamp: 1592.00, state: { value: 'aa你haaa', selectionStart: 3, selectionEnd: 5, selectionDirection: 'forward' }, type: 'beforeinput', data: 'hao', inputType: 'insertCompositionText', isComposing: true },
1343
{ timeStamp: 1592.00, state: { value: 'aa你haaa', selectionStart: 3, selectionEnd: 5, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'hao' },
1344
{ timeStamp: 1606.00, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'input', data: 'hao', inputType: 'insertCompositionText', isComposing: true },
1345
{ timeStamp: 1666.00, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1346
{ timeStamp: 1681.00, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyO', ctrlKey: false, isComposing: true, key: 'o', keyCode: 79, location: 0, metaKey: false, repeat: false, shiftKey: false },
1347
{ timeStamp: 2187.00, state: { value: 'aa你haoaa', selectionStart: 6, selectionEnd: 6, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: true, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1348
{ timeStamp: 2187.00, state: { value: 'aa你haoaa', selectionStart: 3, selectionEnd: 6, selectionDirection: 'forward' }, type: 'beforeinput', data: '好', inputType: 'insertCompositionText', isComposing: true },
1349
{ timeStamp: 2187.00, state: { value: 'aa你haoaa', selectionStart: 3, selectionEnd: 6, selectionDirection: 'forward' }, type: 'compositionupdate', data: '好' },
1350
{ timeStamp: 2199.00, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'input', data: '好', inputType: 'insertCompositionText', isComposing: true },
1351
{ timeStamp: 2199.00, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'compositionend', data: '好' },
1352
{ timeStamp: 2315.00, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: 'Process', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1353
{ timeStamp: 2323.00, state: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'Space', ctrlKey: false, isComposing: false, key: ' ', keyCode: 32, location: 0, metaKey: false, repeat: false, shiftKey: false }
1354
],
1355
final: { value: 'aa你好aa', selectionStart: 4, selectionEnd: 4, selectionDirection: 'forward' },
1356
};
1357
1358
const actualOutgoingEvents = await simulateInteraction(recorded);
1359
assert.deepStrictEqual(actualOutgoingEvents, [
1360
{ type: 'compositionStart', data: '' },
1361
{ type: 'type', text: 'n', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1362
{ type: 'compositionUpdate', data: 'n' },
1363
{ type: 'type', text: 'ni', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1364
{ type: 'compositionUpdate', data: 'ni' },
1365
{ type: 'type', text: '你', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
1366
{ type: 'compositionUpdate', data: '你' },
1367
{ type: 'type', text: '你', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1368
{ type: 'compositionEnd' },
1369
{ type: 'compositionStart', data: '' },
1370
{ type: 'type', text: 'h', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1371
{ type: 'compositionUpdate', data: 'h' },
1372
{ type: 'type', text: 'ha', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1373
{ type: 'compositionUpdate', data: 'ha' },
1374
{ type: 'type', text: 'hao', replacePrevCharCnt: 2, replaceNextCharCnt: 0, positionDelta: 0 },
1375
{ type: 'compositionUpdate', data: 'hao' },
1376
{ type: 'type', text: '好', replacePrevCharCnt: 3, replaceNextCharCnt: 0, positionDelta: 0 },
1377
{ type: 'compositionUpdate', data: '好' },
1378
{ type: 'type', text: '好', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1379
{ type: 'compositionEnd' }
1380
]);
1381
1382
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
1383
assert.deepStrictEqual(actualResultingState, recorded.final);
1384
});
1385
1386
test('Linux - Chrome - Korean', async () => {
1387
// Linux, fcitx Hangul, Type 'rkr' and then click.
1388
const recorded: IRecorded = {
1389
env: { OS: OperatingSystem.Linux, browser: { isAndroid: false, isFirefox: false, isChrome: true, isSafari: false } },
1390
initial: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' },
1391
events: [
1392
{ timeStamp: 0.00, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: '', ctrlKey: false, isComposing: false, key: 'Unidentified', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1393
{ timeStamp: 1.20, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionstart', data: '' },
1394
{ timeStamp: 1.30, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'beforeinput', data: 'ㄱ', inputType: 'insertCompositionText', isComposing: true },
1395
{ timeStamp: 1.40, state: { value: 'aaaa', selectionStart: 2, selectionEnd: 2, selectionDirection: 'forward' }, type: 'compositionupdate', data: 'ㄱ' },
1396
{ timeStamp: 1.70, state: { value: 'aaㄱaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: 'ㄱ', inputType: 'insertCompositionText', isComposing: true },
1397
{ timeStamp: 104.50, state: { value: 'aaㄱaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'r', keyCode: 82, location: 0, metaKey: false, repeat: false, shiftKey: false },
1398
{ timeStamp: 150.60, state: { value: 'aaㄱaa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: '', ctrlKey: false, isComposing: true, key: 'Unidentified', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1399
{ timeStamp: 151.30, state: { value: 'aaㄱaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '가', inputType: 'insertCompositionText', isComposing: true },
1400
{ timeStamp: 151.40, state: { value: 'aaㄱaa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '가' },
1401
{ timeStamp: 151.80, state: { value: 'aa가aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '가', inputType: 'insertCompositionText', isComposing: true },
1402
{ timeStamp: 248.50, state: { value: 'aa가aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyK', ctrlKey: false, isComposing: true, key: 'k', keyCode: 75, location: 0, metaKey: false, repeat: false, shiftKey: false },
1403
{ timeStamp: 322.90, state: { value: 'aa가aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keydown', altKey: false, charCode: 0, code: '', ctrlKey: false, isComposing: true, key: 'Unidentified', keyCode: 229, location: 0, metaKey: false, repeat: false, shiftKey: false },
1404
{ timeStamp: 323.70, state: { value: 'aa가aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'beforeinput', data: '각', inputType: 'insertCompositionText', isComposing: true },
1405
{ timeStamp: 323.90, state: { value: 'aa가aa', selectionStart: 2, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionupdate', data: '각' },
1406
{ timeStamp: 324.10, state: { value: 'aa각aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'input', data: '각', inputType: 'insertCompositionText', isComposing: true },
1407
{ timeStamp: 448.50, state: { value: 'aa각aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'keyup', altKey: false, charCode: 0, code: 'KeyR', ctrlKey: false, isComposing: true, key: 'r', keyCode: 82, location: 0, metaKey: false, repeat: false, shiftKey: false },
1408
{ timeStamp: 1761.00, state: { value: 'aa각aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' }, type: 'compositionend', data: '각' }
1409
],
1410
final: { value: 'aa각aa', selectionStart: 3, selectionEnd: 3, selectionDirection: 'forward' },
1411
};
1412
1413
const actualOutgoingEvents = await simulateInteraction(recorded);
1414
assert.deepStrictEqual(actualOutgoingEvents, [
1415
{ type: 'compositionStart', data: '' },
1416
{ type: 'type', text: 'ㄱ', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 },
1417
{ type: 'compositionUpdate', data: 'ㄱ' },
1418
{ type: 'type', text: '가', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1419
{ type: 'compositionUpdate', data: '가' },
1420
{ type: 'type', text: '각', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1421
{ type: 'compositionUpdate', data: '각' },
1422
{ type: 'type', text: '각', replacePrevCharCnt: 1, replaceNextCharCnt: 0, positionDelta: 0 },
1423
{ type: 'compositionEnd' }
1424
]);
1425
1426
const actualResultingState = interpretTypeEvents(recorded.env.OS, recorded.env.browser, recorded.initial, actualOutgoingEvents);
1427
assert.deepStrictEqual(actualResultingState, recorded.final);
1428
});
1429
1430
});
1431
1432