Nightmarejs multiple pages in same test - javascript

I'm trying to pull the title-tag text from two webpages on a Drupal site. I want to use Nightmarejs.
Here is my code so far:
// get the <title> text from inside the Drupal site
var Nightmare = require('nightmare');
var user = 'foobar#example.com';
var pass = 'foobar';
new Nightmare()
.goto('http://example.com/user')
.type('#edit-name', user)
.type('#edit-pass', pass)
.click('.searchsubmit')
.wait()
.evaluate(function () {
return document.getElementsByTagName("TITLE")[0];
}, function (res) {
console.log('Homepage title: '+res.text);
})
.run(function(err, nightmare){
console.log('Done1.');
// step 2
nightmare
.goto('http://example.com/admin')
.wait()
.evaluate(function () {
return document.getElementsByTagName("TITLE")[0];
}, function (res) {
console.log('Admin page title: '+res.text);
})
.run(function(err, nightmare){
console.log('Done2.');
})
;
})
;
When I run this, with: node app.js I am able to log in successfully to the first page. Unfortunately when I try to open the second page I see an access refused on the second page call (http://example.com/admin). The session is not being carried into the second "goto" command.
What can I do to be able to open up many pages with the same nightmarejs session?

Have you tried chaining the goto methods?
new Nightmare()
.goto('http://example.com/user')
.type('#edit-name', user)
.type('#edit-pass', pass)
.click('.searchsubmit')
.wait()
.evaluate(function () {
return document.getElementsByTagName("TITLE")[0];
}, function (res) {
console.log('Homepage title: '+res.text);
})
.goto('http://example.com/admin')
.wait()
.evaluate(function () {
return document.getElementsByTagName("TITLE")[0];
}, function (res) {
console.log('Admin page title: '+res.text);
})
.run(function(err, nightmare){
console.log('Done2.');
})
;
}).run();
From reading the api docs run only executes the commands that came before it.

After some testing, I discovered that it seems goto() should be used only once. In order to switch to a new page, I use click() instead of an additional goto().

Related

Cannot read values from file in fixture folder, getting error as "TypeError Cannot read properties of undefined (reading 'data')"

I'm trying to use fixtures to hold data for different tests, specifically user credentials. This is an example of the code. I'm getting 'Cannot read properties of undefined (reading 'data')'. I tried to google search , I found Cypress fixtures - Cannot read properties of undefined (reading 'data')
I used closure variable technique as reccomended in that post , yet I got reference error of unable to reference data.Please help me.I know cypress.config can be used but I want to keep that for global configs
Json(credentials.json):
{
"username":"*****",
"password":"*****"
}
Code:
import { LoginPage } from "./pageobject/login_page"
describe('Test Scenario', () => {
before(function () {
cy
.fixture('credentials').then(function (data) {
this.data = data
})
})
it('Simple login', () => {
cy.visit(Cypress.env('url'))
var loginpage = new LoginPage()
loginpage.EnterUsername(this.data.username)
loginpage.clickonSubmit()
loginpage.EnterPassword(this.data.password)
loginpage.clickonSubmit()
Cypress
.on('uncaught:exception', (err, runnable) => {
return false;
});
cy.
wait(10000)
cy.
get('span[id="user"]').should('have.text', this.data.username , 'User Login Unsuccessfully')
});
});
There's a few things need adjusting
use function () {} syntax in the it() block
use beforeEach() and alias to load the fixture, because data on this can be cleared (especially after login)
move uncaught:exception catcher to the top of the block
don't cy.wait(), instead add timeout to next command
.should() only has two parameters in this case, so use .and() to test the 2nd text
import { LoginPage } from './pageobject/login_page';
describe('Test Scenario', () => {
beforeEach(function () {
cy.fixture('credentials').as('data')
})
it('Simple login', function() {
Cypress.on('uncaught:exception', (err, runnable) => {
return false;
});
cy.visit(Cypress.env('url'));
var loginpage = new LoginPage();
loginpage.EnterUsername(this.data.username);
loginpage.clickonSubmit();
loginpage.EnterPassword(this.data.password);
loginpage.clickonSubmit();
cy.get('span[id="user"]', {timout:10_000})
.should('have.text', this.data.username)
.and('have.text', 'User Login Unsuccessfully')
})
})
I suspect it's because you are using an arrow function instead of a regular function, you cannot access the this object with an arrow function.
Cypress docs
If you store and access the fixture data using this test context
object, make sure to use function () { ... } callbacks. Otherwise the
test engine will NOT have this pointing at the test context.
change it to this:
it('Simple login', function() {
...
});

How to add nested functions javascript in custom commands for Nightwatch testing- forEach -loop through elements

Hi I am new to javascript and Nightwatch, I am a manual tester who started doing automation about 6 months ago.
I am writing test cases for checking the details of a product, with collapsible menus. Pressing + button will open and display a list of elements, when closing with the same button, it closes the list, and shows a counter with the number of items on the list.
I have a function that is correctly doing this procedure, but I have it written on the test. I would like to use it in the Page where I have all elements and functions related to that page. And I would like to call that function from the test. I have been able to do this, but not with cases with nested functions, because I do not know how to write it.
These are my pages:
loginPage.js;
productPage.js;
productFuntionalityListPage.js;
This is my test:
module.exports = {
'Buy a Product with Bank Account': function (browser) {
const login = browser.page.loginPage();
const productList = browser.page.productPage();
const productFunctionalityList = browser.page.productFuntionalityListPage();
login
.navigate()
.checkLoginPage();
productList
.getAProduct()
//------------------------------------------Features--------------------------------------
//function to click on each button for functionalities and wait for list to appear
function displayFunctionsList(elems) {
elems.value.forEach(function (element) {
browser.elementIdClick(element.ELEMENT)
//wait for list to appear
.waitForElementVisible('.list_of_items')
.pause(2000)
})
}
// click on each function and wait for list to appear
browser.elements('css selector', '.expand_collapse_btn', displayFunctionsList, 5000)
browser.useCss()
// close each function
function closeFunctionsList(elems) {
elems.value.forEach(function (element) {
browser.elementIdClick(element.ELEMENT)
//after click close wait for count to appear
.waitForElementVisible("input[data-id='counter']")
.pause(2000)
})
}
browser.elements('css selector', '.expand_collapse_btn', closeFunctionsList, 2000)
browser.end()
}
}
This is working correctly.
Below it's what I have tried and does not work:
Page:
productFuntionalityListPage.js
module.exports = {
elements: {
counterOfItemsInList: {
locatorStrategy: 'css selector'
selector: "input[data-id='counter']",
},
expandCollapseBtn: {
locateStrategy: 'css selector',
selector: '.expand_collapse_btn',
},
listOfItems: {
locateStrategy: 'css selector',
selector: '.list_of_items',
}
},
commands: [{
displayFunctionsList: function () {
function displayFunctionsList(elems) {
elems.value.forEach(function (element) {
this.elementIdClick(element.ELEMENT)
//wait for list to appear
.waitForElementVisible('#listOfItems')
.pause(2000)
})
}
this.elements('css selector', '#expandCollapseBtn', displayFunctionsList, 5000)
},
closeFunctionsList: function () {
function closeFunctionsList(elems) {
elems.value.forEach(function (element) {
this.elementIdClick(element.ELEMENT)
//wait for list to appear
.waitForElementVisible('#counterOfItemsInList')
.pause(2000)
})
}
this.elements('css selector', '#expandCollapseBtn', closeFunctionsList, 5000)
}
}]
}
Test calling function from page:
module.exports = {
'Buy a Product with Bank Account': function (browser) {
const login = browser.page.loginPage();
const productList = browser.page.productPage();
const productFunctionalityList = browser.page.productFuntionalityListPage();
login
.navigate()
.checkLoginPage();
productList
.getAProduct()
//------------------------------------------Features--------------------------------------
//calling displayFunctionsList from productFuntionalityListPage.js
productFunctionalityList.displayFunctionsList()
//calling closeFunctionsList from productFuntionalityListPage.js
productFunctionalityList.closeFunctionsList()
browser.end()
}
}
Result after running the test above:
Error:
TypeError: this.elements is not a function
- writing an ES6 async test case? - keep in mind that commands return a Promise;
- writing unit tests? - make sure to specify "unit_tests_mode=true" in your config.
Could anyone please help me adding these functions as custom commands in the productFuntionalityListPage.js and call these functions from the test itself? Not sure what's wrong, because of my lack of javascript and nightwatch knowledge.
Try passing browser as a variable when calling the function like this -
##Test page##
//Example call
gmail.selectEmail(browser, 'browser authentication')
And then the method in the pageObject -
##Page Object##
//Example Method
selectEmail(browser, searchValue){
browser.blah(searchValue);
browser.blah
browser.blah
};
Its slightly messy way of getting it to work but this has saved my bacon a few times

Cypress not intercepting request with render function vue

I'm trying to do a pretty simple intercept in Cypress using a Vue's application. My component has a setup method using render function as such:
setup() {
useInfiniteLoading({ runner: ... })
}
Then on my tests I do the following:
describe("List todo resource", () => {
it("Checks it loads more todos when scrolling to the bottom", function () {
cy.intercept('/todo').as('getTodos');
cy.visit("/todos");
cy.wait("#getTodos").then(({response}) => {
console.log(response);
})
})
})
When running the test I see that the intercept is not stubbing the response.
As you can see from the image the request makes a request to my actual server running locally and the response is stubed. The weird part is that in a previous test I have:
it("Checks the todo list gets updated when clicking on to resolve it (from true to false)", function () {
cy.visit("/todos");
const resolved = false;
const shouldHaveClass = resolved
? "mdi-checkbox-marked-outline"
: "mdi-checkbox-blank-outline";
cy.intercept("GET", "todo", {
fixture: "resources/todo/list.todo.json",
}).as("getTodos");
cy.intercept("PUT", "todo", {
body: { data: { ...this.updateTodoFixture.data, resolved } },
}).as("updateTodo");
cy.get(".todo-list-item__resolve")
.first()
.each((btn) => {
btn.click();
});
cy.get(".todo-list-item__resolve")
.first()
.should("satisfy", ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes(shouldHaveClass);
});
});
And the response is stubbed using intercept as you can see from the previous screenshot. Is it possible that the previous test is affecting the next test? I have tried taking a look into "Intercept too soon" but no luck on trying to apply the fix described in the page.
Any idea on what could be causing the stub not to happen?

Protractor: Non angular login to angular site

Before I can reach the site to be tested I have to visit a login (non-angular) page first
var url = 'http://localhost:9999/login?usern=bar';
browser.driver.get(url);
Although that is a non-angular page, passing the ?usern=bar into the url the server gives an HTTP CODE of 302, and redirects to the page /new-user. Inside the new-user page I have to click a button before I can begin testing
But whatever I do I always get
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
There is something wrong in my flow, because I can see the new-user page, but the button is not clicked (and long after that the errors appear)
The spec file:
var Site = require('helper/site');
describe('Main', function {
beforeAll(function () {
return Site.login();
});
it('should show the main page', function () {
epect(browser.getCurrentUrl()).toMatch(/\/main/);
});
});
Site.js:
function login() {
browser.driver.get('http://localhost:9999/login?usern=bar');
browser.driver.wait(function () {
return browser.driver.getCurrentUrl().then(function (url) {
return /\/new-user/.test(url);
});
});
element(by.css('.save')).click();
}
module.exports = {
login: login
};
Any help would be appreciated
You have to turn the sync off:
describe('Main', function {
beforeAll(function () {
browser.ignoreSynchronization = true;
return Site.login();
});
it('should show the main page', function () {
browser.ignoreSynchronization = false;
expect(browser.getCurrentUrl()).toMatch(/\/main/);
});
});
Though, think of better places to turn it off and then on again once you are on the main page.
I used browser.waitForAngularEnabled(false) to turn off waiting for angular app. My app used O365 login.
public async HandleO365Login() {
await browser.waitForAngularEnabled(false);
await this.o365usernameTextBox.sendKeys(Constants.USER_EMAIL);
await this.o365NextButton.click();
}
You can turn it on after your angular app gets loaded.

Protractor- Generic wait for URL to change

In previous questions I have seen that a nice way to wait for the url to change is to use:
browser.wait( function() {
return browser.getCurrentUrl().then(function(url) {
return /myURL/.test(url);
});
}, 10000, "url has not changed");`
But I am trying to have a method that I can pass myURL as a variable (in case I need to use it with other sites) and is not working.
I am trying this in my Page Object file:
this.waitUrl = function(myUrl) {
browser.wait( function(myUrl) {
return browser.getCurrentUrl().then(function(url, myUrl) {
return myUrl.test(url);
});
}, 10000, "url has not changed");
};
Any ideas if this is even possible and how to do it if so?
Update (July 2016): with Protractor 4.0.0 you can solve it with urlIs and urlContains built-in Expected Conditions.
Original answer:
Don't pass myUrl inside the then function, it is available from the page object function scope:
browser.wait(function() {
return browser.getCurrentUrl().then(function(url) {
return myUrl.test(url);
});
}, 10000, "url has not changed");
I would though define it as an Expected Condition:
function waitUrl (myUrl) {
return function () {
return browser.getCurrentUrl().then(function(url) {
return myUrl.test(url);
});
}
}
So that you can then use it this way:
browser.wait(waitUrl(/my\.url/), 5000);
For those that want an example for Protractor 4.0.0 through 5.3.0
You can use "ExpectedConditions" like so...
var expectedCondition = protractor.ExpectedConditions;
// Waits for the URL to contain 'login page'.
browser.wait(expectedCondition.urlContains('app/pages/login'), 5000);
If you want to validate this with an e2e test.
it('should go to login page', function() {
loginPage.login();
const EC = protractor.ExpectedConditions;
browser.wait(EC.urlContains('app/pages/login'), 5000).then(function(result) {
expect(result).toEqual(true);
});
});

Categories

Resources