protractor first() is working and get(0) is not - protractor

There's a troncated code of a page Object in protractor
that code is working :
var HomePage = function() {
this.publishedShows = element.all(by.repeater('show in showsHomePage'));
this.getFirstShow = function(){
return this.publishedShows.first();
}
};
this one is not :
var HomePage = function() {
this.publishedShows = element.all(by.repeater('show in showsHomePage'));
this.getFirstShow = function(){
return this.publishedShows.get(0);
}
};
I get this error :
Index out of bound. Trying to access element at index: 0, but there are only 0 elements that match locator by.repeater("show in showsHomePage")
Anyone can inlight me?

It is not about get(0) vs first() - they are absolutely the same in terms of implementation. It is probably about the timing, wait for the presence of the element before making any action with it:
var elm = myPageObject.getFirstShow();
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(elm), 5000);
// do smth with elm

alecxe does have a point about waiting for the element to be present and so you may want to the wait as mentioned or browser.waitForAngular();
What I have seen is that if you resolve a finder to a variable then this can get left in the unfulfilled promise state (even though the internals have resolved the query). What needs to be done is to resolve the promise and then you should be able to get the element you require:
So from your code:
`this.publishedShows = element.all(by.repeater('show in showsHomePage'));`
Will still be a promise and not publishedShows.
This returns items when I try your code (I have a slightly different repeater).
var HomePage = function() {
this.publishedShows = element.all(by.repeater('show in showsHomePage'));
this.getFirstShow = function() {
return this.publishedShows.then(function(items){
=>return items[0].getText();
});
}
};
var hp = new HomePage();
=>expect(hp.getFirstShow()).toEqual('hello');
Obviously change your expect to what you want to check for and also the return too. Marked with =>
Ensure also that if you use any track by statement then you should look at the by.exactRepeater command to have an exact match on only the repeater part.
This worked for me, note the resolved promise returns an array of finders.

Related

Meteor delay find() so that I can access the object

I've got a findOne() in a template helper in Meteor but I want to modify an array in the document before returning both the document as well as the updated array. When doing so I get TypeError: Cannot read property 'access' of undefined. On the initial test it worked fine but I suspect that it's now failing because I'm trying to modify the document before findOne() completes. How do I get around this? Code below:
'curMatter': function() {
var curObj = Matters.findOne({_id:Session.get('editing_matter')});
var curAccess = _.without(curObj.access, Meteor.userId());
return { curMatter: curMatter, curAccess: curAccess };
}
Collection.findOne completes before running code code that is after it (var curAccess = _.without(curObj.access, Meteor.userId()); in your case).
The issue is that the document that you expect to get by this query is not available (yet) to the client, so Matters.findOne({_id:Session.get('editing_matter')}); returns undefined.
So what it really means that your subscription is not ready, when the helper is run for the first time, and it fails.
What you can do is to check whether curObj is not undefined before accessing its property (i.e. var curAccess = curObj && _.without(curObj.access, Meteor.userId());
When the document becomes available to the client, the helper will be re-run, and you will get correct results then.
Here is your complete code:
'curMatter': function() {
var curObj = Matters.findOne({_id:Session.get('editing_matter')});
var curAccess = curObj && _.without(curObj.access, Meteor.userId());
return { curMatter: curMatter, curAccess: curAccess };
}
just use it as a cursor, they always seem to work better anyway.
Template['someTemplate'].helpers({
curMatter: function () {
return Matters.find({
_id: Session.get('editing_matter')
}).map(doc => Object.assign(doc, {
curAccess: _.without(doc.access, Meteor.userId())
}))
}
});
Then you can use an {{#each}} in your template (even if it is always one item)

How can I leverage reactive extensions to do caching, without a subject?

I want to be able to fetch data from an external Api for a specific request, but when that data is returned, also make it available in the cache, to represent the current state of the application.
This solution seems to work:
var Rx = require('rx');
var cached_todos = new Rx.ReplaySubject(1);
var api = {
refresh_and_get_todos: function() {
var fetch_todos = Rx.Observable.fromCallback($.get('example.com/todos'));
return fetch_todos()
.tap(todos => cached_todos.onNext(todos));
},
current_todos: function() {
return cached_todos;
}
};
But - apparently Subjects are bad practice in Rx, since they don't really follow functional reactive programming.
What is the right way to do this in a functional reactive programming way?
It is recommended not to use Subjects because there is a tendency to abuse them to inject side-effects as you have done. They are perfectly valid to use as ways of pushing values into a stream, however their scope should be tightly constrained to avoid bleeding state into other areas of code.
Here is the first refactoring, notice that you can create the source beforehand and then your api code is just wrapping it up in a neat little bow:
var api = (function() {
var fetch_todos = Rx.Observable.fromCallback($.get('example.com/todos'))
source = new Rx.Subject(),
cached_todos = source
.flatMapLatest(function() {
return fetch_todos();
})
.replay(null, 1)
.refCount();
return {
refresh: function() {
source.onNext(null);
},
current_todos: function() {
return cached_todos;
}
};
})();
The above is alright, it maintains your current interface and side-effects and state have been contained, but we can do better than that. We can create either an extension method or a static method that accepts an Observable. We can then simplify even further to something along the lines of:
//Executes the function and caches the last result every time source emits
Rx.Observable.withCache = function(fn, count) {
return this.flatMapLatest(function() {
return fn();
})
.replay(null, count || 1)
.refCount();
};
//Later we would use it like so:
var todos = Rx.Observable.fromEvent(/*Button click or whatever*/))
.withCache(
Rx.Observable.fromCallback($.get('example.com/todos')),
1 /*Cache size*/);
todos.subscribe(/*Update state*/);

How to return window.performance object to CasperJS scope

I'm trying to return window.performance object from the web page back to casper's scope with the following code but I'm getting null. Can someone explain why?
performance = casper.evaluate ->
return window.performance
#echo performance
PhantomJS 1.x doesn't implement window.performance, so you can't use it.
PhantomJS 2.0.0 implements it, but it doesn't implement the window.performance.toJSON() function. The problem with PhantomJS is that you have to access this information through evaluate(), but it has the following limitation:
Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.
Closures, functions, DOM nodes, etc. will not work!
You will have to find your own way of serializing this in the page context and passing it to the outside (JavaScript):
var performance = casper.evaluate(function(){
var t = window.performance.timing;
var n = window.performance.navigation;
return {
timing: {
connectStart: t.connectStart,
connectEnd: t.connectEnd,
...
},
navigation: {
type: n.type,
redirectCount: n.redirectCount
},
...
};
});
or look for a deep copy algorithm that produces a serializable object (from here):
var perf = casper.evaluate(function(){
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(typeof(obj[i])=="object" && obj[i] != null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
return cloneObject(window.performance);
});
console.log(JSON.stringify(perf, undefined, 4));

Meteor - no more callbacks for "findOne" function

i'm working on a Meteor project, and I must say that isn't easy at all, especially for one thing: callbacks !
Everything is async, so I wonder how do I must do to get results from my mongodb.
var user = Meteor.users.findOne({username: "john"});
return (user); // sometimes returns "undefined"
...
var user = Meteor.users.findOne({username: "john"});
if (user) // so ok, I check if it exists!
return (user); // Cool, I got my user!
return (); // Ok and what should I return here? I want my user!
I don't want to be dirty and put like setTimeout everywhere.
Anybody has a solution for this ?
EDIT :
I noticed in router.js with console.log that my data is returned 4 times. 2 times with an undefined value and 2 other times with the expected value. In the view, it's still undefined.
Why the router passes like 4 times in this route ? Does it display the first result of the return value in the router ?
What should I return if the find() doesn't find anything ?
EDIT 2: Here is some code to understand.
this.route('profilePage', {
path: 'profil/:_id?',
waitOn: function() {
return [
Meteor.subscribe('article', { prop: this.params._id}), // id can be id or username
Meteor.subscribe('article', { userId: this.params._id}), // id can be id or username
Meteor.subscribe('params'),
Meteor.subscribe('profil', (this.params._id ? this.params._id : Meteor.userId()))
];
},
data: function() {
if (this.params._id) {
var user = Meteor.users.findOne(this.params._id);
if (!user)
user = Meteor.users.findOne({username: this.params._id});
console.log(user);
return user;
}
else if (Meteor.userId())
return Meteor.user();
else
Router.go("userCreate");
}
});
I get this on the console:
http://puu.sh/debdJ/69419911f7.png
(text version following)
undefined
undefined
Object_id: "o3mgLcechYTtHPELh"addresses: (....)
Object_id: "o3mgLcechYTtHPELh"addresses: (....)
findOne(yourId) is a sync method which is equivalent to find({ _id: yourId}, callback). The difference is that find() allows you to define a callback. If you don't pass a callback to find() this method will be sync.
check wrapAsync: http://docs.meteor.com/#/full/meteor_wrapasync
It allows you to code in a sync style with a async operations.
Free lesson on EventedMind: https://www.eventedmind.com/feed/meteor-meteor-wrapasync
My experience thus far is that the Meteor Mongodb package is that the functions do not generally provide callbacks (for some reason insert does...), the functions are atomic (thus sync).
There are meteor packages that can make Mongodb async if you want (I havn't tried any).
I guess this sync approach is in line with the simple maintenance goal of Mongodb. Thinking about it, one of my pet peeves using Node is working with async callback waterfalls/nests, they are a pain to create and maintain... and hopefully this will make my code easier to read and understand and change...
var future = new Future();
var _h = Hunts.findOne({huntId});
if(_h) {
future.return(_h)
} else {
return future.wait();
}
on server/startup.js you need:
Future = Npm.require('fibers/future');

How to use wait() in OnBeforeAction

I see older posts where old versions of Iron-router do wait() in before:
before: function() {
// let's make sure that the topPosts subscription is ready and the posts are loaded
if (this.data()) {
// we can then extract the userIds of the authors
var userIds = this.data().map(function(p) { return p.userId });
// and add the authors subscription to the route's waiting list as well
this.subscribe('authors', userIds).wait(); **<--- this guy!**
}
}
Above is from https://www.discovermeteor.com/blog/reactive-joins-in-meteor/
If I add wait() to my OnBeforeAction subscribe, I get these errors:
You called wait() after calling ready() inside the same computation tree.
You can fix this problem in two possible ways:
1) Put all of your wait() calls before any ready() calls.
2) Put your ready() call in its own computation with Deps.autorun.
My waitOn is
waitOn: function() {
return Meteor.subscribe('weeks', this.params.league);
},
and OnBeforeAction
onBeforeAction: function() {
if (this.ready()) {
// we can now get the latest (first in the list) week
var week = SheetData.find().fetch()[0].week;
this.subscribe('standings', this.params.league, week).wait();
this.next();
}
}
If I remove the wait() the render template starts before my subscription is ready. The suggested fixes don't seem applicable. What am I missing?
Here's a solution based on radzserg's answer to a similar question. He cleverly uses a callback function in the original waitOn function, and no onBeforeAction. In your case it would be:
waitOn: function() {
var that = this;
return Meteor.subscribe('weeks', this.params.league, function() {
var week = SheetData.find().fetch()[0].week;
that.wait(Meteor.subscribe('standings', that.params.league, week));
});
}