I'm stuck in a problem. I have a set of end to end tests written with protractor.js and I made a little menu with inquire.js in which I'll select which tests I would like to run. The problem is, I really can't find any information on how to actually link those two projects together so the menu can call a test once selected. Here is a sample of a test and the menu I made:
This is my protractor test:
var session = require('../login.js');
describe('The customer view', function() {
var physicalPersonRegistration = {};
physicalPersonRegistration.loginTest = function() {
it('should Login', function() {
browser.ignoreSynchronization = true;
browser.get('http://localhost:8080/project');
session.username.sendKeys('admin');
session.password.sendKeys('admin');
session.submit.click();
browser.ignoreSynchronization = false;
});
};
//executing tests
physicalPersonRegistration.loginTest();
});
And this is my inquire.js menu:
var inquirer = require("inquirer");
var questions = [
{
type: "list",
name: "tests",
message: "Which test do you wish to run?",
choices: [
"Login Test",
"Run all Tests"
]
},
];
inquirer.prompt(questions, function(answers) {
console.log(answers);
});
The first thing to do is to determine what DOM elements you want to interact with. You can start by using the protractor element explorer. Use that to determine the kinds of locators to use. And then build your test around interacting with the browser.
Related
here is my code for three Snackbars using the Material Design Lite component library :
(function() {
'use strict';
window['counter'] = 0;
var snackbarContainer = document.querySelector('#sb-message-box');
var showToastButton = document.querySelector('.button1');
showToastButton.addEventListener('click', function() {
'use strict';
var data = {
message: 'Example Message #1',
timeout: 6000
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
});
}());
(function() {
'use strict';
window['counter'] = 0;
var snackbarContainer = document.querySelector('#sb-message-box');
var showToastButton = document.querySelector('.button2');
showToastButton.addEventListener('click', function() {
'use strict';
var data = {
message: 'Example Message #2',
timeout: 6000
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
});
}());
(function() {
'use strict';
window['counter'] = 0;
var snackbarContainer = document.querySelector('#sb-message-box');
var showToastButton = document.querySelector('.button3');
showToastButton.addEventListener('click', function() {
'use strict';
var data = {
message: 'Example Message #3',
timeout: 6000
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
});
}());
Now I would like to hide one Snackbar as soon as another one is clicked. Currently, the divs are shown one after the other as soon as the 6000ms are up. Can you please help me on this one? Thanks so much!
I too ran into this issue. After some digging, I came up with a little hack.
Use the following code to update the current notification for a new one.
Replace where you'd normally use snackbarContainer.MaterialSnackbar.showSnackbar(data); with:
snackbarContainer.MaterialSnackbar.cleanup_();
setTimeout(function(){
snackbarContainer.MaterialSnackbar.showSnackbar(data);
}, snackbarContainer.MaterialSnackbar.Constant_.ANIMATION_LENGTH)
I'm new to javascript, but it seems to work for me.
Edit: It seems like this is not a foolproof method, when the time-out expires on the previous notification, it closes the new one. I don't have an answer for this yet.
Edit 2: Not foolproof, but gets you going, you need to make some changes to material.js for this to work:
Change 1: Somewhere in MaterialSnackbar = function MaterialSnackbar(element)
Change 2: At the start of MaterialSnackbar.prototype.cleanup_
Change 3: Replace your post code with this.
snackbarContainer.MaterialSnackbar.cleanup_();
snackbarContainer.MaterialSnackbar.skipClearing++;
setTimeout(function(){
snackbarContainer.MaterialSnackbar.showSnackbar(data);
}, snackbarContainer.MaterialSnackbar.Constant_.ANIMATION_LENGTH)
I hope this helps.
Given the API provided by this SnackBar component, it is only plossible to show the alert and not possible to hide it. I would suggest either switching to a better component (this one is excessively simplistic) or hacking your way around it by creating a function that directly destroys all the existing alerts before displaying the new one.
There is a slightly hacky approach to accomplish this.
var snackbarContainer = document.querySelector('#toast');
Let us define a handler which will handle the callback to hide the snackbar,
var handler = function(event) {
snackbarContainer.classList.remove('mdl-snackbar--active');
snackbarContainer.setAttribute("aria-hidden", "true");
};
Now, let's show the snackbar as follows,
var data = {
message: 'Snack time!',
timeout: 2000,
actionHandler: handler,
actionText: 'Dismiss'
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
That's all!
In the current version of Material Design lite you can just call
mySnackbar.MaterialSnackbar.cleanup_();
and it will hide smoothly.
I develop some UI tests (running with Phantom.js configuration)
The tests themselves are very simple, for example (see some sample code below):
Browse a page - confirm the title expected is correct
Click a button/item to select an item from a list - confirm that the accurate item has been selected.
module.exports = {
'NodeCeller home page': function (test) {
test
.open('http://localhost:3000')
.assert.title().is('Node Cellar', 'Node Cellar is now open')
.done();
},
'NodeCeller Start Browsing Click': function (test) {
test
.click('a[href="#wines"]')
.assert.url('http://localhost:3000/#wines', 'Showing wines selection')
.done();
},
'NodeCellar Browse First Wine': function (test) {
test
.click('#content > div > ul > li > a')
.assert.text('legend','Wine Details', 'Showing Wines Details')
.done();
},
};
My question is this, I'd like to run the same set of tests in a loop for several times.
I have googled for Dalek.JS help and samples but I couldn't find any sample or article of how to do that.
Any help would be highly appreciated
My Protractor e2e tests are inconsistently passing and failing.
It seems this could be due to asynchronous javascript, as discussed here:
Protractor : How to wait for page complete after click a button?.
However, here it's mentioned that Protractor tests automatically execute sequentially / synchronously:
https://github.com/angular/protractor/issues/909
My test script:
describe('Login', function() {
var ptor;
beforeEach(function() {
browser.get('https://127.0.0.1:8443');
ptor = protractor.getInstance();
element(by.id('splash')).click();
browser.ignoreSynchronization = true; // <-- to proceed beyond splash screen
});
describe('with correct email and password', function() {
beforeEach(function() {
element(by.id('email')).sendKeys('admin#email.com');
element(by.id('password')).sendKeys('adminpassword');
element(by.id('loginButton')).click();
});
afterEach(function() {
ptor.findElement(by.id('logout')).then(function(elem) {
elem.click();
});
});
it('does not show alert', function() { // <-- sometimes passes, sometimes fails
expect(browser.isElementPresent(by.css('.alert-danger'))).toBe(false);
});
it('changes route to /admin', function() { // <-- sometimes passes, sometimes fails
expect(browser.getCurrentUrl()).toMatch(/\/admin/);
});
});
});
In the two tests above, either both tests will pass, or one/both of the tests will fail with these messages:
Failures:
1) Login with correct email and password does not show alert
Message:
NoSuchElementError: no such element
...
==== async task ====
WebDriver.findElement(By.id("logout"))
...
or
Failures:
1) Login with correct email and password changes route to /admin
Message:
NoSuchElementError: no such element
...
==== async task ====
WebDriver.findElement(By.id("logout"))
...
Thoughts / help much appreciated.
I was able to resolve the issue based on the following:
Avishay's answer here about adding ptor.waitForAngular():
No element found using locator: by.model() error
Changing browser.get to ptor.get, as in Harri Siirak's answer here:
Protractor times out waiting for sync with page when using $resource
juliemr's comment here about ignoreSynchronization being an instance variable, and changing browser.ignoreSynchronization=true to ptor.ignoreSynchronization=true:
https://github.com/angular/protractor/issues/49
glepretre's answer here about using .then():
Protractor : How to wait for page complete after click a button?
As mentioned by Nguyen Vu Hoang's comment to the original question, I am testing a pure Angular app with what I think is pure Protractor (no webdriver calls). I know ptor.ignoreSynchronization=true should not be required in this case, but for some reason, the tests are not proceeding at button click without this setting.
My new spec:
describe('Login', function() {
var ptor;
beforeEach(function() {
ptor = protractor.getInstance();
ptor.ignoreSynchronization = true;
ptor.waitForAngular();
ptor.get('https://127.0.0.1:8443');
ptor.findElement(by.id('splash')).then(function(elem) {
elem.click();
});
});
describe('with correct email and password', function() {
beforeEach(function() {
ptor.findElement(by.id('email')).then(function(elem) {
elem.sendKeys('admin#email.com');
});
ptor.findElement(by.id('password')).then(function(elem) {
elem.sendKeys('adminpassword');
});
ptor.findElement(by.id('loginButton')).then(function(elem) {
elem.click();
});
});
afterEach(function() {
ptor.findElement(by.id('logout')).then(function(elem) {
elem.click();
});
});
it('does not show alert', function() {
expect(ptor.isElementPresent(by.css('.alert-danger'))).toBe(false);
});
it('changes route to /admin', function() {
expect(ptor.getCurrentUrl()).toMatch(/\/admin/);
});
});
});
There is also an another technique to make your tests more stable: Explicit Waits and Expected Conditions (docs).
I've found using Expected Conditions especially useful when testing against non-angular pages or angular applications that have a lot of animations involved.
For example, you can wait for an element to be clickable before making a click:
var EC = protractor.ExpectedConditions;
var link = element(by.id("mylink"));
browser.wait(EC.elementToBeClickable(link), "10000", "The link is still not clickable");
link.click();
There are also other built-in Expected Conditions, such as:
presenseOf()
visibilityOf()
alertIsPresent()
textToBePresentInElementValue()
etc
And, it is easy to write a custom Expected Condition, example use case:
Testing link style changes
You can also combine Expected Conditions using and, or and not, e.g.:
var urlChanged = function() {
return browser.getCurrentUrl().then(function(url) {
return url != 'http://www.angularjs.org';
});
};
// condition to wait for url to change, title to contain 'foo', and $('abc') element to contain text 'bar'
var condition = EC.and(urlChanged, EC.titleContains('foo'), EC.textToBePresentInElement($('abc'), 'bar'));
$('navButton').click();
browser.wait(condition, 5000); //wait for condition to be true.
browser.ignoreSynchronization = true; has a global effect for all your tests. you may have to set it back to false, so protractor waits for angular to be finished rendering the page. e.g. in or before your second beforeEach function
I have problem in implementing touch events with backbone.js and hammer.js
I have tried implementing the touch in the conventional way i.e defining the touch events in "events" section.But it has not worked for me.
Please find my code below
define(['Backbone','underscore','Handlebars','Hammer'],function(Backbone,_,Handlebars,Hammer) {
//getting the type of device in a variable using "userAgent"
window.MOBILE = navigator.userAgent.match(/mobile/i);
var HeadderView = Backbone.View.extend(
{
el: 'body',
touchPrevents : false,
initialize: function()
{
this.el$ = $(this.el);
},
events: function() {//returning different functions based on the device
return MOBILE ?
{
'tap #headcontent': 'handleTap',
} :
{
'click #headcontent':'clickbackbone',
}
},
//declaring the corresponding functions
handleTap: function(){
alert("tap event");
},
clickbackbone:function(){
alert('backbone click');
},
render: function ()
{
//rendering the template and appending it to the page
var that = this;
require(['text!gaming/gameHeadder/headder.html'],function(HeaderTemplate){
var template = Handlebars.compile(HeaderTemplate);
var context = {title: "Tick Tack Toe", imageURL: "images/logo.jpg"}
var htmlTemplate = template(context);
that.el$.html( htmlTemplate);
});
},
});
return new HeadderView();
}
);
Can some one help me out and correct my code
This is not how Hammer works. Backbone knows nothing about HammerJS.
If you really want to use Backbone-style event delegation with Hammer, you might want to check out the backbone.hammer project.
We have a single Backbone view comprised of a sidebar and several sub-views. For simplicity, we've decided to have the sidebar and sub-views governed by a single render function. However, the click .edit event seems to be firing multiple times after clicking on one of the sidebar items. For example, if I start out on "general" and click .edit, then hello fires once. If I then click .profile on the sidebar and click .edit again, hello fires twice. Any ideas?
View
events: {
"click .general": "general",
"click .profile": "profile",
"click .edit": "hello",
},
general: function() {
app.router.navigate("/account/general", {trigger: true});
},
profile: function() {
app.router.navigate("/account/profile", {trigger: true});
},
render: function(section) {
$(this.el).html(getHTML("#account-template", {}));
this.$("#sidebar").html(getHTML("#account-sidebar-template", {}));
this.$("#sidebar div").removeClass("active");
switch (this.options.section) {
case "profile":
this.$("#sidebar .profile").addClass("active");
this.$("#content").html(getHTML("#account-profile-template"));
break;
default:
this.$("#sidebar .general").addClass("active");
this.$("#content").html(getHTML("#account-general-template"));
}
},
hello: function() {
console.log("Hello world.");
},
Router
account: function(section) {
if (section) {
var section = section.toLowerCase();
}
app.view = new AccountView({model: app.user, section: section});
},
Solution
My solution was to change the router to this:
account: function(section) {
if (section) {
var section = section.toLowerCase();
}
if (app.view) {
app.view.undelegateEvents();
}
app.view = new AccountView({model: app.user, section: section});
},
This works for now, but will this create a memory leak?
I had exactly the same problem when I first started using backbone. Like Peter says, the problem is that you have more than one instance of the View being created and listening for the event. To solve this, I created this solution in my last backbone project:
/* Router view functions */
showContact:function () {
require([
'views/contact'
], $.proxy(function (ContactView) {
this.setCurrentView(ContactView).render();
}, this));
},
showBlog:function () {
require([
'views/blog'
], $.proxy(function (BlogView) {
this.setCurrentView(BlogView).render();
}, this));
},
/* Utility functions */
setCurrentView:function (view) {
if (view != this._currentView) {
if (this._currentView != null && this._currentView.remove != null) {
this._currentView.remove();
}
this._currentView = new view();
}
return this._currentView;
}
As you can see, it's always removing the last view and creating a new one, which then renders. I also add a require statement in the router because I don't want to have to load all views in the router until they are actually needed. Good luck.
Sounds like you are attaching multiple view instances to the same DOM element and they are all responding to the events. Are you making a new view each time you navigate without removing the previous view?
I have a dynamic view, that renders different templates inside the same element (about 12), based on router params. Now, the container in which the view renders, is defined inside the view.render() like so "el: '#some-container'". Naturally, i have to remove the view if it exists, before creating a new or the same one, to prevent zombies and s#!t. Just as a reminder, calling view.remove(), actually removes '#some-container' from the DOM, meaning the view has no place to render in, except for the first time. Now, there are dozens of methods to prevent this from happening. Just thought i should share in case anyone needs to save a few hours of research.