Playwright - Javascript - CucumberJs - Click and Fill methods fail - javascript

I'm working with Playwright, cucumber and Javascript. I'm facing the next issues below:
page.type: selector: expected string, got object
page.click: selector: expected string, got function
And many similar error messages.
this is the profile.cjs class, where the data is located:
const wrongEmailFormat = 'eyftqeiuyfqwiyfiwqfywqgfywqguddwqguy'
const existingEmail = 'amalg12#gmail.com'
module.exports = { wrongEmailFormat , existingEmail };
This is my profile-page.cjs:
const { secondaryUrl } = require("../config.cjs");
const { wrongEmailFormat , existingEmail } = require("../data/profile.cjs")
const should = require('chai').should(); //Chai assertion
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
const profileTitle = async () => await page.$('//h2[contains(text(),\'My Profile\')]')
const communicationTab = async () => await page.$('//button[#id=\'headlessui-tabs-tab-:R5l6:\']')
const currentEmail = async () => await page.$('#current-email')
const updateEmail = async () => await page.$('#cla9ruxam000w3b630t1c9dl4')
class ProfilePage {
async navigateToProfilePage() {
await page.goto(secondaryUrl)
await delay(2000)
}
async profilePageDisplayed() {
should.exist(profileTitle)
}
async communicationTabDisplayed() {
should.exist(communicationTab)
}
async currentEmailFieldDisplayed(){
should.exist(currentEmail)
}
async updateEmailFieldDisplayed(){
should.exist(updateEmail)
}
async updateEmailWrongFormat(){
//await page.keyboard.press(updateEmail().type(wrongEmailFormat))
//await updateEmail().click()
//await page.click(updateEmail, { force: true })
//await page.fill(wrongEmailFormat).toString()
//await page.waitFor(updateEmail())
//await page.click(updateEmail, { force: true }).toString()
//await page.fill(updateEmail(), wrongEmailFormat).toString()
//await page.dispatchEvent(updateEmail()).click
//await updateEmail().keys(wrongEmailFormat)
//await delay(3000)
}
}
module.exports = { ProfilePage };
updateEmailWrongFormat is all with comments, because I have tried in many ways, but without success.
Could anybody help me with this, please?

The error was the next:
All the selectors included in the profile-page were using the "WebdriverIO" format, so I did the next:
1.- Take all the selectors and move them to a new folder(selectors) inside a new class: profile-selectors with the next format:
module.exports = {
profileTitle: '//h2[contains(text(),\'My Profile\')]',
communicationTab: '//button[#id=\'headlessui-tabs-tab-:R5l6:\']',
currentEmail:'#current-email',
updateEmail:'//html[1]/body[1]/div[1]/div[1]/div[2]/main[1]/div[1]/div[2]/' +
'div[2]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/input[1]'
};
After this, import in the Page object class and use them:
const { profileTitle, communicationTab, currentEmail, updateEmail} = require('../selectors/profile-selectors')
I know there are many xpaths, but much of the webelements do not have identifiers, or any others are dynamic elements.

Related

puppeteer waitForSelector when there are multiple elements with same class and more than one can be visible

What i'm trying to accomplish is to save complete document with all the comments expanded.
Unfortunately there are multiple selectors with same class and most of them are hidden and what i believe puppeteer does it takes first found selector and waits until it's visible which never happens.
Url: https://www.discoverpermaculture.com/permaculture-masterclass-video-1
const puppeteer = require('puppeteer');
const isElementVisible = async (page, cssSelector) => {
let visible = true;
await page
.waitForSelector(cssSelector, { visible: true, timeout: 2000 })
.catch(() => {
visible = false;
});
if(visible)console.log('Selector '+cssSelector+'visible!');
return visible;
};
async function run () {
let browser = await puppeteer.launch({headless: true, defaultViewport: null, args: ['--window-size=1920,10000',],});
const page = await browser.newPage();
const fs = require('fs');
await page.goto('https://www.discoverpermaculture.com/permaculture-masterclass-video-1');
await page.waitForTimeout(4000)
const elementHandle = await page.waitForSelector('iframe');
const frame = await elementHandle.contentFrame();
//loading all the comments (works because there's only one 'a.load-more__button' element a time)
const selectorForLoadMoreButton = 'a.load-more__button';
let loadMoreVisible = await isElementVisible(frame, selectorForLoadMoreButton);
while (loadMoreVisible) {
console.log('Loading comments');
await frame
.click(selectorForLoadMoreButton)
.catch(() => {});
loadMoreVisible = await isElementVisible(frame, selectorForLoadMoreButton);
}
//expanding comments doesn't work because each comment have a.see-more but some are hidden
const selectorForSeeMoreButton = 'a.see-more';
let seeMoreVisible = await isElementVisible(frame, selectorForSeeMoreButton);
while (seeMoreVisible) {
console.log('Expanding comments');
await frame
.click(selectorForSeeMoreButton)
.catch(() => {});
seeMoreVisible = await isElementVisible(frame, selectorForSeeMoreButton);
}
const cdp = await page.target().createCDPSession();
const { data } = await cdp.send('Page.captureSnapshot', { format: 'mhtml' });
fs.writeFileSync('out.mhtml', data);
browser.close();
}
run();
Any ideas how to handle this?
It turned out that each comment have 'a.see-more' element but if it's not a long one it also have '.hidden' class. Had to update this piece of code to search for all the 'a.see-more' elements but without '.hidden' class.
const selectorForSeeMoreButton = 'a.see-more:not(.hidden)';
let seeMoreVisible = await isElementVisible(frame, selectorForSeeMoreButton);
while (seeMoreVisible) {
console.log('Expanding comments');
await frame
.click(selectorForSeeMoreButton)
.catch(() => {});
seeMoreVisible = await isElementVisible(frame, electorForSeeMoreButton);
}

playwright test website with Keycloak, can't find a way to logIn via pageObjectModel

Hi (i am pretty new in palywright),
I need/want to find a way in playwright, to login via pageObjectModel in a aplication which uses keycloak, but I don't know how.
I found a way without pageObject, to logIn in first test and saving the auth in process.env.STORAGE and then use test.use({storageState: auth_storage_path}) in the rest of the test inside the file.spec.js;
note: keycloak works (sorry for this basic info)
user not login -> visit.baseUrl, redirects you to keycloak authPage
user already logIn -> visit.baseUrl goes direct to bareUrl. (so no logIn button in homepage etc)
//tests_with_auth_store.spec.js
const {test, expect} = require('#playwright/test');
const auth_storage_path = 'storage_auth.json';
const baseUrl = 'https://myBaseUrl_xyz.com';
test('mylogin', async ({page, context}) => {
const usernameId = '[id="username"]';
const passwordId = '[id="password"]';
const idLogin = '[id="login-button"]';
const usernameValue = '*****';
const passwordValue = '*****';
//login:
await page.goto(baseUrl);
await page.fill(usernameId, usernameValue);
await page.fill(passwordId, passwordValue);
await Promise.all([
page.waitForNavigation(/*{ baseUrl: baseUrl }*/),
page.click(idLogin)
]);
process.env.STORAGE = null;
const storage = await context.storageState({ path: auth_storage_path });
process.env.STORAGE = JSON.stringify(storage);
JSON.parse(process.env.STORAGE);
});
test.describe("testDescription login via pageObjectModel", () => {
test.use({storageState: auth_storage_path});
test('i- firstTest whatever ', async ({page}) => {
await page.goto(baseUrl);
......
....
});
test('ii- secondTest whatever ', async ({page}) => {
await page.goto(baseUrl);
......
....
});
});
This works ok and all test under test.use({storageState: auth_storage_path});
can jump to baseUrl directly. The problem is that I can not find a way to encapsulate test('login') into a playwright pageObject (in cypress we did it in simple func in commands.js and saving the auth into cookies)
My demo for login-page.js:
// login-page.js
const { expect, test} = require('#playwright/test');
const baseUrl = 'https://myBaseUrl_xyz.com';
const auth_storage_path = 'storage_auth.json';
exports.LoginPage = class LoginPage {
/**
* #param {import('#playwright/test').Page} page
*/
constructor(page) {
this.page = page;
}
async login() {
process.env.STORAGE = null;
const baseUrl = 'https://myBaseUrl_xyz';
await this.page.goto(baseUrl);
await this.page.fill('[id="username"]', '*****');
await this.page.fill('[id="password"]', '*****');
await Promise.all([
this.page.waitForNavigation(/*{ baseUrl: baseUrl }*/),
this.page.click('[id="fc-login-button"]')
]);
const storage = await this.page.context().storageState({path: auth_storage_path});
process.env.STORAGE = JSON.stringify(storage);
JSON.parse(process.env.STORAGE);
}
// async gotoBaseUrl() {
// test.use({storageState: auth_storage_path});
// return this.page.goto(baseUrl);
// }
}
call login-page from tests_with_auth_store_viaPage.spec.js
// tests_with_auth_store_viaPage.spec.js
const {test, expect} = require('#playwright/test');
const { LoginPage } = require('../login/login-page');
const auth_storage_path = 'storage_auth.json';
const baseUrl = 'https://myBaseUrl_xyz.com';
test('login', async ({page}) => {
const loginPage = new LoginPage(page);
await loginPage.login();
});
test.describe("testDEscription tests with save login into json", () => {
test.use({storageState: auth_storage_path});
test('i- firstTest whatever', async ({page}) => {
await page.goto(baseUrl);
......
....
});
But here test('i- firstTest whatever') page.goto(baseUrl) does NOT jump to baseUrl but to keycloak auth page :(.
Eventhoug test('login') is login and creating storage_auth.json. So I am doing something wrong maybe I need something like loginPage.gotoBaseUrl(), but it doesn't work as well.
By now, I am writing the rest all the test including always the first test('mylogin) but I am sure there is a way via pageObject
Regards
Already found the way. I found myself the way. If someone needs any help, do not hesitate to ask me
demo_login.spec.js
const {test, expect} = require('#playwright/test');
const auth_storage_path = 'storage_auth.json';
const {Login} = require('../../yourpathforfile/login_page.js');
const {Logout} = require("../../yourpathforfile/logout_page.js");
test('login_a', async ({page}) => {
const login = new Login(page);
await login.visit_baseurl();
await login.method_login();
await login.check_login_success();
await login.saveAuth(auth_storage_path);
await login.print_url_tenant();
await login.print_browser();
});
test.describe("Login_a tests --> ", () => {
test.use({storageState: auth_storage_path});
test('demo_a test', async () => {
expect( 1 +1).toBe(2); });
test('logout_a', async ({page}) => {
const logout = new Logout(page);
await logout.visit_baseurl();
await logout.method_check_pageIsWorkplace();
await logout.method_logout_workplace();
await logout.method_check_logout();
});
});
objectPage-> login_page.js
const { expect, firefox} = require('#playwright/test');
const uaParser= require("ua-parser-js");
exports.Login = class Login {
/**
* #param {import('#playwright/test').Page} page
*/
constructor(page) {
this.page = page; }
async visit_baseurl () {
await this.page.goto("/");
//await this.page.goto(process.env.env_baseurl );
}
async method_login() {
const usernameId = selectorUserId;
const passwordId = selectorPassId;
const idLogin = '[id="fc-login-button"]';
const usernameValue = 'demo_user_123';
const passwordValue = 'demo_pass_123';
const mydelay = 200;
await this.page.click(usernameId);
await this.page.type(usernameId, usernameValue, {delay:mydelay});
await this.page.click(passwordId);
await this.page.type(passwordId, passwordValue, {delay:mydelay});
await this.page.click(idLogin,{delay:mydelay})
}
etc ....
}
the same for logout_page
If you need any more info just let mne know.

Node js pass parameter to function

I have this code :
const { Logger } = require ("telegram/extensions");
const { TelegramClient } = require ("telegram");
const { StringSession } = require ("telegram/sessions");
const { NewMessage } = require ("telegram/events");
const { NewMessageEvent } = require ("telegram/events/NewMessage");
const { Message } = require ("telegram/tl/custom/message");
const input = require('input'); // npm i input
const puppeteer = require('puppeteer-extra');
async function eventHandler(event, browser) {
//get event.message, ....
const page = await browser.newPage();
}
const client = new TelegramClient(
new StringSession(stringSession),
apiId,
apiHash,
{ connectionRetries: 5 }
);
(async () => {
console.log('Loading interactive example...')
const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--shm-size=2gb', '--start-maximized', '--disable-features=IsolateOrigins,site-per-process', '--disable-web-security'], headless: true});
await client.start({
phoneNumber: "+33...",
password: async () => await input.text('password?'),
phoneCode: async () => await input.text('Code ?'),
onError: (err) => console.log(err),
});
console.log('Telegram bot connected');
console.log(client.session.save());
client.addEventHandler(eventHandler, new NewMessage({}), browser);
})();
I want to pass the browser variable to the eventHandler function.
I try like that, but it does not work, browser came "undefined" in eventHandler.
How pass the browser variable to my eventHandler?
Not sure what is the signature of client.addEventHandler but assuming it takes a single param event, you could try replacing your last line with something like:
client.addEventHandler(
(event) => eventHandler(event, browser),
new NewMessage({}),
);

Can't access functions made through prototype with WebdriverJS

So, I'm using selenium webdriver with javascript, the browser is firefox.
The base_page looks like this:
function Page() {
this.driver = new Builder().forBrowser('firefox').build();
const driver = this.driver;
this.visit = async(url) => {
try {
return await driver.get(url);
} finally {
console.log("visit: OK");
}
};
...
The home_page looks like this:
const Page = require('./base_page');
Page.prototype.requestBtn = async() => {
try {
await this.write('input', 'user#fakemail.com');
return {
opacity: await this.find('.btn-lg').getCssValue('opacity'),
state: await this.find('.btn-lg').isEnabled()
};
} catch(err) {
console.log(err);
}
};
...
In the mocha.test.js I import the home_page. I can use the functions created in base_page, but can't access functions in home_page.
For example, page.visit() and page.quit() are qorking, but page.requestBtn() not.
const assert = require('assert');
const {Builder, By, Key, until} = require('selenium-webdriver');
const Page = require('../lib/home_page');
let page;
describe('Test', () => {
beforeEach(async() => {
page = new Page();
await page.visit('https://library-app.firebaseapp.com');
});
afterEach(async() => {
await page.quit();
});
it("Example", async() => {
await page.requestBtn();
});
...

How to write async function a module function using fat arrow notation

I want to write a module similar to the one below.
It should be callable via "Server.doBackup" and use the fat arrow notation together with an async-await function
Any ideas?
Can you provide a corrected version of my snippet below?
const Server = {
config: {
documents: ['DEFAULT', 'KEYS'],
exportpath: 'data/exportFromCosmos/',
uploadpath: 'data/uploadToAzureBlob/',
crosscheckFile: 'data/crosscheckFile.txt'
},
doBackup: () => async {
let prepareFolders = await Folders.prepare(Server.config, resolve)
let downloadDB_DEFAULT = await Database.downloadDocumentsOfType_DEFAULT()
let downloadDB_KEYS = await Database.downloadDocumentsOfType_KEYS()
let zipDocuments = await Documents.zip(Server.config)
}
}
module.exports = Server
const Server = {
config: {
documents: ['DEFAULT', 'KEYS'],
exportpath: 'data/exportFromCosmos/',
uploadpath: 'data/uploadToAzureBlob/',
crosscheckFile: 'data/crosscheckFile.txt'
},
doBackup: async () => {
let prepareFolders = await Folders.prepare(Server.config, resolve)
let downloadDB_DEFAULT = await Database.downloadDocumentsOfType_DEFAULT()
let downloadDB_KEYS = await Database.downloadDocumentsOfType_KEYS()
let zipDocuments = await Documents.zip(Server.config)
}
}
module.exports = Server
// Sample Code
async function findName(){
return {name: "Stack"}
}
let user = {
getName: async()=>{
let {name} = await findName()
return name
}
}
user.getName()
.then((userDetail)=> console.log(userDetail))
.catch((error) => console.log(error))

Categories

Resources