blob: 55a53470337bdfe9b3a7d09f746db9b4da3d531a [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Expire" content="0" />
<meta charset="utf-8">
<title>click_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.locators');
goog.require('bot.test');
goog.require('bot.userAgent');
goog.require('goog.Uri');
goog.require('goog.debug.DivConsole');
goog.require('goog.log');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.math.Coordinate');
goog.require('goog.style');
goog.require('goog.userAgent');
</script>
<script type="text/javascript">
var clicker;
var clickLocation;
var divConsole;
var findElement = bot.locators.findElement;
var findElements = bot.locators.findElements;
var log = goog.log.getLogger('click_test');
var FIRST_EVENT_OF_CLICK = goog.userAgent.IE ?
goog.events.EventType.MOUSEMOVE : goog.events.EventType.MOUSEOVER;
var SECOND_EVENT_OF_CLICK = goog.userAgent.IE ?
goog.events.EventType.MOUSEOVER : goog.events.EventType.MOUSEMOVE;
function checkActionCompatibility(action) {
if (action == bot.action.tap) {
return bot.events.SUPPORTS_TOUCH_EVENTS;
}
return true;
}
function resetClicker() {
divConsole.clear();
goog.style.showElement(clicker, true);
goog.events.removeAll(clicker);
goog.events.listen(clicker,
[goog.events.EventType.MOUSEOVER,
goog.events.EventType.MOUSEMOVE,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK,
goog.events.EventType.DBLCLICK,
goog.events.EventType.BLUR,
goog.events.EventType.FOCUS],
function (e) {
goog.log.info(log, e.type);
});
blurClicker();
}
function blurClicker() {
clicker.blur();
goog.dom.$('focusSink').focus();
}
function hideClickerOn(eventType) {
goog.events.listen(clicker, eventType, function () {
goog.style.showElement(clicker, false);
});
}
function assertCoordinatesEqual(assert, expected, actual) {
assert.strictEqual(Math.floor(actual), Math.floor(expected));
}
QUnit.testStart(function() {
blurClicker();
});
QUnit.testDone(function() {
resetClicker();
});
// Helper: shouldNotBeAbleToClickOnAnElementThatIsNotShown
function shouldNotBeAbleToClickOnAnElementThatIsNotShown(assert, action) {
if (!checkActionCompatibility(action)) {
assert.ok(true, 'Action not supported, skipping');
return;
}
var noHeightElement = goog.dom.$('noHeight');
assert.throws(
function() {
action(noHeightElement);
},
function(ex) {
return ex.code === bot.ErrorCode.ELEMENT_NOT_VISIBLE;
},
'Should throw ELEMENT_NOT_VISIBLE error'
);
}
QUnit.test('click should not be able to click on an element that is not shown', function(assert) {
shouldNotBeAbleToClickOnAnElementThatIsNotShown(assert, bot.action.click);
});
QUnit.test('tap should not be able to click on an element that is not shown', function(assert) {
shouldNotBeAbleToClickOnAnElementThatIsNotShown(assert, bot.action.tap);
});
// Helper: shouldClickInTheMiddleOfAnElement
function shouldClickInTheMiddleOfAnElement(assert, action) {
if (!checkActionCompatibility(action)) {
assert.ok(true, 'Action not supported, skipping');
return;
}
var clickerEl = findElement({ id: 'clicker' });
goog.events.removeAll(clickerEl);
var clickedXY = null;
goog.events.listen(clickerEl, goog.events.EventType.MOUSEUP, function (e) {
clickedXY = { x: e.clientX, y: e.clientY };
});
action(clickerEl);
assert.ok(clickedXY !== null, 'Click coordinates should be captured');
var clickSize = goog.style.getSize(clickerEl);
var clickPos = goog.style.getClientPosition(clickerEl);
assertCoordinatesEqual(assert, clickPos.x + (clickSize.width / 2), clickedXY.x);
assertCoordinatesEqual(assert, clickPos.y + (clickSize.height / 2), clickedXY.y);
}
QUnit.test('click should click in the middle of an element', function(assert) {
shouldClickInTheMiddleOfAnElement(assert, bot.action.click);
});
QUnit.test('tap should click in the middle of an element', function(assert) {
shouldClickInTheMiddleOfAnElement(assert, bot.action.tap);
});
// Helper: shouldClickTheCoordofAnElement
function shouldClickTheCoordofAnElement(assert, action) {
if (!checkActionCompatibility(action)) {
assert.ok(true, 'Action not supported, skipping');
return;
}
var clickerEl = findElement({ id: 'clicker' });
goog.events.removeAll(clickerEl);
var clickedXY = null;
goog.events.listen(clickerEl, goog.events.EventType.CLICK, function (e) {
clickedXY = { x: e.clientX, y: e.clientY };
});
var coord = new goog.math.Coordinate(3, 3);
action(clickerEl, coord);
assert.ok(clickedXY !== null, 'Click coordinates should be captured');
var clickPos = goog.style.getClientPosition(clickerEl);
assertCoordinatesEqual(assert, clickPos.x + coord.x, clickedXY.x);
assertCoordinatesEqual(assert, clickPos.y + coord.y, clickedXY.y);
}
QUnit.test('click should click the coord of an element', function(assert) {
shouldClickTheCoordofAnElement(assert, bot.action.click);
});
QUnit.test('tap should click the coord of an element', function(assert) {
shouldClickTheCoordofAnElement(assert, bot.action.tap);
});
QUnit.test('should right click the coord of an element', function(assert) {
var clickerEl = findElement({ id: 'rightClickTarget' });
goog.events.removeAll(clickerEl);
var clickedXY = null;
goog.events.listen(clickerEl, goog.events.EventType.MOUSEDOWN,
function (e) {
clickedXY = { x: e.clientX, y: e.clientY };
});
var coord = new goog.math.Coordinate(3, 3);
bot.action.rightClick(clickerEl, coord);
assert.ok(clickedXY !== null, 'Click coordinates should be captured');
assert.strictEqual(clickerEl.value, 'Right Clicked!');
var clickPos = goog.style.getClientPosition(clickerEl);
assertCoordinatesEqual(assert, clickPos.x + coord.x, clickedXY.x);
assertCoordinatesEqual(assert, clickPos.y + coord.y, clickedXY.y);
});
QUnit.test('should generate the correct click sequence', function(assert) {
var expectedEvents = [
FIRST_EVENT_OF_CLICK,
SECOND_EVENT_OF_CLICK,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK];
var events = [];
goog.events.listen(clicker, expectedEvents, function (e) {
events.push(e.type);
});
bot.action.click(clicker);
assert.deepEqual(events, expectedEvents);
});
QUnit.test('should generate the correct double click sequence', function(assert) {
var expectedEvents = [
FIRST_EVENT_OF_CLICK,
SECOND_EVENT_OF_CLICK,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK,
goog.events.EventType.DBLCLICK];
var events = [];
goog.events.listen(clicker, expectedEvents, function (e) {
events.push(e.type);
});
bot.action.doubleClick(clicker);
assert.deepEqual(events, expectedEvents);
});
// Helper: shouldRespondCorrectlyIfElementIsHiddenMidClickSequence
function shouldRespondCorrectlyIfElementIsHiddenMidClickSequence(assert, action) {
if (!checkActionCompatibility(action)) {
assert.ok(true, 'Action not supported, skipping');
return;
}
if (goog.userAgent.IE) {
assert.ok(true, 'Skipping on IE - test must be async to handle focus events properly');
return;
}
var clickEvents = [
goog.events.EventType.MOUSEMOVE,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.FOCUS,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK
];
function runTest(hideOn, expectedEvents) {
resetClicker();
var events = [];
goog.events.listen(clicker, clickEvents, function (e) {
events.push(e.type);
});
hideClickerOn(hideOn);
assert.ok(bot.dom.isShown(clicker), 'Should start shown');
action(clicker);
assert.notOk(bot.dom.isShown(clicker), 'Should end not shown; hid on: ' + hideOn);
assert.deepEqual(events, expectedEvents);
}
for (var i = 0; i < clickEvents.length; i++) {
var hideOn = clickEvents[i];
var expectedEvents;
if (hideOn == goog.events.EventType.MOUSEUP) {
expectedEvents = clickEvents;
} else {
expectedEvents = goog.array.slice(clickEvents, 0, i + 1);
}
if (!goog.array.contains(expectedEvents, goog.events.EventType.FOCUS) ||
bot.test.isWindowFocused()) {
runTest(hideOn, expectedEvents);
}
}
}
QUnit.test('click should respond correctly if element is hidden mid click sequence', function(assert) {
shouldRespondCorrectlyIfElementIsHiddenMidClickSequence(assert, bot.action.click);
});
QUnit.test('tap should respond correctly if element is hidden mid click sequence', function(assert) {
shouldRespondCorrectlyIfElementIsHiddenMidClickSequence(assert, bot.action.tap);
});
// Helper: cancelledMousedownDoesNotFocus
function cancelledMousedownDoesNotFocus(assert, expectedEvents, action) {
if (!checkActionCompatibility(action)) {
assert.ok(true, 'Action not supported, skipping');
return;
}
var events = [];
goog.events.listen(clicker,
[goog.events.EventType.MOUSEOVER,
goog.events.EventType.MOUSEMOVE,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.FOCUS,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK,
goog.events.EventType.MOUSEOUT],
function (e) {
events.push(e.type);
});
goog.events.listen(clicker, goog.events.EventType.MOUSEDOWN, function (e) {
e.preventDefault();
});
action(clicker);
assert.deepEqual(events, expectedEvents);
}
var expectedTapEvents = [
goog.events.EventType.MOUSEMOVE,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK];
var expectedIETouchEvents = [
goog.events.EventType.MOUSEMOVE,
goog.events.EventType.MOUSEOVER,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK,
goog.events.EventType.MOUSEOUT];
var expectedClickEvents = [
FIRST_EVENT_OF_CLICK,
SECOND_EVENT_OF_CLICK,
goog.events.EventType.MOUSEDOWN,
goog.events.EventType.MOUSEUP,
goog.events.EventType.CLICK];
QUnit.test('click cancelled mousedown does not focus', function(assert) {
cancelledMousedownDoesNotFocus(assert, expectedClickEvents, bot.action.click);
});
QUnit.test('tap cancelled mousedown does not focus', function(assert) {
cancelledMousedownDoesNotFocus(assert,
bot.userAgent.IE_DOC_10 ? expectedIETouchEvents : expectedTapEvents,
bot.action.tap);
});
// Helper: shouldBeAbleToClickOnElementsWithOpacityZero
function shouldBeAbleToClickOnElementsWithOpacityZero(assert, action) {
if (!checkActionCompatibility(action)) {
assert.ok(true, 'Action not supported, skipping');
return;
}
var clickJacker = findElement({ id: 'clickJacker' });
clickJacker.style.opacity = 0;
assert.strictEqual(bot.dom.getOpacity(clickJacker), 0,
'Precondition failed: clickJacker should be transparent');
action(clickJacker);
assert.strictEqual(bot.dom.getOpacity(clickJacker), 1);
}
QUnit.test('click should be able to click on elements with opacity zero', function(assert) {
shouldBeAbleToClickOnElementsWithOpacityZero(assert, bot.action.click);
});
QUnit.test('tap should be able to click on elements with opacity zero', function(assert) {
shouldBeAbleToClickOnElementsWithOpacityZero(assert, bot.action.tap);
});
// Helper: shouldNotBeAbleToClickOnElementWithPointerEventsNone
function shouldNotBeAbleToClickOnElementWithPointerEventsNone(assert, action) {
if (!checkActionCompatibility(action) || goog.userAgent.IE ||
(goog.userAgent.GECKO && !bot.userAgent.isEngineVersion('1.9.2'))) {
assert.ok(true, 'Skipping on unsupported browser');
return;
}
var el = findElement({ id: 'pointerEvents' });
action(el);
assert.strictEqual(bot.dom.getVisibleText(el), 'Pass');
}
QUnit.test('click should not be able to click on element with pointer events none', function(assert) {
shouldNotBeAbleToClickOnElementWithPointerEventsNone(assert, bot.action.click);
});
QUnit.test('tap should not be able to click on element with pointer events none', function(assert) {
shouldNotBeAbleToClickOnElementWithPointerEventsNone(assert, bot.action.tap);
});
QUnit.test('click should not scroll when element in view', function(assert) {
if (bot.userAgent.MOBILE) {
assert.ok(true, 'Skipping on mobile');
return;
}
var target = findElement({ id: 'targetInView' });
var oldPos = goog.style.getClientPosition(target);
bot.action.click(target);
var newPos = goog.style.getClientPosition(target);
assert.deepEqual(newPos, oldPos);
});
QUnit.test('click should scroll when element not in view', function(assert) {
if (bot.userAgent.MOBILE) {
assert.ok(true, 'Skipping on mobile');
return;
}
var target = findElement({ id: 'targetNotInView' });
var oldPos = goog.style.getClientPosition(target);
bot.action.click(target);
if (!bot.userAgent.SAFARI_6) {
var newPos = goog.style.getClientPosition(target);
assert.notStrictEqual(newPos.y, oldPos.y);
} else {
assert.ok(true, 'Safari 6 scroll verification skipped');
}
window.scrollTo(0, 0);
});
</script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<form action="javascript:void(0)">
<label for="focusSink">Focus sink:</label><input id="focusSink" type="text" /><br />
<label for="clicker">Click me:</label><input id="clicker" type="checkbox" /><br />
<div id="log"></div>
<script type="text/javascript">
clicker = goog.dom.$('clicker');
clickLocation = goog.style.getBounds(clicker);
divConsole = new goog.debug.DivConsole(goog.dom.$('log'));
divConsole.setCapturing(true);
</script>
</form>
<img id="noHeight" style="height:0;" src="testdata/map.png" />
<input id="disabled" type="text" value="I'm disabled" disabled />
<a id="clickTarget">Click me!</a>
<input id="rightClickTarget" type="text" oncontextmenu="this.value='Right Clicked!'; return false;" />
<div>
<div id="clickJacker" style="position:absolute;float:left;
width:200px;height:100px; padding:10px;
background-color:cyan;
border:1px solid cyan;">Click jacked!</div>
<div style="width:200px; height:100px;
border:1px solid black; padding:10px">Click Me</div>
<script type="text/javascript">
var clickJacker = document.getElementById('clickJacker');
function setOpacity(opacity) {
clickJacker.style.opacity = opacity;
}
setOpacity(0);
goog.events.listen(clickJacker, goog.events.EventType.CLICK,
function (e) {
setOpacity(1);
});
</script>
</div>
<p id="pointerEvents" onclick="this.textContent='Error';" style="pointer-events: none;">Pass</p>
<iframe id="iframe" src="testdata/click_iframe.html">
</iframe>
<div id="targetInView" style="position: absolute; top: 10px; left: 10px">target in view</div>
<div id="targetNotInView" style="position: absolute; top: 10000px">target not in view</div>
</body>
</html>