| <!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> |