Upload a file with puppeteer in Jest

sdgluck picture sdgluck · Feb 28, 2018 · Viewed 7.9k times · Source

I am using Jest and have puppeteer set up as in this repository, which is linked to from the Jest documentation.

I am trying to write some automated smoke tests on a WordPress website using puppeteer. One of the tests attempts to upload an image to the WordPress Media Library.

This is the test:

it('Create test media', async () => {
  // go to Media > Add New
  await page.goto(`${env.WP_HOME}/wp/wp-admin/media-new.php`)
  const display = await page.evaluate(() => {
    const el = document.querySelector('#html-upload-ui')
    return window.getComputedStyle(el).display
  })
  if (display !== 'block') {
    // ensure we use "built-in uploader" as it has `input[type=file]`
    await page.click('.upload-flash-bypass > a')
  }
  const input = await page.$('#async-upload')
  await input.uploadFile(testMedia.path)
})

The file input field's value is populated as expected (I know this because if I save out a screenshot after the call to uploadFile it shows the path of the file in the input), and the form is submitted, however when I go to view the media library there are no items.

I have tried the following amendments to the uploadFile part of the test, to no avail:

// 1. attempt to give time for the upload to complete
await input.uploadFile(testMedia.path)
await page.waitFor(5000)

// 2. attempt to wait until there is no network activity
await Promise.all([
  input.uploadFile(testMedia.path),
  page.waitForNavigation({waitUntil: 'networkidle0'})  
])

// 3. attempt to submit form manually (programmatic)
input.uploadFile(testMedia.path)
page.evaluate(() => document.querySelector('#file-form').submit())
await page.waitFor(5000) // or w/ `waitForNavigation()`

// 4. attempt to submit form manually (by interaction)
input.uploadFile(testMedia.path)
page.click('#html-upload')
await page.waitFor(5000) // or w/ `waitForNavigation()`

Answer

sdgluck picture sdgluck · Feb 28, 2018

The problem is that file uploading doesn't work when connecting to a Browser instance via WebSocket as in jest-puppeteer-example. (GitHub issue here: #2120.)

So instead of doing that just use puppeteer.launch() directly when setting up your test suite (instead of via the custom "Jest Node environment"):

let browser
  , page

beforeAll(async () => {
  // get a page via puppeteer
  browser = await puppeteer.launch({headless: false})
  page = await browser.newPage()
})

afterAll(async () => {
  await browser.close()
})

You also then have to manually submit the form on the page, as in my experience uploadFile() doesn't do this. So in your case, for the WordPress Media Library single file upload form, the test would become:

it('Create test media', async () => {
  // go to Media > Add New
  await page.goto(`${env.WP_HOME}/wp/wp-admin/media-new.php`)
  const display = await page.evaluate(() => {
    const el = document.querySelector('#html-upload-ui')
    return window.getComputedStyle(el).display
  })
  if (display !== 'block') {
    // ensure we use the built-in uploader as it has an `input[type=file]`
    await page.click('.upload-flash-bypass > a')
  }
  const input = await page.$('#async-upload')
  await input.uploadFile(testMedia.path)
  // now manually submit the form and wait for network activity to stop
  await page.click('#html-upload')
  await page.waitForNavigation({waitUntil: 'networkidle0'})
})