Using Microsoft's playwright, I have this testing code that works:
describe('When a user views a list of forms', () => {
let rows;
before(async() => {
await page.waitForSelector('tbody');
rows = await page.$$('tr');
});
it('should show a table of forms', async() => {
rows.length.should.equal(11);
});
I would like to break it out into a page object. I have this as my page object
class UserFormsPage {
constructor(page) {
this.page = page;
}
async rows() {
await this.page.waitForSelector('tbody');
return await this.page.$$('tr');
}
}
And this as my test:
describe.only('List Forms Widget', function() {
let page;
...
before(async function() {
const context = await this.browser.newContext();
page = await context.newPage();
const userFormsPage = new UserFormsPage(page);
...
describe('When a user views a list of forms', () => {
let rows;
...
before(async() => {
rows = await userFormsPage.rows();
});
it('should show a table of forms', async() => {
rows.length.should.equal(11);
});
But I get
1) List Forms Widget
When a user views a list of forms
"before all" hook for "should show a table of forms":
TypeError: Cannot read property 'rows' of undefined
Why is this?
Note: using native es modules in Node 16 if that matters
Your rows() method is async. Every async method returns a promise, so in your hook, rows = userFormsPage.rows(); will result only in Promise { <pending> }.
If you want the number, you have to await it:
before(async () => {
rows = await userFormsPage.rows();
});
Another problem seems to be that you don't have (at least not here in your question) any creation of the UserFormsPage object. The constructor expects page, but you don't seem to provide it.
I think the following should work
row = await (await userFormsPage.rows());
Want to access a text from the group of the elements.
this approach doesn't work, the runner is giving an error saying that invoke is not a function
cy.get('div[class^="lalala"]')
.each(function($sec, i, $sects) {
$sec.find('header[class^="tatata"]')
.invoke('text').then((text) => {
let secText = text
cy.log(secText);
});
})
But without each() it is working when I am accessing any of the elements:
cy.get('div[class^="lalala"]').first()
.find('header[class^="tatata"]')
.invoke('text')
.then((text) => {
let secText = text
cy.log(secText);
});
})
How can I handle that?
$sec is a wrapped jQuery element and $sec.find() returns a jQuery element as well. You have to use cy.wrap to call invoke on it.
cy.get('div[class^="lalala"]').each(function ($sec, i, $sects) {
cy.wrap($sec.find('header[class^="tatata"]'))
.invoke('text')
.then((secText) => {
cy.log(secText)
})
})
My Protractor test fails with below error
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
This is my spec file.
let common=require('./Objects/common.js')
describe('Cloud testing', function() {
it('Cloudtest1', function() {
let EC=protractor.ExpectedConditions;
browser.waitForAngularEnabled(false);
browser.get(common.loginURL);
common.txtUserName.sendKeys('aqaasdas#hkm.com');
common.txtPword.sendKeys('asdasd##$');
common.btnLogin.click();
browser.wait(EC.visibilityOf(element(by.xpath("//a[#class='btn btn-success']"))));
element(by.xpath("//a[#class='btn btn-success']")).click();
common.btnCrtPcr.click();
});
});
Any help is appreciated, I tried answers to similar questions posted here but nothing works for me. I'm running the latest Protractor and Chrome versions.
Have you tried by asyn test? Sometimes even backend response or browser performance may affect the test cases.
Refer: https://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support
describe("Your module", function() {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
it("takes a long time", function(done) {
let EC=protractor.ExpectedConditions;
browser.waitForAngularEnabled(false);
browser.get(common.loginURL);
common.txtUserName.sendKeys('aqaasdas#hkm.com');
common.txtPword.sendKeys('asdasd##$');
common.btnLogin.click();
browser.wait(EC.visibilityOf(element(by.xpath("//a[#class='btn btn-success']"))));
element(by.xpath("//a[#class='btn btn-success']")).click();
common.btnCrtPcr.click();
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
});
Your callback was later than your Jasmins default timout time. You might want to use async/await in your it block even though you have used expected condition.
it('Cloudtest1', async () => {
let EC=protractor.ExpectedConditions;
browser.waitForAngularEnabled(false);
browser.get(common.loginURL);
await common.txtUserName.sendKeys('aqaasdas#hkm.com');
await common.txtPword.sendKeys('asdasd##$');
await common.btnLogin.click();
browser.wait(EC.visibilityOf(element(by.xpath("//a[#class='btn btn-success']"))));
element(by.xpath("//a[#class='btn btn-success']")).click();
await common.btnCrtPcr.click();
});
Suggestion: Perform action in page object or before each/all. Use it block for expect statement.
example:
describe('When user logged Into account ', () => {
beforeAll(async () => {
await browser.waitForAngularEnabled(false);
await loginPO.login();
});
it('Browser title should be displayed.', async () => {
expect(await browser.getTitle()).toBe('test');
});
});
I'm building a web scraper that uses puppeteer. I'd obviously like to ensure that I don't break things as I work the kinks out and so I'm writing some implementation tests.
How would I go about testing out the code below? The issue is that newPage() is nested and I can't figure out how to create a spy for it.
Any ideas? Should I structure the code differently to make it easier to test (from what I've read this a big no-no). Happy to hear your suggestions.
//myFile
myFn(){
let browser = puppeteer.launch()
let page = browser.newPage();
}
describe('searchAddress', () => {
beforeEach(() => {
browserSpy = spyOn(puppeteer,'launch')
pageSpy = spyOn(puppeteer,'newPage') // <--- ????
})
it('should ensure the calls were made', async () => {
await myFn()
expect(sleepSpy).toHaveBeenCalled();
expect(pageSpy).toHaveBeenCalled();
});
});
In this case the spyOn(puppeteer,'launch') should return an object that contains a spy object for newPage call. I mean the followings:
describe('searchAddress', () => {
let newPageSpy;
let browserSpy;
beforeEach(() => {
// ARRANGE
newPageSpy = jasmine.createSpy();
let browserMock = { newPage: newPageSpy };
browserSpy = spyOn(puppeteer, 'launch').and.returnValue(browserMock);
});
it('should ensure the calls were made', async () => {
// ACT
await myFn();
// ASSERT
expect(newPageSpy).toHaveBeenCalled();
});
});
describe('my homepage', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
// ptor.ignoreSynchronization = true;
ptor.get('http://localhost/myApp/home.html');
// ptor.sleep(5000);
})
describe('login', function(){
var email = element.all(protractor.By.id('email'))
, pass = ptor.findElement(protractor.By.id('password'))
, loginBtn = ptor.findElement(protractor.By.css('#login button'))
;
it('should input and login', function(){
// email.then(function(obj){
// console.log('email', obj)
// })
email.sendKeys('josephine#hotmail.com');
pass.sendKeys('shakalakabam');
loginBtn.click();
})
})
});
the above code returns
Error: Error while waiting for Protractor to sync with the page: {}
and I have no idea why this is, ptor load the page correctly, it seem to be the selection of the elements that fails.
TO SSHMSH:
Thanks, your almost right, and gave me the right philosophy, so the key is to ptor.sleep(3000) to have each page wait til ptor is in sync with the project.
I got the same error message (Angular 1.2.13). My tests were kicked off too early and Protractor didn't seem to wait for Angular to load.
It appeared that I had misconfigured the protractor config file. When the ng-app directive is not defined on the BODY-element, but on a descendant, you have to adjust the rootElement property in your protractor config file to the selector that defines your angular root element, for example:
// protractor-conf.js
rootElement: '.my-app',
when your HTML is:
<div ng-app="myApp" class="my-app">
I'm using ChromeDriver and the above error usually occurs for the first test. I've managed to get around it like this:
ptor.ignoreSynchronization = true;
ptor.get(targetUrl);
ptor.wait(
function() {
return ptor.driver.getCurrentUrl().then(
function(url) {
return targetUrl == url;
});
}, 2000, 'It\'s taking too long to load ' + targetUrl + '!'
);
Essentially you are waiting for the current URL of the browser to become what you've asked for and allow 2s for this to happen.
You probably want to switch the ignoreSynchronization = false afterwards, possibly wrapping it in a ptor.wait(...). Just wondering, would uncommenting the ptor.sleep(5000); not help?
EDIT:
After some experience with Promise/Deferred I've realised the correct way of doing this would be:
loginBtn.click().then(function () {
ptor.getCurrentUrl(targetUrl).then(function (newURL){
expect(newURL).toBe(whatItShouldBe);
});
});
Please note that if you are changing the URL (that is, moving away from the current AngularJS activated page to another, implying the AngularJS library needs to reload and init) than, at least in my experience, there's no way of avoiding the ptor.sleep(...) call. The above will only work if you are staying on the same Angular page, but changing the part of URL after the hashtag.
In my case, I encountered the error with the following code:
describe("application", function() {
it("should set the title", function() {
browser.getTitle().then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
Fixed it by doing this:
describe("application", function() {
it("should set the title", function() {
browser.get("#/home").then(function() {
return browser.getTitle();
}).then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
In other words, I was forgetting to navigate to the page I wanted to test, so Protractor was having trouble finding Angular. D'oh!
The rootElement param of the exports.config object defined in your protractor configuration file must match the element containing your ng-app directive. This doesn't have to be uniquely identifying the element -- 'div' suffices if the directive is in a div, as in my case.
From referenceConf.js:
// Selector for the element housing the angular app - this defaults to
// body, but is necessary if ng-app is on a descendant of <body>
rootElement: 'div',
I got started with Protractor by watching the otherwise excellent egghead.io lecture, where he uses a condensed exports.config. Since rootElement defaults to body, there is no hint as to what is wrong with your configuration if you don't start with a copy of the provided reference configuration, and even then the
Error while waiting for Protractor to sync with the page: {}
message doesn't give much of a clue.
I had to switch from doing this:
describe('navigation', function(){
browser.get('');
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
to doing this:
describe('navigation', function(){
beforeEach(function(){
browser.get('');
});
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
the key diff being:
beforeEach(function(){
browser.get('');
});
hope this may help someone.
I was getting this error:
Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
The solution was to call page.navigateTo() before page.getTitle().
Before:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
After:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
page.navigateTo();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
If you are using
browser.restart()
in your spec some times, it throws the same error.
Try to use
await browser.restart()