How to go from ElementHandle to DOM element - puppeteer

I'm using node --inspect-brk to debug my puppeteer test. I'm trying to figure out why the following isn't working:
let relationshipHeader = await page.waitForXPath(xpath);
await relationshipHeader.hover();
In the test Chrome window console, $x(xpath) returns the correct DOM element. But the hover() isn't working, so I wonder which DOM element relationshipHeader is for.
Can one of the ElementHandle fields tell me which DOM element relationshipHeader is for?

_remoteObject.description appears to be the answer. For example, with this code:
await page.goto("https://google.com");
let input = await page.waitForSelector("input.gLFyf");
input._remoteObject.description is input.gLFyf.gsfi. In Chrome developer tools console of test page, $("input.gLFyf.gsfi") yields the DOM element.

Related

Puppeteer: How to capture a screenshot with device frame?

Note: I am asking here a adapted version of this this closed question.
When using Puppeteer, it's easy to produce screenshots. It's even their first example, and it works fine on my web app:
const puppeteer = require('puppeteer');
const iPhone = puppeteer.devices['iPhone 6']; //iPhone 5/SE does not work
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(iPhone);
await page.goto('https://web.replayer.app', {
waitUntil: 'networkidle2',
});
await page.screenshot({ path: 'replayer-app.png' });
await browser.close();
})();
This produces a nice screenshot, but without device frame.
If I try to use one of the device names, that do produce a visual device frame in Google Chrome, like iPhone5/SE, I get
C:\Users\masu\Documents\node_modules\puppeteer\lib\cjs\puppeteer\common\Page.js:1500
this.setViewport(options.viewport),
^
TypeError: Cannot read properties of undefined (reading 'viewport')
at Page.emulate (C:\Users\masu\Documents\node_modules\puppeteer\lib\cjs\puppeteer\common\Page.js:1500:38)
at C:\Users\masu\Documents\puppeteer-question.js:7:14
at processTicksAndRejections (node:internal/process/task_queues:96:5)
However, since it's a web app, I would like to have the screen shot a device frame around it.
How to use and capture a screenshot from an emulated device with a viewport?
What is the cause of the issue?
I. You get this error because you are emulating the desired device with the wrong name. Even if DevTools UI names it "iPhone 5/SE" in reality DevTools Protocol only knows either "iPhone 5" or "iPhone SE" (you can give a try to give any false strings here, like "iPhone 2000", you will end up with the very same error msg). The available and valid device names are listed in DeviceDescriptors.ts on Github.
II. Since the referenced question was asked the situation is still the same: you cannot do a screenshot of the page with the device frames in puppeteer. Simply because those frames are part of the DevTools UI while Puppeteer is a high-level API to control Chrome over the DevTools Protocol: one is related to its frontend the other to its backend functionalities.
How you can proceed?
Implement the functionality in Node yourself. As I mentioned in the other post: it is easy to achieve it with node-images npm package if you have the device frames as transparent png file. E.g.: images('iPhone_5_portrait.png').draw(images('wikipedia.png').size(320), 130, 220).save('output.jpg').
Raise a feature request on Puppeteer's GitHub however it is not likely they plan to go this way (see point II. above).

Stenciljs e2e test doesn't work for no apparent reason

Line within render() :
<div class={{ 'modal-backdrop show': this.show, 'modal-backdrop hide': !this.show }}>
Test :
it('should display correctly', async () => {
const page = await newE2EPage();
await page.setContent('<my-component></my-component>');
let element = await page.find('my-component');
expect(element).not.toBeNull();
element = await page.find('div.modal-backdrop.hide');
expect(element).not.toBeNull();
});
Description of the issue:
I have provided only a part of the code and unfortunately I cannot provide much more due to confidentiality. However I will do my best to describe the issue. There are two components in the project, tests for component A work as they should. Tests for component B (provided above) do not. While the first expect passes, the second one fails due to it being null but it shouldn't.
A few facts:
The project can be built, run and used without a problem.
Unit tests work as intended, including tests for the render() method.
The code in it-self is not wrong, I have tested, retested and tested again and it works for other components but not for this one.
Although the default is .hide, I have tried with both .hide and .show, neither work.
Best guess so far:
I have had many issues getting the tests to work due to how the code is written. While running tests many objects where undefined and that was causing the tests to fail. From everything that I tried and tested my best theory is that for some reason this component half fails in the context of the puppeteer browser, making the core object but nothing else. I don't know if that is possible but it looks like that.
Web components use their own document tree called shadowDOM, which isn't visible from the main DOM (page); thus your page.find fails. This concept is called encapsulation. Btw, I wasn't able to find a method called find on the page object in Puppeteer's documentation; can you explain where it comes from?
To access the shadow tree inside a web component, you'll have to access it using element.shadowRoot:
element = await page.find('my-component');
expect(element.shadowRoot.querySelector('div.modal-backdrop.hide')).not.toBeNull();
There's puppeteer addons and applications which can help with that:
https://github.com/PavelDymkov/puppeteer-shadow-selector
https://docs.puppetry.app/testing-techniques/testing-shadow-dom
To find more, check https://www.google.com/search?q=puppeteer+shadow+DOM.

element click intercepted: element is not clickable at point.. other element would receive click

Error: Element click intercepted only comes in headless chrome, it works fine if I run it on chrome browser without headless.
I have already tried below solutions:
Increasing window size in config chrome options to 'chromeOptions': { args: [ "headless","--disable-gpu", "--window-size=1920,1080" ] }
browser.actions().mouseMove(element).click(); and
browser.actions().mouseMove(element).click().perform();
Set window size in onprepare() method:
// set screen size setTimeout(function() { browser.driver.executeScript(function() { return { width: window.screen.availWidth, height: window.screen.availHeight }; }).then(function(result) { browser.driver.manage().window().setPosition(0,0); browser.driver.manage().window().setSize(result.width, result.height); }); });
Applied all types of waits: Sleep(), Implicit wait, Explicit wait, async wait: await.
Also used browser.driver.manage().window().maximize(); in before each instead of before all. But nothing of above worked. It worked for few tests but not for other.
I am running tests to check add user scenarios. Few of them are passed but not all. All tests are using same code written below only few things are changes as per test case like if it's adding a valid user, invalid user...
Basic common things in all add user script is below
Email:
Search a level on which I want to add a user:
Select Role:
click Add user button.
Problem I have observed is with search box and role dropdown. I think elements in headless chrome are not easily accessible or some other element is overlapping this elements.
Please help to solve this problem. Any help is appraised.
My tests -
it('1. should not add user with invalid email',async function(){
browser.waitForAngularEnabled(true)
browser.wait(EC.presenceOf(basepage.Loc_user_management),5000)
await basepage.Loc_user_management.click(); // click user management menu
browser.waitForAngularEnabled(true);
browser.wait(EC.presenceOf(userpage.Loc_add_user_btn),5000)
await userpage.Loc_add_user_btn.click();
browser.waitForAngularEnabled(true)
await userpage.Loc_email_addr_txtbox.click();
await userpage.Loc_email_addr_txtbox.sendKeys(loginData.users.invalid_username);
browser.manage().timeouts().implicitlyWait(1000);
browser.wait(EC.presenceOf(userpage.Loc_primarylevel_searchbox),5000);
userpage.Loc_primarylevel_searchbox.isDisplayed().then(async function(){
await userpage.Loc_primarylevel_searchbox.click();
await userpage.Loc_primarylevel_searchbox.sendKeys('Clients');
browser.actions().sendKeys(protractor.Key.ARROW_DOWN);
browser.actions().sendKeys(protractor.Key.ENTER);
//userpage.Loc_primarylevel_searchbox.click();
})
browser.sleep(500)
browser.waitForAngularEnabled(true);
browser.wait(EC.presenceOf(userpage.Loc_role_dropdown),5000)
await userpage.Loc_role_dropdown.click();
browser.waitForAngularEnabled(true)
await element(by.cssContainingText('option', 'Role1')).click();
browser.wait(EC.presenceOf(userpage.Loc_add_btn),5000)
userpage.Loc_add_btn.click().then(async function(){
expect(userpage.Loc_error_message_label.getText()).toEqual(errormessage.AddUserMessages.invalid_email_error_message);
})
});
that's LIKELY because you don't handle promises
browser.wait needs await
browser.waitForAngularEnabled also returns a promise and needs await
userpage.Loc_primarylevel_searchbox.isDisplayed().then doesn't wait until the element is displayed, it just gets a boolean if it's displayed or not and proceeds immediately. So in your case it's an extra line and complexity that doesn't do anything
sendKeys needs await
browser.sleep needs await
And don't mix .then and await. Use either one
What happens when you don't handle promises, is protractor just schedules the command, but doesn't wait until it's completed
Coincidentally, your script works with graphic interface, because chrome takes some time to load up. But when you run the same script in headless, it becomes faster (there is no UI to load) and executes one command before a previous is finished

PhantomJS / CasperJS Canvas selector

Using PhantomJS V 1.8.1
Thanks in advance.
I am trying to run some tests on a website that I am developing which is using backbone.js.
One of my tests involve checking to see if a Canvas is present and clicking on it. My problem is that whatever selector I use to get the Canvas Element I cannot get the selector to find it. I use the same CSS selector in Google Chrome when viewing the page and all is OK. At first I thought that the issue may have been due to the element not being present on the page but other elements which are inserted with the canvas are present so I am 99% sure that this is not the problem.
The selectors I have tried to use are:
document.querySelectorAll('#idOfCanvas');
document.querySelectorAll('canvas#idOfCanvas');
Also if I use .classClassName:nth(1) to select the tyre selector, it still fails to work (works in Google Chrome though as does the other examples provided)
The canvas has a class name which is picked up by the selector by I would rather not use a class selector.
Any help would be much appreciated.
Cheers :)
Also
Like I mentioned I am almost certain that the Canvas exists as the container div for it exists. Also I have four elements on the page with the same className (two of which are canvases) and four elements are being returned when I run
return document.querySelectorAll('.className').length = 4;
Assuming you have something like this:
<canvas id="idOfCanvas"></canvas>
This should work:
canvas = document.getElementById("idOfCanvas");
// or
canvas = document.querySelector("#idOfCanvas"); // Only get the first match, ID's should be unique, any way.;
// or
canvas = document.querySelectorAll("#idOfCanvas")[0];
// or
canvas = document.getElementsByTagName("canvas")[0]; // Get the first <canvas> element.
However, you'll have to make sure your canvas element is actually loaded when the script is executed. Have a look at this onload tutorial, for example.
Try this :
canvas = document.getElementById(#IdOfCanvas:nth-child(1));

Console log for Chrome DevTools API

I am extending Google Chrome with the "chrome.devtools.panels.create" API, it means that I have now some logic in my browser by which I need to debug.
Is there anyway to see the Console log / Debug my DevTools additions?
If all you need is console.log you can wrap it up. Actually it works for any other function, not just console.log but here's example for wrapping console.log:
console._log = console.log;
console.log = function(){
// you can do whatever you want with arguments, output to div, alert / etc.
return console._log.apply(console,arguments);
};
You need to eval a script in the inspectedWindow:
chrome.devtools.inspectedWindow.eval('console.log("test")')