Read a page stylesheet and check a specific property with puppeteer - javascript

What I am trying to do is:
Load the page
Gain access to the contents of an external css named "mystyle.css"
Check if ".some_class" border has the value "2px"
I have tried
describe('CSS tests', () => {
it('.some_class border is 2px', async function () {
await page.goto(<homepageurl>);
const stylesheet = await page.evaluate(() => {
return document.querySelector("link[href*='mystyle.css']");
});
console.log(current_styles);
// rest of the code
});
});
I am getting an empty object {} as a result so I am lost and don't know how to carry on.

Related

Chrome Extension: How can I access the new document on page changes?

I'm trying to amend the DOM using a chrome extension. Unfortunately I fail with accessing the new page after some of the actions cause a page change. My code looks like this:
content_script.js
(async () => {
try {
execute_edit = async () => {
console.log(document.title)
const el = document.getElementsByClassName("icon")
const first_el = el[0]
first_el.click()
}
change_element = async () => {
console.log(document.title)
const el = document.querySelector("element")
console.log(el.innerHTML)
}
await execute_edit()
await change_element()
} catch (e) {
console.log(e)
}
})();
I'm getting the error that the "el" in "change_element" does not exist. This is obviously caused by the fact that both "document.title" are identical i.e. the second function still tries to access the original DOM and not the new page.
Any suggestions on how I can have the second function "change_element" access the new page's DOM?

node.js puppeteer "document is not defined"

I am attempting to try click a button using code without an id or class, but my terminal always responds with:
document.getElementsByTagName("Accept Cookies");
^
ReferenceError: document is not defined
This is my code:
const puppeteer = require('puppeteer');
const product_url = "https://www.nike.com/launch"
async function givePage() {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
return page;
}
async function acceptCookies(page) {
await page.goto(product_url);
const btn = await page.waitForSelector('#cookie-settings-layout > div > div > div >
div:nth-child(3) > div.ncss-col-md-6.ncss-col-sm-12.mb5-sm > button')
await btn.click()
}
async function notifyMe(page) {
await page.goto(product_url);
document.querySelector("button[type=\"submit\"]").click("Notify Me");
}
async function checkout() {
var page = await givePage();
await acceptCookies(page);
await notifyMe(page);
}
checkout();
What did I do wrong and how can I fix this?
There's no built-in variable in NodeJS named document, since it doesn't run in the browser.
If you want to access document, in Puppeteer there's a page.evaluate() function where you can access the document variable (as well as everything else inside client-side JS):
// ...
await page.evaluate(() => {
document.querySelector("button[type=\"submit\"]").click();
});
Please note though, that all the JavaScript you run will be run on the browser, not in NodeJS, so if you want to get the value back you can return:
const result = await page.evaluate(() => {
var something = document.getElementById("something");
return something.innerText;
});
console.log(result); // will print in the console "blah blah blah"
Likewise if you want to pass variables to the callback you have to give them to the evaluate function:
await page.evaluate((name, age) => {
// do something with 'name' and 'age'
}, "John", 34);
You already have an example on your code on how to access elements. Instead of document.querySelector, use page.waitForSelector like what you did on line 12.
document.querySelector('button[type="submit"]').click()
should be
(await page.waitForSelector('button[type="submit"]')).click()
In Nodejs, you don't have access to web APIs like a window, document, etc. so you can't use document.querySelector to select elements here.
Instead of handling clicks on DOM elements on server side, you should handle those clicks on the client-side only and then fetch the data from the server accordingly.

How to populate multiple prompts in a row in Cypress before initial page load

Cypress newbie here.
I'm trying to populate data into 3 different prompts that appear before the page loads completely. These values are then added into session storage. It is my understanding that since the site is not fully loaded I can't chain off cy.visit() so I've been using the onBeforeLoad so I can populate the data for these prompts:
before(function() {
cy.visit(base_url, {
onBeforeLoad(win) {
cy.stub(win, 'prompt').returns('someString').as('stub1')
cy.stub(win, 'prompt').returns('someOtherString').as('stub2')
cy.stub(win, 'prompt').returns('anotherString').as('stub3')
}
})
})
The issue is that when I look under "Spies/Stubs" I only see the stub1 being used 3 times as opposed to 3 different stubs being used once.
I also get the error
TypeError: Attempted to wrap prompt which is already wrapped
Any help will be highly appreciated.
Thank you all in advance.
EDIT:
Doing something like
before(function() {
cy.visit(base_url, {
onBeforeLoad(win) {
demo_site_info.forEach(element => {
cy.stub(win, 'prompt').callsFake(() => {
return element
})
});
}
})
})
yields a TypeError:
Attempted to wrap prompt which is already wrapped
Using callsFake(fn) allows multiple fake values.
it('fakes return values multiple times', () => {
const mod = {
doit: () => 'done'
}
let call = 0
const fakes = ['done1', 'done2', 'done3']
cy.stub(mod, 'doit').callsFake(() => {
return fakes[call++]
})
console.log(mod.doit()) // done1
console.log(mod.doit()) // done2
console.log(mod.doit()) // done3
console.log(mod.doit()) // undefiend
})

Office.js API for PowerPoint (Preview)

I am trying to use the new Office.js API for Powerpoint which is currently in preview. I am unable to get anything working with the PowerPoint.run() method call because it seems to behave differently that the ones in Excel and Word. Could anyone help me find a working way to call the SlideCollection.getCount() method referenced here?
https://learn.microsoft.com/en-us/javascript/api/powerpoint/powerpoint.slidecollection?view=powerpoint-js-preview
Sample code (assumed I needed to load items first before trying to get the count):
PowerPoint.run(function (context) {
var properties = context.presentation.slides.items;
context.load(properties);
return context.sync()
.then(function() {
console.log(properties);
})
.catch((error) => console.log(error));
})
Here is a call to slides.getCount() which should work
PowerPoint.run(async (context) => {
context.presentation.load("slides");
await context.sync();
var slides = context.presentation.slides;
var slideCount = slides.getCount();
await context.sync();
console.log("There are ", slideCount.value, " slides in this presentation");
}
PowerPoint.run(async function (context) {
context.presentation.load("slides");
await context.sync();
const slide = context.presentation.slides.getItemAt(0);
slide.load("id");//you just load any property from PPT object model and then sync
await context.sync();
console.log(slide.id);
});

Can I simulate pressing the "Enter" key in Puppeteer using only a frame reference?

I'd really like to submit a form in an iframe using Puppeteer, which I've found I can do pretty easily by going
page.keyboard.press('Enter');
However, for nearly everything else I want to do, all I need to pass around is a reference to the iframe I'm interested in. For instance, I may have a method that fills out and submits a form like so:
// Some other setup script
const page = await context.newPage();
const frame = page.frames().find(frame => frame.name() === 'myFrame'); // Iframe ref
// Utility method
function useTheForm(frame) {
// ...
// Do other misc form setup
// ...
await frame.type('myInput', 'Some Value');
// TODO: Submit the form... somehow...
// "frame.keyboard" doesn't exist. Need some kind of ref like "frame.page"
// frame._frameManager._page.keyboard.press('Enter') works, but is kind of dirty...
}
// Use our utility method
useTheForm(frame);
I'd really like a way to submit the form using the "Enter" key without having to also keep track of and pass around a reference to page as well, but I'm hesitant to use intended-to-be-internal properties that aren't documented in the API.
You can focus an element in the iframe and then press a key with page.keyboard. Here is an example that press Enter on a focused link in an iframe causing iframe navigation (though this navigation seems failed due to site iframe policy):
const puppeteer = require('puppeteer');
(async function main() {
try {
const browser = await puppeteer.launch(
{ headless: false, defaultViewport: null });
const [page] = await browser.pages();
await page.goto('https://example.org/');
const data = await page.evaluate(() => {
document.body.appendChild(document.createElement('iframe')).src =
'https://example.org/?foo=bar';
});
await page.waitFor(3000);
console.log(page.frames().map(frame => frame.url()));
await page.frames()[1].focus('a');
await page.keyboard.press('Enter');
//await browser.close();
} catch (err) {
console.error(err);
}
})();

Categories