Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e8926d4
start hacking on plugin-ifying next plugin
Ethan-Arrowood Feb 12, 2026
49a7afb
wip
Ethan-Arrowood Mar 4, 2026
f646633
Merge remote-tracking branch 'origin/main' into plugin
Ethan-Arrowood Mar 5, 2026
5db5f66
save my work before restarting machine
Ethan-Arrowood Mar 6, 2026
3aaabed
for posterity, save wip
Ethan-Arrowood Mar 11, 2026
646656d
wip
Ethan-Arrowood Mar 13, 2026
3cc8019
switch to require, build in-process, add server handling
Ethan-Arrowood Apr 3, 2026
c62a5ab
add integration tests using @harperfast/integration-testing-framework
Ethan-Arrowood Apr 9, 2026
d11db5d
replace node:test integration test with playwright + worker-scoped ha…
Ethan-Arrowood Apr 9, 2026
542bc8c
fix: set fullyParallel: false so tests within a file run sequentially
Ethan-Arrowood Apr 9, 2026
eb9dceb
omg playwright testing is actually working
Ethan-Arrowood Apr 9, 2026
7579d0f
my brain is fried like a chicken nugget. pick this up again tomorrow
Ethan-Arrowood Apr 9, 2026
67b0068
clean up tests and update contributing docs
Ethan-Arrowood Apr 12, 2026
dfdd3ec
add v14 and v16 tests and touch up plugin
Ethan-Arrowood Apr 12, 2026
9a92328
caching!
Ethan-Arrowood Apr 13, 2026
ef30451
remove dist folder
Ethan-Arrowood Apr 13, 2026
810c26b
add dist folder to .gitignore
Ethan-Arrowood Apr 13, 2026
f1e88b5
remove playwright-report
Ethan-Arrowood Apr 13, 2026
4ed5abe
limit to 14-16
Ethan-Arrowood Apr 15, 2026
c709853
format
Ethan-Arrowood Apr 15, 2026
5a27690
Merge branch 'main' into plugin
Ethan-Arrowood Apr 15, 2026
11b9dee
make caching wip and optional, and update docs
Ethan-Arrowood Apr 15, 2026
6a34b75
improvements from review and clean up for v2 release
Ethan-Arrowood Apr 22, 2026
3c5c639
fix for cjs defaults in Next.js
Ethan-Arrowood Apr 22, 2026
36a9a09
fixes
Ethan-Arrowood Apr 22, 2026
9fcc316
try updating node v to 24
Ethan-Arrowood Apr 22, 2026
fa177e1
node version fixes
Ethan-Arrowood Apr 22, 2026
38cc7b2
beep boop
Ethan-Arrowood Apr 22, 2026
6c1c201
just work please
Ethan-Arrowood Apr 22, 2026
7120aa4
no clue why this isnt working on CI
Ethan-Arrowood Apr 22, 2026
fac324a
install fixutre deps duh
Ethan-Arrowood Apr 22, 2026
a3bd6dd
try this
Ethan-Arrowood Apr 22, 2026
393a2e3
skip ci for now
Ethan-Arrowood Apr 22, 2026
0e61637
more fixes. can i publish later?
Ethan-Arrowood Apr 22, 2026
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
93 changes: 93 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Integration Tests

on:
push:
branches: [main]
pull_request:
workflow_dispatch:
inputs:
node-version:
description: 'Node.js version'
required: true
type: choice
default: 'all'
options:
- 'all'
- '20'
- '22'
- '24'

jobs:
# TODO: Fix CI setup and re-enable
generate-node-version-matrix:
if: false
name: Generate Node Version Matrix
runs-on: ubuntu-latest
outputs:
node-versions: ${{ steps.set-node-versions.outputs.node-versions }}

steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set Node versions
id: set-node-versions
run: |
if [ "${{ github.event.inputs.node-version }}" == "all" ] || [ -z "${{ github.event.inputs.node-version }}" ]; then
echo "node-versions=[20, 22, 24]" >> $GITHUB_OUTPUT
else
echo "node-versions=[${{ github.event.inputs.node-version }}]" >> $GITHUB_OUTPUT
fi

integration-tests:
name: Integration Tests (Node ${{ matrix.node-version }})
needs: generate-node-version-matrix
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
node-version: ${{ fromJson(needs.generate-node-version-matrix.outputs.node-versions) }}

steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: ${{ matrix.node-version }}
package-manager-cache: false

- name: Install dependencies
run: npm install

- name: Build
run: npm run build || true

- name: Install fixture dependencies
run: npm run install:fixtures

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

- name: Run integration tests
run: npm run test:integration
env:
HARPER_INTEGRATION_TEST_LOG_DIR: /tmp/harper-test-logs

- name: Upload Harper logs on failure
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: harper-logs-node-${{ matrix.node-version }}
path: /tmp/harper-test-logs/
retention-days: 7

- name: Upload Playwright traces on failure
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: playwright-traces-node-${{ matrix.node-version }}
path: integrationTests/test-results/
retention-days: 7
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
node_modules/
.next/
test-results/
.DS_Store
.DS_Store
dist/
playwright-report/
1 change: 0 additions & 1 deletion .node-version

This file was deleted.

2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
save-exact=true
package-lock=false
91 changes: 82 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,93 @@

## Code Organization

The key source files for the repo are:
The key source files are:

- `cli.js`
- `extension.js`
- `config.yaml`
- `src/plugin.ts` — the Harper plugin implementation (ESM, loaded by Harper's runtime)
- `src/withHarper.cts` — the `withHarper()` Next.js config helper (CJS, loaded by Next.js config files)
- `src/CacheHandler.cts` — the Harper-backed ISR cache handler (CJS, loaded by Next.js at runtime)
- `schema.graphql` — the plugin table schemas
- `config.yaml` — Harper configuration for the plugin

These are what are included in the published module and what HarperDB relies on for the component to work.
The `.cts` extension marks files as CommonJS, which is required because Next.js config files (`next.config.js`, `next.config.mjs`, `next.config.ts`) all use CommonJS module resolution. `plugin.ts` stays as ESM since it is only loaded by Harper's own runtime.

The `fixtures/` directory contains testable examples of using this extension.
Run `npm run build` to compile `src/withHarper.cts` and `src/CacheHandler.cts` to `dist/cjs/`. The `src/plugin.ts` file is not compiled — Harper loads it directly using Node's type-stripping support.

The `test-utils/` directory contains some scripts for Playwright that the fixtures use to setup HarperDB for the test run.
The published module includes `dist/`, `config.yaml`, and `schema.graphql`.

The `fixtures/` directory contains minimal Next.js applications used as integration test targets. Each subdirectory is a self-contained app with the plugin installed and configured. The three main fixtures each use a different config format to cover the full compatibility matrix:

- `fixtures/next-14/` — `next.config.js` (CommonJS `require`)
- `fixtures/next-15/` — `next.config.mjs` (ESM `import`)
- `fixtures/next-16/` — `next.config.ts` (TypeScript)

The `integrationTests/` directory contains the Playwright test files and supporting infrastructure.

## Testing

After many, many hours and a plethora of different attempts, setting up HarperDB and running the fixture Playwright tests was too flaky for automation. Review the repository at this [commit](https://github.com/HarperDB/nextjs/tree/b72c05e29bd5afd4b91425ae709effa05bd3c2fd) for some of the prior art.
Tests are Playwright integration tests that run against real Harper instances. They live in `integrationTests/` and rely on `@harperfast/integration-testing` to manage Harper process lifecycle.

### How it works

Each test file targets one fixture — a minimal Next.js application in `fixtures/<name>/` that has the plugin installed and configured. At the start of a test run, Harper is started with that fixture as the component root and kept alive for the duration of the file. All `test()` calls in the file run sequentially against the same Harper instance, then Harper is torn down when the file finishes. Separate test files can run in parallel across Playwright workers, each with their own isolated Harper instance.

The `fixture()` helper in `integrationTests/fixture.ts` handles the wiring: it starts Harper, exposes a `harper` object (including `harper.httpURL`) to every test in the file, and tears down Harper afterward.

### Running the tests

Before running tests for the first time (and after updating fixture dependencies), install the fixture dependencies:

```sh
npm run install:fixtures
```

Run all the tests:

```sh
npm run test:integration
```

Run a specific test file:

```sh
npm run test:integration -- integrationTests/next-15.pw.ts
```

### Test parameters

Each test callback receives some combination of these parameters:

- **`harper`** — a [`HarperContext`](https://github.com/harperfast/integration-testing#types) from `@harperfast/integration-testing`
- **`page`** — Playwright's [`Page`](https://playwright.dev/docs/api/class-page) for navigating and asserting against the rendered UI
- **`request`** — Playwright's [`APIRequestContext`](https://playwright.dev/docs/api/class-apirequestcontext) for raw HTTP calls without a browser (status codes, response bodies, headers, etc.)

### Adding a new test file

1. Create a fixture app in `fixtures/<name>/` with the plugin configured.
2. Create `integrationTests/<name>.pw.ts`:

```ts
import { fixture } from './fixture.ts';

const { test, expect } = fixture('<name>');

// Browser-based assertion
test('home page renders', async ({ page, harper }) => {
await page.goto(harper.httpURL);
await expect(page.locator('h1')).toHaveText('Expected');
});

// Raw HTTP assertion (no browser)
test('health endpoint returns 200', async ({ request, harper }) => {
const response = await request.get(`${harper.operationsAPIURL}/health`);
expect(response.status()).toBe(200);
});
```

The `fixture()` call binds the test file to its Harper fixture and handles startup and teardown automatically.

### Future Work: Page Object Models

As the test suite grows, repeated patterns of locator queries and multi-step interactions should be extracted into [Page Object Models](https://playwright.dev/docs/pom). A POM is a class that wraps `page` and encapsulates the selectors and actions for a specific page or feature, keeping test files focused on assertions rather than DOM mechanics.

Instead, for now, fixtures can be tested by running them locally. Make sure to have HarperDB installed globally on your machine. Generally, to run the tests within a fixture, you only have to run `npm install` and `npm run test` from within the specific fixture directory.
For example, testing ISR cache revalidation involves navigating to a page, reading some state, triggering revalidation, and waiting for new content — logic that would benefit from a `CachedPage` POM rather than being inlined across multiple tests. As fixture apps grow more complex and tests start sharing the same navigation and interaction patterns, that's the signal to introduce POMs.
Loading