Validate complicated graphics rich pdf documents using Playwright
Tags: playwright, testautomation, typescript, automation
Published: 2022-07-10 10:32:52

This article will take your pdf verification skills to the next level using [Playwright](https://playwright.dev/). 

The pdf document we will use for this example will be the "**_Tesla Powerwall 2 Datasheet_**".  This file is hosted at following location https://oedtrngbj.wpengine.com/wp-content/uploads/Powerwall_2_AC_Datasheet_EN_NA.pdf

Observe that this pdf document contains multiple pages, some illustrations and a table.

![complex pdf](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zfxq64zx132uufw265sy.png)
 

##Caveats

Let me be upfront with the following issues I have encountered with this approach:

* This solution only seems to work when the test is run using *chromium* in *headed* mode
* The elements contained within pdf viewer component are not accessible to Playwright, this means you will not be able to mask/hide dynamic elements in the pdf. For example, customer id or dynamic date/time stamps
* We are using Playwrights' built in visual comparison library. It is advisable that you get familiar with the maintenance required to keep the baseline images up to date.  See the [Visual comparisons](https://playwright.dev/docs/test-snapshots) page on the Playwright documentation

If you are happy with these compromises, read on!


## page.setContent() + toMatchSnapshot() = 🤩

Using the `setContent()`, load the pdf into an `iframe` like so:

```typescript
  const pdfResource =
    'https://oedtrngbj.wpengine.com/wp-content/uploads/Powerwall_2_AC_Datasheet_EN_NA.pdf';
  let iframe = `<iframe src="${pdfResource}#zoom=60" style="width: 100%;height:100%;border: none;"></iframe>`;
  await page.setContent(iframe);
  await page.waitForTimeout(5000);
```
> **_NOTE:_**  You may need to experiment with the zoom level, width, height attributes to suit your needs


> **_ANOTHER NOTE:_**  The `waitForTimeout` function is used here to wait for the pdf contents to be loaded into the `iframe`

We will make use of Playwrights' assertion `expect(screenshot).toMatchSnapshot(name[, options])` => https://playwright.dev/docs/test-assertions#screenshot-assertions-to-match-snapshot-1,  to capture a screenshot of a particular element matching a locator, in our case - we will need to take a screenshot of the iframe above with the PDF file fully loaded to particular page.

Our solution will make use of this function:

```typescript
  expect(await page.locator('iframe').screenshot()).toMatchSnapshot();
```

The completed test will look like this ...

```typescript
import { test, expect } from '@playwright/test';

test('validate a complex pdf', async ({ page }) => {
  const pdfResource =
    'https://oedtrngbj.wpengine.com/wp-content/uploads/Powerwall_2_AC_Datasheet_EN_NA.pdf';
  let iframe = `<iframe src="${pdfResource}#zoom=60" style="width: 100%;height:100%;border: none;"></iframe>`;
  await page.setContent(iframe);
  await page.waitForTimeout(5000);
  expect(await page.locator('iframe').screenshot()).toMatchSnapshot();
});
```

Run the test.  It should fail with the following error.

![Initial attempt should fail](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dirp5cqgggb69rczc6mz.png)

![This is fine](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qv3jth4uc7zktntyvzmq.png)
 
Playwright has not found a golden snapshot of the element and hence on the very first test execution, it will automatically generate this file for you.  You will need to commit these files into your repo.

![Golden files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbudx5ez7co7nnis6v8s.png)
 
Rerun the test again, this time it should pass since it will already have a baseline image to compare against.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhk9hkt8zn2et0zea03a.gif)

Ok, that's nice. We managed to validate the first page of the pdf. 

But most pdfs you will encounter out in the wild will contain multiple pages.  Let us ammend our test to cater for multiple pages.

```typescript
test('validate a complex pdf II, all pages', async ({page}) => {
  const numberOfPages = 2;
  for (let i = 1; i < numberOfPages + 1; i += 1) {
    let pdfResource =
      'https://oedtrngbj.wpengine.com/wp-content/uploads/Powerwall_2_AC_Datasheet_EN_NA.pdf';
    let iframe = `<iframe src="${pdfResource}#zoom=60&page=${i}" style="width: 100%;height:100%;border: none;"></iframe>`;
    await page.setContent(iframe);
    await page.waitForTimeout(5000);
    expect(await page.locator('iframe').screenshot()).toMatchSnapshot({
      name: `pdf_validation_page_${i}.png`,
    });
  }
});
```

![Multiple pages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8oghu08idkgrnwicybr8.gif)
 

Job done.


#Other improvements for you to consider 

(all totally optional and it entirely up to you to implement)

* Dynamically determine the number of pages, the example above uses a predefined value for the expected number of pages.
* Remove the hard coded `waitForTimeout` and implement a better way of waiting for the contents to be loaded.

## Final solution

{% embed https://gist.github.com/ryanrosello-og/1ee8d47f792224ecd951eb74528acb7f %}