Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SFR-1897: Add Playwright integration tests #487

Merged
merged 17 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
APP_ENV=testing

# API ENDPOINT
API_URL=https://backend.msw
74 changes: 61 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# - Lint
# - Typecheck
# - Test
# - Playwright integration tests
# - Build docker image
name: CI

Expand All @@ -17,8 +18,8 @@ jobs:
name: Updates Changelog
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dangoslen/changelog-enforcer@v2
- uses: actions/checkout@v4
- uses: dangoslen/changelog-enforcer@v3
with:
changeLogPath: "CHANGELOG.md"
skipLabels: "no-changelog"
Expand All @@ -29,16 +30,16 @@ jobs:
env:
CI: true
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: npm

- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
Expand All @@ -62,16 +63,16 @@ jobs:
env:
CI: true
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: npm

- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
Expand All @@ -95,16 +96,16 @@ jobs:
env:
CI: true
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: npm

- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
Expand All @@ -122,6 +123,51 @@ jobs:
- name: Test
run: npm run test

playwright_test:
name: Playwright Integration Tests
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.46.0-jammy
env:
CI: true
steps:
- uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: npm

- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-

- name: Install Dependencies
run: npm ci

- name: Build app
run: NODE_ENV=test npm run build

- name: Test
run: npm run playwright

- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: /playwright-report/
retention-days: 4

docker_build:
# Don't push anything to ECR, just build the docker image to make sure there are no build failures
name: Build Docker Image
Expand All @@ -130,10 +176,12 @@ jobs:
CI: true
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Build image
env:
APP_ENV: development
AIRTABLE_KEY: ${{ secrets.AIRTABLE_API_KEY }}
run: |
docker build --build-arg airtable_api_key=$AIRTABLE_KEY .
docker build --build-arg airtable_api_key=$AIRTABLE_KEY \
--build-arg APP_ENV=$APP_ENV .
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ vega.spec.ts
newrelic_agent.log

.vscode
.vscode/launch.json
.vscode/launch.json

# playwright
jackiequach marked this conversation as resolved.
Show resolved Hide resolved
playwright-report

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Fix error when collections are empty
- Fix error when a collection is empty
- SFR-2077: Validate top 5 language filters for Frontend DRB search results
- Add Playwright integration test for cookie validation

## [0.18.2]

Expand Down
40 changes: 23 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
# Build the environment.
FROM node:18-alpine
FROM node:18-alpine AS base

WORKDIR /
# Install dependencies only when needed
FROM base AS deps
WORKDIR /app

ARG airtable_api_key
# Install dependencies.
COPY package.json package-lock.json ./
RUN npm ci --cache .npm

# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Newrelic settings
ARG airtable_api_key
ARG NEW_RELIC_LICENSE_KEY
ARG NEW_RELIC_APP_NAME

ARG NEXT_PUBLIC_ADOBE_ANALYTICS

ARG APP_ENV

# Set environment variables. NODE_ENV is set early because we
# want to use it when running `npm install` and `npm run build`.
# Build the app!
RUN npm run build

ENV PATH /app/node_modules/.bin:$PATH
ENV PORT=3000 \
NODE_ENV=production
ENV NEXT_PUBLIC_AIRTABLE_API_KEY $airtable_api_key
# Sets READER_VERSION at build time. To revert, remove this variable entirely.
ENV NEXT_PUBLIC_READER_VERSION=v2
ENV NEW_RELIC_LICENSE_KEY $NEW_RELIC_LICENSE_KEY
ENV NEW_RELIC_APP_NAME $NEW_RELIC_APP_NAME
ENV NEXT_PUBLIC_ADOBE_ANALYTICS $NEXT_PUBLIC_ADOBE_ANALYTICS
ENV APP_ENV $APP_ENV

# Install dependencies.
COPY package.json package-lock.json ./
RUN npm ci --cache .npm
# RUNNER, copy all the files and run next
FROM base AS runner
WORKDIR /app

# Copy the app files.
COPY . ./

EXPOSE $PORT
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public

# Build the app!
RUN npm run build
EXPOSE $PORT

# CMD is the default command when running the docker container.
CMD npm run start:newrelic
4 changes: 4 additions & 0 deletions mocks/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { setupWorker } from "msw/browser";
import handlers from "./handlers";

export const worker = setupWorker(...handlers);
60 changes: 60 additions & 0 deletions mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { http, HttpResponse, passthrough } from "msw";
import { workDetailWithUp } from "~/src/__tests__/fixtures/WorkDetailFixture";
import {
API_URL,
DOWNLOAD_PATH,
FULFILL_PATH,
LIMITED_ACCESS_WORK_PATH,
} from "./mockEnv";

const isAuthenticated = (request) => {
const auth = request.headers.get("authorization");
return auth === "Bearer access-token";
};

const workUrl = new URL(LIMITED_ACCESS_WORK_PATH, API_URL).toString();
const fulfillUrl = new URL(FULFILL_PATH, API_URL).toString();
const downloadUrl = new URL(DOWNLOAD_PATH, API_URL).toString();

/** A collection of handlers to be used by default for all tests. */
const handlers = [
/**
* Allow normal requests to pass through
*/
http.all("/_next/*", passthrough),
http.all("/img/*", passthrough),
http.all("/__nextjs_original-stack-frame", passthrough),
http.all("/fonts/*", passthrough),
http.all("/css/*", passthrough),
http.get("/js/*", passthrough),
http.get("/favicon.ico", passthrough),
http.get("/favicon.ico", passthrough),
http.get("https://test-sfr-covers.s3.amazonaws.com/*", passthrough),
http.get("https://ds-header.nypl.org/*", passthrough),

http.get(workUrl, () => {
return HttpResponse.json(workDetailWithUp);
}),

http.get(fulfillUrl, ({ request }) => {
if (!isAuthenticated(request)) {
return new HttpResponse(null, {
status: 401,
});
}
return new HttpResponse(null, {
status: 302,
headers: {
Location: "/test-download-pdf",
},
});
}),

http.get(downloadUrl, () => {
return new HttpResponse(null, {
status: 200,
});
}),
];

export default handlers;
15 changes: 15 additions & 0 deletions mocks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
async function initMocks() {
if (typeof window === "undefined") {
const { server } = await import("./server");
server.listen({
onUnhandledRequest: "bypass",
Toxiapo marked this conversation as resolved.
Show resolved Hide resolved
});
} else if (typeof window !== "undefined") {
const { worker } = await import("./browser");
worker.start();
}
}

initMocks();

export {};
6 changes: 6 additions & 0 deletions mocks/mockEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const NYPL_LOGIN_URL = "https://login.nypl.org/auth/login?redirect_uri=";
export const API_URL = "https://backend.msw";
export const FULFILL_PATH = "/fulfill/12345";
export const LIMITED_ACCESS_WORK_PATH =
"/work/12345678-1234-1234-1234-1234567890ab";
export const DOWNLOAD_PATH = "/test-download-pdf";
4 changes: 4 additions & 0 deletions mocks/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { setupServer } from "msw/node";
import handlers from "./handlers";

export const server = setupServer(...handlers);
28 changes: 26 additions & 2 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
const path = require("path");

module.exports = {
webpack: (config) => {
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
webpack: (config, { isServer }) => {
config.resolve.alias["~"] = path.resolve(__dirname);

// should only include these for testing env
jackiequach marked this conversation as resolved.
Show resolved Hide resolved
if (process.env.APP_ENV === "testing") {
if (isServer) {
config.resolve.alias = {
...config.resolve.alias,
"msw/browser": false,
};
} else {
// Setting `resolve.alias` to `false` will tell webpack to ignore a module.
// `msw/node` is a server-only module that exports methods not available in
// the `browser`.
config.resolve.alias = {
...config.resolve.alias,
"msw/node": false,
};
}
}

return config;
},
sassOptions: {
Expand All @@ -12,5 +33,8 @@ module.exports = {
env: {
APP_ENV: process.env.APP_ENV,
NEW_RELIC_LICENSE_KEY: process.env.NEW_RELIC_LICENSE_KEY,
API_URL: process.env.API_URL,
},
};

module.exports = nextConfig;
Loading
Loading