Skip to content

Commit

Permalink
Merge pull request #340 from MuckRock/playwright-tests
Browse files Browse the repository at this point in the history
Setup for playwright tests, run unit tests on PR
  • Loading branch information
eyeseast authored Nov 20, 2023
2 parents 1c53da2 + af8d693 commit eb4ae50
Show file tree
Hide file tree
Showing 25 changed files with 508 additions and 474 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Playwright Tests

on:
push:
branches: [master]
pull_request:
branches: [master]

env:
URL: "https://deploy-preview-${{ github.event.number }}.muckcloud.com"
PLAYWRIGHT_TEST_BASE_URL: "https://deploy-preview-${{ github.event.number }}.muckcloud.com"
NODE_ENV: staging

jobs:
wait:
runs-on: ubuntu-latest

steps:
- uses: cygnetdigital/[email protected]
with:
url: ${{ env.URL }}
responseCode: "200"
timeout: 120000
interval: 3000

test:
timeout-minutes: 60
runs-on: ubuntu-latest
needs: wait

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18

- name: Install dependencies
run: npm ci

- name: Install Playwright
run: npx playwright install --with-deps

- name: Run Playwright tests
run: npx playwright test
26 changes: 26 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Unit tests

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: "18.x"
- run: npm ci
- run: npm run build
env:
NODE_ENV: production
- run: npm test
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public/assets/
# Local test env file
.env.test
.env.sentry-build-plugin
playwright-report
test-results

# local fixtures, everyone should generate their own
tests/fixtures/development.json

.vscode

Expand Down
4 changes: 2 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
export default {
extensionsToTreatAsEsm: [".svelte"],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
"^@/(.*)$": "<rootDir>/$1",
},
moduleFileExtensions: ["js", "svelte"],
modulePaths: ["src"],
rootDir: "src",
setupFiles: ["dotenv/config"],
testEnvironment: "jsdom",
transform: {
Expand Down
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"webpack-dev-server": "^4.15.1"
},
"devDependencies": {
"@playwright/test": "^1.39.0",
"@storybook/addon-essentials": "^7.4.5",
"@storybook/addon-interactions": "^7.4.5",
"@storybook/addon-links": "^7.4.5",
Expand All @@ -68,6 +69,7 @@
"jest-environment-jsdom": "^29.7.0",
"msw": "^1.2.3",
"msw-storybook-addon": "^1.8.0",
"netlify-plugin-playwright-cache": "^0.0.1",
"playwright": "^1.39.0",
"prettier": "^3.0.2",
"prettier-plugin-svelte": "^3.0.3",
Expand All @@ -83,16 +85,17 @@
"build-staging": "cross-env NODE_ENV=staging webpack",
"build-serve": "cross-env NODE_ENV=production webpack && serve public -l 80 --single",
"build-analyze": "cross-env NODE_ENV=production-analyze webpack",
"build-storybook": "storybook build",
"dev": "concurrently \"npm:dev-app\" \"npm:dev-embed\"",
"dev-app": "cross-env NODE_ENV=development SUPPRESS_WARNINGS=1 webpack serve --config webpack.app.config.js",
"dev-embed": "cross-env NODE_ENV=development webpack --config webpack.embed.config.js",
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
"serve": "serve public -l 80 --single",
"watch-app": "cross-env NODE_ENV=development webpack watch --config webpack.app.config.js",
"watch": "concurrently npm:serve npm:dev-embed npm:watch-app",
"test-watch": "jest --watchAll",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
"test-watch": "jest --watchAll",
"test:browser": "playwright test",
"watch": "concurrently npm:serve npm:dev-embed npm:watch-app",
"watch-app": "cross-env NODE_ENV=development webpack watch --config webpack.app.config.js"
},
"engine": 18,
"browserslist": "> 0.25%, not dead",
Expand Down
65 changes: 65 additions & 0 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { defineConfig, devices } from "@playwright/test";
import dotenv from "dotenv";

const environment = process.env.NODE_ENV || "development";

dotenv.config({
path: environment === "development" ? ".env" : `.env.${environment}`,
});

if (environment === "development") {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
}

export default defineConfig({
// Look for test files in the "tests" directory, relative to this configuration file.
testDir: "tests",

// Run all tests in parallel.
fullyParallel: true,

// Fail the build on CI if you accidentally left test.only in the source code.
forbidOnly: !!process.env.CI,

// Retry on CI only.
retries: process.env.CI ? 2 : 0,

// Reporter to use
reporter: "html",

workers: process.env.CI ? 1 : undefined,

use: {
// Base URL to use in actions like `await page.goto('/')`.
baseURL: process.env.URL || "https://www.dev.documentcloud.org",

// Collect trace when retrying the failed test.
trace: "on-first-retry",
},

// Options specific to each project.
projects: [
{
name: "chromium",
use: devices["Desktop Chrome"],
},
{
name: "firefox",
use: devices["Desktop Firefox"],
},
{
name: "webkit",
use: devices["Desktop Safari"],
},
/* todo configure tests for mobile
{
name: "Mobile Chrome",
use: devices["Pixel 5"],
},
{
name: "Mobile Safari",
use: devices["iPhone 12"],
},
*/
],
});
22 changes: 22 additions & 0 deletions plugins/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// netlify plugin to run playwright on deploy previews

export async function onSuccess({ utils }) {
console.log("Installing Playwright dependencies");
await utils.run("playwright", ["install"]).catch((err) => {
utils.build.failBuild(err);
});

console.log("Running Playwright tests");
result = await utils.run("playwright", ["test"]).catch((err) => {
utils.status.show({
title: "Playwright test failed",
summary: err.toString(),
});
utils.build.failPlugin(err);
});

utils.status.show({
title: "Playwright tests completed.",
summary: "",
});
}
1 change: 1 addition & 0 deletions plugins/test/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name: netlify-plugin-playwright
2 changes: 1 addition & 1 deletion src/pages/app/Anonymous.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
</div>
<div class="text">
<p>
{$_("anonymous.p1", { values: { n: $search.results.count } })}
{$_("anonymous.p1", { values: { n: $search?.results?.count ?? 0 } })}
</p>
<p>
{@html $_("anonymous.p2")}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/app/Documents.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@
on:files={showUploadModal}
disabled={embed || !$orgsAndUsers.loggedIn || !$orgsAndUsers.isVerified}
>
{#if !$orgsAndUsers.loggedIn && $search.params.query === "" && !anonymousClosed}
{#if !$orgsAndUsers.loggedIn && $search.params?.query === "" && !anonymousClosed}
<Anonymous bind:closed={anonymousClosed} />
{:else}
{#each $documents.documents as document (document.id)}
Expand Down
9 changes: 9 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# End-to-end tests

This directory includes [Playwright](https://playwright.dev) tests that run in a headless browser against a running version of the site. To run locally, start a full instance (Squarelet, the DocumentCloud API and frontend) and set the `URL` environment variable. For example:

```sh
URL=https://www.dev.documentcloud.org npx playwright test
```

Netlify will automatically run this against any pull request using a deploy preview, and it will set the `URL` environment variable to the correct target.
12 changes: 12 additions & 0 deletions tests/anonymous/manager/app.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @ts-check

import { test, expect } from "@playwright/test";

test("basic manager rendering", async ({ page }) => {
await page.goto("/app");

const url = new URL(page.url());
expect(url.pathname).toBe("/app");

await expect(page).toHaveTitle("DocumentCloud");
});
9 changes: 9 additions & 0 deletions tests/anonymous/pages/home.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @ts-check

import { test, expect } from "@playwright/test";

test("basic homepage test", async ({ page }) => {
await page.goto("/home");

await expect(page).toHaveTitle("Home | DocumentCloud");
});
62 changes: 62 additions & 0 deletions tests/anonymous/viewer/document.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// @ts-check
import fs from "node:fs/promises";
import { test as base, expect } from "@playwright/test";

const {
DC_BASE = "https://api.dev.documentcloud.org",
NODE_ENV = "development",
} = process.env;

const test = base.extend({
document: async ({ page }, use) => {
const filename = new URL(
`../../fixtures/${NODE_ENV}.json`,
import.meta.url,
);

const documents = await fs
.readFile(filename)
.then((s) => JSON.parse(s.toString()));

await use(documents[0]);
},
});

test.describe("document tests", () => {
test("basic document test", async ({ page, document }) => {
// canonical will point to a URL that might not exist on staging
const path = new URL(document.canonical_url).pathname;
await page.goto(path).catch(console.error);

expect(new URL(page.url()).pathname).toBe(path);

await expect(page.locator(".sidebar").getByRole("heading")).toHaveText(
document.title,
);

await page.getByRole("link", { name: "Original Document (PDF) »" }).click();

await expect(page.locator("h1")).toHaveText(document.title);

await page.getByRole("link", { name: "p. 1" }).click();

expect(new URL(page.url()).hash).toEqual("#document/p1");

await page
.locator("div")
.filter({ hasText: /^DocumentPlain TextThumbnailSearch Results$/ })
.getByRole("combobox")
.selectOption("text");

// check that text view loaded
/*
await expect(page.locator(".text").first()).toHaveText(
text.pages[0].contents,
);
*/

// switch to thumbnail view, click the first image
await page.getByRole("combobox").selectOption("thumbnail");
await page.locator("img").first().click();
});
});
File renamed without changes.
Loading

0 comments on commit eb4ae50

Please sign in to comment.