1

I'm trying to run a for loop within a Puppeteer test that sends values to a search bar:

  test('test one', () => {
    var results = [""]
    var fuzz = ['apple', 'banana', 'kiwi'];
    fuzz.forEach(async function(value) {
      await page.goto('http://localhost:8080/')
      await page.$eval('#searchtext', el => el.value = value);
      await page.$eval('#searchform', form => form.submit())
      await page.waitForSelector('.results-table');
      var text = await page.evaluate(() => document.body.textContent);
      results.push(text)
    });
    expect(results).toEqual(expect.arrayContaining(['bleh']));
  });

However, whenever I run this it returns an empty results array which makes me think the forEach loop isn't finishing.

    expect(received).toEqual(expected) // deep equality

    Expected: ArrayContaining ["bleh"]
    Received: [""]

I know the code within the for loop is correct as I have other tests that aren't looped and do the same thing but return results. Any ideas how to do this cleanly/correctly? Thanks.

  • 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 Jul 29 '21 at 04:11
  • See also [How to return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call). Use a `for .. of` loop or `Promise.all` and make the test callback async. – ggorlen Jul 29 '21 at 04:12

1 Answers1

2

As pointed out in the comments, you are not waiting for your asynchronus functions to finish. I assume you want to execute the tests one after another, as you are using the same page variable in all of them. In that case you will have to use a for loop instead of the forEach function:

test('test one', async () => {
    var results = [""]
    var fuzz = ['apple', 'banana', 'kiwi'];
    for (var value of fuzz) {
      await page.goto('http://localhost:8080/')
      await page.$eval('#searchtext', el => el.value = value);
      await page.$eval('#searchform', form => form.submit())
      await page.waitForSelector('.results-table');
      var text = await page.evaluate(() => document.body.textContent);
      results.push(text)
    }
    expect(results).toEqual(expect.arrayContaining(['bleh']));
  });

Asynchronous functions are executed, as their name implies, asynchronously. This means, they run independent of the function they were called in, unless the calling function waits for their execution using the await keyword. This means by the time you check your results, you have no control over at which line your async functions currently are (in fact they have not even started yet, but that's a different story).

Additionally, as they are all asynchronous, they are also independent from each other. Meaning, they all get executed more or less at the same time resulting in random gibberish inserted into your form.

Aram Becker
  • 2,026
  • 1
  • 20
  • 32
  • Note the addition of the `async` keyword in the test. This enables the use of the `await` keyword inside the `foreach` loop. Whatever test framework you are using, they should all be able to deal with asynchronous tests – Aram Becker Jul 29 '21 at 09:34