@@ -26,11 +26,13 @@ const Plugin = goog.require('goog.editor.Plugin');
26
26
const Range = goog . require ( 'goog.dom.Range' ) ;
27
27
const SafeHtml = goog . require ( 'goog.html.SafeHtml' ) ;
28
28
const TagName = goog . require ( 'goog.dom.TagName' ) ;
29
+ const TestEvent = goog . require ( 'goog.testing.events.Event' ) ;
29
30
const classlist = goog . require ( 'goog.dom.classlist' ) ;
30
31
const editorRange = goog . require ( 'goog.editor.range' ) ;
31
32
const events = goog . require ( 'goog.events' ) ;
32
33
const functions = goog . require ( 'goog.functions' ) ;
33
34
const googDom = goog . require ( 'goog.dom' ) ;
35
+ const platform = goog . require ( 'goog.labs.userAgent.platform' ) ;
34
36
const recordFunction = goog . require ( 'goog.testing.recordFunction' ) ;
35
37
const testSuite = goog . require ( 'goog.testing.testSuite' ) ;
36
38
const testingDom = goog . require ( 'goog.testing.dom' ) ;
@@ -181,7 +183,8 @@ function doTestPlaceCursorAtStart(html = undefined, parentId = undefined) {
181
183
182
184
let startNode = parentId ?
183
185
editableField . getEditableDomHelper ( ) . getElement ( parentId ) . firstChild :
184
- textNode ? textNode : editableField . getElement ( ) ;
186
+ textNode ? textNode :
187
+ editableField . getElement ( ) ;
185
188
assertEquals (
186
189
'The range should start at the specified expected node' , startNode ,
187
190
range . getStartNode ( ) ) ;
@@ -239,13 +242,14 @@ function doTestPlaceCursorAtEnd(
239
242
240
243
const endNode = parentId ?
241
244
editableField . getEditableDomHelper ( ) . getElement ( parentId ) . lastChild :
242
- textNode ? textNode : editableField . getElement ( ) ;
245
+ textNode ? textNode :
246
+ editableField . getElement ( ) ;
243
247
assertEquals (
244
248
'The range should end at the specified expected node' , endNode ,
245
249
range . getEndNode ( ) ) ;
246
- const offset = ( opt_offset != null ) ?
247
- opt_offset :
248
- textNode ? endNode . nodeValue . length : endNode . childNodes . length - 1 ;
250
+ const offset = ( opt_offset != null ) ? opt_offset :
251
+ textNode ? endNode . nodeValue . length :
252
+ endNode . childNodes . length - 1 ;
249
253
if ( hasBogusNode ) {
250
254
assertEquals (
251
255
'The range should end at the ending of the bogus node ' +
@@ -1425,13 +1429,101 @@ testSuite({
1425
1429
1426
1430
assertTrue ( Field . isGeneratingKey_ ( regularKeyEvent , true ) ) ;
1427
1431
assertFalse ( Field . isGeneratingKey_ ( ctrlKeyEvent , true ) ) ;
1428
- if ( userAgent . WINDOWS && ! userAgent . GECKO ) {
1432
+ if ( ( userAgent . WINDOWS || platform . isAndroid ( ) ) && ! userAgent . GECKO ) {
1429
1433
assertTrue ( Field . isGeneratingKey_ ( imeKeyEvent , false ) ) ;
1430
1434
} else {
1431
1435
assertFalse ( Field . isGeneratingKey_ ( imeKeyEvent , false ) ) ;
1432
1436
}
1433
1437
} ,
1434
1438
1439
+ testRegularKeyDispatchesDelayedChange ( ) {
1440
+ if ( userAgent . GECKO ) {
1441
+ // Gecko based browsers handle changes via mutation events
1442
+ return ;
1443
+ }
1444
+ if ( userAgent . WINDOWS || platform . isAndroid ( ) ) {
1445
+ // Windows and Android platforms do not emit events with 'regular' key
1446
+ // codes.
1447
+ return ;
1448
+ }
1449
+ const editableField = new FieldConstructor ( 'testField' ) ;
1450
+ const clock = new MockClock ( true ) ;
1451
+ const delayedChanges = recordFunction ( ) ;
1452
+
1453
+ editableField . makeEditable ( ) ;
1454
+ events . listen ( editableField , Field . EventType . DELAYEDCHANGE , delayedChanges ) ;
1455
+
1456
+ testingEvents . fireKeySequence ( editableField . getElement ( ) , KeyCodes . A ) ;
1457
+ clock . tick ( 1000 ) ;
1458
+
1459
+ if ( ! ( userAgent . WINDOWS || platform . isAndroid ( ) ) || userAgent . GECKO ) {
1460
+ assertEquals (
1461
+ 'Delayed change event should\'ve been dispatched' , 1 ,
1462
+ delayedChanges . getCallCount ( ) ) ;
1463
+ }
1464
+
1465
+ clock . dispose ( ) ;
1466
+ editableField . dispose ( ) ;
1467
+ } ,
1468
+
1469
+ testImeKeyDispatchesDelayedChange ( ) {
1470
+ if ( BrowserFeature . USE_MUTATION_EVENTS ) {
1471
+ // Gecko based browsers handle changes via mutation events
1472
+ return ;
1473
+ }
1474
+ if ( ! ( userAgent . WINDOWS || platform . isAndroid ( ) ) ) {
1475
+ // Only Windows and Android platforms emit these IME-specific events.
1476
+ return ;
1477
+ }
1478
+ const editableField = new FieldConstructor ( 'testField' ) ;
1479
+ const clock = new MockClock ( true ) ;
1480
+ const delayedChanges = recordFunction ( ) ;
1481
+
1482
+ editableField . makeEditable ( ) ;
1483
+ events . listen ( editableField , Field . EventType . DELAYEDCHANGE , delayedChanges ) ;
1484
+
1485
+ testingEvents . fireKeySequence ( editableField . getElement ( ) , KeyCodes . WIN_IME ) ;
1486
+ clock . tick ( 1000 ) ;
1487
+
1488
+ assertEquals (
1489
+ 'Delayed change event should\'ve been dispatched' , 1 ,
1490
+ delayedChanges . getCallCount ( ) ) ;
1491
+
1492
+
1493
+ clock . dispose ( ) ;
1494
+ editableField . dispose ( ) ;
1495
+ } ,
1496
+
1497
+ testInputEventDispatchesDelayedChange ( ) {
1498
+ if ( BrowserFeature . USE_MUTATION_EVENTS ) {
1499
+ // Gecko based browsers handle changes via mutation events
1500
+ return ;
1501
+ }
1502
+ const editableField = new FieldConstructor ( 'testField' ) ;
1503
+ const clock = new MockClock ( true ) ;
1504
+ const delayedChanges = recordFunction ( ) ;
1505
+
1506
+ editableField . makeEditable ( ) ;
1507
+ events . listen ( editableField , Field . EventType . DELAYEDCHANGE , delayedChanges ) ;
1508
+
1509
+ // Non-typing changes on some devices rely on emitting an INPUT event, such
1510
+ // as:
1511
+ // - swipe-typing a word (on iOS)
1512
+ // - accepting a word prediction (on iOS)
1513
+ // - using speech to text (on iOS)
1514
+ // - accepting a spellcheck suggestion (on iOS, Android or Desktop)
1515
+ testingEvents . fireBrowserEvent (
1516
+ new TestEvent ( EventType . INPUT , editableField . getElement ( ) ) ) ;
1517
+ clock . tick ( 1000 ) ;
1518
+
1519
+ assertEquals (
1520
+ 'Delayed change event should\'ve been dispatched' , 1 ,
1521
+ delayedChanges . getCallCount ( ) ) ;
1522
+
1523
+ clock . dispose ( ) ;
1524
+ editableField . dispose ( ) ;
1525
+ } ,
1526
+
1435
1527
testSetEditableClassName ( ) {
1436
1528
const element = googDom . getElement ( 'testField' ) ;
1437
1529
const editableField = new FieldConstructor ( 'testField' ) ;
0 commit comments