Skip to content

Commit

Permalink
Merge pull request #142 from argos-ci/csp-playwright
Browse files Browse the repository at this point in the history
feat: expose `getCSPScriptHash` in `@argos-ci/playwright`
  • Loading branch information
gregberge authored Aug 24, 2024
2 parents 4ca7802 + ec8e439 commit 2d23e3b
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 39 deletions.
80 changes: 43 additions & 37 deletions packages/playwright/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,45 @@ To debug flaky tests, Argos supports using the [Playwright --repeat-each option]
npm exec -- playwright test --repeat-each 5
```

## Configure Content-Security-Policy (CSP)

To stabilize tests, Argos injects a script before taking screenshots. This script may conflict with your CSP settings. To resolve this issue, you have two options:

### 1. Allow the Argos script in your CSP

The `@argos-ci/playwright` package provides a `getCSPScriptHash` method to retrieve the hash of the script injected by Argos. Additionally, Argos requires the `'unsafe-eval'` directive to function properly.

To use this method, you need to be able to inject CSP headers into your server. Here's an example in a Playwright configuration:

```ts
import { defineConfig } from "@playwright/test";
import { getCSPScriptHash } from "@argos-ci/playwright";

export default defineConfig({
webServer: {
command: "node my-app.js",
port: 3000,
env: {
CSP_SCRIPT_SRC: `${getCSPScriptHash()},'unsafe-eval'`,
},
},
});
```

### 2. Configure Playwright to bypass CSP

If you do not have the flexibility to add custom CSP headers for Playwright test execution, you can [configure Playwright to bypass CSP](https://playwright.dev/docs/api/class-testoptions#test-options-bypass-csp) in your Playwright configuration:

```ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
use: {
bypassCSP: true,
},
});
```

## API Overview

### argosScreenshot(page, name[, options])
Expand All @@ -172,6 +211,10 @@ npm exec -- playwright test --repeat-each 5

Unlike [Playwright's `screenshot` method](https://playwright.dev/docs/api/class-page#page-screenshot), set `fullPage` option to `true` by default. Feel free to override this option if you prefer partial screenshots of your pages.

### getCSPScriptHash()

Returns the Content-Security-Policy script hash used by Argos (ex: `'sha256-xaC9wWpMVRiAXSfhxhP+Wyqkw0mgO+MIrHuzmMPIxEI='`).

### Playwright reporter

The Argos reporter offers extensive configuration options. Specifically, all [upload parameters](https://js-sdk-reference.argos-ci.com/interfaces/UploadParameters.html) are available for customizing the reporter.
Expand Down Expand Up @@ -201,43 +244,6 @@ export default defineConfig({
});
```

## Upgrading from v0.0.x

### Step 1: Setup Argos in your Playwright config

```typescript
import { defineConfig } from "@playwright/test";

export default defineConfig({
// ... other configuration

// Add Argos reporter.
reporter: [
["list"],
[
"@argos-ci/playwright/reporter",
{
// Upload artefacts to Argos on CI.
uploadToArgos: !!process.env.CI,
},
],
],

// Setup recording option to enable test debugging features
use: {
// Setting to capture screenshot only when a test fails
screenshot: "only-on-failure",
// Setting to retain traces only when a test fails
trace: "retain-on-failure",
},
});
```

### Step 2: Phase Out CLI Usage:

- Remove `argos upload` calls from your CI. (Note: Screenshots now upload through the reporter.)
- Delete `@argos-ci/cli` from `package.json`.

## Additional Resources

- [Quickstart with Argos + Playwright](/quickstart/playwright)
Expand Down
15 changes: 13 additions & 2 deletions packages/playwright/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { mkdir } from "node:fs/promises";
import { resolve, dirname } from "node:path";
import { createHash } from "node:crypto";

import type {
Page,
PageScreenshotOptions,
Expand Down Expand Up @@ -73,8 +75,9 @@ async function injectArgos(page: Page) {
const injected = await page.evaluate(
() => typeof (window as any).__ARGOS__ !== "undefined",
);
if (injected) return;
await page.addScriptTag({ content: getGlobalScript() });
if (!injected) {
await page.addScriptTag({ content: getGlobalScript() });
}
}

async function getTestInfo() {
Expand Down Expand Up @@ -310,3 +313,11 @@ export async function argosScreenshot(

await teardown();
}

/**
* Get the CSP script hash.
*/
export function getCSPScriptHash() {
const hash = createHash("sha256").update(getGlobalScript()).digest("base64");
return `'sha256-${hash}'`;
}

0 comments on commit 2d23e3b

Please sign in to comment.