The scene call
What the SDK function does, every option it takes, and what a capture records.
One line in a Playwright test marks a scene. This page says what that line does, what it records, and what it deliberately does not do.
The call
import { test } from "@playwright/test";
import { midstreamScene } from "@midstream/sdk/playwright";
test("a customer can review their cart", async ({ page }) => {
await page.goto("/products/kettle");
await page.getByRole("button", { name: "Add to cart" }).click();
await page.getByRole("link", { name: "Cart" }).click();
await midstreamScene(page, "cart-one-item", {
name: "Cart with one item",
folderPath: ["checkout"],
});
});The function keeps its old name for now; it marks a scene.
The signature is:
midstreamScene(page: Page, slug: string, options?): Promise<void>Always await it. In CI it makes a network call, and an un-awaited promise can
outlive the test.
There is also a namespaced form, if you prefer one import:
import { midstream } from "@midstream/sdk/playwright";
await midstream.scene(page, "cart-one-item");midstream.parameter() is the same alias for
midstreamParameter().
page is the Playwright Page from your test. slug is the scene's identity —
choosing it well matters more than anything else
on this page.
Options
Every option is optional.
| Option | Type | Default |
|---|---|---|
name | string | The slug in title case: cart-one-item becomes "Cart One Item". Set it to get a name a non-developer would recognize. |
folderPath | string[] | The root. One segment per level, e.g. ["checkout", "payment"]. A segment cannot contain /, and cannot be . or ... |
project | string | The MIDSTREAM_PROJECT environment variable. If that is unset too, we resolve the project from the repository your CI credentials belong to, which is the usual case — one repository is one project. |
workspace | string | The MIDSTREAM_WORKSPACE environment variable. Only needed to disambiguate: a project slug is unique inside a workspace, so if your credential reaches two workspaces that both have a project by that slug, we ask which one you mean rather than guess. |
gitCommit | string | GIT_COMMIT if you set it. Otherwise the commit CI reports: on GitHub Actions the pull request's head on a pull-request run, GITHUB_SHA on a push. |
capturedOnBranch | string | The source branch your CI reports: GITHUB_HEAD_REF on a pull-request run, GITHUB_REF_NAME on a push. |
capturedOnBranch is a label, not a pointer. It scopes the project view so you
can look at the scenes as captured on one branch. Branches get force-pushed and
deleted, so it records where we last saw the scene, nothing more.
What a capture records
When the test runs in CI, the call registers a capture — the scene as it stands at one commit. It records:
- An accessibility snapshot of the page, the same tree Playwright's
ariaSnapshot()produces. We use it to tell whether the scene actually changed between two commits. - A screenshot, PNG, of the viewport as your Playwright project configures it. Not the full scrollable page.
- The test file path and the chain of
describetitles ending in the test name. That is how a deploy later finds the right test to run. - The commit, and the branch label above.
- The folder path, which is created on first use.
- Any parameters declared earlier in the same test. See Parameters.
It does not record cookies, localStorage, sessionStorage, or
IndexedDB. A capture in CI is a record that the moment exists and what it
looked like. The session
itself is captured later, when someone opens an instance: we run the same test
again inside the deploy and take the browser state at the scene. Nothing about
your CI runner's session is stored.
Identity, and what happens when you push again
A capture is identified by the project, the slug, and the commit. So:
- A new commit produces a new capture. That is the history behind a scene, and what makes before-and-after comparison possible.
- The same slug at the same commit updates the existing capture. Re-running a CI job does not create duplicates.
That last rule has one consequence worth knowing. If your suite runs the same test in several browser projects, every one of them registers the same scene at the same commit, and the last to finish is the one whose screenshot you see. Nothing breaks; you just get one capture, not one per browser.
It never fails your test
The call swallows its own errors. If anything inside it goes wrong it prints a
warning starting with [Midstream] and returns, and your test carries on as if
the line were not there.
That holds all the way down:
- The accessibility snapshot fails: the capture is stored without one.
- The screenshot fails: the capture is stored without a preview image.
- The registration call fails, or there is no commit to attribute it to: nothing is registered, and you get a warning.
This is deliberate. Your test suite is yours, and our instrumentation must never
be the reason a build goes red. The trade is that a silent failure is quiet — if
a scene never appears in the dashboard, read the CI log for [Midstream].
More than one scene in a test
You can mark as many scenes in one test as there are moments worth reviewing. Each is a separate scene with its own history, and in CI each one registers.
A deploy is different: it captures at exactly one scene, the one the reviewer opened. The other calls in that test run through as no-ops. That, and the rest of the mode-by-mode behavior, is in What the SDK does in each environment.