dánwòdánwò

Identity

Gmail, Calendar, Drive

Gmail, Google Calendar and Google Drive for a signed-in user, with push notifications and history that a test can expire.

The Google emulator keeps a mailbox, a calendar and a drive for every seeded account. Your app reaches them with the token it got at sign-in, through googleapis or plain REST, and your test reads and changes them as if from elsewhere.

Set up

Seed each account's data by email, next to the OAuth client:

danwo.config.ts
import { appEnvironment, defineConfig, googleService } from "@danwo/sdk"

const url = "http://localhost:3000"
const ada = "[email protected]"

export default defineConfig({
  testDir: "tests",
  readiness: { mode: "page+backend", settle: true },
  environment: () =>
    appEnvironment({
      app: { command: "npm run dev", url },
      services: [
        googleService({
          seed: {
            oauth_clients: [
              {
                client_id: "danwo-client.apps.googleusercontent.com",
                client_secret: "danwo-client-secret",
                name: "My app",
                redirect_uris: [`${url}/auth/google/callback`],
              },
            ],
            users: [{ email: ada, name: "Ada Lovelace" }],
            gmail: {
              [ada]: [
                { from: "[email protected]", to: ada, subject: "Invoice 42", text: "Due Friday" },
              ],
            },
            calendar: {
              [ada]: [
                {
                  summary: "Design review",
                  start: { dateTime: "2026-11-02T10:00:00Z" },
                  end: { dateTime: "2026-11-02T11:00:00Z" },
                },
              ],
            },
            drive: { [ada]: [{ name: "notes.txt", content: "First draft" }] },
            pubsub: [
              {
                subscription: "projects/my-app/subscriptions/gmail",
                topic: "projects/my-app/topics/gmail",
                endpoint: `${url}/api/google/push`,
              },
            ],
          },
        }),
      ],
    }),
})

Seed it

FieldWhat it seeds
gmailMessages already in each mailbox, with labels and dates.
calendarEvents on each primary calendar, in the Calendar API's own event shape.
driveFiles in each drive, with content and folders.
pubsubPush subscriptions: what Gmail publishes to a topic after users.watch is POSTed to the endpoint.

Use it in a test

Deliver a new email, as if someone wrote in, and check your app filed it:

tests/gmail-sync.danwo.ts
import { type GoogleFixtures, gmailMailbox, test as base } from "@danwo/sdk"

const test = base.extend<GoogleFixtures>()

test("files a new invoice email", async ({ app, screen, expect, settle, google }) => {
  const mailbox = gmailMailbox(google, "[email protected]")

  mailbox.deliver({ from: "[email protected]", to: "[email protected]", subject: "Invoice 43" })
  await settle()

  await app.open("/invoices")
  await expect(screen.getByText("Invoice 43")).toBeVisible()
  expect(mailbox.sent()).toHaveLength(0)
})

deliver sends the push notification too, so an app that watches the mailbox hears of it.

HelperReadsChanges
gmailMailbox(google, email)messages(label), inbox(), sent()deliver(mail), expireHistory()
googleCalendar(google, email)events()add(event), cancel(id), expireSyncTokens()
googleDrive(google, email)files(), read(id)add(file), remove(id)

The two expire helpers make the next incremental sync fail, as Google does after a while, so you can test that your app falls back to a full sync. gmailMail(google) gives the mail to the mail fixture.

What's covered

AreaCovered
GmailMessages with search, sending, labels, threads, drafts, attachments, history, and push with watch.
CalendarCalendars, events with recurrence, quick add, free/busy, sync tokens and watch.
DriveFiles with queries, uploads, copies, permissions, changes and watch.

Faults

CallFaults the matrix tries
A Gmail send, such as POST …/messages/send429, 403, 500, 503, a 10-second delay, connection reset, answer lost after Gmail sent it
A read, such as GET …/history or Calendar's events429, 403, 500, 503, a 10-second delay, connection reset, and the expiry answer: 404 for Gmail history, 410 for a Calendar sync token
Every push notificationarrives twice, arrives 10 minutes late, forged, 1 hour stale

Webhooks

Gmail pushes go to the Pub/Sub subscription's endpoint, in Pub/Sub's push format, and can be signed with an OIDC token for a service account, as an authenticated subscription is. Calendar and Drive watch channels POST to their address with Google's X-Goog-* headers.

Fidelity

Not checked live. These calls need a signed-in Google account, so only the sign-in endpoints are compared; see Google.

Not covered yet

  • Sheets, Docs and Slides
  • Shared drives
  • Gmail settings, filters and delegates

Next

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI