Clicking a button within an iframe with puppeteer - iframe

Trying to click on the "I accept all cookies" button which is inside iFrame (The popup only show for EU country ip).
You can check here also jsfiddle.net/#&togetherjs=VgKpE0jfJF.
//index.js
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless:false,
ignoreHTTPSErrors: true,
slowMo: 50,
args: ['--window-size=1440,900', '--disable-gpu', "--disable-features=IsolateOrigins,site-per-process", '--blink-settings=imagesEnabled=true']
});
const page = await browser.newPage();
await page.goto('https://www.oracle.com/cloud/cost-estimator.html');
await page.waitFor(3000)
const frame = page.frames().find(f => f.name() === 'iframe');
const acceptBtn = await frame.$(`a[class="call"]`);
await acceptBtn.click();
await page.screenshot({path: 'example.png'});
//await browser.close();
})();
The error i get
UnhandledPromiseRejectionWarning: TypeError: Cannot read property '$' of undefined
at
Please help. Thanks

As far as I can tell, this iframe has no name in the HTML code, so you can try its src (URL):
const frame = page.frames().find(f => f.url().startsWith('https://consent-pref.trustarc.com/'));

Related

Unable to fetch rendered dom using puppeteer

I have recently been experimenting with using Puppeteer for fetching content from webpage and I have noticed that for certain webpages like https://www.scmp.com/news/china/diplomacy/article/3174562/china-needs-new-playbook-counter-eus-tougher-trade-and?module=lead_hero_story&pgtype=homepage, I am unable to fetch the actual dom that is rendered when the link is opened using a browser. The snippet I use to fetch the content is below:
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://www.scmp.com/news/china/diplomacy/article/3174562/china-needs-new-playbook-counter-eus-tougher-trade-and?module=lead_hero_story&pgtype=homepage', { waitUntil: 'domcontentloaded', timeout: 60000 });
const data = await page.evaluate(() => document.querySelector('*').outerHTML);
await fs.writeFile("./test.html", data, err => {
if (err) {
console.error(err)
return
}
//file written successfully
});
console.log(data);
await browser.close();
})();
I already tried with a waitUntil value of networkidl0 but could not extract the expected dom. What am I doing wrong here?

Playwright waitForFunction for mouse?

I would like to make a screenshot of a hovered button in storybook. My code not working with headless browsers and I probably need to wait some more but can't seem to figure it out. I'm very grateful for any tips.
test('example test', async ({ url }) => {
const browser = firefox.launch({ headless: false, slowMo: 300 });
const page = await (await browser).newPage();
await page.goto(
'url'
);
await page.waitForNavigation({ waitUntil: 'load' });
await page.waitForSelector('#storybook-preview-iframe');
const elementHandle = await page.$('#storybook-preview-iframe');
const frame = await elementHandle.contentFrame();
await frame.waitForSelector('button[id=btn]');
const el = await frame.$('button[id=btn]');
const box = await el.boundingBox();
// const watchDog = page.waitForFunction(
// page => {
// page.mouse; ??????????????? I know that there is no mouse.getCurrentPosition method
// },
// {},
// page
// );
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
//await watchDog;
expect(await page.screenshot()).toMatchSnapshot('screenshot.png');
});
You can use page.hover() as this method will scroll view into the selected element then move the cursor over the element.
https://pptr.dev/#?product=Puppeteer&version=v13.5.1&show=api-pagehoverselector
test('example test', async ({ url }) => {
const browser = firefox.launch({ headless: false, slowMo: 300 });
const page = await (await browser).newPage();
await page.goto(
'url'
);
await page.waitForNavigation({ waitUntil: 'load' });
await page.waitForSelector('#storybook-preview-iframe');
const elementHandle = await page.$('#storybook-preview-iframe');
const frame = await elementHandle.contentFrame();
await frame.waitForSelector('button[id=btn]');
await frame.hover('button[id=btn]')
expect(await page.screenshot()).toMatchSnapshot('screenshot.png');
});
That's it. Hope this will work.

Setting state parameter to a Stripe query to pass a FireStore uuid

I am trying to pass a FirebaseFirestore User Uid to a Stripe / firestore cloud function.
So I would have an https query like following :
https://connect.stripe.com/express/oauth/authorize?response_type=code&client_id={accountid}&scope=read_write to open in a Webview
Here is my function
exports.connectStripeExpressAccount = functions.https.onRequest((req, res) =>{
console.log('query state is ----> ' + req.query.state);
const authCode = req.query.code;
return stripe.oauth.token({
grant_type: 'authorization_code',
code: authCode,
}).then(async response => {
var connected_account_id = response.stripe_user_id;
const uid = req.query.state
const writeResult = await admin.firestore().collection('Registration').doc(uid)
.set({'customer_id': connected_account_id});
return res.send("Well done, account integration is completed. You can now close the window and go back to the app");
});
});
For new integrations with Express Accounts you should ideally be using the Account Links functionality instead of OAuth. That said, if you provide the state value, it should carry through, so I'd make sure you're actually providing it when opening the WebView.
If the User uid is stored in the query parameter state and the URL looks like this:
https://connect.stripe.com/express/oauth/authorize?response_type=code&client_id=ca_JCV8JW9ZIjBaGkwkhbDDDQegceWGidqh&scope=read_write&state=useruidxxx
Your code would look like this:
exports.connectStripeExpressAccount = functions.https.onRequest((req, res) =>{
console.log('query state is ----> ' + req.query.state);
const authCode = req.query.code;
return stripe.oauth.token({
grant_type: 'authorization_code',
code: authCode,
}).then(async response => {
var connected_account_id = response.stripe_user_id;
const uid = req.query.state
const writeResult = await admin.firestore().collection('Registration').doc(uid)
.set({'customer_id': connected_account_id});
return res.send("Well done, account integration is completed. You can now close the window and go back to the app");
});
});

How to capture dynamic content from element inside an iFrame?

When I try to capture text from an element inside an iFrame where the element content changes every second, I get "undefined". What might I be doing wrong?
Code:
const { firefox } = require('playwright');
const fs = require('fs');
var url = 'http://jsfiddle.net/6vnam1jr/1/show';
var section_path = 'xpath=/html/body/div/div/div/section';
var iframe_path = 'xpath=/html/body/div/iframe';
var text_path = 'xpath=//html/body/div[2]/div[2]';
async function getElementText(browser,page){
await page.goto(url);
await page.click(section_path);
const frame = await page.$(iframe_path);
const contentFrame = await frame.contentFrame();
await sleep(1000);
let handle = await contentFrame.$eval(text_path);
console.log(handle)
// try again
await sleep(1000);
handle = await contentFrame.$eval(text_path);
console.log(handle)
closeBrowser(browser);
}
async function closeBrowser(browser){
await browser.close();
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
(async () => {
const browser = await firefox.launch({ headless: false });
const page = await browser.newPage();
getElementText(browser,page);
})();```
Thanks for the repro. frame.$eval is an API to run a JS function in the browser which takes the element as an argument.
I believe what you are looking for is an ElementHandle to this element. You can use frame.waitForSelector or frame.$ for this purpose. I have verified that they are not undefined.
// ...
let handle = await contentFrame.waitForSelector(text_path);

How do I click a button that has no ID using (Apify's) Puppeteer?

I am using Apify's puppeteer to login to this website. I did research similar questions but to no avail.
I am having trouble finding the clickable id/element for the main Login button seen on the linked login page. Currently, my code reads like this:
const Apify = require('apify');
Apify.main(async () => {
const input = await Apify.getValue('INPUT');
const browser = await Apify.launchPuppeteer();
const page = await browser.newPage();
await page.goto('https://www.sunpass.com/vector/account/home/accountLogin.do');
// Login
await page.type('#tt_username1', input.username);
await page.type('#tt_loginPassword1', input.password);
await page.waitFor(2000);
await page.click('#entryform input');
await page.waitForNavigation();
// Get cookies
const cookies = await page.cookies();
// Use cookies in other tab or browser
const page2 = await browser.newPage();
await page2.setCookie(...cookies);
await page2.goto('https://www.sunpass.com/vector/account/transactions/webtransactionSearch.do'); // Opens page as logged user
await browser.close();
console.log('Done.');
With the id entryform I receive the following error: Node is either not visible or not an HTMLElement
With the id loginP I receive the following error: No node found for selector
I used XPath to locate these, it offered no other ids of use. Any help would be greatly appreciated on how to find a clickable element for this login button, or any other method.
You have to try another selector.
I tried button[name="btnLogin"] and it worked.
tested code:
const Apify = require('apify');
Apify.main(async () => {
const input = await Apify.getValue('INPUT');
const browser = await Apify.launchPuppeteer();
const page = await browser.newPage();
await page.goto('https://www.sunpass.com/vector/account/home/accountLogin.do');
// Login
await page.type('#tt_username1', input.username);
await page.type('#tt_loginPassword1', input.password);
await page.waitFor(2000);
await page.click('button[name="btnLogin"]');
await page.waitForNavigation();
// Get cookies
const cookies = await page.cookies();
// Use cookies in other tab or browser
const page2 = await browser.newPage();
await page2.setCookie(...cookies);
await page2.goto('https://www.sunpass.com/vector/account/transactions/webtransactionSearch.do'); // Opens page as logged user
await browser.close();
console.log('Done.');
});
During my test of the login form on a desktop the "LOGIN" button could be found with this selector:
button[name=btnLogin].btn-large

Resources