blob: 9eb4001b9eab218728713197e18ebbe0621439e7 [file] [log] [blame]
<!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>