Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,3 @@ jobs:
run: yarn playwright install --with-deps
- name: Vitest
run: yarn ci:test
- name: Commit and Push changes
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This was a patch to keep the files in sync across local runs and CI to make sure that the CI versions were always used so they would be the same, but now they are the same in headless mode as they are running on chrome so this isn't needed.

uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: update vitest screenshots [skip ci]"
file_pattern: "**/__screenshots__/**/*.png"
7 changes: 5 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ __tests__/ → Vitest browser-mode tests (Playwright)
- `.res` files must always be capitalized (PascalCase), matching ReScript module conventions.
- Use the pipe-first operator (`->`) for chaining, which is idiomatic ReScript.
- Resolve all warnings and treat them as errors. The project has `"error": "+8"` in `rescript.json`.
- Never directly edit an `.jsx` files, you must edit the corresponding `.res` file. The `.jsx` files are generated by the ReScript compiler and will be overwritten on the next compile.

## ReScript Rules

Expand Down Expand Up @@ -178,8 +179,10 @@ let default: unit => React.element
})
```

- Components requiring React Router context must be wrapped in `<BrowserRouter>`.
- Run tests with `yarn vitest`.
- Components requiring React Router context must be wrapped in `<MemoryRouter>`.
- Do not use `<BrowserRouter>` in tests.
- Run tests with `yarn vitest --browser.headless --run`.
- Do not update snapshots without asking for confirmation.

## MDX Content

Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ yarn dev

## Run Tests

### Unit Tests (Vitest)

We use [Vitest](https://vitest.dev/) with browser mode (Playwright) for component-level unit tests. Test files live in `__tests__/` and are written in ReScript.

```sh
# Run tests in watch mode (headed browser)
yarn vitest

# Run tests once in headless mode (same as CI)
yarn ci:test
```

**Updating screenshots:** Screenshot baselines should only be updated in headless mode so they match CI and stay consistent across devices. Use the dedicated command:

```sh
yarn vitest:update
```

This runs the full suite headlessly with the `--update` flag, regenerating any screenshot baselines that have changed. Commit the updated `.png` files alongside your code changes.
Please be selective in pushing up changes to screenshots and only update files that you have added or expected to change. Pushing up all changes can make it hard to review PRs with small image differences based on different devices or environments that wouldn't trigger failures in CI.

### Markdown Codeblock Tests

We check the validity of our code examples marked with:
Expand Down
9 changes: 4 additions & 5 deletions __tests__/MarkdownComponents_.test.res
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,14 @@ test("renders Image with caption", async () => {
let screen = await render(
<div dataTestId="image-wrapper">
<Markdown.Image
src="https://rescript-lang.org/brand/rescript-brandmark.svg" caption="The ReScript logo"
className="rounded-lg border border-gray-90/5 text-gray-60"
src="https://rescript-lang.org/lp/community-3.avif"
caption="A sample image caption"
/>
</div>,
)

let caption = await screen->getByText("The ReScript logo")
let caption = await screen->getByText("A sample image caption")
await element(caption)->toBeVisible

let wrapper = await screen->getByTestId("image-wrapper")
Expand All @@ -226,9 +228,6 @@ test("renders Video with caption", async () => {

let caption = await screen->getByText("A sample video")
await element(caption)->toBeVisible

let wrapper = await screen->getByTestId("video-wrapper")
await element(wrapper)->toMatchScreenshot("markdown-video")
})

test("renders horizontal rule", async () => {
Expand Down
8 changes: 4 additions & 4 deletions __tests__/NavbarSecondary_.test.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ test("desktop secondary navbar shows all doc section links", async () => {
await viewport(1440, 500)

let screen = await render(
<BrowserRouter>
<MemoryRouter initialEntries=["/docs/manual/introduction"]>
<NavbarSecondary />
</BrowserRouter>,
</MemoryRouter>,
)

let navbar = await screen->getByTestId("navbar-secondary")
Expand All @@ -24,9 +24,9 @@ test("mobile secondary navbar shows all links", async () => {
await viewport(600, 500)

let screen = await render(
<BrowserRouter>
<MemoryRouter initialEntries=["/docs/manual/introduction"]>
<NavbarSecondary />
</BrowserRouter>,
</MemoryRouter>,
)

let navbar = await screen->getByTestId("navbar-secondary")
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"build:vite": "react-router build",
"build": "yarn build:res && yarn build:scripts && yarn build:update-index && yarn build:vite",
"ci:format": "prettier . --check --experimental-cli",
"ci:test": "yarn vitest --run --browser.headless --update",
"ci:test": "yarn vitest --run --browser.headless",
"clean:res": "rescript clean",
"convert-images": "auto-convert-images",
"dev:res": "rescript watch",
Expand All @@ -30,7 +30,8 @@
"preview": "yarn build && static-server build/client",
"reanalyze": "rescript-tools reanalyze -all-cmt .",
"test": "node scripts/test-examples.mjs && node scripts/test-hrefs.mjs",
"vitest": "vitest"
"vitest": "vitest",
"vitest:update": "vitest --run --browser.headless --update"
},
"dependencies": {
"@babel/generator": "^7.24.7",
Expand Down
20 changes: 20 additions & 0 deletions styles/test-overrides.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Test overrides for consistent screenshots.
* This file is only imported in vitest.setup.mjs.
*/

/* Disable all CSS animations and transitions */
*,
*::before,
*::after {
animation-duration: 0s !important;
animation-delay: 0s !important;
transition-duration: 0s !important;
transition-delay: 0s !important;
}

/* Force overlay scrollbars so scrollbar width is 0 in both headless and headed mode */
* {
scrollbar-width: none !important;
scrollbar-gutter: auto !important;
}
18 changes: 17 additions & 1 deletion vitest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,30 @@ export default defineConfig({
setupFiles: ["./vitest.setup.mjs"],
browser: {
enabled: true,
provider: playwright(),
provider: playwright({
contextOptions: {
deviceScaleFactor: 1,
},
}),
ui: false,
// https://vitest.dev/config/browser/playwright
instances: [
{
browser: "chromium",
viewport: { width: 1440, height: 900 },
},
],
expect: {
toMatchScreenshot: {
screenshotOptions: {
scale: "css",
},
comparatorOptions: {
threshold: 0.2,
allowedMismatchedPixelRatio: 0.05,
},
},
},
},
},
});
1 change: 1 addition & 0 deletions vitest.setup.mjs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import "./styles/main.css";
import "./styles/utils.css";
import "./styles/test-overrides.css";
Loading