webdriverio - download pdfs in chrome - webdriver-io

I am new to using webdriverio, and attempting to automatically download a pdf file. The file opens in the browser and I cannot figure out how to download it - ideally to a folder specified on my local machine. I see some old forum posts which possibly suggest using chromedriver, however, due to minimal code snippets provided, I cannot understand if it's the correct approach though. Here is what I have this far (although the last two lines do not work):
const LoginPage = require('../pageobjects/login.page');
describe('Payroll Download Application', () => {
it('Login Fail Page', async () => {
await LoginPage.open();
await LoginPage.login();
await $("a[href='PayCycleReports']").click()
await $('a=Payroll Summary').click()
const handles = await browser.getWindowHandles()
await browser.switchToWindow(handles[1])
const elem = await $("#viewer").shadow$("#toolbar").shadow$("#downloads").shadow$("#downloads").shadow$("#download")
await elem.click()
});
});
Any help to figure it out would be greatly appreciated. Thanks :)

This can be done with browser.execute.
It will be necessary to select the element through JS and add the 'download' attribute to it. If you click on an element with the 'download' attribute, the pdf will not open but will be downloaded.
Example:
await browser.execute(function(){
document
.querySelector("#node-32 > div > div > div > ul:nth-child(4) > li:nth-child(1) > a")
.setAttribute("download", "download")
},
);
If you need a different selector (not CSS) you can use getElementById , getElementsByName, getElementsByTagName, getElementsByClassName, getElementByXPathand others.

Related

How to enter Immersive Reader on Microsoft Edge through Selenium?

On a high level, does anyone know how to enter the Immersive Reader mode on Microsoft Edge when it is available for a given webpage through Selenium?
My aim is to load up a page, enter Immersive Reader, and save the page's source code to disk. I'm firing up Edge through Docker and I'm pragmatically connecting to it via a Node.js script.
I've tried driver.actions().sendKeys(KEY.F9), but that doesn't work since I'm targeting the browser and not a DOM element.
Many thanks for all your help.
New
Just run
driver.get('read://' + url)
and the site is loaded in immersive reader mode if available.
Old
To interact with the UI you have to use pyautogui (pip install pyautogui) and then run this code while the browser window is on focus/active:
import pyautogui
pyautogui.press('F9')
It is also useful for example to save a pdf by interacting with the popup window appearing when pressing CTRL+S.
Here's a bit of code for anyone else who might stumble across this:
Credits to #sound wave for helping me get there!
const { Builder } = require('selenium-webdriver');
const fs = require('fs');
(async () => {
const driver = await new Builder().forBrowser('MicrosoftEdge').usingServer('http://localhost:4444').build();
await driver.get('read://https://www.bbc.co.uk/news/entertainment-arts-64302120'); // this URL needs to be Immersive Reader supported
await driver.switchTo().frame(0);
const pagesource = await driver.getPageSource();
fs.writeFile('test.html', pagesource, err => {
if (err) {
console.log(err);
}
});
const title = (await driver.getTitle()).trim();
console.log(title);
await driver.quit();
})().catch((e) => console.error(e));

How can i click the following Button with puppeteer?

I'm trying to click a button on a website with puppeteer but it doesn't work for me.
Element-info:
<button aria-label="Alles akzeptieren" role="button" data-testid="uc-accept-all-button" class="sc-gtsrHT gqGzpd">OK</button>
My Code:
async function checkout(){
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto(product_url);
await page.waitFor(3000);
await page.click("Button[class='sc-gtsrHT gqGzpd']", elem => elem.click());
}
Error Message:
Error: No node found for selector: Button[class='sc-gtsrHT gqGzpd']
at Object.assert (C:\Coding\Ticket-Bot\node_modules\puppeteer\lib\cjs\puppeteer\common\assert.js:26:15)
at DOMWorld.click (C:\Coding\Ticket-Bot\node_modules\puppeteer\lib\cjs\puppeteer\common\DOMWorld.js:277:21)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async checkout (C:\Coding\Ticket-Bot\bayern.js:14:5)
Pictures:
https://i.stack.imgur.com/BYOnx.png (Button Info)
https://i.imgur.com/of9Rjgo.png (Button)
what is the correct code so that the button will be clicked?
You get the error due to the element does not exist on the page, it may be caused by the fact that CSS classes are autogenerated as #Sirko suggests.
You can even inspect the element's class name in DevTools if you launch puppeteer in headful mode.
You will need to find those selectors that will remain the same, e.g.:
await page.click('[aria-label="Alles akzeptieren"]');
await page.click('[data-testid="uc-accept-all-button"]');
Note: I am not sure if you need elem => elem.click() in the click options.

Does TestCafe support multiple tab testing

I have a scenario where clicking on a button opens a new tab but when I try with testCafe it opens in a new window instead of new tab.Why is this ? Doesn’t testCafe support new tab scenarios?
I think using multiple tabs is not really necessary. Usually people try to test that a link opens up in a new tab, but why?
<a class="c-cookie-bar__text-link" target="_blank" href="/private-data-info">More information</a>
You know that a link with target="_blank" will open up in a new tab. This is nothing that would be programmed by your team, this is how a browser works. Just every imaginable browser behaves the same if it encounters this attribute in a link. It's been tested, you don't need to retest it on your website.
However, you still might need to test that the target content loads or that the link looks the way you want. That's ok, but you don't need multiple tabs for that. The following scenarios make sense:
check that the link has the target="_blank" attribute:
test
('Check Target Attr', async t => {
await t
.expect(Selector('.c-cookie-bar__text-link').withAttribute('target', '_blank').exists)
.ok();
});
check that the href attr is what it should be:
test
('Check Href Attr', async t => {
await t
.expect(Selector('.c-cookie-bar__text-link').getAttribute('href'))
.eql('/private-data-info');
});
check that the content loads in the same tab:
import getBaseUrl from '../Helpers/baseUrl';
import { Selector } from 'testcafe';
const baseUrl = getBaseUrl();
fixture `Link Handling`
.page(baseUrl);
test
('Go To Resource', async t => {
const resource = await Selector('.c-cookie-bar__text-link')
.getAttribute('href');
await t
.navigateTo(baseUrl + resource);
// possibly some other assertions follow
});
check the http status code (I use axios here, but it's not all visible in my example):
import getBaseUrl from '../Helpers/baseUrl';
import { Selector } from 'testcafe';
import request from '../Helpers/networkRequest';
const baseUrl = getBaseUrl();
fixture `Link Handling`
.page(baseUrl);
test
('Check Status Code', async t => {
const resource = await Selector('.c-cookie-bar__text-link')
.getAttribute('href');
const networkReq = await request({
method: 'GET',
url: baseUrl + resource
});
await t
.expect(networkReq.status).eql(200);
});
If you actually ignore all this and just click the link in TestCafe:
await t
.click(Selector('.c-cookie-bar__text-link'));
it will be opened in a new window like so:

Jest + puppeteer best architecture practices

I just entered the world of testing with puppeteer and jest, and I was wondering what the best practice was in terms of folder architecture and logic.
I've never done testing before and I think I'm getting a little lost in the different principles and concepts and how it all fits together.
I learned to do my tests based on the page-object model, so I have classes for each of my pages, but also for each of my modules ( or components ). For example, in my application, the header or the login modal are components.
Then I have a test file per page or per component.
(for example the landingPage.tests.js file, which uses the model of the LandingPage class in the LandingPage.js file)
Here is a concrete example:
I have different login cases and I'd like to test them all. For example I want to test to connect with a "normal" user, for which the process is simply login then password. Then I need to test with a user who has activated 2FA, or with a user from a company that uses SSO.
I first thought about putting my different tests in authentication.tests.js, in different describe blocks, thinking it would open a new tab each time, but it doesn't... I use puppeteer in incognito mode to make sure each tab is an isolated session.
So my questions are:
Where is the best place to do these test suites?
Am I supposed to have test files that "describe" the pages ( for example, the button must be present, such text must be here etc) and also have "scenario type" test file ( a set of contextual actions to a user, like for my different login cases) ?
Here is authentication.tests.js, in which I would like to tests all my different ways of logging in :
import HeaderComponent from "../../../pages/components/HeaderComponent";
import AuthenticationComponent from "../../../pages/components/AuthenticationComponent";
import LandingPage from "../../../pages/landing/LandingPage";
import {
JEST_TIMEOUT,
CREDENTIALS
} from "../../../config";
describe('Component:Authentication', () => {
let headerComponent;
let authenticationComponent;
let landingPage;
beforeAll(async () => {
jest.setTimeout(JEST_TIMEOUT);
headerComponent = new HeaderComponent;
authenticationComponent = new AuthenticationComponent;
landingPage = new LandingPage;
});
describe('Normal login ', () => {
it('should click on login and open modal', async () => {
await landingPage.visit();
await headerComponent.isVisible();
await headerComponent.clickOnLogin();
await authenticationComponent.isVisible();
});
it('should type a normal user email adress and validate', async () => {
await authenticationComponent.typeUsername(CREDENTIALS.normal.username);
await authenticationComponent.clickNext();
});
it('should type the correct password and validate', async () => {
await authenticationComponent.typePassword(CREDENTIALS.normal.password);
await authenticationComponent.clickNext();
});
it('should be logged in', async () => {
await waitForText(page, 'body', 'Success !');
});
});
describe('SSO login ', () => {
// todo ...
});
});
Thank you and sorry if it sounds confusing, like I said I'm trying to figure out how it all fits together.
Regarding the folder structure, Jest will find any files according to the match config, basically anything called *.spec.js or *.test.js. Looks like you know that already.
What that means is the folder structure is completely up to you. Some people like to have the tests for components in the same folders as the components themselves. Personally I prefer to have all the tests in one folder as it makes the project look cleaner.
The other benefit of having all the tests in one folder is that you can then start to distinguish between the types of tests. Component tests check that pure components render and operate as expected. You don't need Puppeteer for this, use snapshots if you're in a React app. Puppeteer is good for integration tests that navigate through so-called 'happy paths', login, signup, add to cart etc., using a headless Chromium browser.
To answer the specific problem you have been having with Jest / Puppeteer on a new page for each test:
//keep a reference to the browser
let browser
//keep a reference to the page
let page
// open puppeteer before all tests
beforeAll(async () => {
browser = await puppeteer.launch()
})
// close puppeteer after all tests
afterAll(async () => {
await browser.close()
})
// Get a new page for each test so that we start fresh.
beforeEach(async () => {
page = await browser.newPage()
})
// Remember to close pages after each test.
afterEach(async () => {
await page.close()
})
describe('Counter', () => {
// "it" blocks go here.
})
Hope that helps a bit.

TestCafe: Testing a Page Opening a PDF in a New Tab

Part of the web application I am testing is to click a button, which normally opens a PDF in a new tab. This enables the user to go forward in the application. The PDF can be ignored.
Currently, my strategy is to click the button and then navigate back using ClientFunction. TestCafe successfully opens the PDF when the button is clicked, but it is opened in the same tab and the test is then stuck. Navigating back changes the URL, but the PDF is still displayed.
const goBack = ClientFunction(() => window.history.back())
await this.t.click(this.button)
await this.t.wait(10000)
await goBack()
Is TestCafe currently able to bypass this issue, as I don't need to actually do anything with the PDF?
TestCafe allows testing html pages, but not PDF files. So, you can check the generated link to the PDF file as a string without actually following this link. For example:
const overrideWindowOpen = ClientFunction(() => {
window.open = function (url) {
window.__lastWindowOpenUrl = url;
};
});
const getResultUrl = ClientFunction(() => window.__lastWindowOpenUrl);
await overrideWindowOpen();
await t
.click(this.button)
.expect(getResultUrl()).eql('http://example.com/path/to/PDF/93023813-0984-1');
See also: Multiple Browser Windows