Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
todomvc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sven Franck
todomvc
Commits
26ceac57
Commit
26ceac57
authored
Jul 04, 2016
by
Oskar Gustafsson
Committed by
Sam Saccone
Jul 03, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve test suite robustness. (#1646)
..by waiting for DOM states instead of checking them only once.
parent
aa9b051b
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
330 additions
and
506 deletions
+330
-506
tests/page.js
tests/page.js
+160
-229
tests/pageLaxMode.js
tests/pageLaxMode.js
+47
-36
tests/test.js
tests/test.js
+69
-75
tests/testOperations.js
tests/testOperations.js
+54
-166
No files found.
tests/page.js
View file @
26ceac57
'
use strict
'
;
var
webdriver
=
require
(
'
selenium-webdriver
'
);
var
until
=
require
(
'
selenium-webdriver/lib/until
'
);
var
idSelectors
=
true
;
var
classOrId
=
idSelectors
?
'
#
'
:
'
.
'
;
module
.
exports
=
function
Page
(
browser
)
{
var
DEFAULT_TIMEOUT
=
3000
;
var
REMOVED_TIMEOUT
=
100
;
// ----------------- utility methods
var
REMOVE_TEXT_KEY_SEQ
=
Array
(
51
).
join
(
webdriver
.
Key
.
BACK_SPACE
+
webdriver
.
Key
.
DELETE
);
this
.
tryFindByXpath
=
function
(
xpath
)
{
return
browser
.
findElements
(
webdriver
.
By
.
xpath
(
xpath
)
);
}
;
// Unique symbols
var
ELEMENT_MISSING
=
Object
.
freeze
({}
);
var
ITEM_HIDDEN_OR_REMOVED
=
Object
.
freeze
({})
;
this
.
findByXpath
=
function
(
xpath
)
{
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
};
module
.
exports
=
function
Page
(
browser
)
{
this
.
getTodoListXpath
=
function
()
{
return
idSelectors
?
'
//ul[@id="todo-list"]
'
:
'
//ul[contains(@class, "todo-list")]
'
;
};
// CSS ELEMENT SELECTORS
this
.
getMainSectionXpath
=
function
()
{
return
idSelectors
?
'
//section[@id="main"]
'
:
'
//section[contains(@class, "main")]
'
;
};
this
.
getMainSectionCss
=
function
()
{
return
classOrId
+
'
main
'
;
};
this
.
getFooterSectionXpath
=
function
()
{
return
idSelectors
?
'
//footer[@id="footer"]
'
:
'
//footer[contains(@class, "footer")]
'
;
};
this
.
getFooterSectionCss
=
function
()
{
return
'
footer
'
+
classOrId
+
'
footer
'
;
};
this
.
getCompletedButtonXpath
=
function
()
{
return
idSelectors
?
'
//button[@id="clear-completed"]
'
:
'
//button[contains(@class, "clear-completed")]
'
;
};
this
.
getClearCompletedButtonCss
=
function
()
{
return
'
button
'
+
classOrId
+
'
clear-completed
'
;
};
this
.
getNewInputXpath
=
function
()
{
return
idSelectors
?
'
//input[@id="new-todo"]
'
:
'
//input[contains(@class,"new-todo")]
'
;
};
this
.
getNewInputCss
=
function
()
{
return
'
input
'
+
classOrId
+
'
new-todo
'
;
};
this
.
getToggleAllXpath
=
function
()
{
return
idSelectors
?
'
//input[@id="toggle-all"]
'
:
'
//input[contains(@class,"toggle-all")]
'
;
};
this
.
getToggleAllCss
=
function
()
{
return
'
input
'
+
classOrId
+
'
toggle-all
'
;
};
this
.
getCountXpath
=
function
()
{
return
idSelectors
?
'
//span[@id="todo-count"]
'
:
'
//span[contains(@class, "todo-count")]
'
;
};
this
.
getItemCountCss
=
function
()
{
return
'
span
'
+
classOrId
+
'
todo-count
'
;
};
this
.
getFiltersElementXpath
=
function
()
{
return
idSelectors
?
'
//*[@id="filters"]
'
:
'
//*[contains(@class, "filters")]
'
;
};
this
.
getFilterCss
=
function
(
index
)
{
return
classOrId
+
'
filters li:nth-of-type(
'
+
(
index
+
1
)
+
'
) a
'
;
};
this
.
getFilterXpathByIndex
=
function
(
index
)
{
return
this
.
getFiltersElementXpath
()
+
'
/li[
'
+
index
+
'
]/a
'
;
};
this
.
getSelectedFilterCss
=
function
(
index
)
{
return
this
.
getFilterCss
(
index
)
+
'
.selected
'
;
};
this
.
getSelectedFilterXPathByIndex
=
function
(
index
)
{
return
this
.
getFilterXpathByIndex
(
index
)
+
'
[contains(@class, "selected")]
'
;
};
this
.
getFilterAllCss
=
function
()
{
return
this
.
getFilterCss
(
0
);
};
this
.
getFilterAllXpath
=
function
()
{
return
this
.
getFilterXpathByIndex
(
1
);
};
this
.
getFilterActiveCss
=
function
()
{
return
this
.
getFilterCss
(
1
);
};
this
.
getFilterActiveXpath
=
function
()
{
return
this
.
getFilterXpathByIndex
(
2
);
};
this
.
getFilterCompletedCss
=
function
()
{
return
this
.
getFilterCss
(
2
);
};
this
.
getFilterCompletedXpath
=
function
()
{
return
this
.
getFilterXpathByIndex
(
3
);
};
this
.
getListCss
=
function
(
suffixCss
)
{
return
'
ul
'
+
classOrId
+
'
todo-list
'
+
(
suffixCss
||
''
);
};
this
.
xPathForItemAtIndex
=
function
(
index
)
{
// why is XPath the only language silly enough to be 1-indexed?
return
this
.
getTodoListXpath
()
+
'
/li[
'
+
(
index
+
1
)
+
'
]
'
;
this
.
getListItemCss
=
function
(
index
,
suffixCss
,
excludeParentSelector
)
{
suffixCss
=
(
index
===
undefined
?
''
:
'
:nth-of-type(
'
+
(
index
+
1
)
+
'
)
'
)
+
(
suffixCss
||
''
);
return
excludeParentSelector
?
'
li
'
+
suffixCss
:
this
.
getListCss
(
'
li
'
+
suffixCss
)
;
};
// ----------------- navigation methods
this
.
getListItemToggleCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
input.toggle
'
);
};
this
.
back
=
function
()
{
return
browser
.
navigate
().
back
();
};
this
.
getListItemLabelCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
label
'
);
};
// ----------------- try / get methods
this
.
getLastListItemLabelCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
:last-of-type label
'
);
};
// unfortunately webdriver does not have a decent API for determining if an
// element exists. The standard approach is to obtain an array of elements
// and test that the length is zero. These methods are used to obtain
// elements which *might* be present in the DOM, hence the try/get name.
this
.
getListItemInputCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
input.edit
'
);
};
this
.
tryGetMainSectionElement
=
function
()
{
return
this
.
tryFindByXpath
(
this
.
getMainSectionXpath
());
};
this
.
getEditingListItemInputCss
=
function
()
{
return
this
.
getListItemCss
(
undefined
,
'
.editing input.edit
'
);
};
this
.
tryGetFooterElement
=
function
()
{
return
this
.
tryFindByXpath
(
this
.
getFooterSectionXpath
());
// This CSS selector returns the _last_ element of a list that exactly matches the provided list of completed states
// It is used as a boolean test of the item states
this
.
getListItemsWithCompletedStatesCss
=
function
(
completedStates
)
{
var
suffixCss
=
'
'
+
completedStates
.
map
(
function
(
completed
,
i
)
{
return
this
.
getListItemCss
(
i
,
completed
?
'
.completed
'
:
'
:not(.completed)
'
,
true
);
},
this
).
join
(
'
+
'
);
return
this
.
getListCss
(
suffixCss
);
};
this
.
tryGetClearCompleteButton
=
function
()
{
return
this
.
findByXpath
(
this
.
getCompletedButtonXpath
());
};
// PUBLIC SYMBOLS
this
.
tryGetToggleForItemAtIndex
=
function
(
index
)
{
var
xpath
=
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[contains(@class,"toggle")]
'
;
return
this
.
findByXpath
(
xpath
);
};
this
.
ITEM_HIDDEN_OR_REMOVED
=
ITEM_HIDDEN_OR_REMOVED
;
this
.
tryGetItemLabelAtIndex
=
function
(
index
)
{
return
this
.
findByXpath
(
this
.
xPathForItemAtIndex
(
index
)
+
'
//label
'
);
// NAVIGATION
this
.
back
=
function
()
{
return
browser
.
navigate
().
back
();
};
// ----------------- DOM element access methods
this
.
getActiveElement
=
function
()
{
return
browser
.
switchTo
().
activeElement
();
// ELEMENT RETREIVAL
// wait* methods guarantees to return an element, or throw an exception
// get* methods may return nothing at all, or in the case of element lists, an older version of the list
this
.
getElements
=
function
(
css
)
{
return
browser
.
findElements
(
webdriver
.
By
.
css
(
css
));
};
this
.
getFocussedTagName
=
function
(
)
{
return
this
.
getActiveElement
().
getTagName
(
);
this
.
waitForElement
=
function
(
css
,
failMsg
,
timeout
)
{
return
browser
.
wait
(
until
.
elementLocated
(
webdriver
.
By
.
css
(
css
)),
timeout
||
DEFAULT_TIMEOUT
,
failMsg
);
};
this
.
getFocussedElementIdOrClass
=
function
()
{
return
this
.
getActiveElement
()
.
getAttribute
(
idSelectors
?
'
id
'
:
'
class
'
);
this
.
waitForFocusedElement
=
function
(
css
,
failMsg
)
{
return
this
.
waitForElement
(
css
+
'
:focus
'
,
failMsg
);
};
this
.
getEditInputForItemAtIndex
=
function
(
index
)
{
var
xpath
=
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[contains(@class,"edit")]
'
;
return
this
.
findByXpath
(
xpath
);
this
.
waitForBlurredElement
=
function
(
css
,
failMsg
)
{
return
this
.
waitForElement
(
css
+
'
:not(:focus)
'
,
failMsg
);
};
this
.
getItemInputField
=
function
()
{
return
this
.
findByXpath
(
this
.
getNewInputXpath
());
this
.
waitForListItemCount
=
function
(
count
)
{
var
self
=
this
;
return
browser
.
wait
(
function
()
{
return
self
.
waitForElement
(
self
.
getListCss
())
.
then
(
function
(
listElement
)
{
return
listElement
.
findElements
(
webdriver
.
By
.
css
(
self
.
getListItemCss
(
undefined
,
undefined
,
true
)));
})
.
then
(
function
(
listItems
)
{
return
listItems
.
length
===
count
;
});
},
DEFAULT_TIMEOUT
,
'
Expected item list to contain
'
+
count
+
'
item
'
+
(
count
===
1
?
''
:
'
s
'
));
};
this
.
getMarkAllCompletedCheckBox
=
function
()
{
return
this
.
findByXpath
(
this
.
getToggleAllXpath
());
this
.
waitForClearCompleteButton
=
function
()
{
return
this
.
waitForElement
(
this
.
getClearCompletedButtonCss
());
};
this
.
getItemElements
=
function
(
)
{
return
this
.
tryFindByXpath
(
this
.
getTodoListXpath
()
+
'
/li
'
);
this
.
waitForToggleForItem
=
function
(
index
)
{
return
this
.
waitForElement
(
this
.
getListItemToggleCss
(
index
)
);
};
this
.
getNonCompletedItemElements
=
function
(
)
{
return
this
.
tryFindByXpath
(
this
.
getTodoListXpath
()
+
'
/li[not(contains(@class,"completed"))]
'
);
this
.
waitForItemLabel
=
function
(
index
)
{
return
this
.
waitForElement
(
this
.
getListItemLabelCss
(
index
)
);
};
this
.
getItemsCountElement
=
function
()
{
return
this
.
findByXpath
(
this
.
getCountXpath
());
this
.
waitForNewItemInputField
=
function
()
{
return
this
.
waitForElement
(
this
.
getNewInputCss
());
};
this
.
getItemLabelAtIndex
=
function
(
index
)
{
return
this
.
findByXpath
(
this
.
xPathForItemAtIndex
(
index
)
+
'
//label
'
);
this
.
waitForMarkAllCompletedCheckBox
=
function
(
)
{
return
this
.
waitForElement
(
this
.
getToggleAllCss
()
);
};
this
.
getItemLabels
=
function
()
{
var
xpath
=
this
.
getTodoListXpath
()
+
'
/li//label
'
;
return
this
.
tryFindByXpath
(
xpath
);
this
.
getListItems
=
function
()
{
return
this
.
getElements
(
this
.
getListItemCss
());
};
this
.
getVisibleLabelText
=
function
()
{
var
self
=
this
;
return
this
.
getVisibileLabelIndicies
()
.
then
(
function
(
indicies
)
{
return
webdriver
.
promise
.
map
(
indicies
,
function
(
elmIndex
)
{
var
ret
;
return
browser
.
wait
(
function
()
{
return
self
.
tryGetItemLabelAtIndex
(
elmIndex
).
getText
()
.
then
(
function
(
v
)
{
ret
=
v
;
return
true
;
})
.
thenCatch
(
function
()
{
return
false
;
});
},
5000
)
.
then
(
function
()
{
return
ret
;
});
this
.
waitForVisibility
=
function
(
shouldBeVisible
,
css
,
failMsg
)
{
if
(
shouldBeVisible
)
{
return
this
.
waitForElement
(
css
,
failMsg
)
.
then
(
function
(
element
)
{
return
browser
.
wait
(
until
.
elementIsVisible
(
element
),
DEFAULT_TIMEOUT
,
failMsg
);
});
}
else
{
return
this
.
waitForElement
(
css
,
undefined
,
REMOVED_TIMEOUT
)
.
catch
(
function
()
{
return
ELEMENT_MISSING
;
})
.
then
(
function
(
elementOrError
)
{
return
elementOrError
===
ELEMENT_MISSING
?
ELEMENT_MISSING
:
// Returning a value will resolve the promise
browser
.
wait
(
until
.
elementIsNotVisible
(
elementOrError
),
DEFAULT_TIMEOUT
,
failMsg
);
});
}
};
this
.
waitForVisibleElement
=
function
(
getElementFn
,
timeout
)
{
var
foundVisibleElement
;
timeout
=
timeout
||
500
;
this
.
waitForMainSectionRemovedOrEmpty
=
function
()
{
return
this
.
waitForElement
(
this
.
getMainSectionCss
(),
undefined
,
REMOVED_TIMEOUT
)
.
catch
(
function
()
{
return
ELEMENT_MISSING
;
})
.
then
(
function
(
elementOrError
)
{
return
elementOrError
===
ELEMENT_MISSING
?
ELEMENT_MISSING
:
this
.
waitForListItemCount
(
0
);
}.
bind
(
this
));
};
return
browser
.
wait
(
function
()
{
foundVisibleElement
=
getElementFn
();
return
foundVisibleElement
.
isDisplayed
();
},
timeout
)
.
then
(
function
()
{
return
foundVisibleElement
;
})
.
thenCatch
(
function
(
err
)
{
return
false
;
});
}
this
.
waitForCheckedStatus
=
function
(
shouldBeChecked
,
failMsg
,
element
)
{
var
condition
=
shouldBeChecked
?
'
elementIsSelected
'
:
'
elementIsNotSelected
'
;
return
browser
.
wait
(
until
[
condition
](
element
),
DEFAULT_TIMEOUT
,
failMsg
);
};
this
.
getVisibileLabelIndicies
=
function
()
{
var
self
=
this
;
return
this
.
getItemLabels
()
.
then
(
function
(
elms
)
{
return
elms
.
map
(
function
(
elm
,
i
)
{
return
i
;
});
})
.
then
(
function
(
elms
)
{
return
webdriver
.
promise
.
filter
(
elms
,
function
(
elmIndex
)
{
return
self
.
waitForVisibleElement
(
function
()
{
return
self
.
tryGetItemLabelAtIndex
(
elmIndex
);
});
});
});
this
.
waitForTextContent
=
function
(
text
,
failMsg
,
element
)
{
return
browser
.
wait
(
until
.
elementTextIs
(
element
,
text
),
DEFAULT_TIMEOUT
,
failMsg
);
};
// ----------------- page actions
this
.
ensureAppIsVisible
=
function
()
{
var
self
=
this
;
return
browser
.
wait
(
function
()
{
// try to find main element by ID
return
browser
.
isElementPresent
(
webdriver
.
By
.
css
(
'
.new-todo
'
))
.
then
(
function
(
foundByClass
)
{
if
(
foundByClass
)
{
idSelectors
=
false
;
return
true
;
}
// PAGE ACTIONS
// try to find main element by CSS class
return
browser
.
isElementPresent
(
webdriver
.
By
.
css
(
'
#new-todo
'
));
});
},
5000
)
.
then
(
function
(
hasFoundNewTodoElement
)
{
if
(
!
hasFoundNewTodoElement
)
{
throw
new
Error
(
'
Unable to find application, did you start your local server?
'
);
}
this
.
ensureAppIsVisibleAndLoaded
=
function
()
{
return
this
.
waitForVisibility
(
false
,
this
.
getFooterSectionCss
(),
'
Footer is not hidden
'
)
// Footer hidden -> app is active
.
then
(
this
.
waitForElement
.
bind
(
this
,
'
.new-todo, #new-todo
'
,
'
Could not find new todo input field
'
,
undefined
))
.
then
(
function
(
newTodoElement
)
{
return
newTodoElement
.
getAttribute
(
'
id
'
);
})
.
then
(
function
(
newTodoElementId
)
{
if
(
newTodoElementId
===
'
new-todo
'
)
{
return
;
}
idSelectors
=
false
;
classOrId
=
idSelectors
?
'
#
'
:
'
.
'
;
});
};
this
.
clickMarkAllCompletedCheckBox
=
function
()
{
return
this
.
getMarkAllCompletedCheckBox
().
then
(
function
(
checkbox
)
{
return
checkbox
.
click
();
});
return
this
.
waitForMarkAllCompletedCheckBox
().
click
();
};
this
.
clickClearCompleteButton
=
function
()
{
var
self
=
this
;
return
self
.
waitForVisibleElement
(
function
()
{
return
self
.
tryGetClearCompleteButton
();
})
return
this
.
waitForVisibility
(
true
,
this
.
getClearCompletedButtonCss
(),
'
Expected clear completed items button to be visible
'
)
.
then
(
function
(
clearCompleteButton
)
{
return
clearCompleteButton
.
click
();
clearCompleteButton
.
click
();
});
};
this
.
enterItem
=
function
(
itemText
)
{
var
self
=
this
;
return
browser
.
wait
(
function
()
{
var
textField
;
return
self
.
getItemInputField
().
then
(
function
(
itemInputField
)
{
textField
=
itemInputField
;
return
textField
.
sendKeys
(
itemText
,
webdriver
.
Key
.
ENTER
);
var
nItems
;
return
self
.
getListItems
()
.
then
(
function
(
items
)
{
nItems
=
items
.
length
;
})
.
then
(
function
()
{
return
self
.
getVisibleLabelText
();
})
.
then
(
function
(
labels
)
{
if
(
labels
.
indexOf
(
itemText
.
trim
())
>=
0
)
{
return
true
;
}
return
textField
.
clear
().
then
(
function
()
{
return
false
;
});
.
then
(
this
.
waitForNewItemInputField
.
bind
(
this
))
.
then
(
function
(
newItemInput
)
{
return
newItemInput
.
sendKeys
(
itemText
).
then
(
function
()
{
return
newItemInput
;
});
})
.
then
(
function
(
newItemInput
)
{
return
browser
.
wait
(
function
()
{
// Hit Enter repeatedly until the text goes away
return
newItemInput
.
sendKeys
(
webdriver
.
Key
.
ENTER
)
.
then
(
newItemInput
.
getAttribute
.
bind
(
newItemInput
,
'
value
'
))
.
then
(
function
(
newItemInputValue
)
{
return
newItemInputValue
.
length
===
0
;
});
},
5000
);
},
DEFAULT_TIMEOUT
);
})
.
then
(
function
()
{
return
self
.
waitForElement
(
self
.
getLastListItemLabelCss
(
nItems
));
})
.
then
(
this
.
waitForTextContent
.
bind
(
this
,
itemText
.
trim
(),
'
Expected new item label to read
'
+
itemText
.
trim
()));
};
this
.
toggleItemAtIndex
=
function
(
index
)
{
return
this
.
tryGetToggleForItemAtIndex
(
index
).
click
();
return
this
.
waitForToggleForItem
(
index
).
click
();
};
this
.
editItemAtIndex
=
function
(
index
,
itemText
)
{
return
this
.
getEditInputForItemAtIndex
(
index
)
return
this
.
waitForElement
(
this
.
getListItemInputCss
(
index
)
)
.
then
(
function
(
itemEditField
)
{
// send 50 delete keypresses, just to be sure the item text is deleted
var
deleteKeyPresses
=
''
;
for
(
var
i
=
0
;
i
<
50
;
i
++
)
{
deleteKeyPresses
+=
webdriver
.
Key
.
BACK_SPACE
;
deleteKeyPresses
+=
webdriver
.
Key
.
DELETE
;
}
itemEditField
.
sendKeys
(
deleteKeyPresses
);
// update the item with the new text.
itemEditField
.
sendKeys
(
itemText
);
return
itemEditField
.
sendKeys
(
REMOVE_TEXT_KEY_SEQ
,
itemText
);
});
};
this
.
doubleClickItemAtIndex
=
function
(
index
)
{
return
this
.
getItemLabelAtIndex
(
index
).
then
(
function
(
itemLabel
)
{
// double click is not 'natively' supported, so we need to send the
// event direct to the element see:
return
this
.
waitForItemLabel
(
index
).
then
(
function
(
itemLabel
)
{
// double click is not 'natively' supported, so we need to send the event direct to the element, see:
// jscs:disable
// http://stackoverflow.com/questions/3982442/selenium-2-webdriver-how-to-double-click-a-table-row-which-opens-a-new-window
// jscs:enable
browser
.
executeScript
(
'
var evt = document.createEvent("MouseEvents");
'
+
return
browser
.
executeScript
(
'
var evt = document.createEvent("MouseEvents");
'
+
'
evt.initMouseEvent("dblclick",true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0,null);
'
+
'
arguments[0].dispatchEvent(evt);
'
,
itemLabel
);
});
};
this
.
filterBy
=
function
(
selectorFn
)
{
var
self
=
this
;
return
browser
.
wait
(
function
()
{
return
self
.
findByXpath
(
selectorFn
()).
click
()
.
then
(
function
()
{
return
self
.
findByXpath
(
selectorFn
()).
getAttribute
(
'
class
'
);
})
.
then
(
function
(
klass
)
{
return
klass
.
indexOf
(
'
selected
'
)
!==
-
1
;
});
},
5000
);
this
.
filterBy
=
function
(
filterCss
)
{
return
this
.
waitForElement
(
filterCss
)
.
click
()
.
then
(
this
.
waitForElement
.
bind
(
this
,
filterCss
+
'
.selected
'
,
undefined
,
undefined
));
};
this
.
filterByActiveItems
=
function
()
{
return
this
.
filterBy
(
this
.
getFilterActive
Xpath
.
bind
(
this
));
return
this
.
filterBy
(
this
.
getFilterActive
Css
(
));
};
this
.
filterByCompletedItems
=
function
()
{
return
this
.
filterBy
(
this
.
getFilterCompleted
Xpath
.
bind
(
this
));
return
this
.
filterBy
(
this
.
getFilterCompleted
Css
(
));
};
this
.
filterByAllItems
=
function
()
{
return
this
.
filterBy
(
this
.
getFilterAll
Xpath
.
bind
(
this
));
return
this
.
filterBy
(
this
.
getFilterAll
Css
(
));
};
};
tests/pageLaxMode.js
View file @
26ceac57
'
use strict
'
;
var
webdriver
=
require
(
'
selenium-webdriver
'
);
var
Page
=
require
(
'
./page
'
);
module
.
exports
=
function
PageLaxMode
(
browser
)
{
Page
.
apply
(
this
,
[
browser
]);
module
.
exports
=
function
PageLaxMode
()
{
Page
.
apply
(
this
,
arguments
);
this
.
tryGetMainSectionElement
=
function
()
{
return
this
.
tryFindByXpath
(
'
//section//section
'
)
;
this
.
getMainSectionCss
=
function
()
{
return
'
section section
'
;
};
this
.
tryGetFooterElement
=
function
()
{
return
this
.
tryFindByXpath
(
'
//section//footer
'
)
;
this
.
getFooterSectionCss
=
function
()
{
return
'
section footer
'
;
};
this
.
getTodoListXpath
=
function
()
{
return
'
(//section/ul | //section/div/ul | //ul[@id="todo-list"])
'
;
this
.
getListCss
=
function
(
suffixCss
)
{
return
[
'
section > ul
'
,
'
section > div > ul
'
,
'
ul#todo-list
'
,
].
map
(
function
(
listCss
)
{
return
listCss
+
(
suffixCss
||
''
);
}).
join
(
'
,
'
);
};
this
.
getMarkAllCompletedCheckBox
=
function
()
{
var
xpath
=
'
(//section/input[@type="checkbox"] | //section/*/input[@type="checkbox"] | //input[@id="toggle-all"])
'
;
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
this
.
getToggleAllCss
=
function
()
{
return
[
'
section > input[type="checkbox"]
'
,
'
section > * > input[type="checkbox"]
'
,
'
input#toggle-all
'
,
].
join
(
'
,
'
);
};
this
.
tryGetClearCompleteButton
=
function
()
{
var
xpath
=
'
(//footer/button | //footer/*/button | //button[@id="clear-completed"])
'
;
return
browser
.
findElements
(
webdriver
.
By
.
xpath
(
xpath
));
this
.
getClearCompletedButtonCss
=
function
()
{
return
[
'
footer > button
'
,
'
footer > * > button
'
,
'
button#clear-completed
'
,
].
join
(
'
,
'
);
};
this
.
getItemsCountElement
=
function
()
{
var
xpath
=
'
(//footer/span | //footer/*/span)
'
;
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
this
.
getItemCountCss
=
function
()
{
return
[
'
footer > span
'
,
'
footer > * > span
'
,
].
join
(
'
,
'
);
};
this
.
getFilter
Elements
=
function
(
)
{
return
browser
.
findElements
(
webdriver
.
By
.
xpath
(
'
//footer//ul//a
'
))
;
this
.
getFilter
Css
=
function
(
index
)
{
return
'
footer ul li:nth-child(
'
+
(
index
+
1
)
+
'
) a
'
;
};
this
.
getItemInputField
=
function
()
{
// allow a more generic method for locating the text getItemInputField
var
xpath
=
'
(//header/input | //header/*/input | //input[@id="new-todo"])
'
;
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
this
.
getNewInputCss
=
function
()
{
return
[
'
header > input
'
,
'
header > * > input
'
,
'
input#new-todo
'
,
].
join
(
'
,
'
);
};
this
.
tryGetToggleForItemAtIndex
=
function
(
index
)
{
// the specification dictates that the checkbox should have the 'toggle' CSS class. Some implementations deviate from
// this, hence in lax mode we simply look for any checkboxes within the specified 'li'.
var
xpath
=
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[@type="checkbox"]
'
;
return
browser
.
findElements
(
webdriver
.
By
.
xpath
(
xpath
));
this
.
getListItemToggleCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
input[type="checkbox"]
'
);
};
this
.
getEditInputForItemAtIndex
=
function
(
index
)
{
// the specification dictates that the input element that allows the user to edit a todo item should have a CSS
// class of 'edit'. In lax mode, we also look for an input of type 'text'.
var
xpath
=
'
(
'
+
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[@type="text"]
'
+
'
|
'
+
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[contains(@class,"edit")]
'
+
'
)
'
;
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
this
.
getListItemInputCss
=
function
(
index
)
{
return
[
this
.
getListItemCss
(
index
,
'
input.edit
'
),
this
.
getListItemCss
(
index
,
'
input[type="text"]
'
),
].
join
(
'
,
'
);
};
};
tests/test.js
View file @
26ceac57
...
...
@@ -8,7 +8,9 @@ var PageLaxMode = require('./pageLaxMode');
var
TestOperations
=
require
(
'
./testOperations
'
);
module
.
exports
.
todoMVCTest
=
function
(
frameworkName
,
baseUrl
,
speedMode
,
laxMode
,
browserName
)
{
test
.
describe
(
'
TodoMVC -
'
+
frameworkName
,
function
()
{
var
TODO_ITEM_ONE
=
'
buy some cheese
'
;
var
TODO_ITEM_TWO
=
'
feed the cat
'
;
var
TODO_ITEM_THREE
=
'
book a doctors appointment
'
;
...
...
@@ -22,7 +24,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
.
then
(
function
()
{
if
(
done
instanceof
Function
)
{
done
();
}
;
}
});
}
...
...
@@ -44,11 +46,11 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
=
laxMode
?
new
PageLaxMode
(
browser
)
:
new
Page
(
browser
);
testOps
=
new
TestOperations
(
page
);
return
page
.
ensureAppIsVisible
()
return
page
.
ensureAppIsVisible
AndLoaded
()
.
then
(
function
()
{
if
(
done
instanceof
Function
)
{
done
();
}
;
}
});
}
...
...
@@ -67,30 +69,15 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
return
browser
.
quit
()
.
then
(
function
()
{
if
(
done
instanceof
Function
)
{
done
();
};
if
(
done
instanceof
Function
)
{
done
();
}
});
}
if
(
speedMode
)
{
test
.
before
(
launchBrowser
);
test
.
after
(
closeBrowser
);
test
.
beforeEach
(
function
(
done
)
{
return
page
.
getItemElements
()
.
then
(
function
(
items
)
{
if
(
items
.
length
==
0
)
{
return
;
}
// find any items that are not complete
page
.
getNonCompletedItemElements
()
.
then
(
function
(
nonCompleteItems
)
{
if
(
nonCompleteItems
.
length
>
0
)
{
return
page
.
clickMarkAllCompletedCheckBox
();
}
})
return
page
.
clickClearCompleteButton
();
})
test
.
afterEach
(
function
(
done
)
{
return
browser
.
executeScript
(
'
window.localStorage && localStorage.clear(); location.reload(true);
'
)
.
then
(
function
()
{
done
();
});
});
}
else
{
...
...
@@ -99,38 +86,43 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
printCapturedLogs
()
.
then
(
function
()
{
return
closeBrowser
(
done
);
})
})
;
});
}
test
.
describe
(
'
When page is initially opened
'
,
function
()
{
test
.
it
(
'
should focus on the todo input field
'
,
function
(
done
)
{
testOps
.
assert
FocussedElement
(
'
new-todo
'
)
testOps
.
assert
NewInputFocused
(
)
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
No Todos
'
,
function
()
{
test
.
it
(
'
should hide #main and #footer
'
,
function
(
done
)
{
testOps
.
assertItemCount
(
0
);
testOps
.
assertMainSection
IsHidden
(
);
testOps
.
assertFooter
IsHidden
(
)
testOps
.
assertMainSection
Visibility
(
false
);
testOps
.
assertFooter
Visibility
(
false
)
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
New Todo
'
,
function
()
{
test
.
it
(
'
should allow me to add todo items
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertItems
([
TODO_ITEM_ONE
]);
testOps
.
assertItems
([
TODO_ITEM_ONE
]);
page
.
enterItem
(
TODO_ITEM_TWO
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
])
.
then
(
function
()
{
done
();
});
});
test
.
it
(
'
should clear text input field when an item is added
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertItemInputFieldText
(
''
)
testOps
.
assert
New
ItemInputFieldText
(
''
)
.
then
(
function
()
{
done
();
});
});
...
...
@@ -151,20 +143,20 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
test
.
it
(
'
should show #main and #footer when items added
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertMainSection
IsVisible
(
);
testOps
.
assertFooter
IsVisible
(
)
testOps
.
assertMainSection
Visibility
(
true
);
testOps
.
assertFooter
Visibility
(
true
)
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
Mark all as completed
'
,
function
()
{
test
.
beforeEach
(
createStandardItems
);
test
.
it
(
'
should allow me to mark all items as completed
'
,
function
(
done
)
{
page
.
clickMarkAllCompletedCheckBox
();
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemAtIndexIsCompleted
(
1
);
testOps
.
assertItemAtIndexIsCompleted
(
2
)
testOps
.
assertItemCompletedStates
([
true
,
true
,
true
])
.
then
(
function
()
{
done
();
});
});
...
...
@@ -175,7 +167,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
toggleItemAtIndex
(
2
);
// ensure checkall is in the correct state
testOps
.
assertCompleteAll
IsChecked
(
)
testOps
.
assertCompleteAll
CheckedStatus
(
true
)
.
then
(
function
()
{
done
();
});
});
...
...
@@ -183,39 +175,37 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
clickMarkAllCompletedCheckBox
();
page
.
clickMarkAllCompletedCheckBox
();
testOps
.
assertItemAtIndexIsNotCompleted
(
0
);
testOps
.
assertItemAtIndexIsNotCompleted
(
1
);
testOps
.
assertItemAtIndexIsNotCompleted
(
2
)
testOps
.
assertItemCompletedStates
([
false
,
false
,
false
])
.
then
(
function
()
{
done
();
});
});
test
.
it
(
'
complete all checkbox should update state when items are completed / cleared
'
,
function
(
done
)
{
page
.
clickMarkAllCompletedCheckBox
();
testOps
.
assertCompleteAll
IsChecked
(
);
testOps
.
assertCompleteAll
CheckedStatus
(
true
);
// all items are complete, now mark one as not-complete
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertCompleteAll
IsClear
(
);
testOps
.
assertCompleteAll
CheckedStatus
(
false
);
// now mark as complete, so that once again all items are completed
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertCompleteAll
IsChecked
(
)
testOps
.
assertCompleteAll
CheckedStatus
(
true
)
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
Item
'
,
function
()
{
test
.
it
(
'
should allow me to mark items as complete
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
page
.
enterItem
(
TODO_ITEM_TWO
);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemAtIndexIsNotCompleted
(
1
);
testOps
.
assertItemCompletedStates
([
true
,
false
]);
page
.
toggleItemAtIndex
(
1
);
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemAtIndexIsCompleted
(
1
)
testOps
.
assertItemCompletedStates
([
true
,
true
])
.
then
(
function
()
{
done
();
});
});
...
...
@@ -224,17 +214,17 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
enterItem
(
TODO_ITEM_TWO
);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemAtIndexIsNotCompleted
(
1
);
testOps
.
assertItemCompletedStates
([
true
,
false
]);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertItemAtIndexIsNotCompleted
(
0
);
testOps
.
assertItemAtIndexIsNotCompleted
(
1
)
testOps
.
assertItemCompletedStates
([
false
,
false
])
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
Editing
'
,
function
(
done
)
{
test
.
describe
(
'
Editing
'
,
function
()
{
test
.
beforeEach
(
function
(
done
)
{
createStandardItems
();
page
.
doubleClickItemAtIndex
(
1
)
...
...
@@ -242,8 +232,8 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
});
test
.
it
(
'
should focus the input
'
,
function
(
done
)
{
testOps
.
assertInputFocused
();
testOps
.
assertNewInput
NotFocused
()
testOps
.
assertI
temI
nputFocused
();
testOps
.
assertNewInput
Blurred
()
// Unnecessary? The HTML spec dictates that only one element can be focused.
.
then
(
function
()
{
done
();
});
});
...
...
@@ -255,7 +245,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
test
.
it
(
'
should save edits on enter
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
'
buy some sausages
'
+
webdriver
.
Key
.
ENTER
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
'
buy some sausages
'
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
'
buy some sausages
'
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
...
...
@@ -263,30 +253,32 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
editItemAtIndex
(
1
,
'
buy some sausages
'
);
// click a toggle button so that the blur() event is fired
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
'
buy some sausages
'
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
'
buy some sausages
'
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
test
.
it
(
'
should trim entered text
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
'
buy some sausages
'
+
webdriver
.
Key
.
ENTER
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
'
buy some sausages
'
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
'
buy some sausages
'
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
test
.
it
(
'
should remove the item if an empty text string was entered
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
webdriver
.
Key
.
ENTER
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
test
.
it
(
'
should cancel edits on escape
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
'
foo
'
+
webdriver
.
Key
.
ESCAPE
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
Counter
'
,
function
()
{
test
.
it
(
'
should display the current number of todo items
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertItemCountText
(
'
1 item left
'
);
...
...
@@ -294,9 +286,11 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
testOps
.
assertItemCountText
(
'
2 items left
'
)
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
Clear completed button
'
,
function
()
{
test
.
beforeEach
(
createStandardItems
);
test
.
it
(
'
should display the correct text
'
,
function
(
done
)
{
...
...
@@ -309,32 +303,27 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
toggleItemAtIndex
(
1
);
page
.
clickClearCompleteButton
();
testOps
.
assertItemCount
(
2
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
test
.
it
(
'
should be hidden when there are no items that are completed
'
,
function
(
done
)
{
page
.
toggleItemAtIndex
(
1
);
testOps
.
assertClearCompleteButton
IsVisible
(
);
testOps
.
assertClearCompleteButton
Visibility
(
true
);
page
.
clickClearCompleteButton
();
testOps
.
assertClearCompleteButton
IsHidden
(
)
testOps
.
assertClearCompleteButton
Visibility
(
false
)
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
Persistence
'
,
function
()
{
test
.
it
(
'
should persist its data
'
,
function
(
done
)
{
function
stateTest
()
{
// wait until things are visible
browser
.
wait
(
function
()
{
return
page
.
getVisibleLabelText
().
then
(
function
(
labels
)
{
return
labels
.
length
>
0
;
});
},
5000
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
]);
testOps
.
assertItemAtIndexIsCompleted
(
1
);
return
testOps
.
assertItemAtIndexIsNotCompleted
(
0
);
testOps
.
assertItemCount
(
2
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
]);
return
testOps
.
assertItemCompletedStates
([
false
,
true
]);
}
// set up state
...
...
@@ -351,15 +340,17 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
stateTest
()
.
then
(
function
()
{
done
();
});
});
});
test
.
describe
(
'
Routing
'
,
function
()
{
test
.
beforeEach
(
createStandardItems
);
test
.
it
(
'
should allow me to display active items
'
,
function
(
done
)
{
page
.
toggleItemAtIndex
(
1
);
page
.
filterByActiveItems
();
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
page
.
ITEM_HIDDEN_OR_REMOVED
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
return
done
();
});
});
...
...
@@ -367,18 +358,18 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
toggleItemAtIndex
(
1
);
page
.
filterByActiveItems
();
page
.
filterByCompletedItems
();
testOps
.
assertItems
([
TODO_ITEM_TWO
]);
// should show completed items
testOps
.
assertItems
([
page
.
ITEM_HIDDEN_OR_REMOVED
,
TODO_ITEM_TWO
]);
// should show completed items
page
.
back
();
// then active items
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
]);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
page
.
ITEM_HIDDEN_OR_REMOVED
,
TODO_ITEM_THREE
]);
page
.
back
();
// then all items
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
test
.
it
(
'
should allow me to display completed items
'
,
function
(
done
)
{
page
.
toggleItemAtIndex
(
1
);
page
.
filterByCompletedItems
();
testOps
.
assertItems
([
TODO_ITEM_TWO
]);
testOps
.
assertItems
([
page
.
ITEM_HIDDEN_OR_REMOVED
,
TODO_ITEM_TWO
]);
page
.
filterByAllItems
()
// TODO: why
.
then
(
function
()
{
done
();
});
});
...
...
@@ -390,7 +381,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
filterByActiveItems
();
page
.
filterByCompletedItems
();
page
.
filterByAllItems
();
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
});
...
...
@@ -403,6 +394,9 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
testOps
.
assertFilterAtIndexIsSelected
(
2
)
.
then
(
function
()
{
done
();
});
});
});
});
};
tests/testOperations.js
View file @
26ceac57
'
use strict
'
;
var
assert
=
require
(
'
assert
'
);
function
TestOperations
(
page
)
{
// unfortunately webdriver does not have a decent API for determining if an
// element exists. The standard approach is to obtain an array of elements
// and test that the length is zero. In this case the item is hidden if
// it is either not in the DOM, or is in the DOM but not visible.
function
testIsHidden
(
elements
,
name
)
{
if
(
elements
.
length
===
1
)
{
elements
[
0
].
isDisplayed
().
then
(
function
(
isDisplayed
)
{
assert
(
!
isDisplayed
,
'
the
'
+
name
+
'
element should be hidden
'
);
});
}
}
function
testIsVisible
(
elements
,
name
)
{
assert
.
equal
(
elements
.
length
,
1
);
return
elements
[
0
].
isDisplayed
()
.
then
(
function
(
isDisplayed
)
{
assert
(
isDisplayed
,
'
the
'
+
name
+
'
element should be displayed
'
);
});
}
this
.
assertNewInputNotFocused
=
function
()
{
return
page
.
getFocussedElementIdOrClass
()
.
then
(
function
(
focussedElementIdOrClass
)
{
assert
.
equal
(
focussedElementIdOrClass
.
indexOf
(
'
new-todo
'
),
-
1
);
});
this
.
assertItemInputFocused
=
function
()
{
return
page
.
waitForFocusedElement
(
page
.
getEditingListItemInputCss
(),
'
Expected the item input to be focused
'
);
};
this
.
assertInputFocused
=
function
()
{
return
page
.
getFocussedTagName
()
.
then
(
function
(
name
)
{
assert
.
equal
(
name
,
'
input
'
,
'
input does not have focus
'
);
});
this
.
assertNewInputFocused
=
function
()
{
return
page
.
waitForFocusedElement
(
page
.
getNewInputCss
());
};
this
.
assertFocussedElement
=
function
(
expectedIdentifier
)
{
return
page
.
getFocussedElementIdOrClass
()
.
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
()
{
return
page
.
tryGetClearCompleteButton
()
.
then
(
function
(
element
)
{
return
testIsHidden
(
element
,
'
clear completed items button
'
);
},
function
(
_error
)
{
assert
(
_error
.
code
===
7
,
'
error accessing clear completed items button, error:
'
+
_error
.
message
);
});
};
this
.
assertClearCompleteButtonIsVisible
=
function
()
{
return
page
.
waitForVisibleElement
(
function
()
{
return
page
.
tryGetClearCompleteButton
();
})
.
then
(
function
(
clearCompleteButton
)
{
assert
(
clearCompleteButton
,
'
the clear completed items button element should be displayed
'
);
});
this
.
assertNewInputBlurred
=
function
()
{
return
page
.
waitForBlurredElement
(
page
.
getNewInputCss
());
};
this
.
assertItemCount
=
function
(
itemCount
)
{
return
page
.
getItemElements
()
.
then
(
function
(
toDoItems
)
{
assert
.
equal
(
toDoItems
.
length
,
itemCount
,
itemCount
+
'
items expected in the todo list,
'
+
toDoItems
.
length
+
'
items observed
'
);
});
return
itemCount
===
0
?
page
.
waitForMainSectionRemovedOrEmpty
()
:
page
.
waitForListItemCount
(
itemCount
);
};
this
.
assertClearCompleteButtonText
=
function
(
buttonText
)
{
return
page
.
waitForVisibleElement
(
function
()
{
return
page
.
tryGetClearCompleteButton
();
})
.
then
(
function
(
clearCompleteButton
)
{
return
clearCompleteButton
.
getText
();
})
.
then
(
function
(
text
)
{
return
assert
.
equal
(
text
,
buttonText
);
});
return
page
.
waitForClearCompleteButton
()
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
buttonText
,
'
Expected clear button text to be
'
+
buttonText
));
};
this
.
assertMainSectionIsHidden
=
function
()
{
return
page
.
tryGetMainSectionElement
()
.
then
(
function
(
mainSection
)
{
return
testIsHidden
(
mainSection
,
'
main
'
);
});
this
.
assertClearCompleteButtonVisibility
=
function
(
shouldBeVisible
)
{
var
failMsg
=
'
Expected the clear completed items button to be
'
+
(
shouldBeVisible
?
'
visible
'
:
'
hidden
'
);
return
page
.
waitForVisibility
(
shouldBeVisible
,
page
.
getClearCompletedButtonCss
(),
failMsg
);
};
this
.
assertFooterIsHidden
=
function
()
{
return
page
.
tryGetFooterElement
()
.
then
(
function
(
footer
)
{
return
testIsHidden
(
footer
,
'
footer
'
);
});
this
.
assertMainSectionVisibility
=
function
(
shouldBeVisible
)
{
var
failMsg
=
'
Expected main section to be
'
+
(
shouldBeVisible
?
'
visible
'
:
'
hidden
'
);
return
page
.
waitForVisibility
(
shouldBeVisible
,
page
.
getMainSectionCss
(),
failMsg
);
};
this
.
assertMainSectionIsVisible
=
function
()
{
return
page
.
tryGetMainSectionElement
()
.
then
(
function
(
mainSection
)
{
return
testIsVisible
(
mainSection
,
'
main
'
);
});
this
.
assertFooterVisibility
=
function
(
shouldBeVisible
)
{
var
failMsg
=
'
Expected footer to be
'
+
(
shouldBeVisible
?
'
visible
'
:
'
hidden
'
);
return
page
.
waitForVisibility
(
shouldBeVisible
,
page
.
getFooterSectionCss
(),
failMsg
);
};
//TODO: fishy!
this
.
assertItemToggleIsHidden
=
function
(
index
)
{
return
page
.
tryGetToggleForItemAtIndex
(
index
)
.
then
(
function
(
toggleItem
)
{
return
testIsHidden
(
toggleItem
,
'
item-toggle
'
);
});
return
page
.
waitForVisibility
(
false
,
page
.
getListItemToggleCss
(
index
),
'
Expected the item toggle button to be hidden
'
);
};
this
.
assertItemLabelIsHidden
=
function
(
index
)
{
return
page
.
tryGetItemLabelAtIndex
(
index
)
.
then
(
function
(
toggleItem
)
{
return
testIsHidden
(
toggleItem
,
'
item-label
'
);
});
return
page
.
waitForVisibility
(
false
,
page
.
getListItemLabelCss
(
index
),
'
Expected the item label to be hidden
'
);
};
this
.
assertFooterIsVisible
=
function
()
{
return
page
.
tryGetFooterElement
()
.
then
(
function
(
footer
)
{
return
testIsVisible
(
footer
,
'
footer
'
);
});
this
.
assertNewItemInputFieldText
=
function
(
text
)
{
return
page
.
waitForNewItemInputField
()
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
text
,
'
Expected the new item input text field contents to be
'
+
text
));
};
this
.
assertItemInputFieldText
=
function
(
text
)
{
return
page
.
getItemInputField
().
getText
()
.
then
(
function
(
inputFieldText
)
{
assert
.
equal
(
inputFieldText
,
text
);
});
this
.
assertItemText
=
function
(
itemIndex
,
text
)
{
return
page
.
waitForItemLabel
(
itemIndex
)
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
text
,
'
Expected the item label to be
'
+
text
));
};
this
.
assertItemText
=
function
(
itemIndex
,
textToAssert
)
{
return
page
.
getItemLabelAtIndex
(
itemIndex
).
getText
()
.
then
(
function
(
text
)
{
assert
.
equal
(
text
,
textToAssert
,
'
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
this
.
assertItems
=
function
(
textArray
)
{
return
page
.
getVisibleLabelText
()
.
then
(
function
(
visibleText
)
{
assert
.
deepEqual
(
visibleText
.
sort
(),
textArray
.
sort
());
return
page
.
getListItems
().
then
(
function
(
items
)
{
if
(
items
.
length
<
textArray
.
length
)
{
// This means that the framework removes rather than hides list items
textArray
=
textArray
.
filter
(
function
(
item
)
{
return
item
!==
page
.
ITEM_HIDDEN_OR_REMOVED
;
});
}
var
ret
;
textArray
.
forEach
(
function
(
text
,
i
)
{
if
(
text
===
page
.
ITEM_HIDDEN_OR_REMOVED
)
{
return
;
}
var
promise
=
page
.
waitForTextContent
(
text
,
'
Expected item text to be
'
+
text
,
items
[
i
]);
ret
=
ret
?
ret
.
then
(
promise
)
:
promise
;
});
};
this
.
assertItemCountText
=
function
(
textToAssert
)
{
return
page
.
getItemsCountElement
().
getText
()
.
then
(
function
(
text
)
{
assert
.
equal
(
text
.
trim
(),
textToAssert
,
'
the item count text was incorrect
'
);
return
ret
;
});
};
// tests for the presence of the 'completed' CSS class for the item at the given index
this
.
assertItemAtIndexIsCompleted
=
function
(
index
)
{
return
page
.
getItemElements
()
.
then
(
function
(
toDoItems
)
{
return
toDoItems
[
index
].
getAttribute
(
'
class
'
);
})
.
then
(
function
(
cssClass
)
{
var
failMsg
=
'
the item at index
'
+
index
+
'
should have been marked as completed
'
;
assert
(
cssClass
.
indexOf
(
'
completed
'
)
!==
-
1
,
failMsg
);
});
this
.
assertItemCountText
=
function
(
text
)
{
return
page
.
waitForElement
(
page
.
getItemCountCss
())
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
text
,
'
Expected item count text to be
'
+
text
));
};
this
.
assertItemAtIndexIsNotCompleted
=
function
(
index
)
{
return
page
.
getItemElements
()
.
then
(
function
(
toDoItems
)
{
return
toDoItems
[
index
].
getAttribute
(
'
class
'
);
})
.
then
(
function
(
cssClass
)
{
// the maria implementation uses an 'incompleted' CSS class which is redundant
// TODO: this should really be moved into the pageLaxMode
var
failMsg
=
'
the item at index
'
+
index
+
'
should not have been marked as completed
'
;
assert
(
cssClass
.
indexOf
(
'
completed
'
)
===
-
1
||
cssClass
.
indexOf
(
'
incompleted
'
)
!==
-
1
,
failMsg
);
});
this
.
assertItemCompletedStates
=
function
(
completedStates
)
{
return
page
.
waitForElement
(
page
.
getListItemsWithCompletedStatesCss
(
completedStates
),
'
Item completed states were incorrect
'
);
};
this
.
assertFilterAtIndexIsSelected
=
function
(
selectedIndex
)
{
return
page
.
findByXpath
(
page
.
getSelectedFilterXPathByIndex
(
selectedIndex
+
1
))
.
then
(
function
(
elm
)
{
var
failMsg
=
'
the filter / route at index
'
+
selectedIndex
+
'
should have been selected
'
;
assert
.
notEqual
(
elm
,
undefined
,
failMsg
);
});
return
page
.
waitForElement
(
page
.
getSelectedFilterCss
(
selectedIndex
),
'
Expexted the filter / route at index
'
+
selectedIndex
+
'
to be selected
'
);
};
this
.
assertCompleteAllIsClear
=
function
()
{
return
page
.
getMarkAllCompletedCheckBox
()
.
then
(
function
(
markAllCompleted
)
{
return
markAllCompleted
.
isSelected
();
})
.
then
(
function
(
isSelected
)
{
assert
(
!
isSelected
,
'
the mark-all-completed checkbox should be clear
'
);
});
};
this
.
assertCompleteAllIsChecked
=
function
()
{
return
page
.
getMarkAllCompletedCheckBox
()
.
then
(
function
(
markAllCompleted
)
{
return
markAllCompleted
.
isSelected
();
})
.
then
(
function
(
isSelected
)
{
assert
(
isSelected
,
'
the mark-all-completed checkbox should be checked
'
);
});
this
.
assertCompleteAllCheckedStatus
=
function
(
shouldBeChecked
)
{
var
failMsg
=
'
Expected the mark-all-completed checkbox to be
'
+
shouldBeChecked
?
'
checked
'
:
'
unchecked
'
;
return
page
.
waitForMarkAllCompletedCheckBox
()
.
then
(
page
.
waitForCheckedStatus
.
bind
(
page
,
shouldBeChecked
,
failMsg
));
};
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment