| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta http-equiv="Expire" content="0" /> | |
| <title>select_test.html</title> | |
| <script type="text/javascript" src="test_bootstrap.js"></script> | |
| <script type="text/javascript" src="jquery.min.js"></script> | |
| <script type="text/javascript"> | |
| goog.require('bot.action'); | |
| goog.require('bot.locators'); | |
| goog.require('bot.userAgent'); | |
| goog.require('goog.array'); | |
| goog.require('goog.events'); | |
| goog.require('goog.testing.jsunit'); | |
| goog.require('goog.userAgent'); | |
| </script> | |
| <script type="text/javascript"> | |
| var singleSelect, multiSelect, radioButtons, checkboxes, changeCount; | |
| var gotPointerCaptureCount, lostPointerCaptureCount; | |
| function setUpPage() { | |
| singleSelect = bot.locators.findElement({ id: 'singleSelect' }); | |
| multiSelect = bot.locators.findElement({ id: 'multiSelect' }); | |
| checkboxes = bot.locators.findElements({ name: 'checkbox' }); | |
| radioButtons = bot.locators.findElements({ name: 'radio' }); | |
| } | |
| function setUp() { | |
| goog.events.removeAll(singleSelect); | |
| goog.events.removeAll(multiSelect); | |
| for (var i = 0; i < checkboxes.length; ++i) { | |
| goog.events.removeAll(checkboxes[i]); | |
| } | |
| for (var i = 0; i < radioButtons.length; ++i) { | |
| goog.events.removeAll(radioButtons[i]); | |
| } | |
| singleSelect.selectedIndex = 0; | |
| goog.array.forEach(multiSelect.options, function (e) { | |
| e.selected = false; | |
| }); | |
| goog.array.forEach(checkboxes, function (e) { | |
| e.checked = false; | |
| }); | |
| goog.array.forEach(radioButtons, function (e) { | |
| e.checked = false; | |
| }); | |
| } | |
| function checkActionCompatibility(action) { | |
| if (action == bot.action.tap) { | |
| return bot.events.SUPPORTS_TOUCH_EVENTS; | |
| } | |
| return true; | |
| } | |
| function expectChanges(target) { | |
| changeCount = 0; | |
| goog.events.listen(target, 'change', function (e) { | |
| changeCount++; | |
| }); | |
| } | |
| function assertChanges(expectedCount) { | |
| assertEquals('Unexpected number of changes', expectedCount, changeCount); | |
| } | |
| function expectPointerCaptures(target) { | |
| gotPointerCaptureCount = 0; | |
| lostPointerCaptureCount = 0; | |
| goog.events.listen(target, goog.events.EventType.MSGOTPOINTERCAPTURE, | |
| function (e) { | |
| gotPointerCaptureCount++; | |
| }); | |
| goog.events.listen(target, goog.events.EventType.MSLOSTPOINTERCAPTURE, | |
| function (e) { | |
| lostPointerCaptureCount++; | |
| }); | |
| } | |
| function assertPointerCaptures(expectedCount) { | |
| assertEquals('Unexpected number of got captures', expectedCount, | |
| gotPointerCaptureCount); | |
| assertEquals('Unexpected number of lost captures', expectedCount, | |
| lostPointerCaptureCount); | |
| } | |
| function canSelectAnOptionFromASingleSelect(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| assertFalse(bot.dom.isSelected(singleSelect.options[1])); | |
| expectChanges(singleSelect); | |
| if (bot.userAgent.IE_DOC_10) { | |
| expectPointerCaptures(singleSelect); | |
| } | |
| action(singleSelect.options[1]); | |
| assertChanges(1); | |
| if (bot.userAgent.IE_DOC_10) { | |
| assertPointerCaptures(1); | |
| } | |
| assertTrue(bot.dom.isSelected(singleSelect.options[1])); | |
| } | |
| var testClickCanSelectAnOptionFromASingleSelect = | |
| goog.partial(canSelectAnOptionFromASingleSelect, bot.action.click); | |
| var testTapCanSelectAnOptionFromASingleSelect = | |
| goog.partial(canSelectAnOptionFromASingleSelect, bot.action.tap); | |
| function cannotDeselectAnOptionFromASingleSelect(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| assertTrue(bot.dom.isSelected(singleSelect.options[0])); | |
| expectChanges(singleSelect); | |
| action(singleSelect.options[0]); | |
| assertChanges(0); | |
| assertTrue(bot.dom.isSelected(singleSelect.options[0])); | |
| } | |
| var testClickCannotDeselectAnOptionFromASingleSelect = | |
| goog.partial(cannotDeselectAnOptionFromASingleSelect, bot.action.click); | |
| var testTapCannotDeselectAnOptionFromASingleSelect = | |
| goog.partial(cannotDeselectAnOptionFromASingleSelect, bot.action.tap); | |
| function cannotSelectAnOptionFromADisabledSelect(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| singleSelect.disabled = true; | |
| assertFalse(bot.dom.isSelected(singleSelect.options[1])); | |
| expectChanges(singleSelect); | |
| action(singleSelect.options[1]); | |
| assertChanges(0); | |
| assertFalse(bot.dom.isSelected(singleSelect.options[1])); | |
| singleSelect.disabled = false; | |
| } | |
| var testClickCannotSelectAnOptionFromADisabled = | |
| goog.partial(cannotSelectAnOptionFromADisabledSelect, bot.action.click); | |
| var testTapCannotSelectAnOptionFromADisabled = | |
| goog.partial(cannotSelectAnOptionFromADisabledSelect, bot.action.tap); | |
| function canSelectAnEnabledOptionFromASelectWithADisabledOption(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| singleSelect.options[1].disabled = true; | |
| assertFalse(bot.dom.isSelected(singleSelect.options[2])); | |
| expectChanges(singleSelect); | |
| action(singleSelect.options[2]); | |
| assertChanges(1); | |
| assertTrue(bot.dom.isSelected(singleSelect.options[2])); | |
| singleSelect.options[1].disabled = false; | |
| } | |
| var testClickCanSelectAnEnabledOptionFromASelectWithADisabledOption = | |
| goog.partial(canSelectAnEnabledOptionFromASelectWithADisabledOption, | |
| bot.action.click); | |
| var testTapCanSelectAnEnabledOptionFromASelectWithADisabledOption = | |
| goog.partial(canSelectAnEnabledOptionFromASelectWithADisabledOption, | |
| bot.action.tap); | |
| function canToggleOptionsInAMultiSelect(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| multiSelect.options[0].selected = true; | |
| expectChanges(multiSelect); | |
| action(multiSelect.options[0]); | |
| action(multiSelect.options[1]); | |
| action(multiSelect.options[2]); | |
| assertChanges(3); | |
| assertFalse(bot.dom.isSelected(multiSelect.options[0])); | |
| assertTrue(bot.dom.isSelected(multiSelect.options[1])); | |
| assertTrue(bot.dom.isSelected(multiSelect.options[2])); | |
| } | |
| var testClickCanToggleOptionsInAMultiSelect = | |
| // Disabled on Chrome due to | |
| // https://code.google.com/p/chromedriver/issues/detail?id=1073 | |
| // Disabled on Safari due to | |
| // https://github.com/SeleniumHQ/selenium/issues/461 | |
| (goog.userAgent.product.CHROME || goog.userAgent.product.SAFARI) ? | |
| goog.nullFunction : | |
| goog.partial(canToggleOptionsInAMultiSelect, bot.action.click); | |
| var testTapCanToggleOptionsInAMultiSelect = | |
| // Disabled on Chrome due to | |
| // https://code.google.com/p/chromedriver/issues/detail?id=1073 | |
| // Disabled on Safari due to | |
| // https://github.com/SeleniumHQ/selenium/issues/461 | |
| (goog.userAgent.product.CHROME || goog.userAgent.product.SAFARI) ? | |
| goog.nullFunction : | |
| goog.partial(canToggleOptionsInAMultiSelect, bot.action.tap); | |
| function canToggleCheckboxes(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| // IE <= 8 fires a change event on the next click after the change. | |
| checkboxes[0].checked = true; | |
| expectChanges(checkboxes[0]); | |
| action(checkboxes[0]); | |
| assertChanges(1); | |
| if (!bot.userAgent.IE_DOC_PRE9) { | |
| expectChanges(checkboxes[1]); | |
| } | |
| action(checkboxes[1]); | |
| assertChanges(1); | |
| expectChanges(checkboxes[2]); | |
| action(checkboxes[2]); | |
| assertChanges(1); | |
| assertFalse(bot.dom.isSelected(checkboxes[0])); | |
| assertTrue(bot.dom.isSelected(checkboxes[1])); | |
| assertTrue(bot.dom.isSelected(checkboxes[2])); | |
| } | |
| var testClickCanToggleCheckboxes = | |
| goog.partial(canToggleCheckboxes, bot.action.click); | |
| var testTapCanToggleCheckboxes = | |
| goog.partial(canToggleCheckboxes, bot.action.tap); | |
| function canSelectRadioButtons(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| // IE <= 8 fires a change event on the next click after the change. | |
| expectChanges(radioButtons[0]); | |
| action(radioButtons[0]); | |
| assertChanges(1); | |
| assertTrue(bot.dom.isSelected(radioButtons[0])); | |
| if (!bot.userAgent.IE_DOC_PRE9) { | |
| expectChanges(radioButtons[1]); | |
| } | |
| action(radioButtons[1]); | |
| assertChanges(1); | |
| assertTrue(bot.dom.isSelected(radioButtons[1])); | |
| expectChanges(radioButtons[2]); | |
| action(radioButtons[2]); | |
| assertChanges(1); | |
| assertTrue(bot.dom.isSelected(radioButtons[2])); | |
| } | |
| var testClickCanSelectRadioButtons = | |
| goog.partial(canSelectRadioButtons, bot.action.click); | |
| var testTapCanSelectRadioButtons = | |
| goog.partial(canSelectRadioButtons, bot.action.tap); | |
| function cannotDeselectRadioButtons(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| radioButtons[0].checked = true; | |
| expectChanges(radioButtons[0]); | |
| action(radioButtons[0]); | |
| assertChanges(0); | |
| assertTrue(bot.dom.isSelected(radioButtons[0])); | |
| } | |
| var testClickCannotDeselectRadioButtons = | |
| goog.partial(cannotDeselectRadioButtons, bot.action.click); | |
| var testTapCannotDeselectRadioButtons = | |
| goog.partial(cannotDeselectRadioButtons, bot.action.tap); | |
| function cannotSelectDisabledElements(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| function assertCannotSelectWhenDisabled(elem, isOption) { | |
| elem.disabled = true; | |
| expectChanges(isOption ? elem.parentNode : elem); | |
| action(elem); | |
| assertChanges(0); | |
| assertFalse(bot.dom.isSelected(elem)); | |
| elem.disabled = false; | |
| } | |
| assertCannotSelectWhenDisabled(singleSelect.options[1], true); | |
| assertCannotSelectWhenDisabled(multiSelect.options[0], true); | |
| assertCannotSelectWhenDisabled(checkboxes[0], false); | |
| assertCannotSelectWhenDisabled(radioButtons[0], false); | |
| } | |
| var testClickCannotSelectDisabledElements = | |
| goog.partial(cannotSelectDisabledElements, bot.action.click); | |
| var testTapCannotSelectDisabledElements = | |
| goog.partial(cannotSelectDisabledElements, bot.action.tap); | |
| function canSelectOptionWhenDefaultPrevented(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| function assertCanSelectWhenDefaultPrevented(elem) { | |
| goog.events.listen(elem, 'click', function (e) { | |
| e.preventDefault(); | |
| }); | |
| expectChanges(elem.parentNode); | |
| action(elem); | |
| assertChanges(1); | |
| assertTrue(bot.dom.isSelected(elem)); | |
| } | |
| assertCanSelectWhenDefaultPrevented(singleSelect.options[1]); | |
| assertCanSelectWhenDefaultPrevented(multiSelect.options[0]); | |
| } | |
| var testClickCanSelectOptionWhenDefaultPrevented = | |
| // Disabled on Safari due to | |
| // https://github.com/SeleniumHQ/selenium/issues/461 | |
| goog.userAgent.product.SAFARI ? | |
| goog.nullFunction : | |
| goog.partial(canSelectOptionWhenDefaultPrevented, bot.action.click); | |
| var testTapCanSelectOptionWhenDefaultPrevented = | |
| // Disabled on Chrome due to | |
| // https://code.google.com/p/chromedriver/issues/detail?id=1073 | |
| // Disabled on Safari due to | |
| // https://github.com/SeleniumHQ/selenium/issues/461 | |
| (goog.userAgent.product.CHROME || goog.userAgent.product.SAFARI) ? | |
| goog.nullFunction : | |
| goog.partial(canSelectOptionWhenDefaultPrevented, bot.action.tap); | |
| function cannotSelectCheckboxWhenDefaultPrevented(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| var elem = checkboxes[0]; | |
| goog.events.listen(elem, 'click', function (e) { | |
| e.preventDefault(); | |
| }); | |
| expectChanges(elem); | |
| action(elem); | |
| // Some browsers fire change events when the checkbox isn't toggled. | |
| assertChanges(firesChangeEventIfNotToggled() ? 1 : 0); | |
| assertFalse(bot.dom.isSelected(elem)); | |
| } | |
| function firesChangeEventIfNotToggled() { | |
| if (goog.userAgent.product.CHROME && bot.userAgent.isProductVersion(45)) { | |
| // Chrome 45 and above do not fire the change event. | |
| return false; | |
| } else if (goog.userAgent.WEBKIT || bot.userAgent.IE_DOC_9 || | |
| goog.userAgent.EDGE) { | |
| // Other browsers with goog.userAgent.WEBKIT fire the change event. | |
| // IE 9 standards mode fires the change event. | |
| return true; | |
| } | |
| // All other browsers do not fire the change event. | |
| return false; | |
| } | |
| var testClickCannotSelectCheckboxWhenDefaultPrevented = | |
| goog.partial(cannotSelectCheckboxWhenDefaultPrevented, | |
| bot.action.click); | |
| var testTapCannotSelectCheckboxWhenDefaultPrevented = | |
| goog.partial(cannotSelectCheckboxWhenDefaultPrevented, bot.action.tap); | |
| function maybeSelectRadioButtonWhenDefaultPrevented(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| var elem = radioButtons[0]; | |
| goog.events.listen(elem, 'click', function (e) { | |
| e.preventDefault(); | |
| }); | |
| expectChanges(elem); | |
| action(elem); | |
| // Chrome < 39 and WebKit select a radio button even when the handler | |
| // prevents the default; IE does not. | |
| if ((goog.userAgent.product.CHROME && | |
| !bot.userAgent.isProductVersion(39)) || | |
| (goog.userAgent.WEBKIT && !goog.userAgent.product.CHROME)) { | |
| assertChanges(1); | |
| assertTrue(bot.dom.isSelected(elem)); | |
| } else { | |
| // Some browsers fire change events when the checkbox isn't toggled. | |
| assertChanges(firesChangeEventIfNotToggled() ? 1 : 0); | |
| assertFalse(bot.dom.isSelected(elem)); | |
| } | |
| } | |
| var testClickMaybeSelectRadioButtonWhenDefaultPrevented = | |
| goog.partial(maybeSelectRadioButtonWhenDefaultPrevented, | |
| bot.action.click); | |
| var testTapMaybeSelectRadioButtonWhenDefaultPrevented = | |
| goog.partial(maybeSelectRadioButtonWhenDefaultPrevented, | |
| bot.action.tap); | |
| /** @see http://code.google.com/p/selenium/issues/detail?id=1941 */ | |
| function canSelectOptionFromTransparentSelect(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| var style = singleSelect.style; | |
| style['opacity'] = '0'; | |
| style['filter'] = 'alpha(opacity=0)'; | |
| assertEquals(0, bot.dom.getOpacity(singleSelect)); | |
| assertFalse(bot.dom.isSelected(singleSelect.options[1])); | |
| expectChanges(singleSelect); | |
| bot.action.click(singleSelect.options[1]); | |
| assertChanges(1); | |
| assertTrue(bot.dom.isSelected(singleSelect.options[1])); | |
| style['opacity'] = ''; | |
| style['filter'] = ''; | |
| assertEquals(1, bot.dom.getOpacity(singleSelect)); | |
| } | |
| var testClickCanSelectOptionFromTransparentSelect = | |
| goog.partial(canSelectOptionFromTransparentSelect, bot.action.click); | |
| var testTapCanSelectOptionFromTransparentSelect = | |
| goog.partial(canSelectOptionFromTransparentSelect, bot.action.tap); | |
| function noErrorAfterSelectWhenOnChangePreventsDefault(action) { | |
| if (!checkActionCompatibility(action) || | |
| (goog.userAgent.IE && !bot.userAgent.isProductVersion(7))) { | |
| return; | |
| } | |
| goog.events.listen(radioButtons[1], 'change', function (e) { | |
| e.preventDefault(); | |
| }); | |
| action(radioButtons[1]); | |
| assertTrue(bot.dom.isSelected(radioButtons[1])); | |
| // In IE, this second click used to throw an "Unspecified error". | |
| action(radioButtons[2]); | |
| } | |
| var testClickNoErrorAfterSelectWhenOnChangePreventsDefault = | |
| goog.partial(noErrorAfterSelectWhenOnChangePreventsDefault, | |
| bot.action.click); | |
| var testTapNoErrorAfterSelectWhenOnChangePreventsDefault = | |
| goog.partial(noErrorAfterSelectWhenOnChangePreventsDefault, | |
| bot.action.tap); | |
| function selectOptionTriggersJQueryChangeHandler(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| var changes = 0; | |
| $('#singleSelect').change(function () { | |
| changes++; | |
| }); | |
| action(singleSelect.options[1]); | |
| assertEquals(1, changes); | |
| $('#singleSelect').unbind(); | |
| } | |
| var testClickSelectOptionTriggersJQueryChangeHandler = | |
| goog.partial(selectOptionTriggersJQueryChangeHandler, bot.action.click); | |
| var testTapSelectOptionTriggersJQueryChangeHandler = | |
| goog.partial(selectOptionTriggersJQueryChangeHandler, bot.action.tap); | |
| function pointerCaptureFiresOnCheckboxToggle(action) { | |
| if (!bot.userAgent.IE_DOC_10) { | |
| return; | |
| } | |
| checkboxes[0].checked = true; | |
| expectPointerCaptures(checkboxes[0]); | |
| action(checkboxes[0]); | |
| // For input elements, the got/lost pointer capture events only fire for | |
| // a tap and not for mouse clicks. | |
| if (action == bot.action.tap) { | |
| assertPointerCaptures(1); | |
| } else { | |
| assertPointerCaptures(0); | |
| } | |
| assertFalse(bot.dom.isSelected(checkboxes[0])); | |
| } | |
| var testClickPointerCaptureFiresOnCheckboxToggle = | |
| goog.partial(pointerCaptureFiresOnCheckboxToggle, bot.action.click); | |
| var testTapPointerCaptureFiresOnCheckboxToggle = | |
| goog.partial(pointerCaptureFiresOnCheckboxToggle, bot.action.tap); | |
| function togglesByTheTimeChangeEventFires(action) { | |
| if (!checkActionCompatibility(action)) { | |
| return; | |
| } | |
| singleSelect.options[0].selected = true; | |
| checkToggleAtChange(action, singleSelect, singleSelect.options[1]); | |
| multiSelect.options[0].selected = false; | |
| checkToggleAtChange(action, multiSelect, multiSelect.options[0]); | |
| radioButtons[0].checked = false; | |
| checkToggleAtChange(action, radioButtons[0], radioButtons[0]); | |
| checkboxes[0].checked = false; | |
| checkToggleAtChange(action, checkboxes[0], checkboxes[0]); | |
| function checkToggleAtChange(action, targetElem, toggleElem) { | |
| var preState = bot.dom.isSelected(toggleElem); | |
| var postState = null; | |
| goog.events.listen(targetElem, 'change', function (e) { | |
| postState = bot.dom.isSelected(toggleElem); | |
| }); | |
| action(toggleElem); | |
| assertNotNull('change event did not fire', postState); | |
| assertNotEquals('element did not toggle', postState, preState); | |
| } | |
| } | |
| var testClickTogglesByTheTimeChangeEventFires = | |
| // Disabled on Safari due to | |
| // https://github.com/SeleniumHQ/selenium/issues/461 | |
| goog.userAgent.product.SAFARI ? | |
| goog.nullFunction : | |
| goog.partial(togglesByTheTimeChangeEventFires, bot.action.click); | |
| var testTapTogglesByTheTimeChangeEventFires = | |
| // Disabled on Chrome due to | |
| // https://code.google.com/p/chromedriver/issues/detail?id=1073 | |
| // Disabled on Safari due to | |
| // https://github.com/SeleniumHQ/selenium/issues/461 | |
| (goog.userAgent.product.CHROME || goog.userAgent.product.SAFARI) ? | |
| goog.nullFunction : | |
| goog.partial(togglesByTheTimeChangeEventFires, bot.action.tap); | |
| </script> | |
| </head> | |
| <body> | |
| <form action="#" method="get"> | |
| <select id="singleSelect"> | |
| <option id="singleOption1" value="foo">foo</option> | |
| <option id="singleOption2" value="bar">bar</option> | |
| <option id="singleOption3" value="baz">baz</option> | |
| </select> | |
| <br> | |
| <select id="multiSelect" multiple> | |
| <option id="multiOption1" value="apples">apples</option> | |
| <option id="multiOption2" value="oranges">oranges</option> | |
| <option id="multiOption3" value="cherries">cherries</option> | |
| </select> | |
| <br> | |
| <input type="checkbox" name="checkbox" value="peas">peas | |
| <input type="checkbox" name="checkbox" value="spinach">spinach | |
| <input type="checkbox" name="checkbox" value="broccoli">broccoli | |
| <br> | |
| <input type="radio" name="radio" value="cheese">cheese | |
| <input type="radio" name="radio" value="water">water | |
| <input type="radio" name="radio" value="bread">bread | |
| </form> | |
| </body> | |
| </html> |