So, I have a front end in React.js or Ember.js - not sure. I'm just trying to automate some testing for it. When looking at the HTML in the Chrome Dev Tools, I see
<label class="MuiFormLabel-root-16 MuiInputLabel-root-5 MuiInputLabel-formControl-10 MuiInputLabel-animated-13 MuiInputLabel-outlined-15" data-shrink="false">Username</label>
This is set in an iframe (which isn't too important for this issue). I'm trying to get the ElementHandler using the puppeteer function
frame.$(selector)
How do I get the selector given
Username
I've tried a few things, but with little success.
If I understand correctly, you need to find an element by its text content. If so, these are at least two ways:
const puppeteer = require('puppeteer');
(async function main() {
try {
const browser = await puppeteer.launch(); // { headless: false, defaultViewport: null, args: ['--lang=en'] }
const [page] = await browser.pages();
await page.goto('https://example.org/');
const element1 = await page.evaluateHandle(() => {
return [...document.querySelectorAll('h1')].find(h1 => h1.innerText === 'Example Domain');
});
console.log(await (await element1.getProperty('outerHTML')).jsonValue());
const [element2] = await page.$x('//h1[text()="Example Domain"]');
console.log(await (await element2.getProperty('outerHTML')).jsonValue());
await browser.close();
} catch (err) {
console.error(err);
}
})();
My solution was to place HTML data attributes in the front end code so that I can easily select them.
Related
I'm using puppeteer and I have a little problem that once in a while new tab with ad appears, and stops the whole script. I'm looking for a way to basically detect when 3rd tab in browser window is open and close this 3rd tab (It is always the 3rd - first is blank, second is my page that i need and third is this ad).
Try something like this:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: false });
try {
browser.addListener('targetcreated', async (target) => {
if (target.type() !== 'page') return;
const properOrigin = 'https://example.org';
const pageUrl = target.url();
if (new URL(pageUrl).origin === properOrigin) return;
console.log(`Closing page ${pageUrl}...`);
const newPage = await target.page();
await newPage.waitForTimeout(3000);
await newPage.close();
console.log(`Page ${pageUrl} closed.`);
});
const [mainPage] = await browser.pages();
await mainPage.goto('https://example.org/');
await mainPage.evaluate(() => {
window.open('https://google.com');
});
} catch (err) { console.error(err); }
Is there any way to avoid being detected by a website that I am using puppeteer? I just can't navigate around the https://www.footlocker.ca/ website using puppeteer. I have tried using stealth plugin and random user-agents to no avail.
Any advice on what else I can try?
This website use navigator.webdriver to check if you are real user or bot. so you can use the code below to delete navigator.webdriver value. docs.
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({
headless: false,
});
const page = await browser.newPage();
await page.evaluateOnNewDocument(() => {
delete navigator.__proto__.webdriver;
});
await page.goto("https://www.footlocker.ca", {
waitUntil: "domcontentloaded",
});
})();
I am using puppeteer to go to URL:https://www.booking.com/hotel/us/l-39-horizon-resort-amp-spa.en-gb.html
The following text in the hotel description sometimes shows up and sometimes not
Hotel Chain: The Leading Hotels of the World
Does anyone knows why this happens?
const puppeteer = require('puppeteer');
let bookingUrl = 'https://www.booking.com/hotel/us/l-39-horizon-resort-amp-spa.en-gb.html';
(async () => {
const browser = await puppeteer.launch({ headless: false ,slowMo: 250});
const page = await browser.newPage();
await page.goto(bookingUrl, { waitUntil : 'networkidle2' });
await delay(4000);
});
Have you tried waiting for it?
const selector = '#hotel_main_content > div.hp_hotel_description_hightlights_wrapper > div.hotel_description_wrapper_exp.hp-description > div.hp_desc_main_content > p.summary.hotel_meta_style';
await page.waitFor(selector, { visible:true });
A consideration about my code: I copied the elector with the Chrome devtools but it could change with time, make a try and if it works, refine it.
I know you can capture a single html node vial the command prompt, but is it possible to do this programmatically from the console similar to Puppeteer? I'd like to loop all elements on a page and capture them for occasional one-off projects where I don't want to set up a full auth process in puppeteer.
I'm referring to this functionality:
But executed from the console like during a foreach or something like that.
See the puppeteer reference here.
Something to the effect of this:
$x("//*[contains(#class, 'special-class-name')]").forEach((el)=> el.screenshot())
I just made a script that take a screenshot every submit button in Google main page. Just take a look and take some inspiration from it.
const puppeteer = require('puppeteer')
;(async () => {
const browser = await puppeteer.launch({
headless:false,
defaultViewport:null,
devtools: true,
args: ['--window-size=1920,1170','--window-position=0,0']
})
const page = (await browser.pages())[0]
const open = await page.goto ( 'https://www.google.com' )
const submit = await page.$$('input[type="submit"]')
const length = submit.length
let num = 0
const shot = submit.forEach( async elemHandle => {
num++
await elemHandle.screenshot({
path : `${Date.now()}_${num}.png`
})
})
})()
You can use ElementHandle.screenshot() to take a screenshot of a specific element on the page. The ElementHandle can be obtained from Page.$(selector) or Page.$$(selector) if you want to return multiple results.
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto("https://stackoverflow.com/questions/50715164");
const userInfo = await page.$(".user-info");
await userInfo.screenshot({ path: "userInfo.png" });
The output image after executing the code:
What I'm trying to do here is loop through Storybook stories so I can perform visual regression testing on them:
const puppeteer = require('puppeteer');
const { toMatchImageSnapshot } = require('jest-image-snapshot');
expect.extend({ toMatchImageSnapshot });
test('no visual regression for button', async () => {
const selector = 'a[href*="?selectedKind=Buttons&selectedStory="]';
const browser = await puppeteer.launch({headless:false, slowMo: 350});
const page = await browser.newPage();
await page.goto('http://localhost:8080');
const storyLinks = await page.evaluate(() => {
const stories = Array.from(document.querySelectorAll('a[href*="?selectedKind=Buttons&selectedStory="]'));
const links = stories.map(story => story.href);
return links;
});
await storyLinks.forEach( (storyLink) => {
page.goto(storyLink).then(async (res,rej) => {
const screen = await page.screenshot();
return await expect(screen).toMatchImageSnapshot();
});
});
await browser.close();
});
One problem is that I get this because of the await broswer.close() that isn't waiting for everything to finish:
Protocol error (Page.navigate): Target closed.
at Session._onClosed (../../node_modules/puppeteer/lib/Connection.js:209:23)
at Connection._onClose (../../node_modules/puppeteer/lib/Connection.js:116:15)
at Connection.dispose (../../node_modules/puppeteer/lib/Connection.js:121:10)
at Browser.close (../../node_modules/puppeteer/lib/Browser.js:60:22)
at Object.<anonymous>.test (__tests__/visual.spec.js:24:16)
at <anonymous>
This happens for each storyLink except the first.
If I comment out the await browser.close() line, the screenshots are being taken, but not in the expected wait. Instead of each story having one screenshot, the last story is being screenshotted for the amount of stories. For example, I've got 4 stories in total, but I will have 4 screenshots of the last story instead of one for each story.
I don't understand why this behaviour is happening. The storyLinks returned from the page.evaluate funtions are correct, but then everything breaks and I've got no idea why.
Any ideas?
forEach is not good for async-await. Use for..of instead,
for (let storyLink of storyLinks) {
await page.goto(storyLink)
const screen = await page.screenshot();
await expect(screen).toMatchImageSnapshot();
};