Skip to content

Commit

Permalink
Merge pull request #349 from chromaui/paid-turbosnap
Browse files Browse the repository at this point in the history
Paid TurboSnap related content
  • Loading branch information
winkerVSbecks authored Jan 30, 2024
2 parents be5874b + af4e910 commit af4ba8d
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 100 deletions.
1 change: 1 addition & 0 deletions src/components/Navigation/Navigation.astro
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Props {
| CollectionEntry<"configuration">
| CollectionEntry<"modes">
| CollectionEntry<"snapshot">
| CollectionEntry<"turbosnap">
| CollectionEntry<"collaborate">
| CollectionEntry<"plugins">
| CollectionEntry<"ci">
Expand Down
1 change: 1 addition & 0 deletions src/components/Navigation/SideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ type Item = (
| CollectionEntry<"configuration">
| CollectionEntry<"modes">
| CollectionEntry<"snapshot">
| CollectionEntry<"turbosnap">
| CollectionEntry<"collaborate">
| CollectionEntry<"plugins">
| CollectionEntry<"ci">
Expand Down
18 changes: 18 additions & 0 deletions src/content/account/billing.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ If you use our free [Publish Storybook](/docs/setup) service only you will not b

</div>

### Snapshots with TurboSnap enabled

Chromatic's default behavior is to capture new a snapshot for every story in every build.

With [TurboSnap](/docs/turbosnap) enabled, Chromatic first analyzes the Git history and the dependency graph of your project to identify stories that have no code changes within them or their dependencies. Instead of capturing new snapshots, it copies over the snapshots from existing baselines that didn't change. These duplicated snapshots are referred to as **TurboSnaps**, billed at 1/5th the cost of a regular snapshot.

For the remaining stories, Chromatic captures new snapshots as usual and bills them at the regular rate.

| Story count | Browsers | Viewports | Stories w/ changes | Snapshots | TurboSnaps | Billed snapshots |
| ----------- | -------- | --------- | ------------------ | --------- | ---------- | ---------------- |
| 50 stories | 1 | 1 | 50 | 50 | 0 | 50 |
| 50 stories | 1 | 1 | 10 | 10 | 40 | 18 |
| 50 stories | 2 | 1 | 50 | 100 | 0 | 100 |
| 50 stories | 2 | 1 | 10 | 20 | 80 | 36 |
| 50 stories | 2 | 2 | 10 | 40 | 160 | 72 |

Note: your [billing page](/docs/billing#view-current-months-usage) will include a breakdown of regular snapshots vs TurboSnaps.

### How we count snapshots

Snapshots are counted at the account level. If your account has multiple projects, we sum the number of snapshots each project uses to get your total usage. Chromatic doesn't break down billing per project.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
---
layout: "../../layouts/Layout.astro"
title: TurboSnap (beta)
title: Setup
description: Speed up tests by detecting file changes with Git
sidebar: { order: 4 }
sidebar: { order: 2 }
---

import { YouTubeCallout } from "../../components/YouTubeCallout";

# TurboSnap (beta)
# Setup TurboSnap

TurboSnap is an advanced Chromatic feature that speeds up builds for faster [UI Tests](/docs/test) and [UI Review](/docs/review) using Git and Webpack's [dependency graph](https://webpack.js.org/concepts/dependency-graph/). It identifies component files and dependencies that have changed, then intelligently snapshots only the stories associated with those changes. TurboSnap is currently in beta.

![TurboSnap tracks dependencies](../../images/turbosnap-dep-tracking.gif)

#### Prerequisites

- Chromatic CLI [5.8+](https://www.npmjs.com/package/chromatic)
- Storybook 6.2+
- Webpack (for experimental Vite support, see [vite-plugin-turbosnap](https://github.com/IanVS/vite-plugin-turbosnap))
- Stories correctly [configured](https://storybook.js.org/docs/react/configure/overview#configure-story-loading) in Storybook's `main.js`
- 10 successful builds on CI with at least one accepted
- For GitHub Actions: run on `push` rather than `pull_request` ([learn more](#github-pullrequest-triggers))

## Enable

Run Chromatic's CLI with the `--only-changed` option to enable TurboSnap. Alternatively, you can use the `onlyChanged` option for the Chromatic [GitHub action](/docs/github-actions#enable-turbosnap).
Enable TurboSnap by running Chromatic's CLI with the `--only-changed` option. Alternatively, you can use the `onlyChanged` option for the Chromatic [GitHub action](/docs/github-actions#enable-turbosnap).

It will build and test stories that may have been affected by the Git changes since the last build. Depending on your project setup, you may need [additional configuration](#configure).

Expand All @@ -38,35 +23,14 @@ It will build and test stories that may have been affected by the Git changes si
after ten successful builds on CI, at least one of which is accepted.
</div>

### How it works

1. Chromatic considers the Git changes between the current commit and the commit of the [ancestor build](/docs/branching-and-baselines#find-the-ancestor-builds).
2. Chromatic then uses Webpack's dependency graph to track those changes back up to the story files that depend on them.
3. Chromatic only tests the stories defined in those story files, as well as any tests that were denied on the parent build.

Stories that have not changed will not be tested (i.e., snapshotted), despite appearing in Chromatic's UI as if they were. In many cases, this will lead to much-decreased snapshot usage and faster build times. If you denied any [UI Tests](/docs/test#verify-ui-changes) on the parent build, we will always re-capture those stories even if TurboSnap would otherwise skip them. This is helpful in dealing with [inconsistent snapshots](/docs/snapshots#improve-snapshot-consistency).

#### Full rebuilds

Certain circumstances could potentially affect all stories. To prevent false positives, we re-test everything if any of the following situations apply:

- Changes to dependency versions in `package.json`, if no valid lockfile is available
- Changes to your Storybook's configuration
- Changes in files that are imported by your [`preview.js`](https://storybook.js.org/docs/react/configure/overview#configure-story-rendering) (as this could affect any story)
- Changes in your static folder (if specified using `--static-dir` / `-s`)
- Changes to files specified by the `--externals` option (see below)
- Re-run of the same build (commit and branch match the parent build)
- [Infrastructure upgrades](/docs/infrastructure-upgrades)
- [UI Test in a new browser](/docs/browsers)
## Prerequisites

#### Missing commits (rebasing)

Under the hood, TurboSnap works by calculating the difference between the current commit and its ancestor. However, there are certain cases (i.e., rebasing, force pushing) where the commit linked to the previous build no longer exists in the repository, which prevents Chromatic from doing this computation accurately.

In this case, it will search the existing builds until it finds a suitable "replacement build" with a valid commit in the repository. Once found, it approximates the difference between the two commits alongside any UI changes. This can lead to a story being re-tested if one of the following requirements is met:

- Code changes detected (according to Git) between the current and replacement commit
- Visual changes identified (according to Chromatic) between the ancestor build and the replacement commit's build
- Chromatic CLI [10.0+](https://www.npmjs.com/package/chromatic)
- Storybook 6.2+
- Webpack (for experimental Vite support, see [vite-plugin-turbosnap](https://github.com/IanVS/vite-plugin-turbosnap))
- Stories correctly [configured](https://storybook.js.org/docs/react/configure/overview#configure-story-loading) in Storybook's `main.js`
- 10 successful builds on CI with at least one accepted
- For GitHub Actions: run on `push` rather than `pull_request` ([learn more](#github-pullrequest-triggers))

## Configure

Expand Down Expand Up @@ -100,7 +64,56 @@ You may need additional config in the following situations:
- You have files that should never trigger a re-test (e.g., in a monorepo)
- You want to enable or disable TurboSnap for specific branches

### Prebuilt Storybook
<div class="aside">

ℹ️ You can verify your glob pattern using this [picomatch-playground](https://picomatch-playground-ebjlxm.csb.app/).

</div>

### Verify that TurboSnap is working

The best way to see if TurboSnap is working is to inspect your CLI output. There are a couple of messages the CLI outputs of particular relevance:

```shell
Traversing dependencies for X files that changed since the last build
```

<div class="aside">
This message tells us how many git changes Chromatic has detected since the
last Chromatic build. Usually, that's just one or two commits' worth of files.
</div>

```shell
Found Y story files affected by recent changes
```

<div class="aside">

This message tells you the number of story files that depend on the X changes above. This message also might be replaced by a message telling you that we need to capture all stories ([see below](#why-are-full-rebuilds-required) ).

</div>

```shell
Tested A stories across B components; capture C snapshots in S seconds.
```

<div class="aside">
This message tells you how many snapshots we actually took instead of the
number of stories we found in your Storybook. Usually, C would be the number
of stories in the Y component files above.
</div>

Once TurboSnap is activated, all subsequent builds will display an indicator with TurboSnap's status. Find it on the Build page above your tests.

![TurboSnap indicator](../../images/build-turbosnap.png)

---

## Recipes

A few common scenarios require additional configuration.

### TurboSnap with prebuilt Storybook

If you're using `--storybook-build-dir` to provide a prebuilt Storybook, adjust your `build-storybook` script to include the `--webpack-stats-json` option. If Chromatic builds your Storybook for you, this is unnecessary, and will take care of it. For example:

Expand Down Expand Up @@ -195,50 +208,7 @@ Similar to source code changes, the `--untraced` flag can also be used to ignore

To enable TurboSnap for specific branches, pass a glob to `--only-changed` (e.g., `chromatic --only-changed "feature/*"`). Use a negating glob (e.g. `chromatic --only-changed "!(main)"`) to enable all but certain branches. See [picomatch] for details.

<div class="aside">

ℹ️ You can verify your glob pattern using this [picomatch-playground](https://picomatch-playground-ebjlxm.csb.app/).

</div>

### Confirm TurboSnap is working

The best way to see if TurboSnap is working is to inspect your CLI output. There are a couple of messages the CLI outputs of particular relevance:

```shell
Traversing dependencies for X files that changed since the last build
```

<div class="aside">
This message tells us how many git changes Chromatic has detected since the
last Chromatic build. Usually, that's just one or two commits' worth of files.
</div>

```shell
Found Y story files affected by recent changes
```

<div class="aside">

This message tells you the number of story files that depend on the X changes above. This message also might be replaced by a message telling you that we need to capture all stories ([see below](#why-are-full-rebuilds-required) ).

</div>

```shell
Tested A stories across B components; capture C snapshots in S seconds.
```

<div class="aside">
This message tells you how many snapshots we actually took instead of the
number of stories we found in your Storybook. Usually, C would be the number
of stories in the Y component files above.
</div>

Once TurboSnap is activated, all subsequent builds will display an indicator with TurboSnap's status. Find it on the Build page above your tests.

![TurboSnap indicator](../../images/build-turbosnap.png)

### Notes on monorepos
### Using TurboSnap in a monorepo

TurboSnap will make working in a monorepo more efficient. Because it detects affected stories based on the actual files changed, pushing a commit that touched only backend code will run faster in CI and not use up your snapshot quota. However, it will still build and publish your Storybook. To avoid that, you can [skip Chromatic entirely](/docs/monorepos#only-run-chromatic-when-changes-occur-in-a-subproject), speeding up your CI pipeline even more.

Expand Down Expand Up @@ -270,13 +240,6 @@ Our own GitHub Action works around that by using `pull_request.head.sha` as the

### Troubleshooting

<details>
<summary>Is TurboSnap ready for production even in beta?</summary>

Yes. Many customers are currently using TurboSnap with enterprise workloads. Our goal for the TurboSnap beta is to fine-tune the algorithm and configuration experience.

</details>

<details>
<summary>Why are no changes being detected?</summary>

Expand Down Expand Up @@ -350,7 +313,7 @@ If this list of files contains things you didn't expect, look at any global deco
<details>
<summary>Why are full rebuilds required?</summary>

Full rebuilds can be required for various reasons (see the list in [how it works](#how-it-works)). Another scenario where a full rebuild will also be required is due to a change to a `package.json` or lock file for a subproject that doesn't affect the Storybook (we need to be very conservative as we cannot tell if a change to a lock file could affect `node_modules` imported by Storybook).
Full rebuilds can be required for various reasons (see the list in [how it works](/docs/turbosnap#how-it-works)). Another scenario where a full rebuild will also be required is due to a change to a `package.json` or lock file for a subproject that doesn't affect the Storybook (we need to be very conservative as we cannot tell if a change to a lock file could affect `node_modules` imported by Storybook).

<div class="aside">
If you run into this situation frequently, upvote the <a href="https://github.com/chromaui/chromatic-cli/issues/383">open issue</a> in the Chromatic CLI's issue tracker to opt-out of this behavior for specific directories in your repository.
Expand Down
59 changes: 59 additions & 0 deletions src/content/turbosnap/turbosnap.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
layout: "../../layouts/Layout.astro"
title: Introduction
description: Speed up tests by detecting file changes with Git
sidebar: { order: 1 }
---

# Introduction to TurboSnap

TurboSnap is an advanced Chromatic feature that speeds up [UI Tests](/docs/test). It analyzes your project's Git history and Webpack's [dependency graph](https://webpack.js.org/concepts/dependency-graph/) to identify which components and their dependencies have changed. It intelligently snapshots only the stories associated with those changes. For the rest, it copies over the snapshots from baselines that didn't change.

![TurboSnap tracks dependencies](../../images/turbosnap-dep-tracking.gif)

## How it works

1. TurboSnap considers the Git changes between the current commit and the [ancestor build's](/docs/branching-and-baselines#find-the-ancestor-builds) commit.
2. Using Webpack's dependency graph, TurboSnap identifies the individual story files affected by those changes.
3. Chromatic utilizes this information to selectively snapshot the stories defined in those specific story files. It also snapshots any tests that were denied on the ancestor build.

Chromatic will not capture a new snapshot for stories that do not have associated code changes. In most cases, this results in faster test runs and reduces the number of billable snapshots.

If you denied any [UI Tests](/docs/test#verify-ui-changes) on the ancestor build, Chromatic will always re-capture those stories even if TurboSnap would otherwise skip them. This is particularly useful for handling [inconsistent snapshots](/docs/snapshots#improve-snapshot-consistency).

### Full rebuilds

Certain code changes have the potential to impact all stories. To avoid false positives, we re-test everything in the following situations:

- Changes to dependency versions in `package.json`, if no valid lockfile is available
- Changes to your Storybook's configuration
- Changes in files that are imported by your [`preview.js`](https://storybook.js.org/docs/react/configure/overview#configure-story-rendering) (as this could affect any story)
- Changes in your static folder (if specified using `--static-dir` / `-s`)
- Changes to files specified by the `--externals` option (see below)
- Re-run of the same build (commit and branch match the ancestor build)
- [Infrastructure upgrades](/docs/infrastructure-upgrades)
- [UI Test in a new browser](/docs/browsers)

### Missing commits (rebasing)

Under the hood, TurboSnap works by calculating the difference between the current commit and its ancestor. However, there are certain cases (i.e., rebasing, force pushing) where the commit linked to the previous build no longer exists in the repository, which prevents Chromatic from doing this computation accurately.

In this case, it will search the existing builds until it finds a suitable "replacement build" with a valid commit in the repository. Once found, it approximates the difference between the two commits alongside any UI changes. This can lead to a story being re-tested if one of the following requirements is met:

- Code changes detected (according to Git) between the current and replacement commit
- Visual changes identified (according to Chromatic) between the ancestor build and the replacement commit's build

## Pricing

By enabling TurboSnap, Chromatic performs a check to identify stories that have no code changes associated with them. Instead of capturing new snapshots, it duplicates snapshots from existing baselines in such cases. We refer to these duplicated snapshots as **TurboSnaps**.

TurboSnaps not only speed up the test runs, they also require less infrastructure resources. We pass these savings on to you by billing them at 1/5th of the cost of a regular snapshot.

For the remaining stories, Chromatic captures new snapshots as usual and bills them at the regular rate.

For example, consider a Storybook with 50 stories. If your code changes impact 10 stories, Chromatic will capture new snapshots for those 10 stories. The cost of that build will be 18 snapshots:

- 10 regular snapshots
- 40 TurboSnaps (which cost: .2 x 40 = 8 regular snapshots)

Check out the [billing docs](/docs/billing#snapshots-with-turbosnap-enabled) for more details.
5 changes: 5 additions & 0 deletions src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const workflow = await getCollection("workflow");
const configuration = await getCollection("configuration");
const modes = await getCollection("modes");
const snapshot = await getCollection("snapshot");
const turbosnap = await getCollection("turbosnap");
const collaborate = await getCollection("collaborate");
const plugins = await getCollection("plugins");
const ci = await getCollection("ci");
Expand Down Expand Up @@ -57,6 +58,10 @@ const navItems = [
title: "Snapshot",
items: snapshot,
},
{
title: "TurboSnap",
items: turbosnap,
},
{
title: "Collaborate",
items: collaborate,
Expand Down
2 changes: 2 additions & 0 deletions src/pages/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export async function getStaticPaths() {
const configuration = await getCollection("configuration");
const modes = await getCollection("modes");
const snapshot = await getCollection("snapshot");
const turbosnap = await getCollection("turbosnap");
const collaborate = await getCollection("collaborate");
const plugins = await getCollection("plugins");
const ci = await getCollection("ci");
Expand All @@ -22,6 +23,7 @@ export async function getStaticPaths() {
...configuration,
...modes,
...snapshot,
...turbosnap,
...collaborate,
...plugins,
...ci,
Expand Down

1 comment on commit af4ba8d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.