I am trying to move my protractor code to a page object design pattern I started by my login tests and please find my code below.
When running my test, protractor load the page bu fails when trying to enter text into the username password inputs, I have tried to locate username text area using by.id and by.input but both did not work.
Please also note that when I run my login test before using the page object pattern protractor is able to find the text area.
page-login.js :
var loginPage = function ()
{
this.userName = element(by.input('userName'));
this.password = element(by.input('userPassword')) ;
this.loginButton = element(by.id('login_form_signin_button'));
this.loginText = element(by.css('#mainGlobalSearchBtn'));
this.loginError = element(by.xpath('html/body/div[1]/div[1]/div[1]/form/div/p'));
this.login = function (userName, password)
{
loginPage.userName.sendKeys(userName);
loginPage.password.sendKeys(password);
loginPage.loginButton.click ();
browser.waitForAngular ();
}
};
it('should not login : incorrect login details', function()
{
var loginPage = new loginPage();
loginPage.login('incorrectusername','incorrectpassword');
expect(loginPage.loginError.getText()).toContain('Access denied');
});
Console output :
1) Login should not login : incorrect login details
Message:
TypeError: undefined is not a function
Stacktrace:
TypeError: undefined is not a function
at null.<anonymous> (C:\Users\orsyp\DUX\k_workload_ar\ui\e2e\login.spec.js:3
1:26)
at C:\Users\orsyp\DUX\k_workload_ar\ui\node_modules\grunt-protractor-runner\
node_modules\protractor\jasminewd\index.js:54:12
at webdriver.promise.ControlFlow.runInNewFrame_ (C:\Users\orsyp\DUX\k_worklo
ad_ar\ui\node_modules\grunt-protractor-runner\node_modules\protractor\node_modul
es\selenium-webdriver\lib\webdriver\promise.js:1445:20)
at webdriver.promise.ControlFlow.runEventLoop_ (C:\Users\orsyp\DUX\k_workloa
d_ar\ui\node_modules\grunt-protractor-runner\node_modules\protractor\node_module
s\selenium-webdriver\lib\webdriver\promise.js:1310:8)
at wrapper [as _onTimeout] (timers.js:252:14)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
If I follow the Getting started doc example, you should add var loginPage = new loginPage(); into the it function.
it('should not login : incorrect login details', function() {
//add this line
var loginPage = new loginPage();
loginPage.login('incorrectusername','incorrectpassword');
expect(loginPage.loginError.getText()).toContain('Access denied');
});
const log = Factory.getLogger("Page.DashDashboardPage");
export class DashDashboardPage extends PageBase{
private lblDashPageTitle : any;
private lnkDashMyDashboard : any;
private btnDashNewTeamDashboard : any;
private txtDashSearchRecord : any;
constructor(){
super();
const element = Elements.DashDashboardPage;
this.lblDashPageTitle = super.findLocators(element.lblDashPageTitle.findBy,element.lblDashPageTitle.value);
this.lnkDashMyDashboard = super.findLocators(element.lnkDashMyDashboard.findBy,element.lnkDashMyDashboard.value);
this.btnDashNewTeamDashboard= super.findLocators(element.btnDashNewTeamDashboard.findBy,element.btnDashNewTeamDashboard.value);
this.txtDashSearchRecord = super.findLocators(element.txtDashSearchRecord.findBy,element.txtDashSearchRecord.value);
}
/**
* Get: load dash-dashboard base url
* #returns {DashDashboardPage}
*/
public get(): DashDashboardPage{
ConfigRoute.visit_page('http://op.xxx-tek.com/test/');
log.info("Step: navigate to http://op.xxx-tek.com/test/ [:get:]");
return new DashDashboardPage();
}
/**
* Validate: verify dash-board page title
* #param title
* #returns {DashDashboardPage}
*/
public check_And_Validate_dash_page_title(title: string): DashDashboardPage{
this.Helper_Assertion.expectToEqual(this.lblDashPageTitle,title);
log.info("Validate: Verify dash page tile [:check_And_Validate_dash_page_title:]");
return new DashDashboardPage();
}
}
Related
I have a page pages/login.js looks like:
function fillAndSubmitLogin(email, password) {
return this
.waitForElementVisible('#emailInput')
.setValue('#emailInput', email)
.setValue('#passwordInput', password)
.waitForElementVisible('#loginSubmitButton')
.click('#loginSubmitButton');
}
export default {
commands: [
fillAndSubmitLogin
],
elements: {
emailInput: 'input#email',
passwordInput: 'input[type=password]',
TFAInput: 'input#token',
loginSubmitButton: '.form-actions button.btn.btn-danger'
}
};
I have another page pages/hompage.js homepage.js attempts to include pages/login.js as a section
import login from "./login.js";
module.exports = {
url: 'http://localhost:2001',
sections: {
login: {
selector: 'div.login-wrapper',
...login
}
}
};
I then have a test case that attempts to login on the hompage section
'Homepage Users can login': (client) => {
const homepage = client.page.homepage();
homepage
.navigate()
.expect.section('#login').to.be.visible;
const login = homepage.section.login;
login
.fillAndSubmitLogin('user#test.com', 'password');
client.end();
}
This test then fails with the following error
TypeError: login.fillAndSubmitLogin is not a function
at Object.Homepage Users can login (/Users/kevzettler//frontend/test/nightwatch/specs/homepage.spec.js:32:6)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:182:7)
login.fillAndSubmitLogin is not a function
at Object.Homepage Users can login (/Users/kevzettler//frontend/test/nightwatch/specs/homepage.spec.js:32:6)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:182:7)
According to the Nightwatch docs, any commands that are exported in page objects should be plain JavaScript objects with a key being a command name and the value being a function. For example:
var googleCommands = {
submit: function() {
this.api.pause(1000);
return this.waitForElementVisible('#submitButton', 1000)
.click('#submitButton')
.waitForElementNotPresent('#submitButton');
}
};
module.exports = {
commands: [googleCommands],
elements: //...etc ...
// etc...
}
In this example, the module exports googleCommands, which is a command object which has a key (submit) and a corresponding function. I believe you should refactor your code as follows:
function fillAndSubmitLogin = {
fillAndSubmitLogin: function(email, password) {
return this
.waitForElementVisible('#emailInput')
.setValue('#emailInput', email)
.setValue('#passwordInput', password)
.waitForElementVisible('#loginSubmitButton')
.click('#loginSubmitButton');
}
};
Of course, you don't have to make the command name the same in both places (as the example shows (googleCommands/submit). This allows you to expose a variety of functions in one command. Hope that answers the question!
I keep getting the following error:
Exception in template helper: TypeError: Cannot read property '_id' of undefined at Object.editable
I've been trying to debug it with no luck.
I'm trying to pass a user object into an editUser template. So when I click edit, and get the form, I'll be able to see the user previous values inside the input fields, but I cannot get the values to appear on the form.
Here is the code I have now:
# users.js
// Helpers
Template.user.helpers({
user: () => {
let user = Meteor.user() ? Meteor.user() : 'Unable to pull user info';
return user
},
editable: function () {
let currentUserId = Meteor.user()._id;
let currentUser = Meteor.user();
// let currentUserFind = Meteor.users.findOne({_id: this._id});
return Session.equals('editUser', currentUserId);
}
});
# user.tpl.jade
// Events
Template.user.events({
'click .form-edit'(e) {
e.preventDefault();
let currentUser= Meteor.user({});
let currentUserId = Meteor.user()._id;
let currentUserFind = Meteor.users.findOne({_id: this._id});
console.log(currentUserId);
console.log(currentUserFind);
// console.log(this._id);
Session.set('editUser', currentUserId);
// Session.set('editUser', this._id);
}
});
if editable
+editUser
else
with currentUser
.card.card--user
h3.card__title= username
p.card__content= profile.email
p.card__content= profile.firstName
p.card__content= profile.lastName
p.card__content= profile.bio
button.card__btn.form-edit Edit
I have tried passing the id and the user object but I cannot get the error to disappear.
I have seen this exception with Meteor.user()._id when you don't import the Meteor pseudo-global. Once you import {Meteor} the exception will disappear. Meteor.userId() will also do the work.
Uncaught TypeError: Cannot read property 'dispatch' of undefined
This occurs in ReactRouter.js in handleLocationChange:
handleLocationChange: function handleLocationChange(change) {
this.dispatch(change.path, change.type);
},
This is downstream from my call to
this.context.transitionTo('something');
where 'something' is one of my defined routes. Why would 'this' be undefined?
Here is my component calling the code:
var React = require("react");
var Router = require("react-router");
var { Link } = Router;
var Button = require('core-ui').Button;
var AppointmentsHeader = React.createClass({
mixins: [React.addons.PureRenderMixin],
contextTypes: {
router: React.PropTypes.func
},
render: function () {
console.log("AppointmentsHeader:render");
var router = this.context.router;
// default to hidden
var displayClassInline = "appointments-hide"; // hide/show this element if the current page is Landing Page
if (this.props.currentState.get('currentState') === "landingPage")
{
displayClassInline = "appointments-block-show";
}
return (
<div className={"appointments-header cv-grid " + displayClassInline}>
<div className="appointments-title">Appointments</div>
<Button label="Create Appointment Event" style="primary" onClick={this._onClick}/>
</div>
);
},
_onClick: function() {
console.log("AppointmentsHeader 'Create Appointment Event' button clicked");
var newStatus = this.props.currentState.set('currentState', "CreateAppointment");
this.props.handleChange(newStatus);
this.context.transitionTo('createAppointmentsEvent');
}
});
module.exports = AppointmentsHeader;
This line:
this.context.transitionTo('createAppointmentsEvent')
Should be:
this.context.router.transitionTo('createAppointmentsEvent');
or
this.transitionTo('createAppointementsEvent')
You are accessing the singleton router two different ways in your example:
The Navigation mixin
The Router that is injected via the contextTypes hash
The API Is very confusing right now because Ryan went down one road (getting rid of mixins entirely) and then decided to "undepricate" the mixins.
Question
Why does driver work fine(the title is retrieved and tested), but web driver is undefined(unable to getText)?
Expected Result
The tests will complete successfully.
Actual Result
․ Google when at home Page should have correct title: 141ms
1) Google when at home Page when searching should input search term
1 passing (3s)
1 failing
1) Google when at home Page when searching should input search term:
ReferenceError: webdriver is not defined
Files Used
Test File
Used to run the tests by executing command: mocha -t -R list index.js (assuming index.js is the filename)
var fs = require('fs'),
chai = require('chai'),
assert = chai.assert,
expect = chai.expect,
test = require('selenium-webdriver/testing'),
webdriver = require('selenium-webdriver'),
Page = require('./pageobjects/pages/home');
test.describe('Google', function(){
test.before(function(){
driver = new webdriver.Builder().
withCapabilities(webdriver.Capabilities.firefox()).
build();
//initialize driver and webdriver on the Page Object
Page.webdriver = webdriver;
Page.driver = driver;
});
test.describe("", function () {
test.before(function(){
//console.log(Page);
});
test.describe("when at home Page", function () {
test.before(function () {
Page.get(Page.URL);
});
test.it("should have correct title", function () {
Page.getTitle()
.then(function (title) {
assert.equal(title, 'Google');
});
});
test.describe("when searching", function () {
test.it("input search term", function () {
Page.sendKeys(Page.Search.INPUT, 'test');
Page.getText(Page.Search.INPUT)
.then(function (text) {
assert.equal(text, 'test');
});
});
});
test.after(function () {
driver.quit();
});
});
});
});
Page
object used to create pages
var Page = {
getTitle : function getTitle() {
return driver.getTitle();
},
get : function get(url) {
return driver.get(url);
},
sendKeys : function sendKeys(element, text) {
console.log(webdriver);
driver.findElement(webdriver.By.css(element)).sendKeys(text);
},
click : function click(element) {
return driver.findElement(webdriver.By.css(element)).click();
}
};
module.exports = Page;
Home
object that represents a page, uses mixins to get Page's functions
the search file is left out because it is irrelevant to the problem
var Page = require('./page'),
Search = require('../components/search'),
extend = require('extend');
var Home = {
URL : 'http://google.com',
Search : Search
};
module.exports = Home;
//extend home with page
extend(module.exports, Page);
I am using protractor to test an Angular JS application.
I would like to use page object pattern therefor I need to export a function into another module.
Here is my page-login.js file:
require("./login.spec.js");
var LoginPage = function ()
{
this.userName = element(by.id('login_form_user_input'));
this.password = element(by.id('login_form_password_input')) ;
this.loginButton = element(by.id('login_form_signin_button'));
this.loginText = element(by.css('#mainGlobalSearchBtn'));
this.loginError = element(by.xpath('html/body/div[1]/div[1]/div[1]/form/div/p'));
this.login = function (userName, password)
{
LoginPage.userName.sendKeys(userName);
LoginPage.password.sendKeys(password);
LoginPage.loginButton.click ();
browser.waitForAngular ();
};
};
exports.LoginPage = LoginPage;
Now in another js file called login.spec.js under the same folder I would like to call the login this method :
var util = require ('util');
require("./myconfig.js");
describe('Login', function() {
var loginPage = require("./login-page.js");
var ptor;
beforeEach (function ()
{
ptor = protractor.getInstance();
ptor.get(settings.winURL);
waits(2000);
//Delete cookies to avoid saving password or username
});
it('should not login : incorrect login details', function()
{
loginPage.login('incorrectusername','incorrectpassword');
expect(loginPage.loginError.getText()).toContain('Access denied');
});
When Launching this code protractor is not entering username and passwoed in corresponding fields and I got this console ouput :
1) Login should not login : incorrect login details
Message:
TypeError: Object #<Object> has no method 'login'
Stacktrace:
TypeError: Object #<Object> has no method 'login'
at null.<anonymous> (C:\******\login.spec.js:34:20)
here is line 34 that is throwing the error :
loginPage.login('incorrectusername','incorrectpassword');
How can I use the login functioninside the login page function in another module?
The require( './login-page.js' ) returns a "class" function. It means that, to call login method of loginPage module you might to use new. Ex.:
it('should not login : incorrect login details', function()
{
var page = new loginPage();
page.login('incorrectusername','incorrectpassword');
expect(page.loginError.getText()).toContain('Access denied');
});
Remove the require("./login.spec.js"); of the begin of your module. The test is running before the module creation.
Protractor has this awesome option called onPrepare. You put this in your config.js file. Your onPrepare can reference a file.
In this file you can have all the variables and functions you need!
Instead of "this" you use global.
global.userName = element(by.id('login_form_user_input'));
global.password = element(by.id('login_form_password_input')) ;
global.loginButton = element(by.id('login_form_signin_button'));
global.loginText = element(by.css('#mainGlobalSearchBtn'));
global.loginError = element(by.xpath('html/body/div[1]/div[1]/div[1]/form/div/p'));
global.login = function (user, pw)
{
userName.sendKeys(user);
password.sendKeys(pw);
loginButton.click ();
browser.waitForAngular ();
};
then in your config you do:
//other config settings,
onPrepare: myvariables.js
in your spec, you don't need to require anything!
Just call what you need (minus the global)
beforeEach (function () {
ptor = protractor.getInstance();
ptor.get(settings.winURL);
waits(2000);
//Delete cookies to avoid saving password or username
});
it('should not login : incorrect login details', function()
{
login('incorrectusername','incorrectpassword');
expect(loginError.getText()).toContain('Access denied');
});
you also might want to name your userName variable and the userName arg and password different things. (fixed above)