From 7e2b15aeeb6d27481bb690a71f0db732037a51a7 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 28 Oct 2026 11:14:29 -0700 Subject: [PATCH 1/7] Log team invite activity after responding Co-Authored-By: Claude Opus 5.5 --- app/(login)/actions.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/(login)/actions.ts b/app/(login)/actions.ts index f22b1cf..738abb4 100644 --- a/app/(login)/actions.ts +++ b/app/(login)/actions.ts @@ -3,6 +3,7 @@ import { and, eq } from "drizzle-orm"; import { cookies } from "next/headers"; import { redirect } from "next/navigation"; +import { after } from "next/server"; import { z } from "zod"; import { logActivity } from "@/lib/auth/activity"; import { signup } from "@/lib/auth/login"; @@ -365,11 +366,9 @@ export const inviteTeamMember = validatedActionWithUser( }) .returning(); - await logActivity( - userWithTeam.teamId, - user.id, - ActivityType.INVITE_TEAM_MEMBER, - ); + // Log the invite once the response has been sent, so inviting feels instant. + const teamId = userWithTeam.teamId; + after(() => logActivity(teamId, user.id, ActivityType.INVITE_TEAM_MEMBER)); // TODO: Send invitation email and include ?inviteId={id} to sign-up URL // await sendInvitationEmail(email, userWithTeam.team.name, role) From cfbda22736594bcb902cb13fa72e05e2f99a7593 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 28 Oct 2026 11:27:32 -0700 Subject: [PATCH 2/7] Run the demo branch against the Netlify preview in the demo org Co-Authored-By: Claude Opus 5.5 --- endform.config.ts | 5 +++++ playwright.config.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 endform.config.ts diff --git a/endform.config.ts b/endform.config.ts new file mode 100644 index 0000000..d087c33 --- /dev/null +++ b/endform.config.ts @@ -0,0 +1,5 @@ +import { defineEndformConfig } from "endform"; + +export default defineEndformConfig({ + organizationId: "68prmZ74", +}); diff --git a/playwright.config.ts b/playwright.config.ts index 4b54bbc..7392763 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -10,7 +10,8 @@ import type { PlaywrightOpentelemetryUseOptions } from "playwright-opentelemetry // dotenv.config({ path: path.resolve(__dirname, '.env') }); export const baseURL = - process.env.BASE_URL || "https://endform-playwright-tutorial.vercel.app"; + process.env.BASE_URL || + "https://deploy-preview-10--endform-playwright-tutorial.netlify.app"; const playwrightOtelEnabled = Boolean( process.env.PLAYWRIGHT_TRACE_API_ENDPOINT || From e6f35b626f2326ddae1d01456ee2418a023f3f14 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 28 Oct 2026 11:30:06 -0700 Subject: [PATCH 3/7] Send the invitation email before logging the invite Co-Authored-By: Claude Opus 5.5 --- app/(login)/actions.ts | 12 +++++++----- lib/email.ts | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 lib/email.ts diff --git a/app/(login)/actions.ts b/app/(login)/actions.ts index 738abb4..41f9906 100644 --- a/app/(login)/actions.ts +++ b/app/(login)/actions.ts @@ -23,6 +23,7 @@ import { type User, users, } from "@/lib/db/schema"; +import { sendInvitationEmail } from "@/lib/email"; import { isFaultActive } from "@/lib/faults"; const signInSchema = z.object({ @@ -366,12 +367,13 @@ export const inviteTeamMember = validatedActionWithUser( }) .returning(); - // Log the invite once the response has been sent, so inviting feels instant. + // Email the invitee and log the invite once the response has been sent, + // so inviting feels instant. const teamId = userWithTeam.teamId; - after(() => logActivity(teamId, user.id, ActivityType.INVITE_TEAM_MEMBER)); - - // TODO: Send invitation email and include ?inviteId={id} to sign-up URL - // await sendInvitationEmail(email, userWithTeam.team.name, role) + after(async () => { + await sendInvitationEmail(email, role); + await logActivity(teamId, user.id, ActivityType.INVITE_TEAM_MEMBER); + }); return { success: "Invitation sent successfully", id: invitation[0].id }; }, diff --git a/lib/email.ts b/lib/email.ts new file mode 100644 index 0000000..c53e241 --- /dev/null +++ b/lib/email.ts @@ -0,0 +1,14 @@ +import "server-only"; + +// Stand-in for a transactional email provider. Most sends are quick but, like +// most third-party APIs, it has a slow tail. +export async function sendInvitationEmail(email: string, role: string) { + const latencyMs = + Math.random() < 0.1 + ? 4000 + Math.random() * 2000 + : 50 + Math.random() * 100; + await new Promise((resolve) => setTimeout(resolve, latencyMs)); + console.log( + `[email] sent ${role} invitation to ${email} in ${Math.round(latencyMs)}ms`, + ); +} From 59b5038a4c99695cda0f1fc4827706b0cfb13742 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 28 Oct 2026 11:40:34 -0700 Subject: [PATCH 4/7] Lengthen the email provider's slow tail Co-Authored-By: Claude Opus 5.5 --- lib/email.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/email.ts b/lib/email.ts index c53e241..fc7dc8a 100644 --- a/lib/email.ts +++ b/lib/email.ts @@ -4,8 +4,8 @@ import "server-only"; // most third-party APIs, it has a slow tail. export async function sendInvitationEmail(email: string, role: string) { const latencyMs = - Math.random() < 0.1 - ? 4000 + Math.random() * 2000 + Math.random() < 0.15 + ? 6000 + Math.random() * 3000 : 50 + Math.random() * 100; await new Promise((resolve) => setTimeout(resolve, latencyMs)); console.log( From 3cfea80d4656cbc40e5488436bc38cf658ad2379 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 28 Oct 2026 11:46:42 -0700 Subject: [PATCH 5/7] Enable all traces during demo. --- playwright.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index 7392763..80733d8 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -48,7 +48,8 @@ export default defineConfig({ // baseURL: "http://localhost:3000", /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: playwrightOtelEnabled ? "on" : "retain-on-failure", + // trace: playwrightOtelEnabled ? "on" : "retain-on-failure", + trace: "on", playwrightOpentelemetry: playwrightOtelEnabled ? { trace: "on", From 97e4705950a2fe2e083d4b877f1d69047f171279 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 28 Oct 2026 12:05:31 -0700 Subject: [PATCH 6/7] Add demo fallback script --- DEMO.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 DEMO.md diff --git a/DEMO.md b/DEMO.md new file mode 100644 index 0000000..4de07e0 --- /dev/null +++ b/DEMO.md @@ -0,0 +1,36 @@ +Prompt 1: + +You're testing a web app by hand, using your browser tool. Don't write any code. + +App: https://deploy-preview-10--endform-playwright-tutorial.netlify.app + +1. Go to `/sign-up` and create an account with a made-up email like `demo-@example.com` and the password `testpassword123`. +2. On the Team Settings page (`/dashboard`), invite a teammate using another made-up email, with the Member role, and wait for "Invitation sent successfully". +3. Open the Activity page from the sidebar. +4. Check that the newest (top) entry says "You invited a team member". + +Report PASS or FAIL. Describe exactly what you saw at each step, including the full list of entries on the Activity page. + +Prompt 2: + +In this repo (branch `demo/invite-activity-after-response`), write a new Playwright test at `tests/team-invite-activity.live.spec.ts`. Explore the app first, using existing tests and functionality through an endform live test. + +- Start a session inside an existing test that already has a logged-in new user: `tests/duplicate-invite.spec.ts` or similar using endform live test. +- Step through the scenario one action at a time. To expore and figure out which playwright code makes sense for the test. + - invite a teammate from the Team Settings page (`/dashboard`); + - wait for "Invitation sent successfully"; + - open the Activity page from the sidebar; + - check that the newest entry is "You invited a team member". +- Once every step has worked live, stop the session and create a nice new playwright test for this flow. +- Follow the suite's conventions: the `testWithNewUser` fixture from `tests/test-with-new-user.ts`, `test.step` blocks, role-based locators, and a unique invite email per run. +- Finish by running it once with `npx endform test team-invite-activity.live`. + +Next commands: + +p playwright test team-invite-activity --debug --no-deps + +p endform test team-invite-activity --repeat-each=100 --no-deps + +Then ask: + +look at `team-invite-activity` playwright/endform runs from the last hour. What's the pass rate, and what's the most common failure? Is it a bug in the app or noise in the test? From 300950c9150153095d31f2580627529bd91d5813 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Wed, 28 Oct 2026 12:11:18 -0700 Subject: [PATCH 7/7] Prepare trace comment. --- playwright.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/playwright.config.ts b/playwright.config.ts index 80733d8..5b1f26f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -50,6 +50,7 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ // trace: playwrightOtelEnabled ? "on" : "retain-on-failure", trace: "on", + // trace: "retain-on-failure", playwrightOpentelemetry: playwrightOtelEnabled ? { trace: "on",