whoknowswho
whoknowswho

Reputation: 1

Problem filling in an input field without an id or identifying attribute. (Puppeteer)

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

Answers (3)

ggorlen
ggorlen

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

theDavidBarton
theDavidBarton

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

Tanuj Vishnoi
Tanuj Vishnoi

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

Related Questions