dánwòdánwò

Email and messaging

Slack

Slack app installs through OAuth, the Web API with its scope checks, incoming webhooks, and signed events, slash commands and interactions sent to your app.

danwo's Slack emulator answers Slack's API, its incoming webhooks and file uploads. Your app installs through OAuth, calls the Web API with @slack/web-api or Bolt, and receives events, slash commands and button clicks signed with its signing secret.

Set up

Add slackService with your app as its configuration page has it, and install it in advance so the test needn't go through the consent page. Hand the app its token and signing secret:

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

const url = "http://localhost:3000"
const signingSecret = "danwo-signing-secret"

export default defineConfig({
  testDir: "tests",
  readiness: { mode: "page+backend", settle: true },
  environment: () =>
    appEnvironment({
      app: {
        command: "npm run dev",
        url,
        env: { SLACK_BOT_TOKEN: "xoxb-danwo", SLACK_SIGNING_SECRET: signingSecret },
      },
      services: [
        slackService({
          seed: {
            users: [{ name: "ada", email: "ada@example.com" }],
            channels: [{ name: "deploys", members: ["ada"] }],
            apps: [
              {
                name: "Deploy bot",
                client_id: "1.1",
                client_secret: "danwo-client-secret",
                signing_secret: signingSecret,
                bot_scopes: ["chat:write", "commands"],
                events: { url: `${url}/api/slack/events`, bot_events: ["app_mention"] },
                commands: [{ command: "/deploy", url: `${url}/api/slack/commands` }],
              },
            ],
            installations: [{ app: "Deploy bot", bot_token: "xoxb-danwo", channels: ["deploys"] }],
          },
        }),
      ],
    }),
})

Seed it

FieldWhat it seeds
teamThe workspace's name, id and domain.
usersMembers by handle, with email, real name and admin.
channelsChannels, private or public, with members; #general always exists.
appsYour app: credentials, scopes, event subscriptions, slash commands, interactivity URL, token rotation.
installationsApps already installed, with fixed bot and user tokens and the channels the bot is in.

Use it in a test

Run a slash command as a member, then read what the bot posted:

tests/deploy.danwo.ts
import { type SlackFixtures, slackWorkspace, test as base } from "@danwo/sdk"

const test = base.extend<SlackFixtures>()

test("announces a deploy in the channel", async ({ expect, settle, slack }) => {
  const workspace = slackWorkspace(slack)

  workspace.command("ada", "#deploys", "/deploy", "production")
  await settle()

  const [message] = workspace.messages("#deploys")

  expect(message?.text).toContain("Deploying production")
})
slackWorkspace(slack)What it's for
post, react, joinAct as a member in a channel.
commandRun a slash command as a member.
click, submitClick a button, or submit a modal.
messages, thread, ephemeralRead what was posted.
views, fileRead opened views and uploaded files.
verifyEventsUrl()Send Slack's challenge to your events URL.

What's covered

AreaCovered
OAuth v2The consent page, token exchange and rotation, testing and revoking tokens.
ChatPost, update, delete and schedule messages, ephemeral messages, threads, blocks.
ConversationsCreate, list, read history and replies, join, invite and open DMs.
UsersList users, read one, look one up by email.
Reactions, views, filesReactions, modals and home tabs, and external file uploads.
IncomingIncoming webhooks and response URLs.
Sent to your appEvents API callbacks, slash commands and interactive payloads.

Every method checks the token's scopes and answers { ok: false, error } as Slack does, such as missing_scope or channel_not_found.

Faults

CallFaults the matrix tries
A Web API write, such as chat.postMessage429, 500, 503, a 200 with ok: false, a 10-second delay, connection reset, answer lost after Slack acted
A Web API readthe same, without the lost answer
Every event, command and interactionarrives twice, arrives 10 minutes late, forged signature, 1 hour stale

Webhooks

What Slack sends your app carries X-Slack-Signature and X-Slack-Request-Timestamp, made with the app's signing_secret, so Bolt's request verification accepts it. A stale timestamp is one your app should refuse.

Fidelity

Public endpoints only. danwo fidelity slack checks what Slack answers without a workspace: api.test, and the errors for a missing or unknown token. Both match.

Not covered yet

  • Socket Mode
  • Enterprise Grid, Slack Connect and workflow steps
  • Search, pins, bookmarks and reminders

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