feat(racetrack): a hosted playground and scenario-driven test runner for PTE plugins#2646
Closed
christianhg wants to merge 3 commits into
Closed
feat(racetrack): a hosted playground and scenario-driven test runner for PTE plugins#2646christianhg wants to merge 3 commits into
christianhg wants to merge 3 commits into
Conversation
…red editor
`createTestEditor` from `@portabletext/editor/test/vitest` discarded the scoped locator from its underlying `render()` and built a document-wide `page.getByRole('textbox')` lookup. Two calls in the same DOM would have the second one's locator address the first editor. Vitest's iframe-per-test isolation hid this; embedders that share a DOM across editors hit it.
Use the scoped locator from `render()` so each editor's locator targets exactly that editor.
Add a regression test that mounts two editors, types different text into each, asserts each editor ended up with its own content.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
🦋 Changeset detectedLatest commit: 1ceae1d The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
📦 Bundle Stats —
|
| Metric | Value | vs main (52da58b) |
|---|---|---|
| Internal (raw) | 743.8 KB | -298 B, -0.0% |
| Internal (gzip) | 142.8 KB | -74 B, -0.1% |
| Bundled (raw) | 1.35 MB | -298 B, -0.0% |
| Bundled (gzip) | 303.8 KB | -57 B, -0.0% |
| Import time | 99ms | +1ms, +0.7% |
@portabletext/editor/behaviors
| Metric | Value | vs main (52da58b) |
|---|---|---|
| Internal (raw) | 467 B | - |
| Internal (gzip) | 207 B | - |
| Bundled (raw) | 424 B | - |
| Bundled (gzip) | 171 B | - |
| Import time | 3ms | +0ms, +0.3% |
@portabletext/editor/plugins
| Metric | Value | vs main (52da58b) |
|---|---|---|
| Internal (raw) | 3.3 KB | -298 B, -8.1% |
| Internal (gzip) | 948 B | -73 B, -7.1% |
| Bundled (raw) | 3.1 KB | -298 B, -8.6% |
| Bundled (gzip) | 878 B | -74 B, -7.8% |
| Import time | 8ms | -0ms, -0.6% |
@portabletext/editor/selectors
| Metric | Value | vs main (52da58b) |
|---|---|---|
| Internal (raw) | 76.3 KB | - |
| Internal (gzip) | 14.3 KB | - |
| Bundled (raw) | 72.4 KB | - |
| Bundled (gzip) | 13.3 KB | - |
| Import time | 8ms | -0ms, -1.1% |
@portabletext/editor/traversal
| Metric | Value | vs main (52da58b) |
|---|---|---|
| Internal (raw) | 9.2 KB | - |
| Internal (gzip) | 2.4 KB | - |
| Bundled (raw) | 9.3 KB | - |
| Bundled (gzip) | 2.4 KB | - |
| Import time | 5ms | +0ms, +4.0% |
@portabletext/editor/utils
| Metric | Value | vs main (52da58b) |
|---|---|---|
| Internal (raw) | 30.6 KB | - |
| Internal (gzip) | 6.5 KB | - |
| Bundled (raw) | 28.4 KB | - |
| Bundled (gzip) | 6.1 KB | - |
| Import time | 7ms | +0ms, +1.6% |
🗺️ . · ./behaviors · ./plugins · ./selectors · ./traversal · ./utils · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
cfff3de to
5654967
Compare
Layer the runtime on top of the skeleton landing page.
Four-panel app: scenarios on the left (driven by Cucumber's gherkin parser
to expand outlines into pickles), playground in the middle (a live editor
with the car's plugin wired in), runner on the right (a second editor that
each scenario drives via racejar's runner-agnostic `compileFeature`), and
an engine panel that shows the car's source - `Plugin.tsx`, `entry.tsx`,
`steps.ts`, `feature.feature` - read-only via Vite's `?raw` imports.
A car lives at `src/garage/<name>/` with a fixed shape: `entry.tsx`
exports a `GarageEntry` manifest pairing a feature file with the React
plugin, step definitions, hooks, and parameter types. The App reads from
`src/garage/index.ts`; a dropdown switches cars.
Two cars in the garage:
- `mention-picker` (19/19 scenarios). Lifted from
`packages/plugin-typeahead-picker/src/mention-picker.test.tsx` with no
changes to scenarios; only the test-target wiring moves.
- `input-rule-edge-cases` (57/57 scenarios). Lifted from
`packages/plugin-input-rule/src/edge-cases.test.tsx`; same scenarios,
matches vitest CI pass count exactly.
The runner re-implements racejar's vitest driver as 49 lines of plain
async iteration, then aliases `vitest`, `vitest/browser`, and
`vitest-browser-react` to in-app shims so existing step definitions
(`@portabletext/editor/test/vitest`) and `createTestEditor` import
cleanly without a vitest worker. The shims diverge from vitest in two
places, both deliberate:
- `Locator.locator(selector)` and `page.locator(selector)` are added
so step definitions can scope assertions to the test target rather
than picking up the playground editor that shares the DOM.
- `@testing-library/user-event`'s `.type()` is invoked with
`{skipClick: true}` so typing into a contenteditable doesn't collapse
the existing selection - matching Playwright's
`page.keyboard.type()` behaviour used by vitest's browser runner.
Knip is told the shim modules are entry points (their exports are
consumed indirectly through Vite aliases).
`getGlobalScope` had a chain - `globalThis` first, then `window`, `self`, `global`. `globalThis` is universal in every environment the editor supports (browsers, Node 12+, workers, Deno, Bun), so the rest of the chain is unreachable. Beyond being dead, the `global` branch needs `@types/node` in scope to type-check. Consumers whose tsconfig doesn't pull Node types - any app that aliases `@portabletext/editor` directly to its `src/` - tripped on it. Simplify to a single `globalThis` read.
49e6f06 to
1ceae1d
Compare
f227bd2 to
7efa486
Compare
7efa486 to
1ceae1d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Racetrack is a hosted playground and scenario-driven test runner for Portable Text Editor plugins. Wire your plugin in, drive it with scenarios you author visually, watch them go green. The same
.featurefile runs in Racetrack playback AND in the customer's vitest CI - identical results.This is the long-running branch where Racetrack is built inside the editor monorepo while PTE v7 stabilises. It will not be merged in this shape. Platform fixes that fall out (one already has, #2644) get split into their own PRs and merged independently. Each push to this branch generates a Vercel preview at https://racetrack.sanity.dev/ so the work is continuously testable.
What's here today
The app at
apps/racetrack/is private, runs against the monorepo's workspace sources, and currently boots a fixed garage of two cars:@portabletext/plugin-typeahead-picker's mention picker scenarios, lifted verbatim frompackages/plugin-typeahead-picker/src/mention-picker.test.tsx. 19 scenarios, all green.@portabletext/plugin-input-rule's edge-case scenarios, lifted verbatim frompackages/plugin-input-rule/src/edge-cases.test.tsx. 57 scenarios, all green, matching the vitest CI baseline exactly.The same
.featurefiles run unmodified in their packages' vitest browser tests; the runner here just provides the playback and visualisation surface.How it works
Four panels. Scenarios on the left (parsed via
@cucumber/gherkin, scenario outlines expanded into individual pickles). A live editor in the middle with the car's plugin wired in. A second editor on the right that each scenario drives via racejar's runner-agnosticcompileFeature. An engine panel surfaces the car's source as read-only tabs -Plugin.tsx,entry.tsx,steps.ts,feature.feature- imported via Vite's?rawsuffix at build time.A car lives at
src/garage/<name>/with a fixed shape: anentry.tsxthat exports aGarageEntrymanifest pairing the feature file with the React plugin, step definitions, hooks, and parameter types. The App reads fromsrc/garage/index.tsand a dropdown switches cars.The runner re-implements racejar's vitest driver as 49 lines of plain async iteration, then aliases
vitest,vitest/browser, andvitest-browser-reactto in-app shims so existing step definitions (@portabletext/editor/test/vitest) andcreateTestEditorimport cleanly outside a vitest worker. The shims diverge from vitest in two places, both deliberate:Locator.locator(selector)andpage.locator(selector)are added so step definitions can scope assertions to the test target rather than picking up the playground editor that shares the DOM.@testing-library/user-event's.type()is invoked with{skipClick: true}so typing into a contenteditable doesn't collapse the existing selection - matching Playwright'spage.keyboard.type()behaviour used by vitest's browser runner.Both divergences were validated by running the same
.featurefiles in vitest CI and confirming identical pass counts.What comes next
The current cars validate the runtime; the next chunks are scenario authoring inside the app (drag-from-palette + AI-assisted drafting), exporting
.featurefiles back to the consumer's repo, and the structured-lists plugin as the dogfood target.Brief at
/project-brief.md. Architecture and design at/specs/racetrack*.md.