Reputation: 1
Here is the input element I would like to type into:
<input name="name" ng-model-options="{ debounce: 300 }" ng-model="contestantState.form.name" ng-pattern=".*" placeholder="Alice Smith" required="" style="width: 246px" type="text" class="ng-empty ng-invalid ng-invalid-required ng-valid-pattern ng-dirty ng-valid-parse ng-touched">
One of my tries:
await page.type('input[name="name"]',"my name");
No matter what way I try to type into this field, nothing happens: it stays blank because it is not identifiable by its name or classes.
What do I do to have Puppeteer enter a Name into that field?
The website I'm trying to do this on is: https://gleam.io/hxVaH/win-a-krooked-big-eyes-too-skateboard?gsr=hxVaH-4hKgdgSzfh (The Full Name and email field).
Upvotes: 0
Views: 668
Reputation: 56965
Existing answers have been useful, but this should do the full submission and give you the share URL.
Since this AngularJS app doesn't seem to use ids and classes much, using text contents seems like a reasonable choice for hitting a couple of the buttons.
I had to use .evaluate(el => el.click())
instead of .click()
to click one of the buttons successfully as described here.
const puppeteer = require("puppeteer");
const clickXPath = async (page, xp) => {
await page.waitForXPath(xp);
const [el] = await page.$x(xp);
await el.evaluate(el => el.click());
};
const randomEmail = () =>
Array(10).fill().map(() =>
String.fromCharCode(~~(Math.random() * 26 + 97))
).join("") +
`${(Math.random() + "").replace(/\./, "")}@gmail.com`
;
let browser;
(async () => {
const url = "https://gleam.io/hxVaH/win-a-krooked-big-eyes-too-skateboard";
browser = await puppeteer.launch({headless: false});
const [page] = await browser.pages();
await page.goto(url, {waitUntil: "networkidle0"});
await clickXPath(page, `//span[contains(text(), "Refer Friends")]`);
const nameSel = '[class="entry-method expanded"] input[name="name"]';
const emailSel = '[class="entry-method expanded"] input[name="email"]';
await page.waitForSelector(nameSel);
await page.type(nameSel, "my name33");
await page.type(emailSel, randomEmail());
const hasText = () => page.evaluate(() =>
!!document.querySelector(".share-link__link")?.innerText.trim()
);
for (let tries = 1000; !(await hasText()) && tries--;) {
await clickXPath(page, `//span[contains(text(), "Save")]`);
await page.waitForTimeout(100);
}
const shareCode = await page.$eval(".share-link__link", el => el.innerText);
console.log(shareCode); // => https://wn.nr/w9Wz5p
})()
.catch(err => console.error(err))
.finally(async () => await browser.close())
;
Upvotes: 1
Reputation: 8841
You need to toggle the input first to make it available in the DOM (until that this element is only an Angular Template inside a <script>
tag). And as Tanuj wrote: you should go with a selector which have only one instance. E.g.:
await page.click('#em5682795 > a'); // "Refer friends for extra entries"
await page.type('[class="entry-method expanded"] input[name="name"]',"my name");
Upvotes: 0
Reputation: 329
There is more than one matching element using your locator. Try this:
await page.type('[class="entry-method expanded"] input[name="name"]',"my name");
Upvotes: 0