I have the following test:
var start_url = 'http://example.com/';
var account_email = 'olav#example.com';
var account_password = '****';
module.exports = {
'test login': function (test) {
'use strict';
test
.open(start_url)
.assert.visible('#login')
.click('#login')
.type('#email-field', account_email)
.type('#password-field', account_password)
.submit('#login-form')
.assert.visible('#logout')
.done();
}
};
The .assert.visible('#logout') does not work. I suspect that I need to introduce some kind of waitFor() but I have not found an example of how to do this.
-- Olav
You should add a
test.waitForElement('#logout')
after your submit() & before your assert.visible('#logout') calls.
If this does not work, than it is maybe you hit the max timeout of the waitForElement function, you can extend the timeout by setting a second parameter like this:
test.waitForElement('#logout', 10000)
You can find more information in the docs
If this still does not work then, please open up an issue here
As a workaround I would suggest, to use the test.wait(7500) method, but this should be the last option if the solution described above does not work. If thats the case, please open up an issue.
Thanks.
Related
EDIT: as the original question was too vague I have updated it to make it more concrete
I'd like to create a dashboard in Meteor that shows some statistics about my collections (e.g. how many docs, how many users...). I have been trying the past days but can't seem to find a good/intelligent way.
I initially just did the following:
Template.dashboard.helpers({
getProductsCount: function() {
return Products.find().count();
}
});
This did not work. I think because it counts the number of products from minimongo, but not sure.
Then I did the following:
In the template helper, call a method and get the value to show on the dashboard page (does not work)
Was told not to use pub/sub mechanism for this type of metric
Worked via Session variables (did work, but feels a bit strange to store this kind of metric data in Session variables
So then I read in another SO response about Reactive Variables and tried the following:
Template.dashboard.helpers({
getProductsCount: function() {
return Template.instance().myAsyncValue.get();
}
});
Template.dashboard.created = function() {
var self = this;
self.myAsyncValue = new ReactiveVar("Waiting for response from server");
Meteor.call('getProductsCount', function(error, asyncValue){
if (error)
console.log(error);
else
self.myAsyncValue.set(asyncValue);
});
};
This works, but I find this extremely difficult for something as simple as showing a product count (or any other metric). Not sure I understand the reason why I should use sth as reactive variables?
Then -out of curiosity- I tried the following (using meteor add simple:reactive-method) and it works:
Template.customerDashboard.helpers({
getProductsCount: function () {
return ReactiveMethod.call("getProductsCount");
}
});
So the question really is why having to use Reactive variables and methods for sth as simple as this. Can someone explain?
If you want to show the count only in the view, the best way is to return the count number only. you do not need publish/subscribe at all. you can use server methods. and if you want to show data also, you can go for pub-sub. and your approach is correct.
On what seems to be random occasions, when a user on my website tries to answer a question on the quiz page, the following error occurs:
TypeError: Cannot read property 'invalidate' of undefined
at Tracker.Dependency.changed (tracker.js:388)
at ReactiveVar.set (reactive-var.js:82)
at null. (builtins.js:22)
at view.js:191
at Function.Template._withTemplateInstanceFunc (template.js:437)
at view.js:190
at Object.Blaze._withCurrentView (view.js:523)
at viewAutorun (view.js:189)
at Tracker.Computation._compute (tracker.js:294)
at Tracker.Computation._recompute (tracker.js:313)
I am unsure as to why this occurs, and I am not entirely sure when this error even means so if someone could point me in the right direction, that would be great!
Note: this error seems to only occur when using Chrome.
I'll hazard a guess, here's the culprit code in Tracker.Dependency
Tracker.Dependency.prototype.changed = function () {
var self = this;
for (var id in self._dependentsById)
// My Comment - we're expecting this to be a `Tracker.Computation` which has an `.invalidate()` method!
self._dependentsById[id].invalidate();
};
So, the problem is that something is modifying Object.prototype.
eg.
Object.prototype.test = function(){}
var emptyObj = {};
for (i in emptyObj)
console.log('has key=', i, 'isOwnProperty?=', emptyObj.hasOwnProperty(i))
will print out:
has key= test isOwnProperty?= false
A possible solution to try, would be adding this snipped to the client, and seeing if it fixes the problem (At least temporarily):
Tracker.Dependency.prototype.changed = function () {
var self = this;
for (var id in self._dependentsById){
if (self._dependentsById.hasOwnProperty(id)){
self._dependentsById[id].invalidate();
}
}
};
Tracker.Dependency.prototype.hasDependents = function () {
var self = this;
for(var id in self._dependentsById)
if (self._dependentsById.hasOwnProperty(id))
return true;
return false;
};
However - It's bad practice to add to Object.prototype, and lots of code will fail in unexpected ways. You'll need to track this down.
Hard to say without more info, but I'd take a look here, specifically the part under "Subscriptions Don't Block":
https://dweldon.silvrback.com/common-mistakes
As the author states, "In meteor, the majority of "Cannot read property of undefined"errors are caused by an incorrect assumption about the existence of subscribed data."
In other words, you may be trying to access the invalidate property before your subscriptions has returned anything.
So I'm trying to figure out a way to get a value from the browser when a test is running with DalekJS. As far as I can tell, it's not currently possible. I even had a look at hacking the Dalek code, but since I know very, very little about CommonJS and NodeJS other than installing and running things, I figured I might actually ask around first.
What I'm trying to do is something like:
// Non-working example
module.exports = {
'My Test': function(test) {
var foo;
test
.open('http://localhost/mysite')
.execute(function(){
foo = document.getElementById('bar');
})
.type('#myField', foo)
.done();
}
}
Now I know why this code doesn't work: because the execute() function is executed in the context of the browser window (as though I was typing it into the console).
The question I'm asking is: is there a way I can retrieve a value to use in my test?
Context information: My use case is that I'm testing an E2E scenario where a form is submitted and a code is provided to the user. This code then needs to be entered on another page. I could potentially set up mock values for the purpose of testing, but then it's not a true scenario.
Thanks in advance!
Yes,
that is a usecase we haven't implemented yet, but until we haven't found a proper solution, you can use a workaround:
module.exports = {
'My Test': function(test) {
test
.open('http://localhost/mysite')
.execute(function(){
var foo = document.getElementById('bar').innerText;
document.getElementById('myField').setAttribute('value', foo);
})
.done();
}
};
This method has some problems, none of the eventhandlers like keyup, keydown etc. will be fired when adding the value to the field in this way.
If your code doesn't rely on them, you are good to go.
Else you would have to wait some weeks until the new version is out, which will provide a better solution for such scenarios.
Hope that helps.
Cheers
Sebastian
Based on the documented example of the .log.message function, you can do something like the following:
module.exports = {
'My Test': function(test) {
test
.open('http://localhost/mysite')
.execute(function(){
var foo = document.getElementById('bar').value;
this.data('foo', foo);
})
.type('#myField', test.data('foo'))
.done();
}
}
If I read a value from Firebase and then remove it, a subsequent limited read (e.g. dataRef.limit(10).once("value") ) will still see the removed value.
If I do an unlimited read, then I won't see the removed value, and a subsequent limited read will also no longer see the removed value.
var gFirebase = new Firebase("https://brianshmrian.firebaseio.com/");
function CreateValue()
{
gFirebase.child("TestBug/Key").set("Value");
}
function ReadValue(limit)
{
var dataRef = gFirebase.child("TestBug");
if (limit)
dataRef = dataRef.limit(10);
dataRef.once("value",function(snapshot)
{
alert((limit?"Limited read\n":"Normal read\n") + snapshot.val());
});
}
function RemoveValue()
{
gFirebase.child("TestBug/Key").remove();
}
In this example code, if I do a CreateValue(), then a ReadValue(), then a RemoveValue(), then a ReadValue(true), the object will still be reported to me in the last ReadValue(). However, if I do a ReadValue(false), I'll no longer see the value, and a subsequent ReadValue(true) will not see the value either.
See here to try it for yourself: http://jsfiddle.net/brianshmrian/5WWR6/
So is this a bug? Or am I making a mistake?
EDIT
Ok, that seems like a not too painful workaround. The code below solves my problem for now:
// Need to do this before the remove to avoid caching problem
dataRef.on("value", function(snapshot)
{
setTimeout(function() { dataRef.off(); }, 3000);
});
dataRef.remove();
I can't find any issues with the code. There is always the gotcha that locally cached data is returned synchronously, but I don't see that as an issue here; there's no way for the read to be getting called before the remove has completed. It looks like a pretty straightforward bug.
I was able to circumvent the behavior by setting up the limit(10).on('value') before calling the add/delete operations. So I think that if you establish your query ref first, you'll be okay.
Example: http://jsfiddle.net/katowulf/6wQFF/2/ (the pre tag is set up on load)
This is a fairly general problem I am having here, so I figured I would ask, hopefully someone can help me out.
I have a flex app using the facebook js bridge to log in, and I am trying to pass the users name back to the app, but I am coming across the thing that I will explain after the code:
function getFriends() {
var nameOfPlayer = "";
FB.api('/me', function(response) {
nameOfPlayer = response.name;
});
return nameOfPlayer;
}
If I have my flex app print nameOfPlayer, it returns undefined, same as if I do an alert with nameOfPlayer, but if I do an alert inside of the FB.api call for nameOfPlayer, then it will pop up with my name, but it still won't return name. I tried having it return a general string, and that works, so it is passing the information over, it just isn't saving to the variable. Any idea how to fix this? This is probably very convoluted, so if I need to clarify anything let me know. Thanks in advance for any help you can give me :)
You should be doing this in an event-driven way. Some pseudo-code is written below to get you started.
var yourFacebookAPIClass = new yourFacebookAPIClass();
yourFacebookAPIClass.addEventListener(Event.UserCallComplete, function(ev){
var nameOfPlayer = SomeParser.ParseData(ev);
someFunctionThatUsesYourVar(nameOfPlayer);
});
yourFacebookAPIClass.getNameOfPlayer();
In your facebook api class:
function getNameOfPlayer(){
makeTheApiCallHere();
}
The function you're defining inside of the FB.api call is actually a callback function which means it's called asynchronously. So the line of code after the api call (the return statement) will execute right away without waiting for Facebook to return a response. The callback function, on the other hand, will wait for Facebook to response, in fact that's what it's designed for. You'd be better off doing all your logic that depends on the nameOfPlayer inside of that callback function.
If you definitely have to return a nameOfPlayer variable the way it's written you'd need to tell your code to wait for nameOfPlayer to be defined:
function getFriends() {
var nameOfPlayer = "";
FB.api('/me', function(response) {
nameOfPlayer = response.name;
});
while(typeof nameOfPlayer === "undefined" || nameOfPlayer == "") {}
return nameOfPlayer;
}
But I really would not recommend this because your app will freeze while it waits for a response. You'd be better off doing everything inside of your callback function instead of returning something.
Good luck! :)