Skip to content

Commit

Permalink
feat(test): allow selecting within anchor elements
Browse files Browse the repository at this point in the history
This commit extends the `FormUtils` API to allow an optional anchor
element to be passed in.  The anchor element will cause the `FormUtils`
library to search only within the subtree contained by that element.

All end-to-end tests now use `FU.typeahead()` when they require a
typeahead.
  • Loading branch information
jniles committed May 30, 2016
1 parent 5c0053c commit 3aab6fb
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 173 deletions.
3 changes: 0 additions & 3 deletions client/src/partials/vouchers/complex.html
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ <h3>{{ 'VOUCHERS.COMPLEX.TITLE' | translate }}</h3>
uib-typeahead="account as account.hrlabel for account in ComplexVoucherCtrl.accounts | filter:$viewValue | limitTo:8 "
typeahead-editable="false"
typeahead-on-select="ComplexVoucherCtrl.checkRowValidity($index)"
data-account-input
class="form-control"
required>
</td>
Expand All @@ -142,7 +141,6 @@ <h3>{{ 'VOUCHERS.COMPLEX.TITLE' | translate }}</h3>
ng-change="ComplexVoucherCtrl.checkRowValidity($index)"
type="number"
class="form-control text-right"
data-debit-input
required>
</td>
<td>
Expand All @@ -151,7 +149,6 @@ <h3>{{ 'VOUCHERS.COMPLEX.TITLE' | translate }}</h3>
ng-change="ComplexVoucherCtrl.checkRowValidity($index)"
type="number"
class="form-control text-right"
data-credit-input
required>
</td>
<td>
Expand Down
12 changes: 1 addition & 11 deletions client/test/e2e/billingServices/billingServices.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,7 @@ describe('Billing Services', function () {
// anticipate that the form should come up
FU.exists(by.css('[name="BillingServicesForm"]'), true);

var root = element(by.css('[data-component-find-account]'));

// search for a particular account using the account input
var accountInput = root.element(by.model('BillingServicesFormCtrl.model.account'));
accountInput.sendKeys('410');

// click select the proper account
var option = root.all(by.repeater('match in matches track by $index')).first();
option.click();

// fill in the rest of the fields
FU.typeahead('BillingServicesFormCtrl.model.account', '410');
FU.input('BillingServicesFormCtrl.model.label', 'Value Added Tax');
FU.input('BillingServicesFormCtrl.model.description', 'A tax added for people who want value!');
FU.input('BillingServicesFormCtrl.model.value', 25);
Expand Down
167 changes: 82 additions & 85 deletions client/test/e2e/shared/FormUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,83 +6,31 @@ const expect = chai.expect;
const helpers = require('./helpers');
helpers.configure(chai);

// Overide the element.all() prototype function provided by protractor to attach custom methods
// @TODO - is there a better way without overriding the prototype function?
var ElementArrayFinder = protractor.ElementArrayFinder;

/**
* Pick a random element from the ElementArrayFinder.
*
* @alias element.all(locator).random()
* @view
* <ul class="items">
* <li>First</li>
* <li>Second</li>
* <li>Third</li>
* </ul>
*
* @example
* var list = element.all(by.css('.items li));
* expect(list.random().getText()).to.have.length.above(4);
*
* @todo - make this work. sigh.
ElementArrayFinder.prototype.random = function() {
var self = this;
return self.count()
.then(function (length) {
var n = Math.floor(Math.random() * length);
return self.filter(function (elem, index) {
return index === n;
})
});
};
*/

/**
* Filter out disabled elements from the ElementArrayFinder.
*
* @alias element.all(locator).enabled()
* @view
* <select>
* <option disabled> I am disabled. </option>
* <option> I am not not. </option>
* </select>
*
* @example
* var options = element.all(by.css('select > option'));
* expect(options.enabled()).to.have.length(1);
*/
ElementArrayFinder.prototype.enabled = function () {
return this.filter(function (elem, index) {
return elem.isEnabled();
});
};

// These buttons depend on custom data tags to indicate actions. This seems
// cleaner than using a whole bunch of ids which may potentially collide.
// However, this decision can be reviewed
var buttons = {
create : function create() { return $('[data-method="create"]').click(); },
search : function search() { return $('[data-method="search"]').click(); },
submit : function submit() { return $('[data-method="submit"]').click(); },
cancel : function cancel() { return $('[data-method="cancel"]').click(); },
back : function back() { return $('[data-method="back"]').click(); },
delete : function delet() { return $('[data-method="delete"]').click(); }
create: function create() { return $('[data-method="create"]').click(); },
search: function search() { return $('[data-method="search"]').click(); },
submit: function submit() { return $('[data-method="submit"]').click(); },
cancel: function cancel() { return $('[data-method="cancel"]').click(); },
back: function back() { return $('[data-method="back"]').click(); },
delete: function delet() { return $('[data-method="delete"]').click(); }
};

// This methods are for easily working with modals. Works with the same custom
// data tags used in form buttons.
var modal = {
submit : function submit() { return $('[uib-modal-window] [data-method="submit"]').click(); },
cancel : function cancel() { return $('[uib-modal-window] [data-method="cancel"]').click(); }
submit: function submit() { return $('[uib-modal-window] [data-method="submit"]').click(); },
cancel: function cancel() { return $('[uib-modal-window] [data-method="cancel"]').click(); }
};

// convenience methods to see if the form contains feedback text. Returns locators.
var feedback = {
success : function success() { return by.css('[data-role="feedback"] > .text-success'); },
error : function error() { return by.css('[data-role="feedback"] > .text-danger'); },
warning : function warning() { return by.css('[data-role="feedback"] > .text-warning'); },
info : function info() { return by.css('[data-role="feedback"] > .text-info'); }
success: function success() { return by.css('[data-role="feedback"] > .text-success'); },
error: function error() { return by.css('[data-role="feedback"] > .text-danger'); },
warning: function warning() { return by.css('[data-role="feedback"] > .text-warning'); },
info: function info() { return by.css('[data-role="feedback"] > .text-info'); }
};

// convenience methods to check form element validation states
Expand All @@ -103,42 +51,91 @@ var validation = {
module.exports = {

// get an <input> element by its ng-model
input : function input(model, value) {
return element(by.model(model)).clear().sendKeys(value);
input : function input(model, value, anchor) {

// get the HTML <input> element
let input = anchor ?
anchor.element(by.model(model)) :
element(by.model(model));

return input.clear().sendKeys(value);
},

// get a <select> element by its ng-model.
select: function select(model, option) {
var root = element(by.model(model));
return root.element(by.cssContainingText('option', option)).click();
/**
* @method select
*
* @description
* Selects an option from an <select> html element. Accepts the model
* selector, the option text, and an optional anchor element to search within.
* If no anchor is provided, it defaults to the body.
*
* @param {String} model - the ng-model target to select
* @param {String} option - the text of the <option> element to choose
* @param {Element} anchor - a protractor element to search within
* @returns {Element} - a protractor <option> element
*/
select: function select(model, option, anchor) {
anchor = anchor || $('body');
let select = anchor.element(by.model(model));
let choice = select.element(by.cssContainingText('option', option));
return choice.click();
},

// get a radio button by its position and click
radio : function radio(model, n) {
radio: function radio(model, n) {
return element.all(by.model(model)).get(n).click();
},

// asserts whether an element exists or not
exists : function exists(locator, bool) {
exists: function exists(locator, bool) {
expect(element(locator).isPresent()).to.eventually.equal(bool);
},

// select the item in the typeahead that matches the value given
// by label
typeahead: function typeahead(model, label) {
this.input(model, label);
/**
* @method typeahead
*
* @description
* Selects a dropdown option from a typeahead html element. Accepts the model
* selector, the option text, and an optional anchor element to search within.
* If no anchor is provided, it defaults to the body.
*
* @param {String} model - the ng-model target to select
* @param {String} option - the text of the option element to choose
* @param {Element} anchor - a protractor element to search within
* @returns {Element} - a protractor option element
*/
typeahead: function typeahead(model, label, anchor) {
anchor = anchor || $('body');

// type into the <input> element
this.input(model, label, anchor);

// select the item of the dropdown menu matching the label
let option = element(by.cssContainingText('.dropdown-menu > [role="option"]', label));
option.click();
let option = anchor.element(by.cssContainingText('.dropdown-menu > [role="option"]', label));
return option.click();
},

// select an item from the dropdown menu identified by 'selector'
dropdown: function dropdown(selector, label) {
element(by.css(selector)).click();
/**
* @method dropdown
*
* @description
* Selects a dropdown option from a dropdown html element. Accepts the target
* selector, the option text, and an optional anchor element to search within.
* If no anchor is provided, it defaults to the body.
*
* @param {String} selector - the css selector to select
* @param {String} option - the text of the option element to choose
* @param {Element} anchor - a protractor element to search within
* @returns {Element} - a protractor option element
*/
dropdown: function dropdown(selector, label, anchor) {
anchor = anchor || $('body');

// open the dropdown menu
$(selector).click();

let option = element(by.cssContainingText('[uib-dropdown-menu] > li', label));
option.click();
return option.click();
},


Expand All @@ -149,12 +146,12 @@ module.exports = {
// bind commonly shown feedback utilities
// to detect feedback, the parent element must have the
// [data-role="feedback"] attribute assigned to it.
feedback : feedback,
feedback: feedback,

// bind validation states. Each method takes in a model's string and asserts
// the validation state.
validation : validation,
validation: validation,

// bindings for modal overlay forms
modal : modal
modal: modal
};
62 changes: 32 additions & 30 deletions client/test/e2e/shared/components/bhFindDebtorGroup.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,63 @@
/* global element, by */

const FU = require('../FormUtils');

/**
* Find Debtor Group component
* findDebtorGroup.js
* @public
*/
* Find Debtor Group component
* findDebtorGroup.js
* @public
*/
module.exports = {
root : element(by.css('[data-bh-find-debtorgroup-name]')),
root: element(by.css('[data-bh-find-debtorgroup-name]')),

/**
* @function search
* @param {string} name The text for the debtor group name
* @description This function permits to insert a debtor group text
*/
search : function search(name) {
* @function search
* @param {string} name The text for the debtor group name
* @description This function permits to insert a debtor group text
*/
search: function search(name) {
var searchName = name || 'Tes';
var input = this.root.element(by.model('$ctrl.debtorGroupName'));
input.sendKeys(searchName);
FU.input('$ctrl.debtorGroupName', name, this.root);
},

/**
* @function select
* @param {number} index The index of a debtor group in the list
* @description Select a particular debtor group in the list of debtor groups
*/
select : function select(index) {
* @function select
* @param {Number} index The index of a debtor group in the list
* @description Select a particular debtor group in the list of debtor groups
*/
select: function select(index) {
var searchIndex = index || 0;

var select = this.root.element(by.css('[uib-typeahead-popup]'));
var item = select.element(by.repeater('match in matches').row(searchIndex));
item.click();
},

/**
* @function popup
* @description Trigger a click on the popup button for details of a debtor group
*/
popup : function popup() {
* @function popup
* @description Trigger a click on the popup button for details of a debtor group
*/
popup: function popup() {
var popupButton = this.root.element(by.id('popupInfo'));
popupButton.click();
},

/**
* @function reload
* @description Trigger a click on the reload button for selecting another debtor group
*/
* @function reload
* @description Trigger a click on the reload button for selecting another debtor group
*/
reload : function reload() {
var reloadButton = this.root.element(by.id('reload'));
reloadButton.click();
},

/**
* @function test
* @param {string} name The text for the debtor group name
* @param {number} index The index of a debtor group in the list
* @description Run a default test of use of the component
*/
test : function test(name, index) {
* @function test
* @param {String} name The text for the debtor group name
* @param {Number} index The index of a debtor group in the list
* @description Run a default test of use of the component
*/
test: function test(name, index) {
this.search(name);
this.select(index);
this.popup();
Expand Down

0 comments on commit 3aab6fb

Please sign in to comment.