Commit 951eb70b authored by Marcel Hoyer's avatar Marcel Hoyer Committed by Colin Eberhardt

Correcting asynchronous testing

removes unnecessary .then() chaining in test.js as selenium-webdriver already queues calls internally

fixed confusing inverted use of idSelector variable

promisified all tests

promisified test.launchBrowser() and global test hooks

promisified test.closeBrowser()

promisified test.createStandardItems()

🛀 streamlined testOps to always return the promise and flattened out the thens

fixed testOps.assertClearCompleteButtonText() to wait for visible clear-complete button before asserting

fixed testOps.assertClearCompleteButtonIsVisible() to lazy wait for visible clear-complete button

renamed testOps.assertFocussedElementId() to .assertFocussedElement() and streamlined code

fixed page.clickMarkAllCompletedCheckBox() to return inner promise

fixed page.enterItem() to return the promise and flattened out the thens

renamed page.getFocussedElementName() with .getFocussedElementIdOrClass()

fixed page.ensureAppIsVisible() to wait for 'new-todo' element instead of 'main' as some frameworks build the contents after DOM-ready

fixed page.clickClearCompleteButton() to wait for actual button to display before clicking

extracted page.waitForVisibleElement() helper function
parent 4de8afb2
'use strict'; 'use strict';
var webdriver = require('selenium-webdriver'); var webdriver = require('selenium-webdriver');
var idSelectors = false; var idSelectors = true;
module.exports = function Page(browser) { module.exports = function Page(browser) {
...@@ -16,35 +16,35 @@ module.exports = function Page(browser) { ...@@ -16,35 +16,35 @@ module.exports = function Page(browser) {
}; };
this.getTodoListXpath = function () { this.getTodoListXpath = function () {
return !idSelectors ? '//ul[@id="todo-list"]' : '//ul[contains(@class, "todo-list")]'; return idSelectors ? '//ul[@id="todo-list"]' : '//ul[contains(@class, "todo-list")]';
}; };
this.getMainSectionXpath = function () { this.getMainSectionXpath = function () {
return !idSelectors ? '//section[@id="main"]' : '//section[contains(@class, "main")]'; return idSelectors ? '//section[@id="main"]' : '//section[contains(@class, "main")]';
}; };
this.getFooterSectionXpath = function () { this.getFooterSectionXpath = function () {
return !idSelectors ? '//footer[@id="footer"]' : '//footer[contains(@class, "footer")]'; return idSelectors ? '//footer[@id="footer"]' : '//footer[contains(@class, "footer")]';
}; };
this.getCompletedButtonXpath = function () { this.getCompletedButtonXpath = function () {
return !idSelectors ? '//button[@id="clear-completed"]' : '//button[contains(@class, "clear-completed")]'; return idSelectors ? '//button[@id="clear-completed"]' : '//button[contains(@class, "clear-completed")]';
}; };
this.getNewInputXpath = function () { this.getNewInputXpath = function () {
return !idSelectors ? '//input[@id="new-todo"]' : '//input[contains(@class,"new-todo")]'; return idSelectors ? '//input[@id="new-todo"]' : '//input[contains(@class,"new-todo")]';
}; };
this.getToggleAllXpath = function () { this.getToggleAllXpath = function () {
return !idSelectors ? '//input[@id="toggle-all"]' : '//input[contains(@class,"toggle-all")]'; return idSelectors ? '//input[@id="toggle-all"]' : '//input[contains(@class,"toggle-all")]';
}; };
this.getCountXpath = function () { this.getCountXpath = function () {
return !idSelectors ? '//span[@id="todo-count"]' : '//span[contains(@class, "todo-count")]'; return idSelectors ? '//span[@id="todo-count"]' : '//span[contains(@class, "todo-count")]';
}; };
this.getFiltersElementXpath = function () { this.getFiltersElementXpath = function () {
return !idSelectors ? '//*[@id="filters"]' : '//*[contains(@class, "filters")]'; return idSelectors ? '//*[@id="filters"]' : '//*[contains(@class, "filters")]';
}; };
this.getFilterXpathByIndex = function (index) { this.getFilterXpathByIndex = function (index) {
...@@ -115,9 +115,9 @@ module.exports = function Page(browser) { ...@@ -115,9 +115,9 @@ module.exports = function Page(browser) {
return this.getActiveElement().getTagName(); return this.getActiveElement().getTagName();
}; };
this.getFocussedElementName = function () { this.getFocussedElementIdOrClass = function () {
return this.getActiveElement() return this.getActiveElement()
.getAttribute(!idSelectors ? 'id' : 'class'); .getAttribute(idSelectors ? 'id' : 'class');
}; };
this.getEditInputForItemAtIndex = function (index) { this.getEditInputForItemAtIndex = function (index) {
...@@ -175,9 +175,24 @@ module.exports = function Page(browser) { ...@@ -175,9 +175,24 @@ module.exports = function Page(browser) {
}); });
}; };
this.waitForVisibleElement = function (getElementFn, timeout) {
var foundVisibleElement;
timeout = timeout || 500;
return browser.wait(function () {
foundVisibleElement = getElementFn();
return foundVisibleElement.isDisplayed();
}, timeout)
.then(function () {
return foundVisibleElement;
})
.thenCatch(function (err) {
return false;
});
}
this.getVisibileLabelIndicies = function () { this.getVisibileLabelIndicies = function () {
var self = this; var self = this;
var ret;
return this.getItemLabels() return this.getItemLabels()
.then(function (elms) { .then(function (elms) {
return elms.map(function (elm, i) { return elms.map(function (elm, i) {
...@@ -186,18 +201,8 @@ module.exports = function Page(browser) { ...@@ -186,18 +201,8 @@ module.exports = function Page(browser) {
}) })
.then(function (elms) { .then(function (elms) {
return webdriver.promise.filter(elms, function (elmIndex) { return webdriver.promise.filter(elms, function (elmIndex) {
return browser.wait(function () { return self.waitForVisibleElement(function () {
return self.tryGetItemLabelAtIndex(elmIndex).isDisplayed() return self.tryGetItemLabelAtIndex(elmIndex);
.then(function (v) {
ret = v;
return true;
})
.thenCatch(function () {
return false;
});
}, 5000)
.then(function () {
return ret;
}); });
}); });
}); });
...@@ -205,58 +210,63 @@ module.exports = function Page(browser) { ...@@ -205,58 +210,63 @@ module.exports = function Page(browser) {
// ----------------- page actions // ----------------- page actions
this.ensureAppIsVisible = function () { this.ensureAppIsVisible = function () {
return browser.findElements(webdriver.By.css('#todoapp')) var self = this;
.then(function (elms) { return browser.wait(function () {
if (elms.length > 0) { // try to find main element by ID
return true; return browser.isElementPresent(webdriver.By.css('.new-todo'))
} else { .then(function (foundByClass) {
return browser.findElements(webdriver.By.css('.todoapp')); if (foundByClass) {
} idSelectors = false;
}) return true;
.then(function (elms) { }
if (elms === true) {
return true;
}
if (elms.length) { // try to find main element by CSS class
idSelectors = true; return browser.isElementPresent(webdriver.By.css('#new-todo'));
return true; });
}, 5000)
.then(function (hasFoundNewTodoElement) {
if (!hasFoundNewTodoElement) {
throw new Error('Unable to find application, did you start your local server?');
} }
throw new Error('Unable to find application root, did you start your local server?');
}); });
}; };
this.clickMarkAllCompletedCheckBox = function () { this.clickMarkAllCompletedCheckBox = function () {
return this.getMarkAllCompletedCheckBox().then(function (checkbox) { return this.getMarkAllCompletedCheckBox().then(function (checkbox) {
checkbox.click(); return checkbox.click();
}); });
}; };
this.clickClearCompleteButton = function () { this.clickClearCompleteButton = function () {
return this.tryGetClearCompleteButton().click(); var self = this;
return self.waitForVisibleElement(function () {
return self.tryGetClearCompleteButton();
})
.then(function (clearCompleteButton) {
return clearCompleteButton.click();
});
}; };
this.enterItem = function (itemText) { this.enterItem = function (itemText) {
var self = this; var self = this;
browser.wait(function () { return browser.wait(function () {
return self.getItemInputField().then(function (textField) { var textField;
return textField.sendKeys(itemText, webdriver.Key.ENTER)
.then(function () { return self.getItemInputField().then(function (itemInputField) {
return textField; textField = itemInputField;
}); return textField.sendKeys(itemText, webdriver.Key.ENTER);
}).then(function (textField) { })
return self.getVisibleLabelText() .then(function () { return self.getVisibleLabelText(); })
.then(function (labels) { .then(function (labels) {
if (labels.indexOf(itemText.trim()) !== -1) { if (labels.indexOf(itemText.trim()) >= 0) {
return true; return true;
} }
return textField.clear().then(function () { return textField.clear().then(function () {
return false; return false;
}); });
});
}); });
}, 5000); }, 5000);
}; };
......
This diff is collapsed.
...@@ -16,15 +16,16 @@ function TestOperations(page) { ...@@ -16,15 +16,16 @@ function TestOperations(page) {
function testIsVisible(elements, name) { function testIsVisible(elements, name) {
assert.equal(elements.length, 1); assert.equal(elements.length, 1);
elements[0].isDisplayed().then(function (isDisplayed) { return elements[0].isDisplayed()
assert(isDisplayed, 'the ' + name + ' element should be displayed'); .then(function (isDisplayed) {
}); assert(isDisplayed, 'the ' + name + ' element should be displayed');
});
} }
this.assertNewInputNotFocused = function () { this.assertNewInputNotFocused = function () {
return page.getFocussedElementName() return page.getFocussedElementIdOrClass()
.then(function (name) { .then(function (focussedElementIdOrClass) {
assert.notEqual(name, 'new-todo'); assert.equal(focussedElementIdOrClass.indexOf('new-todo'), -1);
}); });
}; };
...@@ -35,147 +36,177 @@ function TestOperations(page) { ...@@ -35,147 +36,177 @@ function TestOperations(page) {
}); });
}; };
this.assertFocussedElementId = function (expectedId) { this.assertFocussedElement = function (expectedIdentifier) {
page.getFocussedElementName().then(function (id) { return page.getFocussedElementIdOrClass()
assert.notEqual(id.indexOf(expectedId), -1, 'The focused element did not have the expected id ' + expectedId); .then(function (focusedElementIdentifier) {
}); var failMsg = 'The focused element did not have the expected class or id "' + expectedIdentifier + '"';
assert.notEqual(focusedElementIdentifier.indexOf(expectedIdentifier), -1, failMsg);
});
}; };
this.assertClearCompleteButtonIsHidden = function () { this.assertClearCompleteButtonIsHidden = function () {
page.tryGetClearCompleteButton().then(function (element) { return page.tryGetClearCompleteButton()
testIsHidden(element, 'clear completed items button'); .then(function (element) {
}, function (_error) { return testIsHidden(element, 'clear completed items button');
assert(_error.code === 7, 'error accessing clear completed items button, error: ' + _error.message); }, function (_error) {
}); assert(_error.code === 7, 'error accessing clear completed items button, error: ' + _error.message);
});
}; };
this.assertClearCompleteButtonIsVisible = function () { this.assertClearCompleteButtonIsVisible = function () {
page.tryGetClearCompleteButton().then(function (element) { return page.waitForVisibleElement(function () {
testIsVisible([element], 'clear completed items button'); return page.tryGetClearCompleteButton();
}); })
.then(function (clearCompleteButton) {
assert(clearCompleteButton, 'the clear completed items button element should be displayed');
});
}; };
this.assertItemCount = function (itemCount) { this.assertItemCount = function (itemCount) {
page.getItemElements().then(function (toDoItems) { return page.getItemElements()
assert.equal(toDoItems.length, itemCount, .then(function (toDoItems) {
itemCount + ' items expected in the todo list, ' + toDoItems.length + ' items observed'); assert.equal(toDoItems.length, itemCount,
}); itemCount + ' items expected in the todo list, ' + toDoItems.length + ' items observed');
});
}; };
this.assertClearCompleteButtonText = function (buttonText) { this.assertClearCompleteButtonText = function (buttonText) {
return page.tryGetClearCompleteButton() return page.waitForVisibleElement(function () {
.getText().then(function (text) { return page.tryGetClearCompleteButton();
assert.equal(text, buttonText); })
}); .then(function (clearCompleteButton) {
return clearCompleteButton.getText();
})
.then(function (text) {
return assert.equal(text, buttonText);
});
}; };
this.assertMainSectionIsHidden = function () { this.assertMainSectionIsHidden = function () {
page.tryGetMainSectionElement().then(function (mainSection) { return page.tryGetMainSectionElement()
testIsHidden(mainSection, 'main'); .then(function (mainSection) {
}); return testIsHidden(mainSection, 'main');
});
}; };
this.assertFooterIsHidden = function () { this.assertFooterIsHidden = function () {
page.tryGetFooterElement().then(function (footer) { return page.tryGetFooterElement()
testIsHidden(footer, 'footer'); .then(function (footer) {
}); return testIsHidden(footer, 'footer');
});
}; };
this.assertMainSectionIsVisible = function () { this.assertMainSectionIsVisible = function () {
page.tryGetMainSectionElement().then(function (mainSection) { return page.tryGetMainSectionElement()
testIsVisible(mainSection, 'main'); .then(function (mainSection) {
}); return testIsVisible(mainSection, 'main');
});
}; };
//TODO: fishy! //TODO: fishy!
this.assertItemToggleIsHidden = function (index) { this.assertItemToggleIsHidden = function (index) {
page.tryGetToggleForItemAtIndex(index).then(function (toggleItem) { return page.tryGetToggleForItemAtIndex(index)
testIsHidden(toggleItem, 'item-toggle'); .then(function (toggleItem) {
}); return testIsHidden(toggleItem, 'item-toggle');
});
}; };
this.assertItemLabelIsHidden = function (index) { this.assertItemLabelIsHidden = function (index) {
page.tryGetItemLabelAtIndex(index).then(function (toggleItem) { return page.tryGetItemLabelAtIndex(index)
testIsHidden(toggleItem, 'item-label'); .then(function (toggleItem) {
}); return testIsHidden(toggleItem, 'item-label');
});
}; };
this.assertFooterIsVisible = function () { this.assertFooterIsVisible = function () {
page.tryGetFooterElement().then(function (footer) { return page.tryGetFooterElement()
testIsVisible(footer, 'footer'); .then(function (footer) {
}); return testIsVisible(footer, 'footer');
});
}; };
this.assertItemInputFieldText = function (text) { this.assertItemInputFieldText = function (text) {
page.getItemInputField().getText().then(function (inputFieldText) { return page.getItemInputField().getText()
assert.equal(inputFieldText, text); .then(function (inputFieldText) {
}); assert.equal(inputFieldText, text);
});
}; };
this.assertItemText = function (itemIndex, textToAssert) { this.assertItemText = function (itemIndex, textToAssert) {
page.getItemLabelAtIndex(itemIndex).getText().then(function (text) { return page.getItemLabelAtIndex(itemIndex).getText()
assert.equal(text, textToAssert, .then(function (text) {
'A todo item with text \'' + textToAssert + '\' was expected at index ' + assert.equal(text, textToAssert,
itemIndex + ', the text \'' + text + '\' was observed'); 'A todo item with text \'' + textToAssert + '\' was expected at index ' +
}); itemIndex + ', the text \'' + text + '\' was observed');
});
}; };
// tests that the list contains the following items, independant of order // tests that the list contains the following items, independant of order
this.assertItems = function (textArray) { this.assertItems = function (textArray) {
return page.getVisibleLabelText() return page.getVisibleLabelText()
.then(function (visibleText) { .then(function (visibleText) {
assert.deepEqual(visibleText.sort(), textArray.sort()); assert.deepEqual(visibleText.sort(), textArray.sort());
}); });
}; };
this.assertItemCountText = function (textToAssert) { this.assertItemCountText = function (textToAssert) {
page.getItemsCountElement().getText().then(function (text) { return page.getItemsCountElement().getText()
assert.equal(text.trim(), textToAssert, 'the item count text was incorrect'); .then(function (text) {
}); assert.equal(text.trim(), textToAssert, 'the item count text was incorrect');
});
}; };
// tests for the presence of the 'completed' CSS class for the item at the given index // tests for the presence of the 'completed' CSS class for the item at the given index
this.assertItemAtIndexIsCompleted = function (index) { this.assertItemAtIndexIsCompleted = function (index) {
page.getItemElements().then(function (toDoItems) { return page.getItemElements()
toDoItems[index].getAttribute('class').then(function (cssClass) { .then(function (toDoItems) {
assert(cssClass.indexOf('completed') !== -1, return toDoItems[index].getAttribute('class');
'the item at index ' + index + ' should have been marked as completed'); })
.then(function (cssClass) {
var failMsg = 'the item at index ' + index + ' should have been marked as completed';
assert(cssClass.indexOf('completed') !== -1, failMsg);
}); });
});
}; };
this.assertItemAtIndexIsNotCompleted = function (index) { this.assertItemAtIndexIsNotCompleted = function (index) {
page.getItemElements().then(function (toDoItems) { return page.getItemElements()
toDoItems[index].getAttribute('class').then(function (cssClass) { .then(function (toDoItems) {
return toDoItems[index].getAttribute('class');
})
.then(function (cssClass) {
// the maria implementation uses an 'incompleted' CSS class which is redundant // the maria implementation uses an 'incompleted' CSS class which is redundant
// TODO: this should really be moved into the pageLaxMode // TODO: this should really be moved into the pageLaxMode
assert(cssClass.indexOf('completed') === -1 || cssClass.indexOf('incompleted') !== -1, var failMsg = 'the item at index ' + index + ' should not have been marked as completed';
'the item at index ' + index + ' should not have been marked as completed'); assert(cssClass.indexOf('completed') === -1 || cssClass.indexOf('incompleted') !== -1, failMsg);
}); });
});
}; };
this.assertFilterAtIndexIsSelected = function (selectedIndex) { this.assertFilterAtIndexIsSelected = function (selectedIndex) {
page.findByXpath(page.getSelectedFilterXPathByIndex(selectedIndex + 1)) return page.findByXpath(page.getSelectedFilterXPathByIndex(selectedIndex + 1))
.then(function (elm) { .then(function (elm) {
assert.notEqual(elm, undefined, 'the filter / route at index ' + selectedIndex + ' should have been selected'); var failMsg = 'the filter / route at index ' + selectedIndex + ' should have been selected';
}); assert.notEqual(elm, undefined, failMsg);
});
}; };
this.assertCompleteAllIsClear = function () { this.assertCompleteAllIsClear = function () {
page.getMarkAllCompletedCheckBox().then(function (markAllCompleted) { return page.getMarkAllCompletedCheckBox()
markAllCompleted.isSelected().then(function (isSelected) { .then(function (markAllCompleted) {
return markAllCompleted.isSelected();
})
.then(function (isSelected) {
assert(!isSelected, 'the mark-all-completed checkbox should be clear'); assert(!isSelected, 'the mark-all-completed checkbox should be clear');
}); });
});
}; };
this.assertCompleteAllIsChecked = function () { this.assertCompleteAllIsChecked = function () {
page.getMarkAllCompletedCheckBox().then(function (markAllCompleted) { return page.getMarkAllCompletedCheckBox()
markAllCompleted.isSelected().then(function (isSelected) { .then(function (markAllCompleted) {
return markAllCompleted.isSelected();
})
.then(function (isSelected) {
assert(isSelected, 'the mark-all-completed checkbox should be checked'); assert(isSelected, 'the mark-all-completed checkbox should be checked');
}); });
});
}; };
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment