Identity
GitHub
Sign in with GitHub, GitHub Apps and their installations, the REST API for repositories, issues, pull requests and checks, and signed webhooks.
danwo's GitHub emulator answers github.com for sign-in and api.github.com for the REST API.
Octokit works against it unchanged, with an OAuth token, a personal access token or a GitHub
App's installation token.
Set up
Add githubService with the repositories your app works on and a token for it:
import { appEnvironment, defineConfig, githubService } from "@danwo/sdk"
const token = "ghp_danwo"
export default defineConfig({
testDir: "tests",
readiness: { mode: "page+backend", settle: true },
environment: () =>
appEnvironment({
app: { command: "npm run dev", url: "http://localhost:3000", env: { GITHUB_TOKEN: token } },
services: [
githubService({
seed: {
users: [{ login: "ada", name: "Ada Lovelace" }],
orgs: [{ login: "acme", members: ["ada"] }],
repos: [{ repo: "acme/app", files: { "README.md": "# App" } }],
tokens: [{ token, login: "ada", scopes: ["repo"] }],
},
}),
],
}),
})Seed it
| Field | What it seeds |
|---|---|
users, orgs | Accounts by login, with names, emails and members. |
repos | Repositories by owner/name, with files on the default branch, topics and privacy. |
issues | Issues already open. |
tokens | Personal access tokens, with their scopes. |
oauthApps | OAuth Apps, with their callback URLs. |
apps | GitHub Apps: slug, owner, private key, webhook URL and secret, permissions and events. |
installations | Where each app is installed, and on which repositories. |
hooks | Repository webhooks. |
Use it in a test
Report a bug in the app, then check the issue it opened:
import { type GithubFixtures, githubRepo, test as base } from "@danwo/sdk"
const test = base.extend<GithubFixtures>()
test("opens an issue for a bug report", async ({ app, screen, expect, github }) => {
await app.open("/feedback")
await screen.getByLabel("What went wrong?").fill("The export button does nothing")
await screen.getByRole("button", { name: "Report bug" }).click()
await expect(screen.getByText("Thanks, we're on it")).toBeVisible()
const [issue] = githubRepo(github, "acme/app").issues()
expect(issue?.title).toBe("The export button does nothing")
expect(issue?.labels).toEqual(["bug"])
})| Helper | What it's for |
|---|---|
githubRepo(github, "owner/name") | A repository's issues, comments, check runs, files and commits. |
installGithubApp, uninstallGithubApp | Install or remove an app as a person would, with its webhook. |
githubAppKey(github, slug) | An app's private key, for signing its JWT. |
githubSignIns(github) | Who signed in. |
What's covered
| Area | Covered |
|---|---|
| Sign-in | OAuth Apps and GitHub Apps, with expiring user tokens. |
| Apps | Installations and installation access tokens. |
| Repositories | Repos, branches, commits, file contents and refs. |
| Issues | Issues, comments and labels. |
| Pull requests | Files, commits, reviews and merging. |
| Checks | Check runs and their annotations. |
| Webhooks | Repository hooks, pings and App webhooks. |
| Users | The signed-in user, their emails, and public profiles. |
Every call checks the token's scopes or the app's permissions and answers with GitHub's error shapes.
Faults
| Call | Faults the matrix tries |
|---|---|
A write, such as POST …/issues/:number/comments | 403, 429, 502, 503, a 10-second delay, connection reset, answer lost after GitHub acted, 422 |
| A read | 403, 429, 502, 503, a 10-second delay, connection reset |
| A contents write or a merge | adds a 409, as when the branch moved |
POST github.com/login/oauth/access_token | a 200 with bad_verification_code, 502, a delay, connection reset |
| Every webhook delivery | arrives twice, arrives 10 minutes late, forged signature, 1 hour stale |
The 403 and 429 are GitHub's secondary rate limit, with Retry-After: 60.
Webhooks
Deliveries carry X-GitHub-Event and X-Hub-Signature-256, made with the webhook's secret, so
@octokit/webhooks verifies them.
Fidelity
Public endpoints only. danwo fidelity github reads a public repository and an unknown one
from GitHub's API. Both match.
Not covered yet
- GraphQL
- Actions, releases, packages and deployments
- Git over HTTPS: clone and push