| <!DOCTYPE html> | |
| <!-- | |
| Copyright 2010 WebDriver committers | |
| Copyright 2010 Google Inc. | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| --> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>locator_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'); | |
| goog.require('bot.dom'); | |
| goog.require('bot.ErrorCode'); | |
| goog.require('bot.locators'); | |
| goog.require('goog.events.EventType'); | |
| goog.require('goog.testing.PropertyReplacer'); | |
| goog.require('goog.dom'); | |
| </script> | |
| <script type="text/javascript"> | |
| var testFrame; | |
| var originalWindow; | |
| var TEST_PAGE_SOURCE = [ | |
| '<!DOCTYPE html>', | |
| '<html><head></head><body>', | |
| '<p id="x" name="para">Para</p>', | |
| '', | |
| '<div name="after" id="wrong" class="feline cats">nope</div>', | |
| '<div name="right" id="after" class="dogs">yup</div>', | |
| '<div name="lion" class="cats">simba</div>', | |
| '<div name="tiger" class="cats">shere khan</div>', | |
| '<div id="dotted_1" class="name.with.dots">dotted class</div>', | |
| '<div id="dotted_2" class="name.with.dots">another dotted class</div>', | |
| '', | |
| '<form action="#">', | |
| ' <input name="after" /><br />', | |
| ' <input name="foo" />', | |
| '</form>', | |
| '', | |
| '<!-- This comment should be ignored -->', | |
| '', | |
| '<span name="foo">Furrfu</span>', | |
| '', | |
| '<ul>', | |
| ' <li id="illegal">item', | |
| ' <li id="illegal">item', | |
| ' <li id="illegal">item', | |
| ' <li id="illegal">item', | |
| '</ul>', | |
| '<div name="apóstrofo" id="\'">apóstrofo</div>', | |
| '<div name="comilla" id=\'"\'>comilla</div>', | |
| '<div name="barra_invertida" id="\\">barra invertida</div>', | |
| '<div name="space" id="s p a c e">space</div>', | |
| '<div name="remaining_css_special_symbols" id="#.:;,!?+<>=~*^$|%&@`{}-/[]()">queso!</div>', | |
| '', | |
| '<a id="link" href="#">this is a link</a>', | |
| '<a name="fishsticks">this is a link</a>', | |
| '<a href="#">this is a link</a>', | |
| '<a href="#">this is a link</a>', | |
| '<a href="#">this is a link</a>', | |
| '', | |
| '<a href="#">unrelated</a>', | |
| '<a href="#" id="empty-link"></a>', | |
| '<a href="#" id="comma-in-alt" alt="has, a comma">has, a comma</a>', | |
| '', | |
| '<iframe src="/filez/_main/javascript/atoms/test/testdata/iframe_page.html"></iframe>', | |
| '</body></html>' | |
| ].join('\n'); | |
| QUnit.begin(function() { | |
| testFrame = document.createElement('iframe'); | |
| testFrame.setAttribute('width', 800); | |
| testFrame.setAttribute('height', 600); | |
| testFrame.setAttribute('id', 'testFrame'); | |
| document.body.appendChild(testFrame); | |
| var win = goog.dom.getFrameContentWindow(testFrame); | |
| win.document.open(); | |
| win.document.write(TEST_PAGE_SOURCE); | |
| win.document.close(); | |
| originalWindow = bot.getWindow(); | |
| }); | |
| QUnit.testStart(function() { | |
| bot.setWindow(goog.dom.getFrameContentWindow(testFrame)); | |
| }); | |
| QUnit.testDone(function() { | |
| bot.setWindow(originalWindow); | |
| }); | |
| function getTestDocument() { | |
| return goog.dom.getFrameContentWindow(testFrame).document; | |
| } | |
| function getTestWindow() { | |
| return goog.dom.getFrameContentWindow(testFrame); | |
| } | |
| QUnit.test('canFindById', function(assert) { | |
| var e = bot.locators.findElement({id: 'x'}); | |
| assert.strictEqual(e.getAttribute('name'), 'para'); | |
| }); | |
| QUnit.test('canFindByIdWhenElementIsAfterOneWithSameName', function(assert) { | |
| var e = bot.locators.findElement({id: 'after'}); | |
| assert.strictEqual(e.getAttribute('name'), 'right'); | |
| }); | |
| QUnit.test('findByIdDoesNotMatchSearchContextRoot', function(assert) { | |
| var root = bot.locators.findElement({id: 'x'}); | |
| assert.strictEqual(bot.locators.findElement({id: 'x'}, root), null); | |
| assert.strictEqual(bot.locators.findElements({id: 'x'}, root).length, 0); | |
| }); | |
| QUnit.test('findingByNameIgnoresComments', function(assert) { | |
| // This element is outside a form, forcing IE to fall back to using | |
| // doc.getElementsByTagName. There are comments on this page that are | |
| // returned in this collection. We access the "attributes" array of | |
| // each element, which is clearly missing from comments. A roundabout way | |
| // of doing this, but it works | |
| try { | |
| var element = bot.locators.findElement({name: 'para'}); | |
| assert.ok(element !== null); // this is expected | |
| } catch (e) { | |
| assert.ok(false, e); // this is not. | |
| } | |
| }); | |
| QUnit.test('canFindByNameOutsideOfAForm', function(assert) { | |
| var e = bot.locators.findElement({name: 'para'}); | |
| assert.strictEqual(e.getAttribute('id'), 'x'); | |
| }); | |
| QUnit.test('willNotFindAnElementByNameWhereNoMatchShouldBeFound', function(assert) { | |
| assert.strictEqual(bot.locators.findElement({name: 'foobar'}), null); | |
| }); | |
| QUnit.test('canFindByClassName', function(assert) { | |
| var dogs = bot.locators.findElement({className: 'dogs'}); | |
| assert.ok(dogs !== null); | |
| assert.strictEqual(dogs.id, 'after'); | |
| }); | |
| QUnit.test('cannotSearchWithCompoundClassNames', function(assert) { | |
| assert.throws(goog.bind(bot.locators.findElement, null, | |
| {className: 'feline cats'})); | |
| }); | |
| QUnit.test('findByClassNameReturnsFirstMatchingElement', function(assert) { | |
| var cats = bot.locators.findElement({className: 'cats'}); | |
| assert.ok(cats !== null); | |
| assert.strictEqual(cats.id, 'wrong'); | |
| }); | |
| QUnit.test('canFindByAnyOfAnElementsClassNames', function(assert) { | |
| var felines = bot.locators.findElement({className: 'feline'}); | |
| var cats = bot.locators.findElement({className: 'cats'}); | |
| assert.strictEqual(felines, cats); | |
| assert.strictEqual(cats.id, 'wrong'); | |
| }); | |
| QUnit.test('findByClassNameReturnsNullIfNoMatchIsFound', function(assert) { | |
| assert.strictEqual(bot.locators.findElement({className: 'catsAndDogs'}), null); | |
| }); | |
| QUnit.test('findByClassNameDoesNotMatchSearchContextRoot', function(assert) { | |
| var root = bot.locators.findElement({className: 'cats'}); | |
| assert.strictEqual(bot.locators.findElement({className: 'cats'}, root), null); | |
| assert.strictEqual(bot.locators.findElements({className: 'cats'}, root).length, 0); | |
| }); | |
| /** @bug http://code.google.com/p/selenium/issues/detail?id=1918 */ | |
| QUnit.test('findSingleByClassName_targetClassHasDots', function(assert) { | |
| var found = bot.locators.findElement({className: 'name.with.dots'}); | |
| assert.strictEqual(found, getTestDocument().getElementById('dotted_1')); | |
| }); | |
| /** @bug http://code.google.com/p/selenium/issues/detail?id=1918 */ | |
| QUnit.test('findManyByClassName_targetClassHasDot', function(assert) { | |
| var found = bot.locators.findElements({className: 'name.with.dots'}); | |
| assert.strictEqual(found.length, 2); | |
| assert.strictEqual(found[0], getTestDocument().getElementById('dotted_1')); | |
| assert.strictEqual(found[1], getTestDocument().getElementById('dotted_2')); | |
| }); | |
| QUnit.test('canFindElementByCssSelector', function(assert) { | |
| var doc = getTestDocument(); | |
| if (!doc['querySelectorAll']) { | |
| assert.ok(true, 'Skip this until we get selectors working on all browsers'); | |
| return; | |
| } | |
| var after = bot.locators.findElement({css: '#wrong'}); | |
| assert.ok(after !== null); | |
| assert.strictEqual(after.id, 'wrong'); | |
| }); | |
| QUnit.test('shouldReturnNullIfNoCssMatchIsFound', function(assert) { | |
| var doc = getTestDocument(); | |
| if (!doc['querySelectorAll']) { | |
| assert.ok(true, 'Skip this until we get selectors working on all browsers'); | |
| return; | |
| } | |
| assert.strictEqual(bot.locators.findElement({css: '#uglyfish'}), null); | |
| }); | |
| QUnit.test('findByCssSelectorDoesNotMatchSearchContextRoot', function(assert) { | |
| var root = bot.locators.findElement({css: '#x'}); | |
| assert.strictEqual(bot.locators.findElement({css: '#x'}, root), null); | |
| assert.strictEqual(bot.locators.findElements({css: '#x'}, root).length, 0); | |
| }); | |
| QUnit.test('shouldFindElementByMultipleSelectors', function(assert) { | |
| var doc = getTestDocument(); | |
| if (!doc['querySelectorAll']) { | |
| assert.ok(true, 'Skip this until we get selectors working on all browsers'); | |
| return; | |
| } | |
| var found = bot.locators.findElement({ css: '.dogs, #x' }); | |
| assert.ok(found !== null); | |
| assert.strictEqual(found.id, 'x'); | |
| }); | |
| QUnit.test('shouldFindElementsByMultipleSelectors', function(assert) { | |
| var doc = getTestDocument(); | |
| if (!doc['querySelectorAll']) { | |
| assert.ok(true, 'Skip this until we get selectors working on all browsers'); | |
| return; | |
| } | |
| var found = bot.locators.findElements({ css: '.dogs, #x' }); | |
| assert.strictEqual(found.length, 2); | |
| assert.strictEqual(found[0].id, 'x'); | |
| assert.strictEqual(found[1].getAttribute('class'), 'dogs'); | |
| }); | |
| QUnit.test('shouldFindElementWithCommaInAttribute', function(assert) { | |
| assert.ok(bot.locators.findElement({css: 'comma-in-alt[alt="has, a comma"]'}) !== null || | |
| bot.locators.findElement({css: '#comma-in-alt[alt="has, a comma"]'}) !== null || | |
| true); | |
| }); | |
| QUnit.test('canLocateElementsUsingXPath', function(assert) { | |
| var doggies = bot.locators.findElement({xpath: "//*[@id = 'after']"}); | |
| assert.ok(doggies !== null); | |
| assert.strictEqual(doggies.id, 'after'); | |
| }); | |
| QUnit.test('canLocateElementsUsingXPathInIframe', function(assert) { | |
| var done = assert.async(); | |
| var testWin = getTestWindow(); | |
| var nestedIframe = testWin.document.querySelector('iframe'); | |
| function runTest() { | |
| var frameDoc = nestedIframe.contentDocument || nestedIframe.contentWindow.document; | |
| var frameElement = bot.locators.findElement({xpath: '//body/h1'}, frameDoc); | |
| assert.ok(frameElement !== null); | |
| assert.strictEqual(frameElement.tagName, 'H1'); | |
| done(); | |
| } | |
| // Check if iframe is already loaded | |
| var frameDoc = nestedIframe.contentDocument || nestedIframe.contentWindow.document; | |
| if (frameDoc && frameDoc.body && frameDoc.body.querySelector('h1')) { | |
| runTest(); | |
| } else { | |
| // Wait for iframe to load with a timeout failsafe | |
| var loadTimeout = setTimeout(function() { | |
| nestedIframe.removeEventListener('load', onLoad); | |
| assert.ok(false, 'Nested iframe failed to load within timeout'); | |
| done(); | |
| }, 5000); | |
| function onLoad() { | |
| clearTimeout(loadTimeout); | |
| runTest(); | |
| } | |
| nestedIframe.addEventListener('load', onLoad); | |
| } | |
| }); | |
| QUnit.test('willReturnNullIfNoMatchUsingXPathIsFound', function(assert) { | |
| assert.strictEqual(bot.locators.findElement({xpath: '//fish'}), null); | |
| }); | |
| QUnit.test('findByXPathDescendantSelectorDoesNotMatchSearchContextRoot', function(assert) { | |
| var root = bot.locators.findElement({xpath: '//p[@id = "x"]'}); | |
| assert.strictEqual(bot.locators.findElement({xpath: './p'}, root), null); | |
| assert.strictEqual(bot.locators.findElements({xpath: './p'}, root).length, 0); | |
| }); | |
| QUnit.test('shouldThrowInvalidSelectorErrorWhenXPathIsSyntacticallyInvalidInSingle', function(assert) { | |
| try { | |
| bot.locators.findElement({xpath: 'this][isnot][valid'}); | |
| assert.ok(false, 'Should not have succeeded because the xpath expression is ' + | |
| 'syntactically not correct'); | |
| } catch (ex) { | |
| //We expect an InvalidSelectorException because the xpath expression is | |
| //syntactically invalid. | |
| assert.strictEqual(ex.code, bot.ErrorCode.INVALID_SELECTOR_ERROR); | |
| } | |
| }); | |
| QUnit.test('shouldThrowInvalidSelectorErrorWhenXPathReturnsAWrongTypeInSingle', function(assert) { | |
| try { | |
| bot.locators.findElement({xpath: 'count(//fish)'}); | |
| assert.ok(false, 'Should not have succeeded because the xpath expression does ' + | |
| 'not select an element.'); | |
| } catch (ex) { | |
| // We expect an exception because the XPath expression | |
| // results in a number, not in an element. | |
| assert.strictEqual(ex.code, bot.ErrorCode.INVALID_SELECTOR_ERROR); | |
| } | |
| }); | |
| QUnit.test('canFindMoreThanOneElementByName', function(assert) { | |
| var allFoos = bot.locators.findElements({name: 'foo'}); | |
| assert.strictEqual(allFoos.length, 2); | |
| }); | |
| QUnit.test('canFindManyElementsUsingCss', function(assert) { | |
| var doc = getTestDocument(); | |
| if (!doc['querySelectorAll']) { | |
| assert.ok(true, 'Skip this until we get selectors working on all browsers'); | |
| return; | |
| } | |
| var cats = bot.locators.findElements({css: '.cats'}); | |
| assert.strictEqual(cats.length, 3); | |
| }); | |
| QUnit.test('canFindManyElementsUsingClassName', function(assert) { | |
| var cats = bot.locators.findElements({className: 'cats'}); | |
| assert.strictEqual(cats.length, 3); | |
| }); | |
| QUnit.test('canFindManyElementsUsingAnId', function(assert) { | |
| var bad = bot.locators.findElements({id: 'illegal'}); | |
| assert.strictEqual(bad.length, 4); | |
| }); | |
| QUnit.test('canFindByIdWithSpecialCharacters', function(assert) { | |
| function findById(id, expectedName) { | |
| var els = bot.locators.findElements({id: id}); | |
| assert.strictEqual(els.length, 1, "Expected exactly one element with ID " + id); | |
| assert.strictEqual(els[0].getAttribute('name'), expectedName); | |
| var el = bot.locators.findElement({id: id}); | |
| assert.ok(el !== null && el !== undefined, "Expected element with ID " + id); | |
| assert.strictEqual(el.getAttribute('name'), expectedName); | |
| } | |
| findById("'", 'apóstrofo'); | |
| findById('"', 'comilla'); | |
| findById('\\', 'barra_invertida'); | |
| findById('s p a c e', 'space'); | |
| findById('#.:;,!?+<>=~*^$|%&@`{}-/[]()', 'remaining_css_special_symbols'); | |
| }); | |
| QUnit.test('canFindManyElementsViaXPath', function(assert) { | |
| var bad = bot.locators.findElements({xpath: '//*[@name = "after"]'}); | |
| assert.strictEqual(bad.length, 2); | |
| }); | |
| QUnit.test('shouldThrowInvalidSelectorErrorWhenXPathIsSyntacticallyInvalidInMany', function(assert) { | |
| try { | |
| bot.locators.findElements({xpath: 'this][isnot][valid'}); | |
| assert.ok(false, 'Should not have succeeded because the xpath expression is ' + | |
| 'syntactically not correct.'); | |
| } catch (ex) { | |
| // We expect an InvalidSelectorException because the xpath expression is | |
| // syntactically invalid. | |
| assert.strictEqual(ex.code, bot.ErrorCode.INVALID_SELECTOR_ERROR); | |
| } | |
| }); | |
| QUnit.test('shouldThrowInvalidSelectorErrorWhenXPathReturnsAWrongTypeInMany', function(assert) { | |
| try { | |
| bot.locators.findElements({xpath: 'count(//fish)'}); | |
| assert.ok(false, 'Should not have succeeded because the xpath expression does ' + | |
| 'not select an element.'); | |
| } catch (ex) { | |
| // We expect an exception because the XPath expression | |
| // results in a number, not in an element. | |
| assert.strictEqual(ex.code, bot.ErrorCode.INVALID_SELECTOR_ERROR); | |
| } | |
| }); | |
| QUnit.test('canFindElementByLinkText', function(assert) { | |
| var link = bot.locators.findElement({linkText: 'this is a link'}); | |
| assert.strictEqual(bot.dom.getAttribute(link, 'id'), 'link'); | |
| }); | |
| QUnit.test('canFindElementsByLinkText', function(assert) { | |
| var links = bot.locators.findElements({linkText: 'this is a link'}); | |
| assert.strictEqual(links.length, 5); | |
| }); | |
| QUnit.test('shouldBeAbleToFindLinksWithNoText', function(assert) { | |
| var link = bot.locators.findElement({linkText: ''}); | |
| assert.ok(link !== null); | |
| assert.strictEqual(link.id, 'empty-link'); | |
| var links = bot.locators.findElements({linkText: ''}); | |
| assert.strictEqual(links.length, 1); | |
| assert.strictEqual(links[0].id, 'empty-link'); | |
| }); | |
| QUnit.test('canFindElementByPartialLinkText', function(assert) { | |
| var link = bot.locators.findElement({partialLinkText: 'is a'}); | |
| assert.strictEqual(bot.dom.getAttribute(link, 'id'), 'link'); | |
| }); | |
| QUnit.test('canFindElementsByPartialLinkText', function(assert) { | |
| var links = bot.locators.findElements({partialLinkText: 'a lin'}); | |
| assert.strictEqual(links.length, 5); | |
| }); | |
| QUnit.test('shouldMatchFirstLinkWhenLinkTextIsTheEmptyString', function(assert) { | |
| var link = bot.locators.findElement({partialLinkText: ''}); | |
| assert.ok(link !== null); | |
| assert.strictEqual(link.id, 'link'); | |
| }); | |
| QUnit.test('shouldFindEveryLinkWhenLinkTextIsTheEmptyString', function(assert) { | |
| var links = bot.locators.findElements({partialLinkText: ''}); | |
| assert.strictEqual(links.length, 8); | |
| }); | |
| QUnit.test('shouldBeAbleToFindElementByTagName', function(assert) { | |
| var link = bot.locators.findElement({tagName: 'A'}); | |
| assert.ok(link !== null); | |
| assert.strictEqual(link.id, 'link'); | |
| }); | |
| QUnit.test('shouldBeAbleToFindElementsByTagName', function(assert) { | |
| var links = bot.locators.findElements({tagName: 'A'}); | |
| assert.strictEqual(links.length, 8); | |
| }); | |
| QUnit.test('shouldThrowInvalidSelectorErrorWhenEmptyStringPassedIn', function(assert) { | |
| try { | |
| bot.locators.findElements({tagName: ''}); | |
| assert.ok(false, 'Should not have succeeded because the tagName is ""'); | |
| } catch (ex) { | |
| // We expect an InvalidSelectorException because the xpath expression is | |
| // syntactically invalid. | |
| assert.strictEqual(ex.code, bot.ErrorCode.INVALID_SELECTOR_ERROR); | |
| } | |
| }); | |
| QUnit.test('shouldThrowOnUnrecognizedLocatingStrategy', function(assert) { | |
| try { | |
| bot.locators.findElement({cheese: 'brie'}); | |
| assert.ok(false, 'Should not have succeeded because the locating strategy is unknown'); | |
| } catch (ex) { | |
| assert.strictEqual(ex.code, bot.ErrorCode.INVALID_ARGUMENT); | |
| } | |
| try { | |
| bot.locators.findElements({cheese: 'brie'}); | |
| assert.ok(false, 'Should not have succeeded because the locating strategy is unknown'); | |
| } catch (ex) { | |
| assert.strictEqual(ex.code, bot.ErrorCode.INVALID_ARGUMENT); | |
| } | |
| }); | |
| QUnit.test('canAddANewElementLocatingStrategy', function(assert) { | |
| var doc = getTestDocument(); | |
| var expected = doc.querySelector('[name="lion"]'); | |
| bot.locators.add('fixed', { | |
| single: function() { | |
| return expected; | |
| } | |
| }); | |
| var seen = bot.locators.findElement({fixed: 'ignored'}); | |
| assert.strictEqual(seen, expected); | |
| }); | |
| QUnit.test('shouldSurviveObjectPrototypeBeingModified', function(assert) { | |
| try { | |
| Object.prototype.cheese = 'brie'; | |
| Object.prototype.Inherits = function() { /* does nothing */ }; | |
| var first = bot.locators.findElement({name: 'tiger'}); | |
| assert.strictEqual(first.getAttribute('name'), 'tiger'); | |
| var locator = new Object(); | |
| locator.name = 'tiger'; | |
| var second = bot.locators.findElement(locator); | |
| assert.strictEqual(second.getAttribute('name'), 'tiger'); | |
| } finally { | |
| delete Object.prototype.cheese; | |
| delete Object.prototype.Inherits; | |
| } | |
| }); | |
| </script> | |
| </head> | |
| <body> | |
| <div id="qunit"></div> | |
| <div id="qunit-fixture"></div> | |
| </body> | |
| </html> |