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
'
;
'
use strict
'
;
var
webdriver
=
require
(
'
selenium-webdriver
'
);
var
webdriver
=
require
(
'
selenium-webdriver
'
);
var
until
=
require
(
'
selenium-webdriver/lib/until
'
);
var
idSelectors
=
true
;
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
)
{
// Unique symbols
return
browser
.
findElements
(
webdriver
.
By
.
xpath
(
xpath
)
);
var
ELEMENT_MISSING
=
Object
.
freeze
({}
);
}
;
var
ITEM_HIDDEN_OR_REMOVED
=
Object
.
freeze
({})
;
this
.
findByXpath
=
function
(
xpath
)
{
module
.
exports
=
function
Page
(
browser
)
{
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
};
this
.
getTodoListXpath
=
function
()
{
// CSS ELEMENT SELECTORS
return
idSelectors
?
'
//ul[@id="todo-list"]
'
:
'
//ul[contains(@class, "todo-list")]
'
;
};
this
.
getMainSectionXpath
=
function
()
{
this
.
getMainSectionCss
=
function
()
{
return
classOrId
+
'
main
'
;
};
return
idSelectors
?
'
//section[@id="main"]
'
:
'
//section[contains(@class, "main")]
'
;
};
this
.
getFooterSectionXpath
=
function
()
{
this
.
getFooterSectionCss
=
function
()
{
return
'
footer
'
+
classOrId
+
'
footer
'
;
};
return
idSelectors
?
'
//footer[@id="footer"]
'
:
'
//footer[contains(@class, "footer")]
'
;
};
this
.
getCompletedButtonXpath
=
function
()
{
this
.
getClearCompletedButtonCss
=
function
()
{
return
'
button
'
+
classOrId
+
'
clear-completed
'
;
};
return
idSelectors
?
'
//button[@id="clear-completed"]
'
:
'
//button[contains(@class, "clear-completed")]
'
;
};
this
.
getNewInputXpath
=
function
()
{
this
.
getNewInputCss
=
function
()
{
return
'
input
'
+
classOrId
+
'
new-todo
'
;
};
return
idSelectors
?
'
//input[@id="new-todo"]
'
:
'
//input[contains(@class,"new-todo")]
'
;
};
this
.
getToggleAllXpath
=
function
()
{
this
.
getToggleAllCss
=
function
()
{
return
'
input
'
+
classOrId
+
'
toggle-all
'
;
};
return
idSelectors
?
'
//input[@id="toggle-all"]
'
:
'
//input[contains(@class,"toggle-all")]
'
;
};
this
.
getCountXpath
=
function
()
{
this
.
getItemCountCss
=
function
()
{
return
'
span
'
+
classOrId
+
'
todo-count
'
;
};
return
idSelectors
?
'
//span[@id="todo-count"]
'
:
'
//span[contains(@class, "todo-count")]
'
;
};
this
.
getFiltersElementXpath
=
function
()
{
this
.
getFilterCss
=
function
(
index
)
{
return
classOrId
+
'
filters li:nth-of-type(
'
+
(
index
+
1
)
+
'
) a
'
;
};
return
idSelectors
?
'
//*[@id="filters"]
'
:
'
//*[contains(@class, "filters")]
'
;
};
this
.
getFilterXpathByIndex
=
function
(
index
)
{
this
.
getSelectedFilterCss
=
function
(
index
)
{
return
this
.
getFilterCss
(
index
)
+
'
.selected
'
;
};
return
this
.
getFiltersElementXpath
()
+
'
/li[
'
+
index
+
'
]/a
'
;
};
this
.
getSelectedFilterXPathByIndex
=
function
(
index
)
{
this
.
getFilterAllCss
=
function
()
{
return
this
.
getFilterCss
(
0
);
};
return
this
.
getFilterXpathByIndex
(
index
)
+
'
[contains(@class, "selected")]
'
;
};
this
.
getFilterAllXpath
=
function
()
{
this
.
getFilterActiveCss
=
function
()
{
return
this
.
getFilterCss
(
1
);
};
return
this
.
getFilterXpathByIndex
(
1
);
};
this
.
getFilterActiveXpath
=
function
()
{
this
.
getFilterCompletedCss
=
function
()
{
return
this
.
getFilterCss
(
2
);
};
return
this
.
getFilterXpathByIndex
(
2
);
};
this
.
getFilterCompletedXpath
=
function
()
{
this
.
getListCss
=
function
(
suffixCss
)
{
return
'
ul
'
+
classOrId
+
'
todo-list
'
+
(
suffixCss
||
''
);
};
return
this
.
getFilterXpathByIndex
(
3
);
};
this
.
xPathForItemAtIndex
=
function
(
index
)
{
this
.
getListItemCss
=
function
(
index
,
suffixCss
,
excludeParentSelector
)
{
// why is XPath the only language silly enough to be 1-indexed?
suffixCss
=
(
index
===
undefined
?
''
:
'
:nth-of-type(
'
+
(
index
+
1
)
+
'
)
'
)
+
(
suffixCss
||
''
);
return
this
.
getTodoListXpath
()
+
'
/li[
'
+
(
index
+
1
)
+
'
]
'
;
return
excludeParentSelector
?
'
li
'
+
suffixCss
:
this
.
getListCss
(
'
li
'
+
suffixCss
)
;
};
};
// ----------------- navigation methods
this
.
getListItemToggleCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
input.toggle
'
);
};
this
.
back
=
function
()
{
this
.
getListItemLabelCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
label
'
);
};
return
browser
.
navigate
().
back
();
};
// ----------------- 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
this
.
getListItemInputCss
=
function
(
index
)
{
return
this
.
getListItemCss
(
index
,
'
input.edit
'
);
};
// 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
.
tryGetMainSectionElement
=
function
()
{
this
.
getEditingListItemInputCss
=
function
()
{
return
this
.
getListItemCss
(
undefined
,
'
.editing input.edit
'
);
};
return
this
.
tryFindByXpath
(
this
.
getMainSectionXpath
());
};
this
.
tryGetFooterElement
=
function
()
{
// This CSS selector returns the _last_ element of a list that exactly matches the provided list of completed states
return
this
.
tryFindByXpath
(
this
.
getFooterSectionXpath
());
// 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
()
{
// PUBLIC SYMBOLS
return
this
.
findByXpath
(
this
.
getCompletedButtonXpath
());
};
this
.
tryGetToggleForItemAtIndex
=
function
(
index
)
{
this
.
ITEM_HIDDEN_OR_REMOVED
=
ITEM_HIDDEN_OR_REMOVED
;
var
xpath
=
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[contains(@class,"toggle")]
'
;
return
this
.
findByXpath
(
xpath
);
};
this
.
tryGetItemLabelAtIndex
=
function
(
index
)
{
// NAVIGATION
return
this
.
findByXpath
(
this
.
xPathForItemAtIndex
(
index
)
+
'
//label
'
);
this
.
back
=
function
()
{
return
browser
.
navigate
().
back
();
};
};
// ----------------- DOM element access methods
// ELEMENT RETREIVAL
this
.
getActiveElement
=
function
()
{
// wait* methods guarantees to return an element, or throw an exception
return
browser
.
switchTo
().
activeElement
();
// 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
(
)
{
this
.
waitForElement
=
function
(
css
,
failMsg
,
timeout
)
{
return
this
.
getActiveElement
().
getTagName
(
);
return
browser
.
wait
(
until
.
elementLocated
(
webdriver
.
By
.
css
(
css
)),
timeout
||
DEFAULT_TIMEOUT
,
failMsg
);
};
};
this
.
getFocussedElementIdOrClass
=
function
()
{
this
.
waitForFocusedElement
=
function
(
css
,
failMsg
)
{
return
this
.
getActiveElement
()
return
this
.
waitForElement
(
css
+
'
:focus
'
,
failMsg
);
.
getAttribute
(
idSelectors
?
'
id
'
:
'
class
'
);
};
};
this
.
getEditInputForItemAtIndex
=
function
(
index
)
{
this
.
waitForBlurredElement
=
function
(
css
,
failMsg
)
{
var
xpath
=
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[contains(@class,"edit")]
'
;
return
this
.
waitForElement
(
css
+
'
:not(:focus)
'
,
failMsg
);
return
this
.
findByXpath
(
xpath
);
};
};
this
.
getItemInputField
=
function
()
{
this
.
waitForListItemCount
=
function
(
count
)
{
return
this
.
findByXpath
(
this
.
getNewInputXpath
());
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
()
{
this
.
waitForClearCompleteButton
=
function
()
{
return
this
.
findByXpath
(
this
.
getToggleAllXpath
());
return
this
.
waitForElement
(
this
.
getClearCompletedButtonCss
());
};
};
this
.
getItemElements
=
function
(
)
{
this
.
waitForToggleForItem
=
function
(
index
)
{
return
this
.
tryFindByXpath
(
this
.
getTodoListXpath
()
+
'
/li
'
);
return
this
.
waitForElement
(
this
.
getListItemToggleCss
(
index
)
);
};
};
this
.
getNonCompletedItemElements
=
function
(
)
{
this
.
waitForItemLabel
=
function
(
index
)
{
return
this
.
tryFindByXpath
(
this
.
getTodoListXpath
()
+
'
/li[not(contains(@class,"completed"))]
'
);
return
this
.
waitForElement
(
this
.
getListItemLabelCss
(
index
)
);
};
};
this
.
getItemsCountElement
=
function
()
{
this
.
waitForNewItemInputField
=
function
()
{
return
this
.
findByXpath
(
this
.
getCountXpath
());
return
this
.
waitForElement
(
this
.
getNewInputCss
());
};
};
this
.
getItemLabelAtIndex
=
function
(
index
)
{
this
.
waitForMarkAllCompletedCheckBox
=
function
(
)
{
return
this
.
findByXpath
(
this
.
xPathForItemAtIndex
(
index
)
+
'
//label
'
);
return
this
.
waitForElement
(
this
.
getToggleAllCss
()
);
};
};
this
.
getItemLabels
=
function
()
{
this
.
getListItems
=
function
()
{
var
xpath
=
this
.
getTodoListXpath
()
+
'
/li//label
'
;
return
this
.
getElements
(
this
.
getListItemCss
());
return
this
.
tryFindByXpath
(
xpath
);
};
};
this
.
getVisibleLabelText
=
function
()
{
this
.
waitForVisibility
=
function
(
shouldBeVisible
,
css
,
failMsg
)
{
var
self
=
this
;
if
(
shouldBeVisible
)
{
return
this
.
getVisibileLabelIndicies
()
return
this
.
waitForElement
(
css
,
failMsg
)
.
then
(
function
(
indicies
)
{
.
then
(
function
(
element
)
{
return
webdriver
.
promise
.
map
(
indicies
,
function
(
elmIndex
)
{
return
browser
.
wait
(
until
.
elementIsVisible
(
element
),
DEFAULT_TIMEOUT
,
failMsg
);
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
;
});
});
});
}
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
)
{
this
.
waitForMainSectionRemovedOrEmpty
=
function
()
{
var
foundVisibleElement
;
return
this
.
waitForElement
(
this
.
getMainSectionCss
(),
undefined
,
REMOVED_TIMEOUT
)
timeout
=
timeout
||
500
;
.
catch
(
function
()
{
return
ELEMENT_MISSING
;
})
.
then
(
function
(
elementOrError
)
{
return
elementOrError
===
ELEMENT_MISSING
?
ELEMENT_MISSING
:
this
.
waitForListItemCount
(
0
);
}.
bind
(
this
));
};
return
browser
.
wait
(
function
()
{
this
.
waitForCheckedStatus
=
function
(
shouldBeChecked
,
failMsg
,
element
)
{
foundVisibleElement
=
getElementFn
();
var
condition
=
shouldBeChecked
?
'
elementIsSelected
'
:
'
elementIsNotSelected
'
;
return
foundVisibleElement
.
isDisplayed
();
return
browser
.
wait
(
until
[
condition
](
element
),
DEFAULT_TIMEOUT
,
failMsg
);
},
timeout
)
};
.
then
(
function
()
{
return
foundVisibleElement
;
})
.
thenCatch
(
function
(
err
)
{
return
false
;
});
}
this
.
getVisibileLabelIndicies
=
function
()
{
this
.
waitForTextContent
=
function
(
text
,
failMsg
,
element
)
{
var
self
=
this
;
return
browser
.
wait
(
until
.
elementTextIs
(
element
,
text
),
DEFAULT_TIMEOUT
,
failMsg
);
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
);
});
});
});
};
};
// ----------------- page actions
// 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
;
}
// try to find main element by CSS class
this
.
ensureAppIsVisibleAndLoaded
=
function
()
{
return
browser
.
isElementPresent
(
webdriver
.
By
.
css
(
'
#new-todo
'
));
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
))
},
5000
)
.
then
(
function
(
newTodoElement
)
{
.
then
(
function
(
hasFoundNewTodoElement
)
{
return
newTodoElement
.
getAttribute
(
'
id
'
);
if
(
!
hasFoundNewTodoElement
)
{
})
throw
new
Error
(
'
Unable to find application, did you start your local server?
'
);
.
then
(
function
(
newTodoElementId
)
{
}
if
(
newTodoElementId
===
'
new-todo
'
)
{
return
;
}
idSelectors
=
false
;
classOrId
=
idSelectors
?
'
#
'
:
'
.
'
;
});
});
};
};
this
.
clickMarkAllCompletedCheckBox
=
function
()
{
this
.
clickMarkAllCompletedCheckBox
=
function
()
{
return
this
.
getMarkAllCompletedCheckBox
().
then
(
function
(
checkbox
)
{
return
this
.
waitForMarkAllCompletedCheckBox
().
click
();
return
checkbox
.
click
();
});
};
};
this
.
clickClearCompleteButton
=
function
()
{
this
.
clickClearCompleteButton
=
function
()
{
var
self
=
this
;
return
this
.
waitForVisibility
(
true
,
this
.
getClearCompletedButtonCss
(),
'
Expected clear completed items button to be visible
'
)
return
self
.
waitForVisibleElement
(
function
()
{
return
self
.
tryGetClearCompleteButton
();
})
.
then
(
function
(
clearCompleteButton
)
{
.
then
(
function
(
clearCompleteButton
)
{
return
clearCompleteButton
.
click
();
clearCompleteButton
.
click
();
});
});
};
};
this
.
enterItem
=
function
(
itemText
)
{
this
.
enterItem
=
function
(
itemText
)
{
var
self
=
this
;
var
self
=
this
;
var
nItems
;
return
browser
.
wait
(
function
()
{
return
self
.
getListItems
()
var
textField
;
.
then
(
function
(
items
)
{
nItems
=
items
.
length
;
return
self
.
getItemInputField
().
then
(
function
(
itemInputField
)
{
textField
=
itemInputField
;
return
textField
.
sendKeys
(
itemText
,
webdriver
.
Key
.
ENTER
);
})
})
.
then
(
function
()
{
return
self
.
getVisibleLabelText
();
})
.
then
(
this
.
waitForNewItemInputField
.
bind
(
this
))
.
then
(
function
(
labels
)
{
.
then
(
function
(
newItemInput
)
{
if
(
labels
.
indexOf
(
itemText
.
trim
())
>=
0
)
{
return
newItemInput
.
sendKeys
(
itemText
).
then
(
function
()
{
return
newItemInput
;
});
return
true
;
})
}
.
then
(
function
(
newItemInput
)
{
return
browser
.
wait
(
function
()
{
return
textField
.
clear
().
then
(
function
()
{
// Hit Enter repeatedly until the text goes away
return
false
;
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
)
{
this
.
toggleItemAtIndex
=
function
(
index
)
{
return
this
.
tryGetToggleForItemAtIndex
(
index
).
click
();
return
this
.
waitForToggleForItem
(
index
).
click
();
};
};
this
.
editItemAtIndex
=
function
(
index
,
itemText
)
{
this
.
editItemAtIndex
=
function
(
index
,
itemText
)
{
return
this
.
getEditInputForItemAtIndex
(
index
)
return
this
.
waitForElement
(
this
.
getListItemInputCss
(
index
)
)
.
then
(
function
(
itemEditField
)
{
.
then
(
function
(
itemEditField
)
{
// send 50 delete keypresses, just to be sure the item text is deleted
return
itemEditField
.
sendKeys
(
REMOVE_TEXT_KEY_SEQ
,
itemText
);
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
);
});
});
};
};
this
.
doubleClickItemAtIndex
=
function
(
index
)
{
this
.
doubleClickItemAtIndex
=
function
(
index
)
{
return
this
.
getItemLabelAtIndex
(
index
).
then
(
function
(
itemLabel
)
{
return
this
.
waitForItemLabel
(
index
).
then
(
function
(
itemLabel
)
{
// double click is not 'natively' supported, so we need to send the
// double click is not 'natively' supported, so we need to send the event direct to the element, see:
// event direct to the element see:
// jscs:disable
// jscs:disable
// http://stackoverflow.com/questions/3982442/selenium-2-webdriver-how-to-double-click-a-table-row-which-opens-a-new-window
// http://stackoverflow.com/questions/3982442/selenium-2-webdriver-how-to-double-click-a-table-row-which-opens-a-new-window
// jscs:enable
// 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);
'
+
'
evt.initMouseEvent("dblclick",true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0,null);
'
+
'
arguments[0].dispatchEvent(evt);
'
,
itemLabel
);
'
arguments[0].dispatchEvent(evt);
'
,
itemLabel
);
});
});
};
};
this
.
filterBy
=
function
(
selectorFn
)
{
this
.
filterBy
=
function
(
filterCss
)
{
var
self
=
this
;
return
this
.
waitForElement
(
filterCss
)
.
click
()
return
browser
.
wait
(
function
()
{
.
then
(
this
.
waitForElement
.
bind
(
this
,
filterCss
+
'
.selected
'
,
undefined
,
undefined
));
return
self
.
findByXpath
(
selectorFn
()).
click
()
.
then
(
function
()
{
return
self
.
findByXpath
(
selectorFn
()).
getAttribute
(
'
class
'
);
})
.
then
(
function
(
klass
)
{
return
klass
.
indexOf
(
'
selected
'
)
!==
-
1
;
});
},
5000
);
};
};
this
.
filterByActiveItems
=
function
()
{
this
.
filterByActiveItems
=
function
()
{
return
this
.
filterBy
(
this
.
getFilterActive
Xpath
.
bind
(
this
));
return
this
.
filterBy
(
this
.
getFilterActive
Css
(
));
};
};
this
.
filterByCompletedItems
=
function
()
{
this
.
filterByCompletedItems
=
function
()
{
return
this
.
filterBy
(
this
.
getFilterCompleted
Xpath
.
bind
(
this
));
return
this
.
filterBy
(
this
.
getFilterCompleted
Css
(
));
};
};
this
.
filterByAllItems
=
function
()
{
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
'
;
'
use strict
'
;
var
webdriver
=
require
(
'
selenium-webdriver
'
);
var
Page
=
require
(
'
./page
'
);
var
Page
=
require
(
'
./page
'
);
module
.
exports
=
function
PageLaxMode
(
browser
)
{
module
.
exports
=
function
PageLaxMode
()
{
Page
.
apply
(
this
,
[
browser
]);
Page
.
apply
(
this
,
arguments
);
this
.
tryGetMainSectionElement
=
function
()
{
this
.
getMainSectionCss
=
function
()
{
return
this
.
tryFindByXpath
(
'
//section//section
'
)
;
return
'
section section
'
;
};
};
this
.
tryGetFooterElement
=
function
()
{
this
.
getFooterSectionCss
=
function
()
{
return
this
.
tryFindByXpath
(
'
//section//footer
'
)
;
return
'
section footer
'
;
};
};
this
.
getTodoListXpath
=
function
()
{
this
.
getListCss
=
function
(
suffixCss
)
{
return
'
(//section/ul | //section/div/ul | //ul[@id="todo-list"])
'
;
return
[
'
section > ul
'
,
'
section > div > ul
'
,
'
ul#todo-list
'
,
].
map
(
function
(
listCss
)
{
return
listCss
+
(
suffixCss
||
''
);
}).
join
(
'
,
'
);
};
};
this
.
getMarkAllCompletedCheckBox
=
function
()
{
this
.
getToggleAllCss
=
function
()
{
var
xpath
=
'
(//section/input[@type="checkbox"] | //section/*/input[@type="checkbox"] | //input[@id="toggle-all"])
'
;
return
[
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
'
section > input[type="checkbox"]
'
,
'
section > * > input[type="checkbox"]
'
,
'
input#toggle-all
'
,
].
join
(
'
,
'
);
};
};
this
.
tryGetClearCompleteButton
=
function
()
{
this
.
getClearCompletedButtonCss
=
function
()
{
var
xpath
=
'
(//footer/button | //footer/*/button | //button[@id="clear-completed"])
'
;
return
[
return
browser
.
findElements
(
webdriver
.
By
.
xpath
(
xpath
));
'
footer > button
'
,
'
footer > * > button
'
,
'
button#clear-completed
'
,
].
join
(
'
,
'
);
};
};
this
.
getItemsCountElement
=
function
()
{
this
.
getItemCountCss
=
function
()
{
var
xpath
=
'
(//footer/span | //footer/*/span)
'
;
return
[
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
'
footer > span
'
,
'
footer > * > span
'
,
].
join
(
'
,
'
);
};
};
this
.
getFilter
Elements
=
function
(
)
{
this
.
getFilter
Css
=
function
(
index
)
{
return
browser
.
findElements
(
webdriver
.
By
.
xpath
(
'
//footer//ul//a
'
))
;
return
'
footer ul li:nth-child(
'
+
(
index
+
1
)
+
'
) a
'
;
};
};
this
.
getNewInputCss
=
function
()
{
this
.
getItemInputField
=
function
()
{
return
[
// allow a more generic method for locating the text getItemInputField
'
header > input
'
,
var
xpath
=
'
(//header/input | //header/*/input | //input[@id="new-todo"])
'
;
'
header > * > input
'
,
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
'
input#new-todo
'
,
].
join
(
'
,
'
);
};
};
this
.
tryGetToggleForItemAtIndex
=
function
(
index
)
{
this
.
getListItemToggleCss
=
function
(
index
)
{
// the specification dictates that the checkbox should have the 'toggle' CSS class. Some implementations deviate from
return
this
.
getListItemCss
(
index
,
'
input[type="checkbox"]
'
);
// 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
.
getEditInputForItemAtIndex
=
function
(
index
)
{
this
.
getListItemInputCss
=
function
(
index
)
{
// the specification dictates that the input element that allows the user to edit a todo item should have a CSS
return
[
// class of 'edit'. In lax mode, we also look for an input of type 'text'.
this
.
getListItemCss
(
index
,
'
input.edit
'
),
var
xpath
=
'
(
'
+
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[@type="text"]
'
+
'
|
'
+
this
.
getListItemCss
(
index
,
'
input[type="text"]
'
),
this
.
xPathForItemAtIndex
(
index
)
+
'
//input[contains(@class,"edit")]
'
+
'
)
'
;
].
join
(
'
,
'
);
return
browser
.
findElement
(
webdriver
.
By
.
xpath
(
xpath
));
};
};
};
};
tests/test.js
View file @
26ceac57
...
@@ -8,7 +8,9 @@ var PageLaxMode = require('./pageLaxMode');
...
@@ -8,7 +8,9 @@ var PageLaxMode = require('./pageLaxMode');
var
TestOperations
=
require
(
'
./testOperations
'
);
var
TestOperations
=
require
(
'
./testOperations
'
);
module
.
exports
.
todoMVCTest
=
function
(
frameworkName
,
baseUrl
,
speedMode
,
laxMode
,
browserName
)
{
module
.
exports
.
todoMVCTest
=
function
(
frameworkName
,
baseUrl
,
speedMode
,
laxMode
,
browserName
)
{
test
.
describe
(
'
TodoMVC -
'
+
frameworkName
,
function
()
{
test
.
describe
(
'
TodoMVC -
'
+
frameworkName
,
function
()
{
var
TODO_ITEM_ONE
=
'
buy some cheese
'
;
var
TODO_ITEM_ONE
=
'
buy some cheese
'
;
var
TODO_ITEM_TWO
=
'
feed the cat
'
;
var
TODO_ITEM_TWO
=
'
feed the cat
'
;
var
TODO_ITEM_THREE
=
'
book a doctors appointment
'
;
var
TODO_ITEM_THREE
=
'
book a doctors appointment
'
;
...
@@ -22,7 +24,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -22,7 +24,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
.
then
(
function
()
{
.
then
(
function
()
{
if
(
done
instanceof
Function
)
{
if
(
done
instanceof
Function
)
{
done
();
done
();
}
;
}
});
});
}
}
...
@@ -44,11 +46,11 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -44,11 +46,11 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
=
laxMode
?
new
PageLaxMode
(
browser
)
:
new
Page
(
browser
);
page
=
laxMode
?
new
PageLaxMode
(
browser
)
:
new
Page
(
browser
);
testOps
=
new
TestOperations
(
page
);
testOps
=
new
TestOperations
(
page
);
return
page
.
ensureAppIsVisible
()
return
page
.
ensureAppIsVisible
AndLoaded
()
.
then
(
function
()
{
.
then
(
function
()
{
if
(
done
instanceof
Function
)
{
if
(
done
instanceof
Function
)
{
done
();
done
();
}
;
}
});
});
}
}
...
@@ -67,30 +69,15 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -67,30 +69,15 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
return
browser
return
browser
.
quit
()
.
quit
()
.
then
(
function
()
{
.
then
(
function
()
{
if
(
done
instanceof
Function
)
{
if
(
done
instanceof
Function
)
{
done
();
}
done
();
};
});
});
}
}
if
(
speedMode
)
{
if
(
speedMode
)
{
test
.
before
(
launchBrowser
);
test
.
before
(
launchBrowser
);
test
.
after
(
closeBrowser
);
test
.
after
(
closeBrowser
);
test
.
beforeEach
(
function
(
done
)
{
test
.
afterEach
(
function
(
done
)
{
return
page
.
getItemElements
()
return
browser
.
executeScript
(
'
window.localStorage && localStorage.clear(); location.reload(true);
'
)
.
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
();
})
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
}
else
{
}
else
{
...
@@ -99,38 +86,43 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -99,38 +86,43 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
printCapturedLogs
()
printCapturedLogs
()
.
then
(
function
()
{
.
then
(
function
()
{
return
closeBrowser
(
done
);
return
closeBrowser
(
done
);
})
})
;
});
});
}
}
test
.
describe
(
'
When page is initially opened
'
,
function
()
{
test
.
describe
(
'
When page is initially opened
'
,
function
()
{
test
.
it
(
'
should focus on the todo input field
'
,
function
(
done
)
{
test
.
it
(
'
should focus on the todo input field
'
,
function
(
done
)
{
testOps
.
assert
FocussedElement
(
'
new-todo
'
)
testOps
.
assert
NewInputFocused
(
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
No Todos
'
,
function
()
{
test
.
describe
(
'
No Todos
'
,
function
()
{
test
.
it
(
'
should hide #main and #footer
'
,
function
(
done
)
{
test
.
it
(
'
should hide #main and #footer
'
,
function
(
done
)
{
testOps
.
assertItemCount
(
0
);
testOps
.
assertItemCount
(
0
);
testOps
.
assertMainSection
IsHidden
(
);
testOps
.
assertMainSection
Visibility
(
false
);
testOps
.
assertFooter
IsHidden
(
)
testOps
.
assertFooter
Visibility
(
false
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
New Todo
'
,
function
()
{
test
.
describe
(
'
New Todo
'
,
function
()
{
test
.
it
(
'
should allow me to add todo items
'
,
function
(
done
)
{
test
.
it
(
'
should allow me to add todo items
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertItems
([
TODO_ITEM_ONE
]);
testOps
.
assertItems
([
TODO_ITEM_ONE
]);
page
.
enterItem
(
TODO_ITEM_TWO
);
page
.
enterItem
(
TODO_ITEM_TWO
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
])
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
test
.
it
(
'
should clear text input field when an item is added
'
,
function
(
done
)
{
test
.
it
(
'
should clear text input field when an item is added
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertItemInputFieldText
(
''
)
testOps
.
assert
New
ItemInputFieldText
(
''
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -151,20 +143,20 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -151,20 +143,20 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
test
.
it
(
'
should show #main and #footer when items added
'
,
function
(
done
)
{
test
.
it
(
'
should show #main and #footer when items added
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertMainSection
IsVisible
(
);
testOps
.
assertMainSection
Visibility
(
true
);
testOps
.
assertFooter
IsVisible
(
)
testOps
.
assertFooter
Visibility
(
true
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
Mark all as completed
'
,
function
()
{
test
.
describe
(
'
Mark all as completed
'
,
function
()
{
test
.
beforeEach
(
createStandardItems
);
test
.
beforeEach
(
createStandardItems
);
test
.
it
(
'
should allow me to mark all items as completed
'
,
function
(
done
)
{
test
.
it
(
'
should allow me to mark all items as completed
'
,
function
(
done
)
{
page
.
clickMarkAllCompletedCheckBox
();
page
.
clickMarkAllCompletedCheckBox
();
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemCompletedStates
([
true
,
true
,
true
])
testOps
.
assertItemAtIndexIsCompleted
(
1
);
testOps
.
assertItemAtIndexIsCompleted
(
2
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -175,7 +167,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -175,7 +167,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
toggleItemAtIndex
(
2
);
page
.
toggleItemAtIndex
(
2
);
// ensure checkall is in the correct state
// ensure checkall is in the correct state
testOps
.
assertCompleteAll
IsChecked
(
)
testOps
.
assertCompleteAll
CheckedStatus
(
true
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -183,39 +175,37 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -183,39 +175,37 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
clickMarkAllCompletedCheckBox
();
page
.
clickMarkAllCompletedCheckBox
();
page
.
clickMarkAllCompletedCheckBox
();
page
.
clickMarkAllCompletedCheckBox
();
testOps
.
assertItemAtIndexIsNotCompleted
(
0
);
testOps
.
assertItemCompletedStates
([
false
,
false
,
false
])
testOps
.
assertItemAtIndexIsNotCompleted
(
1
);
testOps
.
assertItemAtIndexIsNotCompleted
(
2
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
test
.
it
(
'
complete all checkbox should update state when items are completed / cleared
'
,
function
(
done
)
{
test
.
it
(
'
complete all checkbox should update state when items are completed / cleared
'
,
function
(
done
)
{
page
.
clickMarkAllCompletedCheckBox
();
page
.
clickMarkAllCompletedCheckBox
();
testOps
.
assertCompleteAll
IsChecked
(
);
testOps
.
assertCompleteAll
CheckedStatus
(
true
);
// all items are complete, now mark one as not-complete
// all items are complete, now mark one as not-complete
page
.
toggleItemAtIndex
(
0
);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertCompleteAll
IsClear
(
);
testOps
.
assertCompleteAll
CheckedStatus
(
false
);
// now mark as complete, so that once again all items are completed
// now mark as complete, so that once again all items are completed
page
.
toggleItemAtIndex
(
0
);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertCompleteAll
IsChecked
(
)
testOps
.
assertCompleteAll
CheckedStatus
(
true
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
Item
'
,
function
()
{
test
.
describe
(
'
Item
'
,
function
()
{
test
.
it
(
'
should allow me to mark items as complete
'
,
function
(
done
)
{
test
.
it
(
'
should allow me to mark items as complete
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
page
.
enterItem
(
TODO_ITEM_ONE
);
page
.
enterItem
(
TODO_ITEM_TWO
);
page
.
enterItem
(
TODO_ITEM_TWO
);
page
.
toggleItemAtIndex
(
0
);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemCompletedStates
([
true
,
false
]);
testOps
.
assertItemAtIndexIsNotCompleted
(
1
);
page
.
toggleItemAtIndex
(
1
);
page
.
toggleItemAtIndex
(
1
);
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemCompletedStates
([
true
,
true
])
testOps
.
assertItemAtIndexIsCompleted
(
1
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -224,17 +214,17 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -224,17 +214,17 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
enterItem
(
TODO_ITEM_TWO
);
page
.
enterItem
(
TODO_ITEM_TWO
);
page
.
toggleItemAtIndex
(
0
);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertItemAtIndexIsCompleted
(
0
);
testOps
.
assertItemCompletedStates
([
true
,
false
]);
testOps
.
assertItemAtIndexIsNotCompleted
(
1
);
page
.
toggleItemAtIndex
(
0
);
page
.
toggleItemAtIndex
(
0
);
testOps
.
assertItemAtIndexIsNotCompleted
(
0
);
testOps
.
assertItemCompletedStates
([
false
,
false
])
testOps
.
assertItemAtIndexIsNotCompleted
(
1
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
Editing
'
,
function
(
done
)
{
test
.
describe
(
'
Editing
'
,
function
()
{
test
.
beforeEach
(
function
(
done
)
{
test
.
beforeEach
(
function
(
done
)
{
createStandardItems
();
createStandardItems
();
page
.
doubleClickItemAtIndex
(
1
)
page
.
doubleClickItemAtIndex
(
1
)
...
@@ -242,8 +232,8 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -242,8 +232,8 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
});
});
test
.
it
(
'
should focus the input
'
,
function
(
done
)
{
test
.
it
(
'
should focus the input
'
,
function
(
done
)
{
testOps
.
assertInputFocused
();
testOps
.
assertI
temI
nputFocused
();
testOps
.
assertNewInput
NotFocused
()
testOps
.
assertNewInput
Blurred
()
// Unnecessary? The HTML spec dictates that only one element can be focused.
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -255,7 +245,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -255,7 +245,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
test
.
it
(
'
should save edits on enter
'
,
function
(
done
)
{
test
.
it
(
'
should save edits on enter
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
'
buy some sausages
'
+
webdriver
.
Key
.
ENTER
);
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
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -263,30 +253,32 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -263,30 +253,32 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
editItemAtIndex
(
1
,
'
buy some sausages
'
);
page
.
editItemAtIndex
(
1
,
'
buy some sausages
'
);
// click a toggle button so that the blur() event is fired
// click a toggle button so that the blur() event is fired
page
.
toggleItemAtIndex
(
0
);
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
();
});
.
then
(
function
()
{
done
();
});
});
});
test
.
it
(
'
should trim entered text
'
,
function
(
done
)
{
test
.
it
(
'
should trim entered text
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
'
buy some sausages
'
+
webdriver
.
Key
.
ENTER
);
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
();
});
.
then
(
function
()
{
done
();
});
});
});
test
.
it
(
'
should remove the item if an empty text string was entered
'
,
function
(
done
)
{
test
.
it
(
'
should remove the item if an empty text string was entered
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
webdriver
.
Key
.
ENTER
);
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
();
});
.
then
(
function
()
{
done
();
});
});
});
test
.
it
(
'
should cancel edits on escape
'
,
function
(
done
)
{
test
.
it
(
'
should cancel edits on escape
'
,
function
(
done
)
{
page
.
editItemAtIndex
(
1
,
'
foo
'
+
webdriver
.
Key
.
ESCAPE
);
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
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
Counter
'
,
function
()
{
test
.
describe
(
'
Counter
'
,
function
()
{
test
.
it
(
'
should display the current number of todo items
'
,
function
(
done
)
{
test
.
it
(
'
should display the current number of todo items
'
,
function
(
done
)
{
page
.
enterItem
(
TODO_ITEM_ONE
);
page
.
enterItem
(
TODO_ITEM_ONE
);
testOps
.
assertItemCountText
(
'
1 item left
'
);
testOps
.
assertItemCountText
(
'
1 item left
'
);
...
@@ -294,9 +286,11 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -294,9 +286,11 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
testOps
.
assertItemCountText
(
'
2 items left
'
)
testOps
.
assertItemCountText
(
'
2 items left
'
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
Clear completed button
'
,
function
()
{
test
.
describe
(
'
Clear completed button
'
,
function
()
{
test
.
beforeEach
(
createStandardItems
);
test
.
beforeEach
(
createStandardItems
);
test
.
it
(
'
should display the correct text
'
,
function
(
done
)
{
test
.
it
(
'
should display the correct text
'
,
function
(
done
)
{
...
@@ -309,32 +303,27 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -309,32 +303,27 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
toggleItemAtIndex
(
1
);
page
.
toggleItemAtIndex
(
1
);
page
.
clickClearCompleteButton
();
page
.
clickClearCompleteButton
();
testOps
.
assertItemCount
(
2
);
testOps
.
assertItemCount
(
2
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
])
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_THREE
])
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
test
.
it
(
'
should be hidden when there are no items that are completed
'
,
function
(
done
)
{
test
.
it
(
'
should be hidden when there are no items that are completed
'
,
function
(
done
)
{
page
.
toggleItemAtIndex
(
1
);
page
.
toggleItemAtIndex
(
1
);
testOps
.
assertClearCompleteButton
IsVisible
(
);
testOps
.
assertClearCompleteButton
Visibility
(
true
);
page
.
clickClearCompleteButton
();
page
.
clickClearCompleteButton
();
testOps
.
assertClearCompleteButton
IsHidden
(
)
testOps
.
assertClearCompleteButton
Visibility
(
false
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
Persistence
'
,
function
()
{
test
.
describe
(
'
Persistence
'
,
function
()
{
test
.
it
(
'
should persist its data
'
,
function
(
done
)
{
test
.
it
(
'
should persist its data
'
,
function
(
done
)
{
function
stateTest
()
{
function
stateTest
()
{
// wait until things are visible
testOps
.
assertItemCount
(
2
);
browser
.
wait
(
function
()
{
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
]);
return
page
.
getVisibleLabelText
().
then
(
function
(
labels
)
{
return
testOps
.
assertItemCompletedStates
([
false
,
true
]);
return
labels
.
length
>
0
;
});
},
5000
);
testOps
.
assertItems
([
TODO_ITEM_ONE
,
TODO_ITEM_TWO
]);
testOps
.
assertItemAtIndexIsCompleted
(
1
);
return
testOps
.
assertItemAtIndexIsNotCompleted
(
0
);
}
}
// set up state
// set up state
...
@@ -351,15 +340,17 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -351,15 +340,17 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
stateTest
()
stateTest
()
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
test
.
describe
(
'
Routing
'
,
function
()
{
test
.
describe
(
'
Routing
'
,
function
()
{
test
.
beforeEach
(
createStandardItems
);
test
.
beforeEach
(
createStandardItems
);
test
.
it
(
'
should allow me to display active items
'
,
function
(
done
)
{
test
.
it
(
'
should allow me to display active items
'
,
function
(
done
)
{
page
.
toggleItemAtIndex
(
1
);
page
.
toggleItemAtIndex
(
1
);
page
.
filterByActiveItems
();
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
();
});
.
then
(
function
()
{
return
done
();
});
});
});
...
@@ -367,18 +358,18 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -367,18 +358,18 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
toggleItemAtIndex
(
1
);
page
.
toggleItemAtIndex
(
1
);
page
.
filterByActiveItems
();
page
.
filterByActiveItems
();
page
.
filterByCompletedItems
();
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
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
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
();
});
.
then
(
function
()
{
done
();
});
});
});
test
.
it
(
'
should allow me to display completed items
'
,
function
(
done
)
{
test
.
it
(
'
should allow me to display completed items
'
,
function
(
done
)
{
page
.
toggleItemAtIndex
(
1
);
page
.
toggleItemAtIndex
(
1
);
page
.
filterByCompletedItems
();
page
.
filterByCompletedItems
();
testOps
.
assertItems
([
TODO_ITEM_TWO
]);
testOps
.
assertItems
([
page
.
ITEM_HIDDEN_OR_REMOVED
,
TODO_ITEM_TWO
]);
page
.
filterByAllItems
()
// TODO: why
page
.
filterByAllItems
()
// TODO: why
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -390,7 +381,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -390,7 +381,7 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
page
.
filterByActiveItems
();
page
.
filterByActiveItems
();
page
.
filterByCompletedItems
();
page
.
filterByCompletedItems
();
page
.
filterByAllItems
();
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
();
});
.
then
(
function
()
{
done
();
});
});
});
...
@@ -403,6 +394,9 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
...
@@ -403,6 +394,9 @@ module.exports.todoMVCTest = function (frameworkName, baseUrl, speedMode, laxMod
testOps
.
assertFilterAtIndexIsSelected
(
2
)
testOps
.
assertFilterAtIndexIsSelected
(
2
)
.
then
(
function
()
{
done
();
});
.
then
(
function
()
{
done
();
});
});
});
});
});
});
});
};
};
tests/testOperations.js
View file @
26ceac57
'
use strict
'
;
'
use strict
'
;
var
assert
=
require
(
'
assert
'
);
function
TestOperations
(
page
)
{
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
()
{
this
.
assertItemInputFocused
=
function
()
{
return
page
.
getFocussedElementIdOrClass
()
return
page
.
waitForFocusedElement
(
page
.
getEditingListItemInputCss
(),
'
Expected the item input to be focused
'
);
.
then
(
function
(
focussedElementIdOrClass
)
{
assert
.
equal
(
focussedElementIdOrClass
.
indexOf
(
'
new-todo
'
),
-
1
);
});
};
};
this
.
assertInputFocused
=
function
()
{
this
.
assertNewInputFocused
=
function
()
{
return
page
.
getFocussedTagName
()
return
page
.
waitForFocusedElement
(
page
.
getNewInputCss
());
.
then
(
function
(
name
)
{
assert
.
equal
(
name
,
'
input
'
,
'
input does not have focus
'
);
});
};
};
this
.
assertFocussedElement
=
function
(
expectedIdentifier
)
{
this
.
assertNewInputBlurred
=
function
()
{
return
page
.
getFocussedElementIdOrClass
()
return
page
.
waitForBlurredElement
(
page
.
getNewInputCss
());
.
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
.
assertItemCount
=
function
(
itemCount
)
{
this
.
assertItemCount
=
function
(
itemCount
)
{
return
page
.
getItemElements
()
return
itemCount
===
0
?
.
then
(
function
(
toDoItems
)
{
page
.
waitForMainSectionRemovedOrEmpty
()
:
assert
.
equal
(
toDoItems
.
length
,
itemCount
,
page
.
waitForListItemCount
(
itemCount
);
itemCount
+
'
items expected in the todo list,
'
+
toDoItems
.
length
+
'
items observed
'
);
});
};
};
this
.
assertClearCompleteButtonText
=
function
(
buttonText
)
{
this
.
assertClearCompleteButtonText
=
function
(
buttonText
)
{
return
page
.
waitForVisibleElement
(
function
()
{
return
page
.
waitForClearCompleteButton
()
return
page
.
tryGetClearCompleteButton
();
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
buttonText
,
'
Expected clear button text to be
'
+
buttonText
));
})
.
then
(
function
(
clearCompleteButton
)
{
return
clearCompleteButton
.
getText
();
})
.
then
(
function
(
text
)
{
return
assert
.
equal
(
text
,
buttonText
);
});
};
};
this
.
assertMainSectionIsHidden
=
function
()
{
this
.
assertClearCompleteButtonVisibility
=
function
(
shouldBeVisible
)
{
return
page
.
tryGetMainSectionElement
()
var
failMsg
=
'
Expected the clear completed items button to be
'
+
(
shouldBeVisible
?
'
visible
'
:
'
hidden
'
);
.
then
(
function
(
mainSection
)
{
return
page
.
waitForVisibility
(
shouldBeVisible
,
page
.
getClearCompletedButtonCss
(),
failMsg
);
return
testIsHidden
(
mainSection
,
'
main
'
);
});
};
};
this
.
assertFooterIsHidden
=
function
()
{
this
.
assertMainSectionVisibility
=
function
(
shouldBeVisible
)
{
return
page
.
tryGetFooterElement
()
var
failMsg
=
'
Expected main section to be
'
+
(
shouldBeVisible
?
'
visible
'
:
'
hidden
'
);
.
then
(
function
(
footer
)
{
return
page
.
waitForVisibility
(
shouldBeVisible
,
page
.
getMainSectionCss
(),
failMsg
);
return
testIsHidden
(
footer
,
'
footer
'
);
});
};
};
this
.
assertMainSectionIsVisible
=
function
()
{
this
.
assertFooterVisibility
=
function
(
shouldBeVisible
)
{
return
page
.
tryGetMainSectionElement
()
var
failMsg
=
'
Expected footer to be
'
+
(
shouldBeVisible
?
'
visible
'
:
'
hidden
'
);
.
then
(
function
(
mainSection
)
{
return
page
.
waitForVisibility
(
shouldBeVisible
,
page
.
getFooterSectionCss
(),
failMsg
);
return
testIsVisible
(
mainSection
,
'
main
'
);
});
};
};
//TODO: fishy!
this
.
assertItemToggleIsHidden
=
function
(
index
)
{
this
.
assertItemToggleIsHidden
=
function
(
index
)
{
return
page
.
tryGetToggleForItemAtIndex
(
index
)
return
page
.
waitForVisibility
(
false
,
page
.
getListItemToggleCss
(
index
),
'
Expected the item toggle button to be hidden
'
);
.
then
(
function
(
toggleItem
)
{
return
testIsHidden
(
toggleItem
,
'
item-toggle
'
);
});
};
};
this
.
assertItemLabelIsHidden
=
function
(
index
)
{
this
.
assertItemLabelIsHidden
=
function
(
index
)
{
return
page
.
tryGetItemLabelAtIndex
(
index
)
return
page
.
waitForVisibility
(
false
,
page
.
getListItemLabelCss
(
index
),
'
Expected the item label to be hidden
'
);
.
then
(
function
(
toggleItem
)
{
return
testIsHidden
(
toggleItem
,
'
item-label
'
);
});
};
};
this
.
assertFooterIsVisible
=
function
()
{
this
.
assertNewItemInputFieldText
=
function
(
text
)
{
return
page
.
tryGetFooterElement
()
return
page
.
waitForNewItemInputField
()
.
then
(
function
(
footer
)
{
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
text
,
'
Expected the new item input text field contents to be
'
+
text
));
return
testIsVisible
(
footer
,
'
footer
'
);
});
};
};
this
.
assertItemInputFieldText
=
function
(
text
)
{
this
.
assertItemText
=
function
(
itemIndex
,
text
)
{
return
page
.
getItemInputField
().
getText
()
return
page
.
waitForItemLabel
(
itemIndex
)
.
then
(
function
(
inputFieldText
)
{
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
text
,
'
Expected the item label to be
'
+
text
));
assert
.
equal
(
inputFieldText
,
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
)
{
this
.
assertItems
=
function
(
textArray
)
{
return
page
.
getVisibleLabelText
()
return
page
.
getListItems
().
then
(
function
(
items
)
{
.
then
(
function
(
visibleText
)
{
if
(
items
.
length
<
textArray
.
length
)
{
assert
.
deepEqual
(
visibleText
.
sort
(),
textArray
.
sort
());
// 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
;
});
});
};
return
ret
;
this
.
assertItemCountText
=
function
(
textToAssert
)
{
return
page
.
getItemsCountElement
().
getText
()
.
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
this
.
assertItemCountText
=
function
(
text
)
{
this
.
assertItemAtIndexIsCompleted
=
function
(
index
)
{
return
page
.
waitForElement
(
page
.
getItemCountCss
())
return
page
.
getItemElements
()
.
then
(
page
.
waitForTextContent
.
bind
(
page
,
text
,
'
Expected item count text to be
'
+
text
));
.
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
.
assertItemAtIndexIsNotCompleted
=
function
(
index
)
{
this
.
assertItemCompletedStates
=
function
(
completedStates
)
{
return
page
.
getItemElements
()
return
page
.
waitForElement
(
.
then
(
function
(
toDoItems
)
{
page
.
getListItemsWithCompletedStatesCss
(
completedStates
),
return
toDoItems
[
index
].
getAttribute
(
'
class
'
);
'
Item completed states were incorrect
'
);
})
.
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
.
assertFilterAtIndexIsSelected
=
function
(
selectedIndex
)
{
this
.
assertFilterAtIndexIsSelected
=
function
(
selectedIndex
)
{
return
page
.
findByXpath
(
page
.
getSelectedFilterXPathByIndex
(
selectedIndex
+
1
))
return
page
.
waitForElement
(
.
then
(
function
(
elm
)
{
page
.
getSelectedFilterCss
(
selectedIndex
),
var
failMsg
=
'
the filter / route at index
'
+
selectedIndex
+
'
should have been selected
'
;
'
Expexted the filter / route at index
'
+
selectedIndex
+
'
to be selected
'
);
assert
.
notEqual
(
elm
,
undefined
,
failMsg
);
});
};
};
this
.
assertCompleteAllIsClear
=
function
()
{
this
.
assertCompleteAllCheckedStatus
=
function
(
shouldBeChecked
)
{
return
page
.
getMarkAllCompletedCheckBox
()
var
failMsg
=
'
Expected the mark-all-completed checkbox to be
'
+
shouldBeChecked
?
'
checked
'
:
'
unchecked
'
;
.
then
(
function
(
markAllCompleted
)
{
return
page
.
waitForMarkAllCompletedCheckBox
()
return
markAllCompleted
.
isSelected
();
.
then
(
page
.
waitForCheckedStatus
.
bind
(
page
,
shouldBeChecked
,
failMsg
));
})
.
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
'
);
});
};
};
}
}
...
...
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