I m trying to test web page of my project with JEST & Puppeteer testing tool. In web page when i right click on element one menu pops up in page with setting some style attributes on element. So with this flow i m trying to test the same with JEST, I have written following code for the same.
describe('Test for Rest Data', () => {
jest.setTimeout(100000);
beforeEach(async () => {
await page.goto("url", { waitUntil: 'networkidle2' })
await page.waitForSelector('table');
});
});
test("Assert for delete row !",async () => {
await page.click('tr','right');
const tbl = await page.evaluate(()=>{
return document.querySelector('tr').getAttribute('style');
});
expect(tbl).not.toBeNull();
});
here when i click on of table style attribute gets added but with above code tbl is not getting any value.
Am I doing something wrong ? How should I do this right ?
You should also probably wait for some time after the click, maybe the style changes but not instantly, maybe the element is not there yet.
Try,
await page.waitFor(1000); // wait for some time
// or this below
await page.waitFor('tr'); // wait for the element
Which will wait for some time or the element. Check if that is the case.
Related
At the following site, after entering a search phrase such as "baby" (try it!), the Puppeteer call page.mouse.down() doesn't have the same effect as clicking and holding the physical mouse: https://www.dextools.io/app/bsc
After entering a search phrase, a fake dropdown select menu appears, which is really an UL, and I am trying to click the first search result. So I use code like this
await page.mouse.move(200, 350); // let's assume this is inside the element I want
await page.mouse.down();
await new Promise((resolve) => setTimeout(resolve, 2000)); // wait 2 secs
await page.mouse.up();
The expected effect of this code is that, for the 2 seconds that Puppeteer is "holding" the mouse button down, the fake dropdown stays visible, and when Puppeteer "releases" the mouse button, the site redirects to the search result for the item selected.
This is exactly what happens when I use the physical mouse.
However, what happens with Puppeteer is, the dropdown just disappears, as if I had hit the Escape key, and the page.mouse.up() command later has no effect any more.
I am aware that PPT has some quirks in respect to mouse, keyboard, holding and releasing buttons and modifier keys, especially when doing all of the above at once. For example, Drag & Drop doesn't work as expected, but none of the workarounds proposed here work for me: https://github.com/puppeteer/puppeteer/issues/1265
I cannot reproduce the issue with this test script. The link is clicked with following navigation:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: false, defaultViewport: null });
try {
const [page] = await browser.pages();
await page.goto('https://www.dextools.io/app/bsc', { timeout: 0 });
const input = await page.waitForSelector('.input-container input');
await input.type('baby');
const link = await page.waitForSelector('.suggestions-container.is-visible a:not(.text-sponsor)');
await link.click();
} catch (err) { console.error(err); }
Instead of two separate mouse-down and up operations, you could try this according to puppeteer docs:
// selector would uniquely identify the button on your page that you would like to click
selector = '#dropdown-btn'
await page.click(selector, {delay: 2000})
Once you have the element of the list that you wanna click, you should look for the first <a> tag inside this element and use the reference you make on this <a> to perform a click.
From puppeteer's documentation it's saying if there is a navigation you should use:
const [response] = await Promise.all([
page.waitForNavigation(waitOptions),
page.click(selector, clickOptions),
]);
where selector will be a reference to the mentioned <a> tag.
I'm trying to use puppeteer to load a page, submit a form (which takes me to a different URL) and then ideally run something once this new page had loaded. I'm using Node JS, and am generalising my logic into separate files, one of which is search.js as per the below:
const puppeteer = require('puppeteer')
const createSearch = async (param1) => {
puppeteer.launch({
headless: false,
}).then(async browser => {
const page = await browser.newPage(term, location)
await page.goto('https://example.com/')
await page.waitForSelector('body')
await page.evaluate(() => {
const searchForm = document.querySelector('form.searchBar--form')
searchForm.submit() // this takes me to a new page which I need to wait for and then ideally return something.
// I've tried adding code here, but it doesn't run...
}, term, location)
})
}
exports.createSearch = createSearch
I'm then calling my function from my app's entry point...
(async () => {
// current
search.createSearch('test')
// proposed
search.createSearch('test').then(() => {
// trigger puppeteer to look at the new page and start running asserts.
})
})()
Unfortunately, due to the form submitting, I'm unsure how I can wait for the new page to load and run a new function? The new URL will be unknown, and different each time, e.g: https://example.com/page20
After form submit, you need to wait until the page reloads. Please add this following the await page.evaluate() function call.
await page.waitForNavigation();
And then you can perform action you want.
When I click the Next button to continue my test the page has a transition, so the password may be inputted, this transition is not allowing me to click on the password input section, so to combat the problem I used the wait method to wait for 1s until the element is located. The error is described after code
const {
Builder,
By,
until,
Capabilities
} = require('selenium-webdriver');
// requiring needed modules
(async function login() {
const pageLoad = new Capabilities().setPageLoadStrategy('normal')
//configuring the way the page loads
let driver = await new Builder().withCapabilities(pageLoad).forBrowser('firefox').build();
try {
await driver.get('https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=13&rver=7.3.6963.0&wp=MBI_SSL&wreply=https://www.microsoft.com/en-us/&lc=1033&id=74335&aadredir=1');
//going to link
var userName = (await driver.findElement(By.css('#i0116'))).sendKeys((USERNAME));
//finding element and typing
(await driver.findElement(By.css('#idSIButton9'))).click();
// clicking element
await driver.wait(until.elementLocated(By.css('#i0116')),1000);
//This is where I think the error is happening
var passWd = (await driver.findElement(By.css('#i0116'))).click().sendKeys(PASSWORD);
(await driver.findElement(By.css('#idSIButton9'))).click();
} catch (error) {
console.log(error)
} finally {
console.log('finished')
}
}())
{ NoSuchElementError: Unable to locate element: #i0116
at Object.throwDecodedError (/home/name/Desktop/projects/Test/Chrome/pecPrea/node_modules/selenium-webdriver/lib/error.js:550:15)
at parseHttpResponse (/home/name/Desktop/projects/Test/Chrome/pecPrea/node_modules/selenium-webdriver/lib/http.js:565:13)
at Executor.execute (/home/name/Desktop/projects/Test/Chrome/pecPrea/node_modules/selenium-webdriver/lib/http.js:491:26)
at process._tickCallback (internal/process/next_tick.js:68:7)
name: 'NoSuchElementError',
remoteStacktrace:
'WebDriverError#chrome://marionette/content/error.js:175:5\nNoSuchElementError#chrome://marionette/content/error.js:387:5\nelement.find/</<#chrome://marionette/content/element.js:330:16\n' }
finished
If you just want to wait for a action to be completed you can try sleep from
java.util.concurrent.TimeUnit:
if u want it in seconds u can use
TimeUnit.SECONDS.sleep(int);
and for minutes you can use
TimeUnit.MINUTES.sleep(int);
The raw way to use sleep would be to use Thread.sleep() but the input here should be in milliseconds of if your program contains multiple sleep statements i would prefer TimeUnit
I chose the incorrect element. Instead of selecting #i0118, the correct element. I STUPIDLY selected #i0116
I am using this code in Try Puppeteer:
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.barchart.com/futures/quotes/ESM19/interactive-chart/fullscreen');
const linkHandlers = await page.$x("//li[contains(text(), '1D')]");
if (linkHandlers.length > 0) {
await linkHandlers[0].click();
} else {
throw new Error("Link not found");
}
await page.$eval('input[name="fieldInput"]', el => el.value = '1');
console.log(await page.content())
// const text = page.evaluate(() => document.querySelector('rect'))
// text.then((r) => {console.log(r[0])})
await page.screenshot({path: 'screenshot.png'});
await browser.close();
The same page loaded in the Chrome browser shows the bars indicating price movements, but in the screenshot obtained in Puppeteer the chart is empty.
Also page.content() gives an html that is completely different from the one I see when inspecting the element in Chrome.
Problem
You are not waiting for the request to resolve when the input is changed. As a change will trigger a request, you should use page.waitForResponse to wait until the data is loaded.
In addition, this is an Angular application, which does not seem to like it if you simply change the value of the field via el.value = '1'. Instead you need to try to behave more like a human (and hit backspace and type the input value).
Solution
First, you get the element handle (input[name="fieldInput") from the document. Then, you focus the element, remove the value inside by pressing backspace. After that you type the desired input value.
The input field now has the correct value, now we need to trigger the blur event by calling blur() on the element. In parallel, we wait for the request to the server to finish. After the request finishes, we should give the page a few milliseconds to render the data.
All together, the resulting code looks like this:
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.barchart.com/futures/quotes/ESM19/interactive-chart/fullscreen');
// wait until the element appears
const linkHandler = await page.waitForXPath("//li[contains(text(), '1D')]");
await linkHandler.click();
// get the input field, focus it, remove what's inside, then type the value
const elementHandle = await page.$('input[name="fieldInput"]');
await elementHandle.focus();
await elementHandle.press('Backspace');
await elementHandle.type('1');
// trigger the blur event and wait for the response from the server
await Promise.all([
page.waitForResponse(response => response.url().includes('https://www.barchart.com/proxies/timeseries/queryminutes.ashx')),
page.evaluate(el => el.blur(), elementHandle)
]);
// give the page a few milliseconds to render the diagram
await page.waitFor(100);
await page.screenshot({path: 'screenshot.png'});
await browser.close();
Code improvement
I also removed the page.$x function and replaced it with the page.waitForXPath function. This makes sure that your scripts waits until the page is loaded and the element you want to click is available before the script continues.
I am new to JS. I need to parse comments from Instagram, but first I need to load them all. I am using Puppeteer in Node.js, so I wrote this code:
await page.evaluate(() => {
while(document.querySelector('main').querySelector('ul').querySelector('button'))
document.querySelector('main').querySelector('ul').querySelector('button').click()
})
It does nothing and starts an endless loop. I tried to make a timeout inside the loop and so on...
I expect that code will check if this button exists and if true - click() on it while it exists loading more and more comments.
I can't catch what am I doing wrong.
Have a look at my answer to a question very similar to this one here:
Puppeteer / Node.js to click a button as long as it exists -- and when it no longer exists, commence action
You should be able to apply it to finding and continually clicking on your "load more" button.
Instead of a using a while() loop, you can use setInterval() to slow down each iteration to a more manageable pace while you load the comments:
await page.evaluate(async () => {
await new Promise((resolve, reject) => {
const interval = setInterval(() => {
const button = document.querySelector('main ul button');
if (button !== null) {
button.click();
} else {
clearInterval(interval);
resolve();
}
}, 100);
});
});