Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
82 changes: 82 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: E2E

on:
push:
branches: [main]
paths-ignore:
- "apps/website/**"
- "docs/**"
- "plans/**"
- "**/*.md"
pull_request:
branches: [main]
paths-ignore:
- "apps/website/**"
- "docs/**"
- "plans/**"
- "**/*.md"
workflow_dispatch:

permissions:
contents: read

# Not test.yml's test-db-<ref> group: GitHub keeps one pending run per group,
# so a queued e2e run would cancel a pending Test run, and deploy-api.yml
# deploys only when Test completes. e2e only inserts uniquely named rows
# (a fresh owner and org per run), so it can't delete a fixture test-web reads.
concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
e2e:
# Forks get no secrets; the setup test needs the database and the model key.
if: github.repository == 'wraps-team/wraps'
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4

- name: Setup pnpm
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0

- name: Setup Node.js 24.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24.x
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build web workspace dependencies
run: pnpm --filter "@wraps/web^..." build

- name: Install Chromium
run: pnpm --filter @wraps/e2e exec e2e-web install chromium --with-deps

# Replays the committed recordings in apps/e2e/.e2e/cache (read-only in
# CI). A step whose recording no longer matches the UI falls back to the
# live agent, which is what the model key is for.
- name: Run e2e
run: pnpm test:e2e --reporter list,junit
env:
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
BETTER_AUTH_SECRET: test-secret-key-for-testing-only
AI_GATEWAY_API_KEY: ${{ secrets.AI_GATEWAY_API_KEY }}

# Not .e2e/logs: the app log is written unredacted, and artifacts on a
# public repo are downloadable by anyone signed in.
- name: Upload report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: e2e-report
path: |
apps/e2e/.e2e/report.json
apps/e2e/.e2e/junit.xml
apps/e2e/.e2e/results
if-no-files-found: warn
retention-days: 7
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
{
"schemaVersion": "trace-1",
"createdAt": "2026-10-26T22:23:49.470Z",
"payload": {
"actions": [
{
"name": "tap",
"summary": "tap button \"Add contact\"",
"target": {
"role": "button",
"name": "Add contact"
}
},
{
"name": "type",
"summary": "type \"ada@e2e.wraps.test\" into textbox \"Email address\"",
"target": {
"role": "textbox",
"name": "Email address",
"placeholder": "contact@example.com"
},
"value": "ada@e2e.wraps.test"
},
{
"name": "type",
"summary": "type \"Ada\" into textbox \"First name\"",
"target": {
"role": "textbox",
"name": "First name",
"placeholder": "John"
},
"value": "Ada"
},
{
"name": "tap",
"summary": "tap button \"Add Contact\"",
"target": {
"role": "button",
"name": "Add Contact"
}
}
],
"executor": {
"name": "e2e-default-agent",
"version": "2"
},
"recordedFor": {
"testId": "tests/contacts.e2e.ts::the%20owner%20adds%20a%20contact%20by%20hand",
"targetId": "chromium",
"instructionDigest": "d3c053047e2ff27bc94b503e153507dc99fa0f21807a3dc6a70f39e6f259d3e6",
"paramsDigest": "5679c80a86c6abe433c50bd869592ed746923f8d80af15173107576b023b6c4f",
"callIndex": 0,
"agent": "default"
},
"recordedBy": {
"provider": "gateway",
"model": "anthropic/claude-sonnet-5.5"
},
"summary": "In e2e-mvptoww7 contacts, added a contact with email ada@e2e.wraps.test and first name Ada. A \"Contact created\" toast appeared. The contacts list now shows ada@e2e.wraps.test as Active, \"Showing 1-1 of 1 contacts\".",
"startPath": "/{{param:/workspace}}/contacts",
"endPath": "/{{param:/workspace}}/contacts",
"endAnchors": [
{
"role": "heading",
"name": "Contact Growth"
},
{
"text": "New contacts added over time"
},
{
"role": "button",
"name": "Refresh contact growth"
},
{
"text": "+1"
},
{
"text": "+100%"
},
{
"role": "button",
"name": "What these figures cover"
},
{
"text": "Whole organization, not the filtered list below"
},
{
"text": "Engagement"
}
],
"goneAnchors": [
{
"role": "heading",
"name": "No contacts yet"
},
{
"text": "Add contacts manually, import a CSV, or use the Platform SDK to manage contacts programmatically."
},
{
"text": "create-contact.ts"
},
{
"text": "import { createClient } from '@wraps.dev/platform'; const client = createClient({ organizationId: 'org_...', apiKey: process.env.WRAPS_API_KEY, }); const { data } = await client.POST('/v1/contacts/', { body: { email: 'user@example.com', firstName: 'Jane', lastName: 'Doe', }, });"
},
{
"role": "link",
"name": "Read the docs"
},
{
"role": "button",
"name": "Import CSV"
},
{
"role": "button",
"name": "Add contact"
}
],
"endWaitMs": 14472,
"keyedBy": {
"cacheSchema": "trace-1",
"policyVersion": "conservative/6",
"project": "6270d378e32b1181044da35433df046bd2ea7bbc8b700e3a614fe441981c5456",
"platform": "web",
"engineName": "web",
"engineSpiVersion": 1,
"appIdentity": "f9f49ed79c2662c7f6e0522da33af26cd0588919fce8b674530ba7c83e5d217c",
"agentContextDigest": "d436824420520e934fb52613db21890f6152750d6c044528fbf9cb9ed5b0ab6d"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{
"schemaVersion": "trace-1",
"createdAt": "2026-10-26T22:23:38.093Z",
"payload": {
"actions": [
{
"name": "tap",
"summary": "tap button \"Create API Key\" (1 of 2)",
"target": {
"role": "button",
"name": "Create API Key",
"position": {
"index": 0,
"of": 2
}
}
},
{
"name": "type",
"summary": "type \"CI deploys\" into textbox \"Name\" in \"Create API Key\"",
"target": {
"role": "textbox",
"name": "Name",
"placeholder": "Production API Key",
"within": "Create API Key"
},
"value": "CI deploys"
},
{
"name": "tap",
"summary": "tap button \"Create API Key\"",
"target": {
"role": "button",
"name": "Create API Key"
}
}
],
"executor": {
"name": "e2e-default-agent",
"version": "2"
},
"recordedFor": {
"testId": "tests/api-keys.e2e.ts::the%20owner%20creates%20an%20API%20key",
"targetId": "chromium",
"instructionDigest": "9327f197ffea6ccc67a6e73c2df3106fa154431622ca38770e0f14f444f6f3cf",
"paramsDigest": "cd767e925690ff4596425f8639c9b2c6994503d8e49e70972502a0c2ec2bd1a7",
"callIndex": 0,
"agent": "default"
},
"recordedBy": {
"provider": "gateway",
"model": "anthropic/claude-sonnet-5.5"
},
"summary": "Created API key \"CI deploys\" in e2e-mvptoww7; \"API key created\" toast and \"API Key Created\" dialog (showing the key once) appeared.",
"startPath": "/{{param:/workspace}}/settings/api-keys",
"endPath": "/{{param:/workspace}}/settings/api-keys",
"endAnchors": [
{
"role": "alert",
"name": "Important This is the only time you'll see this API key. Make sure to copy it and store it securely."
},
{
"text": "API key created"
},
{
"role": "heading",
"name": "API Key Created"
},
{
"text": "Copy your API key now. You won't be able to see it again!"
},
{
"text": "Important"
},
{
"text": "This is the only time you'll see this API key. Make sure to copy it and store it securely."
},
{
"text": "Your API Key"
},
{
"role": "button",
"name": "Toggle visibility"
}
],
"goneAnchors": [
{
"role": "status",
"name": "Connect your AWS account to start sending emails. Get Started Dismiss banner"
},
{
"role": "image",
"name": "Wraps Logo"
},
{
"text": "EW"
},
{
"text": "E2E Workspace"
},
{
"text": "owner"
},
{
"text": "Overview"
},
{
"text": "Audience"
},
{
"text": "Email"
}
],
"endWaitMs": 12560,
"keyedBy": {
"cacheSchema": "trace-1",
"policyVersion": "conservative/6",
"project": "6270d378e32b1181044da35433df046bd2ea7bbc8b700e3a614fe441981c5456",
"platform": "web",
"engineName": "web",
"engineSpiVersion": 1,
"appIdentity": "f9f49ed79c2662c7f6e0522da33af26cd0588919fce8b674530ba7c83e5d217c",
"agentContextDigest": "d436824420520e934fb52613db21890f6152750d6c044528fbf9cb9ed5b0ab6d"
}
}
}
Loading
Loading