DalekJS - Retrieve value from browser - dalekjs

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();
}
}

Related

Cypress.io: Reading and storing value of form input into a JS variable (or constant)

I have an HTML form (really simple one, no tricky parts like Ajax, ...) on a simple page. I try to read default value of any input (type="text", again no tricks) and store it in constant for later use (assert).
HTML looks like:
<form method="post" ...>
<input type="text" id="xyz" name="xyz" value="123">
</form>
Cypress test, that DOESN'T work, looks like:
describe('General tests', function(){
context('General', function() {
it('can read the input and store in const', function () {
cy.visit('http://localhost/settings')
const xyz = Cypress.$('#xyz').val()
cy.log(xyz)
})
})
})
This doesn't work. BUT after several hours of playing (by accident this worked for me in more complex test suite = file). What I realized is, that this construct works as expected in case previous test ( = it() ) visits the same URL as the last visited URL. Than it works like a miracle.
Cypress test, that WORKS, looks like:
describe('General tests', function(){
context('General', function() {
it('makes magic and allows the next test to work', function () {
cy.visit('http://localhost/settings')
})
it('can read the input and store in const', function () {
cy.visit('http://localhost/settings')
const xyz = Cypress.$('#xyz').val()
cy.log(xyz)
})
})
})
I thought that tests should be independent, but it looks like they are not.
I tried other ways how to get the value of input info variable and the closest what I needed was using closure ".then()", but it can be used for just a single input, not for more complex forms.
Simple asserting like "cy.get('#id_of_input').should('eq', ...)" works fine, but it doesn't allow me to work with default (and by test overridden) value of inputs.
So my questions:
1) Is it OK to use included jQuery in this way to get and store value of input into constant? If now what is the other way for cases when I need to do this for like 5 various input fields in the form (for a signel input closure will be just fine)
2) Is it OK that tests influence each other?
Thanks everyone for any help.
To answer your questions:
1) According to the docs, Cypress.$ is "a great way to synchronously query for elements". (emphasis theirs)
The way you're using it circumvents the intended Cypress workflow with asynchronous command queuing. If you don't know what I'm talking about here, I suggest reading the fantastic introduction to Cypress in their documentation.
I would suggest the following as an alternative to the code you posted:
cy.visit('http://localhost/settings');
cy.get('#xyz').then(elem => {
// elem is the underlying Javascript object targeted by the .get() command.
const xyz = Cypress.$(elem).val();
cy.log(xyz);
});
.then(). lets you queue up code to be run in sequence with your test by the test runner. See this answer for more info about command queuing and .then().
2) Yes, it is okay for describe functions to influence each other. Cypress runs each file separately, but separate describes are simply run in the sequence they are queued in.
For example, the following code will work just fine:
let message = "";
describe("Set a value", () => {
it("Sets a value", () => {
message = "hi";
});
});
describe("Retrieve a value", () => {
it("Retrieves and prints the set value", () => {
cy.log(message); // Logs "hi" to the Cypress log window
});
});

Meteor Dashboard and publication/subscription

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.

How to assert the result of a form submission

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.

Firebase remove+limited query caching bug?

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)

change collection before publishing

I would like to add a property to the objects that get published to the client.
My publish function looks like that
Meteor.publish("forms", function() {
return Forms.find();
});
I would like to do something like this
Meteor.publish("forms", function() {
var forms = Forms.find();
forms.forEach(function (form) {
form.nbForms = 12;
}
return forms;
});
What I would like is that all the documents in forms have a new count attribute which gets sent to the client.
But this obviously does not work.
thank you for your help
Not sure it will work in your case but you might use the new transform collection function introduced with Meteor 0.5.8
When declaring your collection, add this function as the second parameter :
Forms = new Meteor.Collection("forms", {
transform: function(f) {
f.nbForms = 12;
return f;
}
});
But this will be on both server and client. I don't know if there is a way to define a transform function in a publish context.
I think you need to do something similar to this Meteor counting example in Publish:
How does the messages-count example in Meteor docs work?
I also posted a question here that may help once it's answered. Meteor has a this.added which may work, but I'm currently uncertain how to use it. Hence the question below:
Meteor, One to Many Relationship & add field only to client side collection in Publish?

Resources