I have an automation test that I have switched out most variables for properties which has been working fantastic for me unless i need to chain something. Here is an example of what I'd like it to look like:
var test = module.exports = {
outerElement: element(by.cssContainingText('some.div' 'A name'),
innerElement: $('something.else'),
clickOnaName: function () {
this.outerElement.this.innerElement.click();
},
However I have to use this code because chaining doesn't work the way I am using it:
var outerElement = element(by.cssContainingText('some.div'
'A name');
var innerElement = $('something.else');
var test = module.exports = {
clickOnaName: function() {
outerElement.innerElement.click();
},
Is there a way for me to do chain or should i just leave those elements as variables
please let me know if this helps!
You can use .element(el.locator()) to extend the elements. You can use multiple selectors at once. You can also hit arrays of elements.
Keep in mind that this kind of chaining of protractor selectors is the same as a css space child selector, and not as a > selector.
I.e. $('.parent').$('.child') will select the same elements as in a css file .parent .child, getting ALL children and not just direct children.
module.exports = function(){
this.parent = $('.parent');
this.child = $('.child');
this.childOfParent = parent.element(child.locator());
this.directParentChild = $('.parent').$('.child');
this.parentArray = $$('.parents');
this.child = $('.child');
this.children = parentArray.get(2).element(child.locator());
}
Adding in clicks and such should be pretty straightforward from there, page.childOfParent.click for example.
Related
I need to perform a behavior in FrontEnd but I don't know how to do it: Inside the textarea I have to put a background on certain keywords like "+project", "#context", while the user types, as if it were a markup text similar to testing tools for Regex.
Its not the complete solution, but you can adapt this example:
https://jsfiddle.net/julmot/hdyLpy37/
It uses the markjs library:
https://markjs.io/
Here is the javascript code:
// Create an instance of mark.js and pass an argument containing
// the DOM object of the context (where to search for matches)
var markInstance = new Mark(document.querySelector(".context"));
// Cache DOM elements
var keywordInput = document.querySelector("input[name='keyword']");
var optionInputs = document.querySelectorAll("input[name='opt[]']");
function performMark() {
// Read the keyword
var keyword = keywordInput.value;
// Determine selected options
var options = {};
[].forEach.call(optionInputs, function(opt) {
options[opt.value] = opt.checked;
});
// Remove previous marked elements and mark
// the new keyword inside the context
markInstance.unmark({
done: function(){
markInstance.mark(keyword, options);
}
});
};
// Listen to input and option changes
keywordInput.addEventListener("input", performMark);
for (var i = 0; i < optionInputs.length; i++) {
optionInputs[i].addEventListener("change", performMark);
}
I have the following function:
function focusIsNotInInput() {
// If the element currently in focus is of a certain type, then the key handler shouldn't run
var currentlyInFocus = $window.document.activeElement;
var blacklist = ['INPUT', 'TEXTAREA', 'BUTTON', 'SELECT', 'IFRAME', 'MD-OPTION'];
return !blacklist.some(function (nodeName) {
return nodeName === currentlyInFocus.nodeName;
});
}
And I need to mock that the element currently in focus is of one of the specified types, but can't get it to work.
I've tried injecting window, like this:
beforeEach(function() {
var $windowMock;
inject(function(_$window_) {
$windowMock = _$window_;
$windowMock.document.activeElement.nodeName = 'INPUT';
});
});
But when the code above runs, the active element is always still body. It's getting overwritten. I have also tried creating an element and setting focus on it:
var elementInFocus = $('<input>');
this.elem.append(elementInFocus);
elementInFocus.triggerHandler('focus');
elementInFocus.focus();
But it's the same, body is always in focus, what ever I do.
I had some trouble with this too, a possible solution (worked for me) is to add a spyOn(element, 'focus') -- here's a reference: How do I check if my element has been focussed in a unit test
My successful solution:
const htmlItem = fixture.nativeElement;
const searchBar = htmlItem.querySelector('.search-box');
let focusSpy = spyOn(searchBar, 'focus');
searchBar.focus();
expect(focusSpy).toHaveBeenCalled();
This might be a really silly question but I couldn't find an answer anywhere else.
I want to unit test a service that for now has two simple operations:
angular.module('transaction').factory('Transaction',
function() {
var transactionList = [];
// Public API
return {
addTransaction: function(transaction) {
transactionList.push(transaction);
},
getTransactions: function(){
return transactionList;
}
};
}
);
Tests
Now in my tests I want to test if the service's addTransaction and getTransactions methods work, but I'm not really sure how to do it properly, because what I'm doing right now is using the other method to test the first, e.g:
it('should be able to return an array of transactions', function() {
//add new transaction
var trans = {id: 1, value: test};
Transaction.addTransaction(trans);
//test get
var result = Transaction.getTransactions();
expect(Array.isArray(result)).toBe(true);
expect(result).toEqual([trans]);
});
it('should be able to add a transaction', function(){
var trans = {id: 1, value: test};
Transaction.addTransaction(trans);
var result = Transaction.getTransactions();
expect(result).toEqual([trans]);
});
I expect these methods to become more complex, but what I wanted to do would be to have a way to test one of the methods without having to use the other. It would be great if anyone could point me to an example of some tests for operations similar to this.
I can see two ways for testing these independently:
1) make transactionList publicly available:
Transaction.addTransaction(trans);
expect(Transaction.transactionList).toEqual([trans]);
2) return transactionList in both methods
expect(Transaction.addTransaction(trans)).toEqual([trans]);
I would go with the second solution, as you can keep transactionList private.
The $save() in Angularfire 0.8 is confusing me.
Here's a minimal example - a snippet from my controllers.js file:
.controller('LandingPageController', ['$scope','$firebase', function($scope,$firebase) {
$scope.addNode = function() {
var FB = new Firebase('https://protodb.firebaseio.com/testrecords/');
var fbr = $firebase(FB);
fbr.$set(1,{firstname: 'James'});
}
$scope.addAttribute = function() {
var FB = new Firebase('https://protodb.firebaseio.com/testrecords/1');
var fbr = $firebase(FB).$asObject();
fbr.lastname = "Bond";
fbr.$save();
}
}])
When addNode() is called, sure enough, a node is created in my firebase:
But when addAttribute() is called, the entire record is replaced, rather than what I expected, which was for the 'lastname' attribute to be added.
I've no doubt misunderstood the docs. Can anyone help?
Update:
OK, I needed to wait until the object was loaded. It works now, after changing addAttribute to:
$scope.addAttribute = function() {
var FB = new Firebase('https://protodb.firebaseio.com/testrecords/1');
var fbr = $firebase(FB).$asObject();
fbr.$loaded().then(function() {
fbr.lastname = "Bond";
fbr.$save();
});
}
As you found out yourself already:
a FirebaseObject (as returned by $asObject()) does not have a $update method.
when you call $save() on a FirebaseObject before it is completely loaded, you may end up deleting other properties
To patch existing data you can:
Either wait for the entire object to be loaded (as you did in your update to the question)
Or call $firebase.$update directly
$firebase(FB).$update({ lastname: "Bond" });
This last approach has the advantage that you don't pull down the entire object, only to update a single property. Note that this is probably premature optimization in most cases, but still...
I am writing a directive in AngularJS and one of the things I need to do is find all of the controls inside a form and iterate over them.
If jQuery was loaded I do this:
var inputs = element.find(".ng-invalid,.ng-valid");
inputs.each(function ()
{
var i = $(this);
});
But since it is not loaded and I just have Angular I can only think to do something like:
var inputs = element.find("input");
//for loop thru this
var selects = element.find("select");
//for loop thru this
//....etc
Is there some better way using jQLite to accomplish this?
If supporting IE7 or older is not a requirement, you can use the querySelectorAll method of Element:
var controls = element[0].querySelectorAll('.ng-invalid, .ng-valid');
[].forEach.call(controls, function (ctl) {
var c = angular.element(ctl);
...
});
Use a for-loop.
var inputs = element.find(".ng-invalid,.ng-valid");
for (var index=0; index < inputs.length; index++) {
var subElement = angular.element(inputs)[index];
// do something with subElement
}