Skip to content

Commit 69d95d2

Browse files
authored
docs: add Rstest 0.11 release blog (#1508)
1 parent ff120f9 commit 69d95d2

16 files changed

Lines changed: 445 additions & 89 deletions

File tree

website/AGENTS.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,38 @@ This is the documentation website for Rstest, built with [Rspress](https://rspre
1414
pnpm dev # Start dev server
1515
pnpm build # Build for production
1616
pnpm preview # Preview production build
17-
pnpm gen:og # Generate a release Open Graph image (see below)
17+
pnpm gen:release-image # Generate a release's banner + og image (see below)
1818
```
1919

20-
## Open Graph image generation
20+
## Release image generation
2121

22-
Per-release og images live in [rstackjs/rstack-design-resources](https://github.com/rstackjs/rstack-design-resources) and are served by the `assets.rspack.rs` CDN. The template lives **in this repo** to keep design-resources as a passive PNG store.
22+
Each release blog needs **two** images, generated together so they share one gradient:
2323

24-
- `scripts/og-image/cli.mts` — entry, parses `--version`/`--description`/`--out`
25-
- `scripts/og-image/render.mts` — fetches the Rstest logo SVG → rasterizes → composes with [satori](https://github.com/vercel/satori) → renders with [@resvg/resvg-js](https://github.com/yisibl/resvg-js) at 2x zoom for retina
26-
- `scripts/og-image/template.mts`[satori-html](https://github.com/natemoo-re/satori-html) template, modeled after the `Rsbuild og image 1.0` artboard in design-resources
24+
- **banner**`4096x1152`, no tagline, the in-page `<img>` at the top of the post. Committed to `docs/public/` and referenced by a site-relative path (`/rstest-banner-v<ver>.png`).
25+
- **og image**`2400x1260`, optional tagline, the social-share card. Committed to [rstackjs/rstack-design-resources](https://github.com/rstackjs/rstack-design-resources) under `rstest/` and served by the `assets.rspack.rs` CDN. `rspress.config.ts` wires `og:image` per blog route: `blog/announcing-<major>-<minor>``assets.rspack.rs/rstest/rstest-og-image-v<major>-<minor>.png`.
26+
27+
The templates live **in this repo**; design-resources stays a passive PNG store.
28+
29+
- `scripts/release-image/cli.mts` — entry, parses `--version`/`--description`/`--out-dir`; rolls one background and renders both images
30+
- `scripts/release-image/render.mts` — fetches the Rstest logo SVG → rasterizes → composes with [satori](https://github.com/vercel/satori) → renders with [@resvg/resvg-js](https://github.com/yisibl/resvg-js) at 2x zoom for retina; `randomBackground()` re-rolls the gradient every run (no seed flag)
31+
- `scripts/release-image/template.mts`[satori-html](https://github.com/natemoo-re/satori-html) template driven by the `LAYOUTS.banner` / `LAYOUTS.og` presets
2732

2833
### Release workflow
2934

30-
1. Run `pnpm gen:og --version <ver> --description "<tagline>"` from `website/`. Use `--out` to write directly into a local clone of the design-resources repo at `rstest/assets/rstest-og-image-v{version-with-hyphens}.png` (e.g. `v0-5.png`). The background gradient is randomized (color scheme, blob count, placement) on every run and there is no seed flag — re-run until you get a composition you like before committing.
31-
2. Commit the PNG in the design-resources repo and open a PRthat repo is the only place release PNGs are stored.
32-
3. After CDN deploy, the PNG is reachable at `assets.rspack.rs/rstest/assets/rstest-og-image-v0-5.png`. Wiring it up per blog `routePath` in `rspress.config.ts` is a separate follow-up — the site currently sets a single static `og:image` via `pluginOpenGraph`.
35+
1. Run `pnpm gen:release-image --version <ver> [--description "<tagline>"] --out-dir <dir>` from `website/`. The gradient is randomized every run — re-run until both images look good.
36+
2. Compress both PNGs with [TinyPNG](https://tinypng.com) (or Squoosh / ImageOptim / `pngquant`)the raw resvg output is ~200 KB and palette quantization typically drops it to ~1/4 the size with no visible loss.
37+
3. Put the banner in `docs/public/`. Commit the og image to the design-resources repo under `rstest/` and open a PR — that repo is the only place og images are stored. After CDN deploy it is reachable at `assets.rspack.rs/rstest/rstest-og-image-v<ver>.png`.
3338

3439
### Do
3540

36-
- Use Space Grotesk (committed under `scripts/og-image/assets/fonts/` with SIL OFL license)
41+
- Use Space Grotesk (committed under `scripts/release-image/assets/fonts/` with SIL OFL license)
3742
- Render at 2x via `Resvg({ fitTo: { mode: 'zoom', value: 2 } })` so the PNG stays crisp on retina displays
38-
- Before committing the PNG to design-resources, run it through [TinyPNG](https://tinypng.com) (or Squoosh / ImageOptim / `pngquant`) — the raw resvg output is ~300 KB and palette quantization typically drops it to ~1/4 the size with no visible loss
3943
- Fetch the logo from the canonical CDN URL at generation time, not from a committed copy
4044

4145
### Don't
4246

4347
- Don't depend on packages like `geist` that pull in framework peer deps (`next>=13.2`); commit raw `.ttf` files directly instead
44-
- Don't write generated PNGs into this repo; they belong in design-resources
48+
- Don't commit og images into this repo; they belong in design-resources (the banner is the one exception — it lives in `docs/public/`)
4549
- Don't bake the logo into a static asset; always fetch the SVG so logo updates propagate automatically
4650

4751
## Writing style guidelines

website/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ For images you use in the document, it's better to upload them to the [rstackjs/
1616

1717
After you upload the images there, they will be automatically deployed under the <https://assets.rspack.rs/>.
1818

19-
## Open Graph images
19+
## Release images
2020

21-
`scripts/og-image/` generates per-release Open Graph images used by the `og:image` and `twitter:image` meta tags on each release blog post. See [AGENTS.md](./AGENTS.md#open-graph-image-generation) for the template architecture and release workflow.
21+
`scripts/release-image/` generates each release's banner and Open Graph image (the latter used by the `og:image` / `twitter:image` meta tags on the release blog post). See [AGENTS.md](./AGENTS.md#release-image-generation) for the template architecture and release workflow.

website/docs/en/api/runtime-api/rstest/fake-timers.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Advances the timers to the next animation frame.
167167

168168
## rs.jumpTimersByTime
169169

170-
<ApiMeta addedVersion="0.10.7" />
170+
<ApiMeta addedVersion="0.11.0" />
171171

172172
- **Alias:** `rstest.jumpTimersByTime`
173173
- **Type:** `(ms: number | string | Temporal.Duration) => RstestUtilities`
@@ -187,7 +187,7 @@ expect(cb).toHaveBeenCalledTimes(1);
187187

188188
## rs.setTickMode
189189

190-
<ApiMeta addedVersion="0.10.7" />
190+
<ApiMeta addedVersion="0.11.0" />
191191

192192
- **Alias:** `rstest.setTickMode`
193193
- **Type:** `(mode: { mode: 'manual' | 'nextAsync' } | { mode: 'interval'; delta?: number }) => RstestUtilities`

website/docs/en/blog/_meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
["index", "announcing-0-10"]
1+
["index", "announcing-0-11", "announcing-0-10"]
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
description: 'Rstest 0.11 is a transitional release on the way to 1.0: it breaks and settles the test options, mock, pool, and shard APIs, and also makes V8 coverage faster and aligns fake timers with the latest @sinonjs/fake-timers.'
3+
date: 2026-07-06 16:00:00
4+
sidebar: false
5+
authors:
6+
- name: 9aoy
7+
avatar: 'https://github.com/9aoy.png'
8+
github: '9aoy'
9+
- name: fi3ework
10+
avatar: 'https://github.com/fi3ework.png'
11+
github: 'fi3ework'
12+
---
13+
14+
import { BlogAuthors } from '@rstack-dev/doc-ui/blog-authors';
15+
16+
# Announcing Rstest 0.11
17+
18+
_July 6, 2026_
19+
20+
<BlogAuthors />
21+
22+
<img
23+
src="https://assets.rspack.rs/rstest/rstest-banner-v0-11.png"
24+
alt="Rstest 0.11"
25+
style={{
26+
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.08)',
27+
}}
28+
/>
29+
30+
Rstest 0.11 is a transitional release on the road to 1.0. Its focus is to settle several public APIs through breaking changes ahead of the stable release; alongside that, it also brings faster V8 coverage and updated fake timers.
31+
32+
Notable changes:
33+
34+
- [API changes on the road to 1.0](#api-changes)
35+
- [Faster V8 coverage](#coverage-v8)
36+
- [Fake timers aligned with `@sinonjs/fake-timers` 15](#fake-timers)
37+
- [Other improvements](#other-improvements)
38+
39+
## API changes on the road to 1.0 \{#api-changes}
40+
41+
To stabilize the public API before 1.0, Rstest 0.11 adjusts a few surfaces. These are breaking changes, but the migrations are straightforward.
42+
43+
### `TestOptions` moves to the second argument
44+
45+
The [`TestOptions`](/api/runtime-api/test-api/test#testoptions) object (`retry` / `repeats` / `timeout`) for `test` / `it` / `test.each` / `test.for` was previously accepted as the **last** argument, after the test function. With a long test body it was easy to miss, and formatters tended to push it onto a separate trailing line. It now goes in the **second** position, before the test function, matching the shape used by Vitest and Node.js' built-in test runner (`node:test`).
46+
47+
```diff
48+
- test('name', () => {}, { retry: 2 });
49+
+ test('name', { retry: 2 }, () => {});
50+
```
51+
52+
The numeric timeout shorthand is unchanged — the Jest-style `test('name', fn, 1000)` still works as the last argument. `describe` now accepts a `TestOptions` object in the same second-argument position.
53+
54+
### Mock factories are synchronous-only
55+
56+
The `rs.mock` / `rs.doMock` factory type is now synchronous-only. Async factories are prone to hoisting and module-initialization issues, so TypeScript no longer suggests them. For partial mocks, keep the real module via a synchronous `importActual` import attribute and override only the exports you need:
57+
58+
```ts
59+
import * as actual from './date-utils' with { rstest: 'importActual' };
60+
61+
rs.mock('./date-utils', () => ({
62+
...actual,
63+
formatDate: rs.fn().mockReturnValue('2026-03-19'),
64+
}));
65+
```
66+
67+
See [Partially mock modules](/guide/basic/mock#partially-mock-modules) for more details.
68+
69+
### `pool.minWorkers` is removed
70+
71+
`pool.minWorkers` set a floor on retained idle workers, but it only applied under `isolate: false` — the default `isolate: true` never reuses workers, so the option had no effect. It is removed so that `pool` settles on the flat `{ type?, maxWorkers?, execArgv? }` shape; use `pool.maxWorkers` to control file-level parallelism. The internal floor still defaults to `min(maxWorkers, recommended)`, so default warm-worker behavior is unchanged.
72+
73+
### `shard` is CLI-only
74+
75+
Sharding is inherently per-invocation — every CI runner passes a different index, which a value committed to a shared config file cannot express. The `shard` config field is removed from `RstestConfig`; use the [`--shard <index>/<count>`](/guide/basic/cli#sharding-tests) CLI flag instead. The flag and its behavior are unchanged.
76+
77+
```bash
78+
npx rstest run --shard=1/3
79+
```
80+
81+
## Faster V8 coverage \{#coverage-v8}
82+
83+
On a large bundled project, collecting V8 coverage could spend tens of seconds turning raw V8 data into Istanbul coverage. Rstest 0.11 reworks the conversion path.
84+
85+
Previously, the provider ran every raw V8 entry through the generic `ast-v8-to-istanbul` converter, including many that never reach the final report. Rstest 0.11 uses a converter tailored for Rstest's output, filters out irrelevant entries before converting, and moves the conversion from each worker into the main process.
86+
87+
Measured on the [Rsbuild](https://github.com/web-infra-dev/rsbuild) repository's own test suite (`pnpm test --coverage --coverage.provider=v8`):
88+
89+
| Version | Wall span | Phase sum |
90+
| ------------------------------- | --------: | --------: |
91+
| baseline (`ast-v8-to-istanbul`) | 6.05s | 60.11s |
92+
| Rstest 0.11 | 1.81s | 10.28s |
93+
94+
Compared with the baseline, the reworked path is about **3.34x faster**, and the summed per-file phase time drops **5.85x**. Resolving raw coverage in the main process further cuts Rstest's own processing time (about **2x** on the same project) and lowers CPU usage. The coverage output is unchanged.
95+
96+
There is no configuration change — projects using `coverage.provider: 'v8'` get the speedup automatically.
97+
98+
## Fake timers aligned with `@sinonjs/fake-timers` 15 \{#fake-timers}
99+
100+
Rstest 0.11 upgrades to `@sinonjs/fake-timers@15.4.0` and aligns the fake timers runtime with its API. Two methods are now available:
101+
102+
- `rstest.jumpTimersByTime(ms)` jumps the clock forward by `ms` without running the timers in between, so any timer scheduled within that span fires at most once, at the destination.
103+
- `rstest.setTickMode({ mode })` switches the clock between manual and auto-advancing modes.
104+
105+
```ts title="example.test.ts"
106+
import { expect, rstest, test } from '@rstest/core';
107+
108+
test('jump without running intermediate timers', () => {
109+
rstest.useFakeTimers();
110+
const fn = rstest.fn();
111+
112+
setInterval(fn, 1000);
113+
rstest.jumpTimersByTime(5000);
114+
115+
expect(fn).toHaveBeenCalledTimes(1);
116+
});
117+
```
118+
119+
`rstest.setSystemTime(date)` also works on its own now — without a prior `rstest.useFakeTimers()` call it pins only the global `Date` and leaves the real timers intact, so you can freeze the system clock without taking over `setTimeout` / `setInterval`.
120+
121+
This release also closes a Browser Mode gap. Browser Mode previously aliased `@sinonjs/fake-timers` to a minimal stub that kept a clock-shaped object but did not actually fake browser globals such as `setTimeout` or `Date`. Rstest 0.11 removes that stub and bundles the real implementation, so fake timers behave consistently across Node mode and Browser Mode — including `Date` / `performance`, animation frames, microtasks, and string durations. See [Fake Timers](/api/runtime-api/rstest/fake-timers) for more details.
122+
123+
## Other improvements \{#other-improvements}
124+
125+
- **Clearer error for `rs.spyOn` on ESM namespace exports.** Previously, spying on a read-only ESM namespace export produced an opaque failure; it now produces an actionable error explaining why the binding cannot be reassigned.
126+
- **`new URL()` and Wasm resolve from on-disk source.** Assets referenced via `new URL(..., import.meta.url)` and Wasm modules now resolve from the original source location, matching runtime behavior.
127+
- **Shared module state under `isolate: false`.** Imported module state is now shared and context-bound APIs stay live across files when `isolate: false`, so cross-file singletons behave as expected.
128+
- **Browser Mode project isolation.** Each project now runs in its own Rsbuild instance, fixing cross-project module resolution in multi-project browser runs.
129+
- **OS-native `testPath`.** `testPath` is now surfaced with the platform's native path separators.
130+
- **Custom `runCLI` argv.** Programmatic callers can pass a custom `argv` array to `runCLI`, while `process.argv` remains the default.
131+
132+
## Upgrade
133+
134+
Upgrade the `@rstest/*` packages to version 0.11 to get these improvements. Rstest 0.11 includes a few breaking API changes — see [API changes on the road to 1.0](#api-changes) for the migration steps.
135+
136+
For a full list of changes, see the [v0.11.0 release notes](https://github.com/web-infra-dev/rstest/releases/tag/v0.11.0).

website/docs/zh/api/runtime-api/rstest/fake-timers.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ const realTime = rs.getRealSystemTime();
167167

168168
## rs.jumpTimersByTime
169169

170-
<ApiMeta addedVersion="0.10.7" />
170+
<ApiMeta addedVersion="0.11.0" />
171171

172172
- **别名:** `rstest.jumpTimersByTime`
173173
- **类型:** `(ms: number | string | Temporal.Duration) => RstestUtilities`
@@ -187,7 +187,7 @@ expect(cb).toHaveBeenCalledTimes(1);
187187

188188
## rs.setTickMode
189189

190-
<ApiMeta addedVersion="0.10.7" />
190+
<ApiMeta addedVersion="0.11.0" />
191191

192192
- **别名:** `rstest.setTickMode`
193193
- **类型:** `(mode: { mode: 'manual' | 'nextAsync' } | { mode: 'interval'; delta?: number }) => RstestUtilities`

website/docs/zh/blog/_meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
["index", "announcing-0-10"]
1+
["index", "announcing-0-11", "announcing-0-10"]

0 commit comments

Comments
 (0)