Unable to attach new click event handlers on HTML element using Puppeteer - javascript

Why is the on click event on anchor not triggering while manually clicking in a debug session from VS Code?
Broadly here is my goal:
Go to linkedin.com using Puppeteer in headful Chrome
Login
Go to linkedin.com/jobs
Attach a click event handler for all the links on the page
Pause the node.js execution after attaching the event handlers
Click the links manually with my mouse to observe this new event handler in action
In code, this is what I got
const puppeteer = require('puppeteer')
async function main() {
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage();
await page.goto('https://www.linkedin.com/');
await login(page);
await page.goto('https://www.linkedin.com/jobs/');
await attachLinks(page);
await page.screenshot({ path: "bla" })
browser.close();
};
async function attachLinks(page) {
const bodyHandle = await page.$('body');
await page.evaluate( (body, numLinks=3) => {
let anchors = Array.from( body.querySelectorAll("a") ).
filter( (e, i) => i < numLinks );
for(let i = 0; i < anchors.length; i++) {
let a = anchors[i];
console.log(`Binding for ${a.href}`);
// This event does not trigger!!!!!
a.addEventListener("click", e => {
console.log("some one clicked the link");
e.preventDefault();
e.stopPropagation();
return false;
});
};
}, bodyHandle);
await bodyHandle.dispose();
}
main();
Then using VS Code and node.js debugging support, I put a breakpoint on line await page.screenshot({ path: "bla" }) after the onclick event for <a> tags is attached. In the browser that opens (as headless is set to false), while the code is waiting to be resumed, I clicked the <a> tags in the <body> with my mouse, expecting to see "some one clicked the link" in the headful debug Chrome browser's console. But I dont see a logs either in the browser or in VS Code's Debug console. Am I missing something here?

That's because you are not actually clicking the anchor tag. You've attached an event to it "click" and defined what will happen when we will click it, but you're not actually clicking it. Just add a.click() like
// This event will now trigger
a.addEventListener("click", e => {
console.log("some one clicked the link");
e.preventDefault();
e.stopPropagation();
return false;
});
a.click();
see the name addEventListener, you're attaching an event listener, not actually clicking it

Related

Playwright page.click scrolls too much

Given I want to record the action being done the page, when clicking on an element
Playwright scrolls the page until the element is on top of the page, even though before the action the element is visible in the viewport.
P.S. the option "scroll: false" does nothing
code snippet:
await Promise.all([
global.page.waitForNavigation(),
global.page.click(global.identifier, { scroll: false })
]);
L.E.:
I want to add some more details about my issue using an example:
So, let's assume I want to do a click action on the "Answer your question" button, and page looks like this:
after the click is performed the page automatically scrolls, putting the button on top of the page, like this:
My expectation is, if the element is in viewpoint, just to perform the action, without scrolling or moving the viewpoint.
I also faced this issue so I resolve this by doing scroll into the view function reference
await page.$eval(ele, (element) => {
element.scrollIntoView();
});
So here's the full code
const playwright = require('playwright');
const moment = require('moment');
(async () => {
const browser = await playwright.chromium.launch({
headless: false,
args: ['--disable-notifications']
});
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://stackoverflow.com/questions/68127885/playwright-page-click-scrolls-too-much');
await page.waitForSelector('.js-accept-cookies')
.then(() => page.click('.js-accept-cookies') );
let ele = '#submit-button';
await page.$eval(ele, (element) => {
element.scrollIntoView();
});
await page.click(ele);
let date = moment();
await page.screenshot({ path: `example-scroll-${date}.png` });
await browser.close();
})();
Attaching screenshot for reference:

TestCafe persist data when clicking

In TestCafe, on click of an anchor tag, I have a redirect to a new page and an event that basically logs the click event to an object.
I'm wanting to test the value of the click event in the object in TestCafe, but because the redirect happens, I lose the object.
Manually I'm able to do this, as I can hold shift while clicking the link and open a new window for the redirected page, but keep the original page in the original window and then see the object in the console.
Trying to figure out if there's a way "simulate" a click, but not do the redirect. Or alternatively, be able to assert somehow immediately after the click, but before the redirect?
mini example:
await t
.click('a') // here it already redirects so I'll lose the object
.expect(object).contains('value')
The following test shows how you can disable and enable navigation for a link:
import { Selector, ClientFunction } from 'testcafe';
fixture `Navigation`
.page `example.com`;
const enableNavigationControl = ClientFunction(selector => {
const element = selector();
element.addEventListener('click', event => window.disableNavigation && event.preventDefault());
});
const disableNavigation = ClientFunction(() => window.disableNavigation = true);
const enableNavigation = ClientFunction(() => window.disableNavigation = false);
test('navigation', async t => {
const link = Selector('a');
await enableNavigationControl(link);
await disableNavigation();
await t.click(link);
// Perform assertions...
await enableNavigation();
await t.click(link);
});

How to catch the load event for a window, after location.replace (url change)

So I opened a window with window.open('https://example.com/foo'). I am on the same domain and need to listen for the load event.
The load event triggers for the first window.open but I can't get another event when i change the url with window.replace('https://example.com/bar')
CodeSandbox
Open the browser console
Click the open button (observe the log in the console)
Click the switch button (observe that nothing gets logged)
Full example
let open = null;
document.getElementById('open-button').addEventListener('click', () => {
open = window.open('https://523k29z484.codesandbox.io/pages/foo.html');
open.addEventListener('load', () => console.log('this fires'));
});
document.getElementById('switch-button').addEventListener('click', () => {
open.addEventListener('load', () => console.log('before change does not fire'));
open.location.replace('https://523k29z484.codesandbox.io/pages/bar.html');
open.addEventListener('load', () => console.log('after change does not fire'));
});
I solved this by carefully chaining the following code.
open.location.replace('https://211vv50z30.codesandbox.io/pages/bar.html');
// old page unloaded
open.addEventListener('unload', () => {
// we can only add the load event after one tick
setTimeout(() => {
open.addEventListener('load', () => console.log('gotcha'));
}, 0);
});
location.replace -> unload -> setTimeout (one tick) -> load
CodeSandbox

How to prevent new tab creation in puppeteer's chromium instance?

I would like to prevent new tab creation. Either by clicking 'new tab button' either by window.open method.
Is there any way to listen on new tab creation. I don't mean 'targetcreated' event because then tab is already created. Maybe there is something similar to event.preventDefault?
Below is a very simple code that closes every new tab but I think it's very ugly and I want to improve it.
browser.on('targetcreated', (target) => {
function closeNewTabs(target) {
let targetBrowser = target.browser();
targetBrowser.pages().then(data => {
if (data.length>1) {
for (let i = 0; i < data.length; i++) {
if (i>1) data[i].close();
}
}
})
}
})
A possible workaround would be to listen to targetcreated event and then page.close
It would be something similar to
browser.on("targetcreated", async (target)=>{
const page=await target.page();
if(page) page.close();
});
It doesn't prevent creating new tabs, but it should close them immediately after opening

Listening for Automatically-Triggered Client Events with page.on('customEvent', fn)

This gist seems to cover what I want to do, but it appears to be a thought experiment, rather than working code. In any case, I'm having trouble getting it to work for me.
I'm opening a page in PhantomJs that loads a JavaScript library and starts a process. When the process completes, that library triggers an event within the context of the instance object. I'd like to either
(a) set up PhantomJS to listen for the right event in the instance object in the client page
OR
(b) add some code to the client page that "bubbles-up" the event to window and set up PhantomJS to listen for that.
Here's what I've tried for option B.
client.html:
<html>
<body>
<script src='https://example.com/library.js'></script>
<script>
function sendWindowEvent() {
// I've also tried document, top.parent, etc.
window.dispatchEvent( new CustomEvent('myEvent', {
detail: {
message: "Hello World!",
time: new Date(),
},
bubbles: true,
cancelable: true
}));
}
var instance = new myLibrary.constructor();
instance.addEventListener("onInitialized", sendWindowEvent);
instance.start();
</script>
</body>
</html>
In node.js app:
const headlessBrowser = await phantom.create();
const page = await headlessBrowser.createPage();
await page.on('onResourceRequested', (requestData) => {
console.info('Requesting', requestData.url); // displays http://localhost:1337/client.html & https://example.com/library.js
});
await page.on('myEvent', async (evt) => {
console.log('event detected:', evt); // never triggered
});
const openStatus = await page.open('http://localhost:1337/client.html');
console.log('phantom status:', openStatus); // displays true
Any ideas on what I'm missing? Is this not a supported feature? Thanks in advance.
page.on event listeners are responding to specific technical events generated by PhantomJS, not by its target page. To receive native page events you will have to subscribe to them in the browser context:
await page.evaluate(function(){
window.addEventListener("myEvent", function(e){ console.log(e)})
});
Be sure to subscribe to page.onConsoleMessage callback to get that message.
window.callPhantom() was what I was looking for. Documentation is here.
client.html:
<html>
<body>
<script src='https://example.com/library.js'></script>
<script>
function sendWindowEvent() {
if (typeof window.callPhantom === 'function') {
window.callPhantom({hello: 'world'});
}
}
var instance = new myLibrary.constructor();
instance.addEventListener("onInitialized", sendWindowEvent);
instance.start();
</script>
</body>
</html>
In node.js app:
const headlessBrowser = await phantom.create();
const page = await headlessBrowser.createPage();
page.on('onCallback', data => {
console.log('CALLBACK: ' + JSON.stringify(data)); // Prints 'CALLBACK: {"hello":"world"}'
});
page.open('http://localhost:1337/client.html');

Categories