0

I have code that will login to a page, navigate to a list of messages, get the first message, and delete it. I need to be able to get the list of messages and delete each in turn. When I try to do that, I run into problems.

The site is rendered as plain html until the delete button is clicked. At this point, an iframe opens with the delete confirmation inside of it. If the confirmation is clicked, it returns me to the list of messages.

This is working until the iframe pops up. The existing code doesn't find the selector in the iframe. The code does work when it is not in a loop, though. So how can I interact with the iframe in the loop?

TimeoutError: Waiting for selector .navigation-footer button failed: Waiting failed: 30000ms exceeded

const messageList = await page.$$(".message-list tr");

for (message of messageList) {

    //get first message
    await page.click(".message-list tr");

    //wait for the message to load
    await page.waitForSelector(".circle-cross");

    //get time and message text
    const msgTime = await page.$eval("time", el => el.getAttribute("dateTime"));
    const paragraphs = await page.evaluate(() => {
        let paraElements = document.querySelectorAll(".bubble p");
        //array literal
        const paraList = [...paraElements];
        //gets the innerText of each element
        return paraList.map((el, index) => el.innerText);
    });

    //get author name
    await page.waitForSelector(".user p a")
    let authorLink = await page.$(".user p a")
    let authorName = await authorLink.evaluate(el => el.innerText.trim());
    
    //append message to messages.txt
    const stream = fs.createWriteStream("messages.txt", { flags: 'a' });
    stream.write(authorName + "\n");
    stream.write(msgTime + "\n");
    paragraphs.forEach((item, index) => {
        stream.write(item + "\n");
    });
    stream.end();

    //delete the message
    await page.click(".circle-cross");

    //handle the iframe verification
     const elementHandle = await page.waitForSelector("iframe.fancybox-iframe");
    const frame = await elementHandle.contentFrame();
    const button = await frame.waitForSelector(".navigation-footer button");
  await frame.click(".navigation-footer button");
    }
    
Mike C
  • 353
  • 1
  • 5
  • 12
  • Does this answer your question? [Using async/await with a forEach loop](https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop) – ggorlen Jan 19 '23 at 00:59
  • @ggorlen that got me part of the way, thanks! I am still stuck because of the iframe. I will edit my question. – Mike C Jan 19 '23 at 15:48
  • Hard to help with that without a [mcve] that shows the site(s) we're working with. – ggorlen Jan 19 '23 at 15:56

2 Answers2

0

UPDATE: I did get this working, eventually, by substituting in:

await button.evaluate(el => el.click());

instead of

await frame.click(".navigation-footer button")
Mike C
  • 353
  • 1
  • 5
  • 12
-1

Simply put the code you have in the forEach loop, however you will have to add async on this line:

messageList.forEach(async (el) => {

Thus your ending result should then be:

    //get a list of messages
const messageList = await page.$$(".message-list tr");
messageList.forEach(async (el) => {
    //get the first message
    await page.click(".message-list tr");

    //wait for the message to load
    await page.waitForSelector(".circle-cross");

    //delete the message
    await page.click(".circle-cross");

    //handle the iframe verification
    const elementHandle = await page.waitForSelector("iframe");
    const = await elementHandle.contentFrame();
    await frame.waitForSelector(".navigation-footer button");
    await frame.click(".navigation-footer button");
}
Morgan
  • 44
  • 4