Is it possible to locate sub-elements in protractor's .map?
I'm trying to modify example code from here to parse ToDo items into a data structure.
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
browser.get('https://angularjs.org');
element(by.model('todoList.todoText')).sendKeys('write first protractor test');
element(by.css('[value="add"]')).click();
var todoList = element.all(by.repeater('todo in todoList.todos'));
var todoStruct = todoList.map(function(el) {
return {
checkbox: el.element(by.model('todo.done')),
label: el.element(by.tagName('span')).getText()
};
});
todoStruct.then(function(resolvedTodoStruct) {
expect(todoStruct.length).toEqual(3);
expect(todoStruct[2].label).toEqual('write first protractor test');
});
});
});
From what I've read, map is the right choice for this kind of task. But for some reason, as soon as I use .element inside .map, protractor freezes. :(
Why would it happen? Did I miss anything?
I found a couple of work-arounds for this, though, while not pretty, seem work fine.
For the getText(), I moved the process of getting the text out of the JSON and then save the variable in the returned JSON.
For the element, the workaround is to wrap the element in a function:
var todoList = element.all(by.repeater('todo in todoList.todos'));
var spanText;
var todoStruct = todoList.map(function(el, index) {
var spanText = el.element(by.tagName('span')).getText().then(function(t){
return t;
});
return {
checkbox: function(){
return todoList.get(this.index).element(by.model('todo.done'));
},
label: spanText
};
});
Then when you want to access the element at a later time, you access as a function:
todoStruct.checkbox().click();
Yes, we can get sub elements from a map in the following way
getRows() {
return this.element.all(by.css('.ag-row')).map((row, index) => {
return {
index: index,
title: row.element(by.css("[colid=\"title\"]")).getText(),
operationStatus: row.element(by.css("[colid=\"operation_status_enum\"]")).getText()
};
});
}
// You can print this in the following way
this.getRows().then((rows) => {
for (var i=0; i<rows.length; i++) {
console.log(rows[i].title);
console.log(rows[i].operationStatus);
}
});
Related
Not able to initilize the below JS view in index page. Could you please help me?
I m getting this error msg "Uncaught (in promise) TypeError: t.createContent is not a function"
Plunker link:
https://plnkr.co/edit/7v0CN93aDrAOY9WqU269?p=preview
app.view.js:
sap.ui.jsview("app",{
getControllerName:function(){
return "app";
},
createContent:function(oContoller){
var oButton = new sap.m.Button(this.createId("helloButton"),{
text:"Click Me"
});
return oButton;
}
});
change the view code like this
sap.ui.jsview("view.js.app", {
getControllerName: function() {
return "view.js.app";
},
createContent: function(oContoller) {
var oButton = new sap.m.Button(this.createId("helloButton"), {
text: "Click Me"
});
return oButton;
}
});
Also add a controller file app.controller.js and paste below code.
sap.ui.controller("view.js.app", {
onInit: function() {
}
});
You are specifying the resourceroots as "view.js" in the index file.
So while instantiating use below code
var view = sap.ui.view({
id:"idApp1",
type:sap.ui.core.mvc.ViewType.JS,
viewName:"view.js.app"
});
And in the view add these changes,
sap.ui.jsview("view.js.app",{
getControllerName:function(){
return "view.js.app";
},
createContent:function(oContoller){
var oButton = new sap.m.Button(this.createId("helloButton"),{
text:"Click Me"
});
return oButton;
}
});
And add one controller file named app as below,
sap.ui.define(["sap/ui/core/mvc/Controller"],
function (Controller) {
"use strict";
return Controller.extend("view.js.app", {
});
});
Here is the working link.
//This is my AngularPage.cs page object file
var AngularPage= function()
{
var nameInput= element(by.model('yourName'));
var greeting = element(by.binding('yourName'));
this.get=function()
{
browser.get('http://www.angularjs.org');
};
this.setName= function(name)
{
nameInput.sendKeys(name);
};
this.getGreeting= function()
{
return greeting.getText();`
};
};
module.exports = new AngularPage();
//This is my AngularHome_spec.js file
var angularPage = require('./AngularPage.js');
describe('angularjs homepage',function()
{
var angular_page;
beforeEach(function()
{
angular_page= new AngularPage();
});
it('greetings for new user', function()
{
// var angular_page= new AngularPage();
angular_page.get();
angular_page.setName('Rahul');
expect(angular_page.getGreeting()).toEqual('Hello Rahul!');
}
);
}
);
//I am unable to use page objects in my spec file as it is throwing an error
:AngularPage is not defined
In your code, object has been created twice. First time on page "AngularPage.js" and second time on spec "AngularHome_spec.js" level.
Do following on page "AngularHome_spec.js"
module.exports = AngularPage;
Change the
var AngularPage = require('./AngularPage.js');//Capital the 'A'
With Protractor, and using Mocha framework, I am comparing two arrays of values, one from a bar chart, one from text fields.
The code looks like this:
it("Each bar should have a value equal to its percentage", () => {
var identicalValue: boolean = false;
helper.getFirstValues(textLocator).then((textValue) => {
helper.getBarValues(barLocator).then((barValue) => {
identicalValue = helper.compareArray(textValue, barValue);
//compareArray returns a boolean, true if the arrays have the same values
expect(identicalValue).to.equal(true);
});
});
});
the functions are coded this way:
public getFirstValues(factsheetTab: protractor.ElementFinder): webdriver.promise.Promise<{}> {
var deferred = protractor.promise.defer();
factsheetTab.all(by.tagName("tr")).map((ele, index) => {
return {
index: index,
text: ele.all(by.tagName("td")).first().getText()
}
}).then((topValue) => {
deferred.fulfill(topValue);
},
(reason) => { deferred.reject(reason) });
return deferred.promise;
};
public getBarValues(factsheetTab: protractor.ElementFinder): webdriver.promise.Promise<{}> {
var deferred = protractor.promise.defer();
factsheetTab.all(by.tagName("tr")).map((ele, index) => {
return {
index: index,
text: ele.all(by.tagName("td")).last().element(by.tagName("div")).getAttribute("data-value")
}
}).then((barValue) => {
deferred.fulfill(barValue);
},
(reason) => { deferred.reject(reason) });
return deferred.promise;
};
My problem is that when the comparison returns false, so when the two arrays have differences, the test is blocked. It doesn't fail, the browser remains opened on that step, and the process stops, ignoring the remaining tasks.
Note: the function helper.compareArray returns a correct value. I could also write "expect(false).to.equal(true)" and get blocked too.
Am I doing something wrong in this test? Do you see a reason why this test is not finished?
edit: I found a workaround, to not get stuck in case of the test failing:
it("Each bar should have a value equal to its percentage", () => {
var identicalValue: boolean = false;
var textValue = null;
helper.getFirstValues(textLocator).then((value) => {
textValue = value;
});
helper.getBarValues(barLocator).then((barValue) => {
chai.assert.deepEqual(barValue, textValue);
});
});
(using #Brine's suggestion, for the deepEqual)
This seems to work, the other tests are ran if this one fails.
I'm still curious to know what was wrong with the first version though.
Not sure if this is a bug in your helper.compareArray or Mocha... but why not compare the arrays directly? I don't use Mocha but it seems something like this would work:
expect(textValue).deepEqual(barValue);
This is my code
/******************************************************/
import Ember from "ember";
var TodosController = Ember.ArrayController.extend({
actions: {
createTodo: function(){
// Get the todo title by the "New Todo" input
var title = this.get('newTitle');
if(!title.trim()){ return; }
// Create the new Todo model
var todo = this.store.createRecord('todo', {
title: title,
isCompleted: false
});
// Clear the 'New Todo' input field
this.set('newTitle', '');
// Save the new model
todo.save();
},
clearCompleted: function(){
var completed = this.filterBy('isCompleted', true);
completed.invoke('deleteRecord');
completed.invoke('save');
}
},
remaining: function() {
return this.filterBy('isCompleted', false).get('length');
}.property('#each.isCompleted'),
inflection: function() {
var remaining = this.get('remaining');
return remaining === 1 ? 'todo' : 'todos';
}.property('remaining'),
hasCompleted: function(){
return this.get('completed') > 0;
}.property('completed'),
completed: function(){
return this.filterBy('isCompleted', true).get('length');
}.property('#each.isCompleted'),
allAreDone: function(key, value) {
if(value === undefined){
return !!this.get('length') && this.everyProperty('isCompleted', true);
} else {
this.setEach('isCompleted', value);
this.invoke('save');
return value;
}
}.property('#each.isCompleted')
});
export default TodosController;
/*******************************************************/
In terminal not showing any error when i run this command
$ ember server
but in browser not showing any thing and console showing this error
Uncaught Error: Assertion Failed: ArrayProxy expects an Array or
Ember.ArrayProxy, but you passed object
Please suggest me what i m doing wrong, the code is also on github : https://github.com/narayand4/emberjs
thanks in advance.
The most likely reason for this is that you have a controller which extends from Ember.ArrayController while you only return a plain object in the corresponding model.
I had the same issue and changed my controller to extend Ember.Controller instead.
In the related route for this controller, your model method doesn't return an array, as you've indicated by extending an arrayController.
I'm trying to create a list with endless scroll in angularjs. For this I need to fetch new data from an api and then append it to the existing results of a scope in angularjs. I have tried several methods, but none of them worked so far.
Currently this is my controller:
userControllers.controller('userListCtrl', ['$scope', 'User',
function($scope, User) {
$scope.users = User.query();
$scope.$watch('users');
$scope.orderProp = 'name';
window.addEventListener('scroll', function(event) {
if (document.body.offsetHeight < window.scrollY +
document.documentElement.clientHeight + 300) {
var promise = user.query();
$scope.users = $scope.users.concat(promise);
}
}, false);
}
]);
And this is my service:
userServices.factory('User', ['$resource',
function($resource) {
return $resource('api/users', {}, {
query: {
method: 'GET',
isArray: true
}
});
}
]);
How do I append new results to the scope instead of replacing the old ones?
I think you may need to use $scope.apply()
When the promise returns, because it isnt
Part of the angular execution loop.
Try something like:
User.query().then(function(){
$scope.apply(function(result){
// concat new users
});
});
The following code did the trick:
$scope.fetch = function() {
// Use User.query().$promise.then(...) to parse the results
User.query().$promise.then(function(result) {
for(var i in result) {
// There is more data in the result than just the users, so check types.
if(result[i] instanceof User) {
// Never concat and set the results, just append them.
$scope.users.push(result[i]);
}
}
});
};
window.addEventListener('scroll', function(event) {
if (document.body.offsetHeight < window.scrollY +
document.documentElement.clientHeight + 300) {
$scope.fetch();
}
}, false);