dánwòdánwò

Realtime

Novu

Novu's server API, workflows run through your app's bridge or from seeded templates, the Inbox API and socket the browser uses, and emails sent through the Resend emulator.

danwo's Novu emulator answers Novu's API and the Inbox's socket, US and EU. Your server triggers workflows with @novu/api, code-first workflows run through your app's bridge, and the browser's @novu/react Inbox shows each message live.

Set up

Add novuService with your API key and, for code-first workflows, the bridge URL. To send the workflows' emails somewhere a test can read them, give it a Resend service:

danwo.config.ts
import { appEnvironment, defineConfig, NOVU_DEFAULTS, novuService, resendService } from "@danwo/sdk"

const url = "http://localhost:3000"

export default defineConfig({
  testDir: "tests",
  readiness: { mode: "page+backend", settle: true },
  environment: () => {
    const resend = resendService()

    return appEnvironment({
      app: {
        command: "npm run dev",
        url,
        env: {
          NOVU_SECRET_KEY: NOVU_DEFAULTS.apiKey,
          NEXT_PUBLIC_NOVU_APPLICATION_IDENTIFIER: NOVU_DEFAULTS.applicationIdentifier,
        },
      },
      services: [
        resend,
        novuService({
          email: resend,
          seed: {
            bridgeUrl: `${url}/api/novu`,
            subscribers: [{ subscriberId: "ada", email: "ada@example.com" }],
          },
        }),
      ],
    })
  },
})

With no seed, the API key and application identifier are NOVU_DEFAULTS'.

Seed it

FieldWhat it seeds
apiKey, applicationIdentifierThe environment's keys.
bridgeUrlYour app's @novu/framework endpoint: workflows are discovered and run through it.
hmacRequire the Inbox's subscriber hash, as with HMAC encryption turned on.
subscribersSubscribers with names, email, phone and data.
workflowsDashboard-style workflows, with in-app, email, SMS, push, chat, delay and digest steps.

Use it in a test

Trigger a notification from the app, then check the Inbox showed it and the email went out:

tests/notify.danwo.ts
import { type NovuFixtures, novuProject, type ResendFixtures, test as base } from "@danwo/sdk"

const test = base.extend<NovuFixtures & ResendFixtures>()

test("tells Ada her order shipped", async ({ app, screen, expect, settle, novu }) => {
  await app.open("/orders/o_1")
  await screen.getByRole("button", { name: "Mark shipped" }).click()
  await settle()

  await expect(screen.getByText("Order o_1 is on its way")).toBeVisible()

  const message = await novuProject(novu).waitForMessage("ada", { channel: "in_app" })

  expect(message.content).toBe("Order o_1 is on its way")
  expect(novuProject(novu).messages({ channel: "email", subscriberId: "ada" })).toHaveLength(1)
})
novuProject(novu)What it's for
subscribers(), executions()Subscribers, and workflow runs.
messages(filter), inbox(id)Every step's message, and a subscriber's Inbox.
waitForMessage(id)Wait for a subscriber's next message.
openInboxes()The Inbox sockets open now.
idle()Wait until every run has finished; settle does too.

What's covered

AreaCovered
EventsTrigger, bulk, broadcast and cancel.
SubscribersSubscribers, their credentials and notifications.
TopicsTopics and their subscriptions.
WorkflowsCode-first through your bridge, or seeded; delays and digests run straight through.
InboxSessions, notifications, counts, read and seen, archive, preferences, the socket.
EmailEmail steps sent through a Resend service, so they land in its inbox.

Faults

CallFaults the matrix tries
POST /v1/events/trigger429, 500, connection reset, answer lost after Novu started the run
A read500, connection reset
The Inbox socketthe upgrade refused, and the connection dropped for one person

Webhooks

Novu sends your app no webhooks here. Its calls to your bridge are signed with novu-signature, an HMAC of the timestamp and body under the API key, so @novu/framework accepts them.

Fidelity

Live. danwo fidelity novu runs 2 scenarios against a real environment: an unknown key, and creating, reading, patching and deleting a subscriber. Both match.

Not covered yet

  • Delays and digests that wait: they run straight through
  • SMS, push and chat providers: those steps are recorded, not sent anywhere
  • Layouts, translations and the dashboard's workflow editor

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