| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>focus_test.html</title> | |
| <link rel="stylesheet" href="/filez/_main/third_party/js/qunit/qunit.css"> | |
| <script src="/filez/_main/third_party/js/qunit/qunit.js"></script> | |
| <script src="/filez/_main/third_party/js/qunit/qunit_test_runner.js"></script> | |
| <script src="test_bootstrap.js"></script> | |
| <script type="text/javascript"> | |
| goog.require('bot.action'); | |
| goog.require('bot.test'); | |
| goog.require('goog.Promise'); | |
| goog.require('goog.dom'); | |
| goog.require('goog.events'); | |
| goog.require('goog.events.EventType'); | |
| goog.require('goog.userAgent'); | |
| goog.require('goog.userAgent.product'); | |
| </script> | |
| <script type="text/javascript"> | |
| var events = goog.events; | |
| var eventType = goog.events.EventType; | |
| var text1, text2, actualEvents, expectedEvents; | |
| QUnit.testStart(function() { | |
| actualEvents = []; | |
| expectedEvents = []; | |
| text1 = goog.dom.$('text1'); | |
| text2 = goog.dom.$('text2'); | |
| bot.action.focusOnElement(goog.dom.$('dummyFocusHolder')); | |
| }); | |
| QUnit.testDone(function() { | |
| events.removeAll(text1); | |
| events.removeAll(text2); | |
| }); | |
| function buildRecord(id, type) { | |
| return id + ': ' + type; | |
| } | |
| function recordBlurAndFocus(el) { | |
| events.listen(el, [eventType.FOCUS, eventType.BLUR], function(e) { | |
| actualEvents.push(buildRecord(e.target.id, e.type)); | |
| }); | |
| } | |
| function expect(eventType, onElement) { | |
| expectedEvents.push(buildRecord(onElement.id, eventType)); | |
| } | |
| function assertHasExpectedEventOrder(assert) { | |
| assert.deepEqual(actualEvents, expectedEvents); | |
| } | |
| function assertElementIsActiveElement(assert, expected) { | |
| var actual = goog.dom.getActiveElement(document); | |
| if (actual) { | |
| var msg = 'Expected "' + expected.id + '" to be the activeElement, ' + | |
| 'but was "' + actual.id + '".'; | |
| assert.ok(expected == actual, msg); | |
| } | |
| } | |
| function timeout() { | |
| return new goog.Promise(function(done) { | |
| setTimeout(done, 5); | |
| }); | |
| } | |
| QUnit.test('should be able to focus on an element', function(assert) { | |
| if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8')) { | |
| assert.ok(true, 'Skipping: IE 8+ issue'); | |
| return; | |
| } | |
| if (!bot.test.isWindowFocused()) { | |
| assert.ok(true, 'Skipping: window not focused'); | |
| return; | |
| } | |
| var done = assert.async(); | |
| recordBlurAndFocus(text1); | |
| recordBlurAndFocus(text2); | |
| expect(eventType.FOCUS, text1); | |
| bot.action.focusOnElement(text1); | |
| timeout().then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| assertElementIsActiveElement(assert, text1); | |
| expect(eventType.BLUR, text1); | |
| expect(eventType.FOCUS, text2); | |
| bot.action.focusOnElement(text2); | |
| return timeout(); | |
| }).then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| assertElementIsActiveElement(assert, text2); | |
| expect(eventType.BLUR, text2); | |
| expect(eventType.FOCUS, text1); | |
| bot.action.focusOnElement(text1); | |
| return timeout(); | |
| }).then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| assertElementIsActiveElement(assert, text1); | |
| done(); | |
| }); | |
| }); | |
| QUnit.test('should retain focus if element is already active', function(assert) { | |
| if (!bot.test.isWindowFocused()) { | |
| assert.ok(true, 'Skipping: window not focused'); | |
| return; | |
| } | |
| var done = assert.async(); | |
| recordBlurAndFocus(text1); | |
| recordBlurAndFocus(text2); | |
| expect(eventType.FOCUS, text1); | |
| bot.action.focusOnElement(text1); | |
| timeout().then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| assertElementIsActiveElement(assert, text1); | |
| bot.action.focusOnElement(text1); | |
| return timeout(); | |
| }).then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| assertElementIsActiveElement(assert, text1); | |
| done(); | |
| }); | |
| }); | |
| QUnit.test('should retain focus if first focusable ancestor is already active', function(assert) { | |
| if (!bot.test.isWindowFocused()) { | |
| assert.ok(true, 'Skipping: window not focused'); | |
| return; | |
| } | |
| var done = assert.async(); | |
| var parent = document.getElementById('hideOnBlur'); | |
| var child = document.getElementById('hideOnBlurChild'); | |
| recordBlurAndFocus(parent); | |
| expect(eventType.FOCUS, parent); | |
| bot.action.focusOnElement(parent); | |
| timeout().then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| assertElementIsActiveElement(assert, parent); | |
| bot.action.focusOnElement(child); | |
| return timeout(); | |
| }).then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| assertElementIsActiveElement(assert, parent); | |
| bot.action.focusOnElement(text1); | |
| expect(eventType.BLUR, parent); | |
| return timeout(); | |
| }).then(function() { | |
| assertHasExpectedEventOrder(assert); | |
| done(); | |
| }); | |
| }); | |
| QUnit.test('focus on hidden element throws', function(assert) { | |
| var element = document.getElementById('invisibleText'); | |
| assert.throws(function() { bot.action.focusOnElement(element); }); | |
| }); | |
| QUnit.test('focus on disabled element throws', function(assert) { | |
| var element = document.getElementById('disabledText'); | |
| assert.throws(function() { bot.action.focusOnElement(element); }); | |
| }); | |
| QUnit.test('is focusable', function(assert) { | |
| if (goog.userAgent.IE) { | |
| assert.ok(true, 'Skipping: IE issue'); | |
| return; | |
| } | |
| function assertIsFocusable(element, opt_reason) { | |
| assert.ok(bot.dom.isFocusable(element), opt_reason || ''); | |
| } | |
| function assertNotFocusable(element, opt_reason) { | |
| assert.notOk(bot.dom.isFocusable(element), opt_reason || ''); | |
| } | |
| goog.array.forEach(bot.dom.FOCUSABLE_FORM_FIELDS_, function(tagName) { | |
| assertIsFocusable(goog.dom.createDom(tagName), | |
| tagName + ' is a focusable form field'); | |
| }); | |
| var div = goog.dom.createDom('DIV'); | |
| assertNotFocusable(div); | |
| div.setAttribute('tabindex', '0'); | |
| assertIsFocusable(div, 'has a tab index of 0'); | |
| div.tabIndex = -1; | |
| assert.strictEqual(bot.dom.getAttribute(div, 'tabindex'), '-1'); | |
| assertNotFocusable(div, 'has a negative tab index'); | |
| div.tabIndex = 2; | |
| assert.strictEqual(bot.dom.getAttribute(div, 'tabindex'), '2'); | |
| assertIsFocusable(div, 'has a tab index of 2'); | |
| div.removeAttribute('tabindex'); | |
| assert.strictEqual(bot.dom.getAttribute(div, 'tabindex'), null); | |
| assertNotFocusable(div, 'tab index was removed'); | |
| }); | |
| </script> | |
| </head> | |
| <body> | |
| <div id="qunit"></div> | |
| <div id="qunit-fixture"></div> | |
| <form action="javascript:void(0)"> | |
| <input id="text1" type="text"/> | |
| <input id="text2" type="text"/> | |
| <input id="dummyFocusHolder" type="text"/> | |
| <div> | |
| <div>Input fields for manual testing. Each will report when they receive or | |
| lose focus.</div> | |
| Test 1: <input id="test1" type="text"/> | |
| Test 2: <input id="test2" type="text"/> | |
| <div> | |
| <button id="focusDance">Click to simulate focus change</button> | |
| <button id="clearTestLog">Click to clear log</button> | |
| <button id="opennewwindow" onclick="window.open(window.location)"> | |
| Re-run test in a new window</button> | |
| </div> | |
| <div style="border:1px solid black; margin: 5px; padding: 5px; width:50%"> | |
| <div style="border-bottom: 1px solid black">Test Log</div> | |
| <div id="testLog"></div> | |
| </div> | |
| <div> | |
| <div><strong>Invisible textbox</strong></div> | |
| <input id="invisibleText" type="text" style="visibility:hidden"/> | |
| </div> | |
| <div> | |
| <div><strong>Disabled textbox</strong></div> | |
| <input id="disabledText" type="text" value="I'm disabled" disabled/> | |
| </div> | |
| <div id="hideOnBlur" tabindex="0" | |
| style="width: 100px; height: 100px; border: 1px solid red" | |
| onblur="this.style.display = 'none';"> | |
| <div id="hideOnBlurChild" | |
| style="width: 30px; height: 30px; border: 1px solid blue"> | |
| x | |
| </div> | |
| Focusable. Will hide when focus is lost. | |
| </div> | |
| <script type="text/javascript"> | |
| function log(msg) { | |
| goog.dom.$('testLog').appendChild( | |
| goog.dom.createDom('div', null, | |
| goog.dom.createTextNode(msg))); | |
| } | |
| function reportBlurFocus(e) { | |
| var et = eventType; | |
| goog.events.listen(e, [et.FOCUS, et.BLUR], function(e) { | |
| log(e.type + ' on ' + e.target.id); | |
| }); | |
| } | |
| reportBlurFocus(goog.dom.$('test1')); | |
| reportBlurFocus(goog.dom.$('test2')); | |
| goog.events.listen(goog.dom.$('focusDance'), eventType.CLICK, | |
| function(e) { | |
| log('starting focus dance in .5s'); | |
| log(' will focus on 1, then 2, then 1 again'); | |
| window.setTimeout(function() { | |
| bot.action.focusOnElement(goog.dom.$('test1')); | |
| bot.action.focusOnElement(goog.dom.$('test2')); | |
| bot.action.focusOnElement(goog.dom.$('test1')); | |
| log('if you see this before event logs, then events are ' + | |
| 'fired in a separate event loop'); | |
| }, 500); | |
| }); | |
| goog.events.listen(goog.dom.$('clearTestLog'), eventType.CLICK, | |
| function(e) { | |
| goog.dom.removeChildren(goog.dom.$('testLog')); | |
| }); | |
| </script> | |
| </div> | |
| </form> | |
| </body> | |
| </html> |