I am having a bit of trouble with getting values from Protractor testing and being able to reuse those values.
I have an app that creates new records from a form and then displays them back to the user. On a successful addition, the user is presented with a success alert, displaying the ID of the newly created record. "You have successfully added an entry with the ID {{newEntry.id}}".
I have a suite of tests asserting that all the fields are correctly validated etc, which all work correctly. I now want to test the Update side of things by taking the newly created record and testing if a new set of values updates correctly. Therefore I want to take that ID of the newly created record and reuse it.
I have created the variable ID at the top of my suite,
var id;
I then run all the validation tests on the form and submit a correct submission. I then check if the success message is shown and that, in this instance, the ID = 2.
describe('Add users', function() {
var endpoint = "users";
var id;
correctSubmission(endpoint, id);
function correctSubmission(endpoint, id) {
describe('Testing correct submission', function() {
it('should navigate back to the list page', function() {
expect(browser.getCurrentUrl()).toBe("list/" + endpoint);
});
it('should display a success message', function() {
expect(element(by.css('.alert-success')).isPresent()).toBeTruthy();
});
it('should get the record ID from the success message', function() {
expect(element(by.css('.add-message')).evaluate('newEntry.id')).toEqual(2);
id = element(by.css('.add-message')).evaluate('newEntry.id');
return id;
});
});
};
});
I need to basically get that ID that equals 2 and return it back to the Global ID so that I can use it across other tests. Obviously that ID is currently an unresolved promise, and I have tried to use:
protractor.promise.all(id).then(function (result) {
console.log("ID is: " + result);
});
But this only logs the string.
I am a bit lost with what to do next as I have tried all sorts, but to no avail and I am pushed for time on this project.
Many thanks if you can help this Protractor n00b.
did you try using a protractor params config attribute?
exports.config = {
params: {
myid: 'somevaluehere'
}
};
Then u can access it by
browser.params.myid
Related
I have a page that generates a series of post templates, each of which subscribes to a single publication. I'm running into an issue where passing the post ID to my publication does not return any results. Can anyone help me figure out what's wrong?
I have tried using console.log() inside onCreated (both inside and outside of the autorun) to make sure that my IDs are correctly generated and have also tried other query values (such as {$exists:true}) to ensure that my query is working.
// client
CommentSubs = new SubsManager();
Template.post.onCreated(function() {
var self = this;
self.ready = new ReactiveVar();
self.autorun(function() {
var commentParent = this._id;
var handle = CommentSubs.subscribe('comments', commentParent);
self.ready.set(handle.ready());
});
});
// server
Meteor.publish("comments", function (commentParent) {
return Posts.find({commentParent: commentParent}, {sort: {createdAt: 1}});
});
I am new to meteor so perhaps I am missing something that should be obvious.
I've built an app that is form-based. I want to enable users to partially fill out a form, and then come back to it at a later date if they can't finish it at the present. I've used iron router to create a unique URL for each form instance, so they can come back to the link. My problem is that Meteor doesn't automatically save the values in the inputs, and the form comes up blank when it is revisited/refreshes. I tried the below solution to store the data in a temporary document in a separate Mongo collection called "NewScreen", and then reference that document every time the template is (re)rendered to auto fill the form. However, I keep getting an error that the element I'm trying to reference is "undefined". The weird thing is that sometimes it works, sometimes it doesn't. I've tried setting a recursive setTimeout function, but on the times it fails, that doesn't work either. Any insight would be greatly appreciated. Or, if I'm going about this all wrong, feel free to suggest a different approach:
Screens = new Meteor.Collection('screens') //where data will ultimately be stored
Forms = new Meteor.Collection('forms') //Meteor pulls form questions from here
NewScreen = new Meteor.Collection('newscreen') //temporary storage collection
Roles = new Meteor.Collection('roles'); //displays list of metadata about screens in a dashboard
//dynamic routing for unique instance of blank form
Router.route('/forms/:_id', {
name: 'BlankForm',
data: function(){
return NewScreen.findOne({_id: this.params._id});
}
});
//onRendered function to pull data from NewScreen collection (this is where I get the error)
Template.BlankForm.onRendered(function(){
var new_screen = NewScreen.findOne({_id: window.location.href.split('/')[window.location.href.split('/').length-1]})
function do_work(){
if(typeof new_screen === 'undefined'){
console.log('waiting...');
Meteor.setTimeout(do_work, 100);
}else{
$('input')[0].value = new_screen.first;
for(i=0;i<new_screen.answers.length;i++){
$('textarea')[i].value = new_screen.answers[i];
}
}
}
do_work();
});
//onChange event that updates the NewScreen document when user updates value of input in the form
'change [id="on-change"]': function(e, tmpl){
var screen_data = [];
var name = $('input')[0].value;
for(i=0; i<$('textarea').length;i++){
screen_data.push($('textarea')[i].value);
}
Session.set("updateNewScreen", this._id);
NewScreen.update(
Session.get("updateNewScreen"),
{$set:
{
answers: screen_data,
first: name
}
});
console.log(screen_data);
}
If you get undefined that could mean findOne() did not find the newscreen with the Id that was passed in from the url. To investigate this, add an extra line like console.log(window.location.href.split('/')[window.location.href.split('/').length-1], JSON.stringify(new_screen));
This will give you both the Id from the url and the new_screen that was found.
I would recommend using Router.current().location.get().path instead of window.location.href since you use IR.
And if you're looking for two way binding in the client, have a look at Viewmodel for Meteor.
I have an issue where, when I log a JavaScript object (a subscription handler) in Meteor I am able to see the field 0 but I am unable to retrieve it from the code.
I am using numtel:mysql package.
Here is the entire Meteor code used to reproduce the issue:
Bootstrap
Meteor.startup(function() {
Meteor.readLiveDb = new LiveMysql(Meteor.Config.mysql.read);
Meteor.writeLiveDb = new LiveMysql(Meteor.Config.mysql.write);
var closeAndExit = function() {
Meteor.readLiveDb.end();
Meteor.writeLiveDb.end();
process.exit();
};
// Close connections on hot code push
process.on('SIGTERM', closeAndExit);
// Close connections on exit (ctrl + c)
process.on('SIGINT', closeAndExit);
});
Publish Code
Meteor.publish("checkLoginSubscription", function(username, password) {
if(typeof username === undefined || typeof password === undefined) {
throw new error('Username or password cannot be blank');
return;
}
var user = Meteor.readLiveDb.select(
'select * from users where username = "' + username + '" and password = "' + password + '"', [{
table: 'users'
}]
);
return user;
});
Event Code
Template.login.events({
'submit #loginform': function(event) {
event.preventDefault();
$('#message').fadeIn();
var username = event.target.username.value;
var password = CryptoJS.MD5(event.target.password.value).toString();
console.log('The Password entered is ', password);
if(username == '' || password == '') {
$('#message').addClass('alert-danger').removeClass('alert-success');
$('#message').html('Username or password cannot be blank');
return;
}
$('#message')
.html('<img src="img/loaders/1.gif" alt="loading" /> Logging you in...')
.addClass('alert-success')
.removeClass('alert-danger');
var cLogin = new MysqlSubscription('checkLoginSubscription', username, password);
console.log(cLogin); // This is the variable holding the object
console.log(cLogin[0]); //undefined!
}
});
On the last line you can see the log:
console.log(cLogin);
When I try to get the 0: Object part of the handler it returns undefined.
What is going wrong? How can I access this data?
Meteor subscriptions are reactive stuff.
When you cast one it starts reaching over to the server's publication handler.
This publication has two ways to resolve:
The publication handler returns a cursor
The publication handler calls its ready function (this.ready())
This signals to the client that the publication is ready and that he can start using the data.
To keep track of this, you have again two ways:
The native way: provide a callback. I'm not sure if SQL subscriptions allow it though.
The Meteor way (which I know is supported thanks to your screenshot) : ready().
ready() is a reactive data source (hence the pun). It means you can run a Tracker computation with it!
Such computations are natively implemented in template helpers.
Here is another way to implement it using the native Tracker goodness:
Tracker.autorun(function doStuffOnceDataIsReady() {
if(cLogin.ready()) {
doStuff();
}
});
Seeing your use case, you might want to delve in template subscriptions or template computations.
console.log is asynchronous for objects, in the sense that it's only logging a reference, which is evaluated the moment you click on it to expand. (It's interesting to note that the first line, however, is capturing the current state of the object at the time of the log call, as pointed out by Bergi in the comments)
So what's probably happening here is that some asynchronous function is adding property 0 to data between the time of logging and you opening the object in the console.
See, for example, this answer and discussion on the topic.
I have an issue where we have the Trello API adding a new card to a defined list, and it is successfully adding to the list, however we are trying to use the .done to close the current window and open a hard-coded trello board. This code is used to take a record from CRM and copy the code to Trello.
var creatingtheCard = function () {
var record = getRecord(getParameterByName("id"))
if (record.Description == null) {
record.Description = "";
}
var options = document.getElementById("ListNameItem");
var listId = options[options.selectedIndex].value
Trello.post("cards", { name: record.Title, desc: record.Description + " - " + record.CustomerId.Name, idList: listId})
.done(closeWinOpenTrello)
.error(failure);
}
function closeWinOpenTrello() {
window.open("http://trello.com/b/8QWBDiTI")
window.close()
}
This function is called and it successfully creates the new card in Trello, but it wont perform the closeWinOpenTrello function, but it will perform the .error.
Also i ran this in the debugger and when i run the code step by step it will give the error and then close the window and open a new window with Trello.
Thanks in advance
Update 1
This is the failure function
var failure = function (error) {
alert(error.statusText);
}
You need to cancel the form submission. Otherwise the page changing will cause the cancelled state of the request - the browser isn't going to bother waiting for the response after the page changes, because there's no longer anything waiting on the response.
In order to do this, just return false from the onsubmit handler.
I'm trying to wrap my mind around Backbone (as my recent flurry of questions indicate...). In particular I'm working through this project:
https://github.com/ccoenraets/nodecellar
http://nodecellar.coenraets.org/#
I want to conceptually understand what happens when I click the "Save" button on a new Wine for example this one:
http://nodecellar.coenraets.org/#wines/506df6b6849a990200000001
I'm thinking it goes something like this:
1) The Backbone winedetails view catches the save button click as an event and launches the "Before Save" method. See below from /public/js/views/winedetails.js.
beforeSave: function () {
var self = this;
var check = this.model.validateAll();
if (check.isValid === false) {
utils.displayValidationErrors(check.messages);
return false;
}
this.saveWine();
return false;
},
saveWine: function () {
var self = this;
console.log('before save');
this.model.save(null, {
success: function (model) {
self.render();
app.navigate('wines/' + model.id, false);
utils.showAlert('Success!', 'Wine saved successfully', 'alert-success');
},
error: function () {
utils.showAlert('Error', 'An error occurred while trying to delete this item', 'alert-error');
}
});
},
In that Save method (the 2nd method) there is a call to this.model.save. SOMEHOW that model save method MUST be making a PUT request to the '/wines' URL as evidenced in the server.js file (This is for a node.js server):
app.get('/wines', wine.findAll);
app.get('/wines/:id', wine.findById);
app.post('/wines', wine.addWine);
app.put('/wines/:id', wine.updateWine);
app.delete('/wines/:id', wine.deleteWine);
From there obviously it runs the addWine method which is defined in the routes/wines.js. What I don't understand is how the MODEL understands which URL to send the request to. I can't find anywhere that links the model.save method with the correct REST API. Does my question make sense?
Wait I might have answered my own question. It must be this line in: /public/js/models/models.js
urlRoot: "/wines"
And then Backbone knows if you are doing an "New" model it must send a POST request. If you are doing an update it must append the :id to the URL and send a PUT request, etc. Is that how it works?
Here is the documentation for the model urlRoot : http://backbonejs.org/#Model-urlRoot
If you have specified the urlRoot on the model, it will use that. If the model is part of a collection, it will reference the url property on the collection.
When saving, Backbone will use PUT for an update and POST for a create. It determines which is should use based on the result of the isNew function. This checks whether the model has an id property.