dánwòdánwò

Identity

Microsoft

Sign in with Microsoft, then Microsoft Graph's Outlook mail, calendars, OneDrive and change notifications for each seeded user.

danwo's Microsoft emulator answers login.microsoftonline.com for sign-in and tokens, and graph.microsoft.com for Outlook mail, calendars, OneDrive and subscriptions. MSAL and the Graph client work against it unchanged.

Set up

Add microsoftService with your app registration and the users who may sign in:

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

const url = "http://localhost:3000"
const app = {
  client_id: "00000000-0000-0000-0000-00000000a001",
  client_secret: "danwo-client-secret",
  name: "My app",
  redirect_uris: [`${url}/auth/microsoft/callback`],
}

export default defineConfig({
  testDir: "tests",
  readiness: { mode: "page+backend", settle: true },
  environment: () =>
    appEnvironment({
      app: {
        command: "npm run dev",
        url,
        env: { AZURE_AD_CLIENT_ID: app.client_id, AZURE_AD_CLIENT_SECRET: app.client_secret },
      },
      services: [
        microsoftService({
          seed: {
            oauth_clients: [app],
            users: [{ email: "[email protected]", name: "Ada Lovelace" }],
            mail: [
              { user: "[email protected]", from: "[email protected]", subject: "Quarterly plan" },
            ],
            events: [
              {
                user: "[email protected]",
                subject: "Planning",
                start: "2026-11-02T10:00:00",
                end: "2026-11-02T11:00:00",
              },
            ],
            files: [{ user: "[email protected]", path: "/Documents/plan.txt", content: "Draft" }],
          },
        }),
      ],
    }),
})

Seed it

FieldWhat it seeds
usersWork or school accounts by email, with names and a tenant.
oauth_clientsApp registrations, with redirect URIs and an optional single tenant.
mailMessages in a user's folders, Inbox by default.
eventsCalendar events, with time zones, attendees, all-day and free/busy.
filesOneDrive files and folders, by path.

Use it in a test

Sign in, then check the app sent mail from the user's mailbox:

tests/outlook.danwo.ts
import { type MicrosoftFixtures, outlookMailbox, test as base } from "@danwo/sdk"

const test = base.extend<MicrosoftFixtures>()

test("sends the weekly summary from Outlook", async ({ app, screen, expect, microsoft }) => {
  await app.open("/")
  await screen.getByRole("button", { name: "Sign in with Microsoft" }).click()
  await screen.getByRole("button", { name: "Continue as Ada Lovelace" }).click()
  await screen.getByRole("button", { name: "Send summary" }).click()

  await expect(screen.getByText("Summary sent")).toBeVisible()

  const sent = outlookMailbox(microsoft, "[email protected]").latest("Sent Items")

  expect(sent?.subject).toBe("Your weekly summary")
})
HelperWhat it's for
outlookMailbox(microsoft, email)Read a user's mail by folder, and deliver new mail.
outlookCalendar(microsoft, email)Read events in a range, and add one.
oneDrive(microsoft, email)List, read, write and remove files.
graphSubscriptions(microsoft)The change subscriptions apps made, live or expired.
microsoftSignIns(microsoft)Who signed in to which app.
revokeMicrosoftSessions(microsoft, email)Sign the user out everywhere, so refreshes fail.

outlookMail(microsoft) gives the mail to the mail fixture.

What's covered

AreaCovered
Sign-inThe account chooser, tokens, refresh, logout, OpenID discovery and keys.
MailMessages, folders, sending, drafts, replies and forwards, delta sync.
QueriesFilters with plain comparisons, ordering, paging and selected fields.
CalendarEvents, calendar views, recurrence, time zones and delta sync.
OneDriveItems by id or path, uploads and upload sessions, downloads, delta.
SubscriptionsChange notifications, with validation and your clientState.
UsersThe signed-in user, and revoking their sessions.

Faults

CallFaults the matrix tries
POST …/oauth2/v2.0/token500, 503, 400, a 10-second delay, connection reset, answer lost after Microsoft issued the token
A Graph write, such as POST /me/sendMail429, 503, 504, a 10-second delay, connection reset, answer lost after Graph acted
A Graph read429, 503, 504, a 10-second delay, connection reset
Every change notificationarrives twice, arrives 10 minutes late, forged, 1 hour stale

Webhooks

Change notifications go to each subscription's notificationUrl, after Graph's validation handshake, and carry the clientState the app chose, so your app can refuse a forged one.

Fidelity

Public endpoints only. danwo fidelity microsoft compares the discovery document and the signing keys with Microsoft's. Both match. Graph calls need a signed-in account, so they aren't checked live.

Not covered yet

  • Teams, SharePoint sites and Planner
  • Personal Microsoft accounts' consumer-only features
  • Batch requests ($batch) and $filter beyond plain comparisons

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