Agent skill

Writing Tests

by portabletext in portabletext/editor

How to write tests in the Portable Text Editor monorepo. An agent skill from portabletext/editor.

MITAuto-check passedTesting & QA

Install Writing Tests

skills CLI
$ npx skills add portabletext/editor --skill writing-tests -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install portabletext/editor writing-tests --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/portabletext/editor.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/writing-tests .claude/skills/writing-tests && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
writing-tests
GitHub stars
280
Token cost
~1.9k tokens
SKILL.md length
997 words
Files
1
Skills in repo
10
Repo updated
First seen
Licence
MIT

At a glance

How to write tests in the Portable Text Editor monorepo. An agent skill from portabletext/editor.

  • Editing tests in PTE packages
  • SKILL.md covers Test placement, Harnesses, Triggers and Assertion style: no…, plus 2 more sections
  • Calls pnpm and rg
  • Tasks that involve Test generation

What it does

Writing Tests is an agent skill from portabletext/editor. How to write tests in the Portable Text Editor monorepo. Use whenever writing or editing tests in PTE packages. Covers test placement, harnesses, deterministic fixtures, and the house assertion style (no indirection, full values with toEqual).

Its SKILL.md is about 1.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Testing & QA, covering Test generation and Monorepo tooling. It works with Vitest. The repository describes itself as: The Standalone Portable Text Editor. The licence is MIT.

When your agent uses it

  • Editing tests in PTE packages
  • Tasks that involve Test generation
  • Tasks that involve Monorepo tooling

Example prompts

  • “/writing-tests”

What it can do on your machine

Read from SKILL.md and the folder at commit 60a494b. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • pnpm
    • rg

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use pnpm, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Writing Tests loads about 1.9k tokens when it runs. Until then it costs about 64 tokens; SKILL.md has 997 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~64
When it runs · the whole SKILL.md, loaded when a task matches
~1.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from portabletext/editor at commit 60a494b, republished under its MIT licence (© portabletext). 997 words, ~1,899 tokens.

Download SKILL.mdSave it as .claude/skills/writing-tests/SKILL.md (or your agent's skills folder).
name
writing-tests
description
How to write tests in the Portable Text Editor monorepo. Use whenever writing or editing tests in PTE packages. Covers test placement, harnesses, deterministic fixtures, and the house assertion style (no indirection, full values with toEqual).

Writing PTE tests

Test placement

Before writing any test, find its existing home. Always search for an established suite covering the domain (ls tests/ | grep -i <topic>, rg -l <mechanism> tests/) and read its scenario list before creating a file. The canonical suites are broad (behavior-api.test.tsx carries 30+ Behavior contract scenarios); a contract pin belongs next to its siblings, where reviewers and future failures will look for it. A new file is the exception, for a genuinely new domain, not the default. Also check whether the contract is already pinned in a different form before adding it at all.

The file extension is the discriminator, never a .browser suffix:

  • *.test.tsx runs in the browser project (playwright: chromium, firefox, webkit)
  • *.test.ts runs in the unit project (node)
  • *.test-d.ts is type-level, using expectTypeOf from vitest
  • gherkin-tests/*.feature + racejar for behavior specs (Feature({featureText, stepDefinitions, parameterTypes}))

When scaffolding a new package, mirror plugin-sdk-value's vitest config: a browser project including src/**/*.test.tsx and a unit project including src/**/*.test.ts.

Harnesses

  • Editor integration tests: createTestEditor from @portabletext/editor/test/vitest (inside the editor package: ../src/test/vitest). Returns {editor, locator}. Plugins and probes go in as children, but note they mount as siblings after PortableTextEditable; a provider that must wrap the editable needs a hand-rolled render with a comment explaining why.
  • Selector/pure-function tests: createTestSnapshot (in the editor package: test-utils/create-test-snapshot; in plugins: duplicate it locally on public types, it is ~30 lines).
  • Keys: always createTestKeyGenerator from @portabletext/test. It is deterministic (k0, k1, ...), which is what makes full-literal assertions possible. Generated keys may be asserted literally.

Triggers

  • Real user interaction via userEvent (userEvent.type(locator, 'foo')). Typing with no caret set lands at the block start.
  • Everything else via editor.send({type: ...}) with full payloads. This includes native-shaped events: drag.dragover, drag.drop etc. accept complete position/dragOrigin/originEvent objects, so no DOM event simulation is ever needed.
  • Async settling via vi.waitFor around the assertion that proves the trigger landed. Debounced channels (the mutation batcher) need their own vi.waitFor; comparing them synchronously after another assertion passes is a race.
  • Never sleep (setTimeout, arbitrary delays), even for negative asserts. To prove "nothing was emitted", create a deterministic flush point: perform a local edit through the same ordered channel and assert the full emission list contains exactly that edit's patches, a would-be echo would have to surface no later than the edit's own emission. This also upgrades the negative assert to a full-value pin.
  • Simulated races are event-driven, never clock-driven. To reconstruct a stale-echo or interleaving scenario, capture the payload at one observable anchor (vi.waitFor on the emission that carries it) and deliver it after a later observable anchor (the subsequent flush landing in the collected events). Staleness is then a property of which value arrives, independent of runner speed. A fixed-delay reconstruction is flaky by construction on slow runners, even when its premise check fails in the safe direction: the CI failure is honest but intermittent, which is still a broken test.

Assertion style: no indirection, full values

  • Assert complete literal values with toEqual: the full value array, the full event array, the full operation objects. Deterministic keys make this possible. A full-value assertion pins ordering, count, and content at once, and drift shows up as a readable diff.
  • Do not build summarizer helpers (string transcripts, custom matchers, mapping functions) between the collected data and the assertion. The reader should see exactly what the editor emitted.
  • expect.objectContaining, expect.arrayContaining, expect.any, expect.anything, toMatchObject, and toBeDefined are banned. The pte/no-weak-value-assertions lint rule fails on them in every test file.
  • A field that looks variable is almost always a key. Build the editor with createTestKeyGenerator and assert the generated keys literally. Create the key generator and fixtures inside each test, so the keys a test sees never depend on which tests ran before it.
  • expect.stringContaining and expect.stringMatching stay allowed for error and warning text, where the full message would couple the test to its wording.
  • For value-shape assertions where the full tree is noise, use toTextspec(editor.getSnapshot().context) and assert the textspec string ('B: foo bar|'), which is itself a full-value assertion in compact notation.
  • Exact-sequence event tests (EventListenerPlugin collecting EditorEmittedEvents) assert the whole sequence; do not filter event types out to make assertions easier.
Show full SKILL.md (313 more words)Show less

Structure

  • test('Scenario: ...') naming for integration tests. Every integration test carries its own Scenario:; a describe may group by mechanism but never carries the scenario, narration, or a comment preamble.
  • Unit suites covering a single function group under describe(fn.name, ...) (for example describe(buildIndexMaps.name, ...)), tying the group to the symbol it pins and surviving renames. Every other describe and every test name is prose describing behavior. A quoted identifier string is the one banned form: describe('isActiveListItem', ...) goes stale on rename without gaining readability.
  • Test files hold comments to the same bar as source files: the default is none. The scenario name and the full-value assertions are the documentation; narrative about why a contract exists belongs in the commit body, not in a comment above the test.
  • A known-broken contract is pinned with test.fails asserting the desired behavior: it documents the bug executably, and the fix flips it to a plain test (CI reports it as unexpectedly passing until someone does).
  • A quote character inside a test name is solved by switching the string's quotes ("...set's fallout..."), never with \u escapes or backslash-escaping. Prettier keeps whichever quote style avoids the escape.
  • Tests first, helpers below (per repo AGENTS.md: helper functions below main functions).
  • Fixtures are plain functions returning complete objects (function block(key, text): PortableTextBlock), local to the test file.
  • Comments explain why, placed inside the scope they explain, not above it.
  • When test code duplicates production logic from elsewhere (a copied helper in a plugin), port the source's test suite wholesale as the drift alarm, with a header comment naming the source and the keep-in-sync contract.

Gates before pushing

From the package: pnpm check:types, pnpm test:unit, pnpm test:browser (or :chromium while iterating), pnpm build. From the repo root: pnpm check:lint, pnpm check:knip. The unit project fails on "no tests found": a package with only browser tests needs at least one .test.ts, which the drift-alarm suite usually provides.

© portabletext, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/writing-tests of portabletext/editor.

Open the folder on GitHubat commit 60a494b

Compare with similar skills

Writing Tests next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Writing Tests compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Writing Tests this skillportabletext/editor280—~1.9kAutomated safety check: PassMIT
Creating A Packagec15t/c15t1.9k—~913Automated safety check: PassApache-2.0
MoAI TDD Workflowmodu-ai/moai-adk1.2k—~3.1kAutomated safety check: PassApache-2.0
Ever Worksever-works/ever-works158—~2.2kAutomated safety check: PassAGPL-3.0
Concept Page Test Writerleonardomso/33-js-concepts67k—~5.5kAutomated safety check: PassMIT
Ckeditor5 TestingTriliumNext/Trilium38k—~3.3kAutomated safety check: PassAGPL-3.0

Similar skills

  • Scaffold a new workspace package in the c15t monorepo. An agent skill from c15t/c15t.

    1.9k GitHub stars~913 tokensUpdated today
    Testing & QAAuto-check passed
  • MoAI TDD Workflow

    modu-ai/moai-adk

    Drives test-first development through the RED, GREEN, REFACTOR cycle, with a config switch that selects between TDD and a DDD workflow for existing code.

    1.2k GitHub stars~3.1k tokensUpdated today
    Testing & QAAuto-check passed
  • Ever Works

    ever-works/ever-works

    Repo-specific guide to the Ever Works monorepo (ever-works/ever-works) — its pnpm + Turborepo layout, the per-workspace test runners (Jest vs Vitest vs Playwright), file-naming and import-alias…

    158 GitHub stars~2.2k tokensUpdated 3 days ago
    Testing & QAAuto-check passed
  • Concept Page Test Writer

    leonardomso/33-js-concepts

    Generates Vitest tests for every runnable code example on a JavaScript concept documentation page, following a four-phase extraction and conversion process.

    67k GitHub stars~5.5k tokensUpdated 27 days ago
    Testing & QAAuto-check passed
  • Ckeditor5 Testing

    TriliumNext/Trilium

    Testing CKEditor 5 plugins in the Trilium monorepo. An agent skill from TriliumNext/Trilium.

    38k GitHub stars~3.3k tokensUpdated today
    Testing & QAAuto-check passed
  • Caliber Testing

    caliber-ai-org/ai-setup

    Writes Vitest tests following project patterns: tests/ directories, vi.mock() for module mocking with vi.hoisted() for test-time factories, global LLM mock from src/test/setup.ts, environment…

    1.3k GitHub stars~3.2k tokensUpdated 14 days ago
    Testing & QAAuto-check passed

More from portabletext/editor

All 10 skills in this repo
  • Product Copy Assistant

    portabletext/editor

    Write clear, concise, accessible product copy for interfaces, docs, and system messages.

    280 GitHub stars~635 tokensUpdated 2 days ago
    Auto-check passed
  • Changesets

    portabletext/editor

    How to write changesets in the Portable Text Editor monorepo.

    280 GitHub stars~2.1k tokensUpdated 2 days ago
    Auto-check passed
  • Commits

    portabletext/editor

    How to write commit messages in the Portable Text Editor monorepo.

    280 GitHub stars~1.9k tokensUpdated 2 days ago
    Auto-check passed
  • Next Branch

    portabletext/editor

    How the next prerelease branch for the upcoming editor major works in the Portable Text Editor monorepo.

    280 GitHub stars~1.8k tokensUpdated 2 days ago
    Auto-check passed
  • Backporting

    portabletext/editor

    How to backport a fix from main to a maintenance branch (editor-v6.x, editor-v7.x) in the Portable Text Editor monorepo.

    280 GitHub stars~1.1k tokensUpdated 2 days ago
    Auto-check passed
  • Code Comments

    portabletext/editor

    How to write and review code comments in the Portable Text Editor monorepo.

    280 GitHub stars~1.1k tokensUpdated 2 days ago
    Auto-check passed

Works with

Questions about Writing Tests

What does Writing Tests do?

How to write tests in the Portable Text Editor monorepo. An agent skill from portabletext/editor. Writing Tests is an agent skill from portabletext/editor. How to write tests in the Portable Text Editor monorepo.

When should I use Writing Tests?

Writing Tests fits situations like: editing tests in PTE packages; tasks that involve Test generation; tasks that involve Monorepo tooling.

How do I install Writing Tests in Claude Code?

Run `npx skills add portabletext/editor --skill writing-tests -a claude-code`. Or copy the skill folder (.agents/skills/writing-tests in portabletext/editor) into .claude/skills/writing-tests in your project. Claude Code loads it when a task matches its description.

How do I install Writing Tests in Codex?

Run `npx skills add portabletext/editor --skill writing-tests -a codex`. Or copy the skill folder (.agents/skills/writing-tests in portabletext/editor) into .agents/skills/writing-tests in your project. Codex loads it when a task matches its description.

Can I use Writing Tests in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add portabletext/editor --skill writing-tests -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/writing-tests, .gemini/skills/writing-tests, .github/skills/writing-tests and .opencode/skills/writing-tests in your project.

What does Writing Tests need to run?

Going by SKILL.md and its folder, Writing Tests needs the command-line tools its instructions call (pnpm and rg).

Does Writing Tests access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Writing Tests safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Writing Tests use?

Writing Tests is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Writing Tests use?

About 1.9k tokens (SKILL.md is roughly 7.6k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Writing Tests?

Skills that share tags, products or a category with Writing Tests: Creating A Package (c15t/c15t, 1.9k stars), MoAI TDD Workflow (modu-ai/moai-adk, 1.2k stars), Ever Works (ever-works/ever-works, 158 stars) and Concept Page Test Writer (leonardomso/33-js-concepts, 67k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Writing Tests?

portabletext (a GitHub organization) maintains it in portabletext/editor, which has 280 GitHub stars. The repository holds 10 skills in this directory. The repository was last updated on October 6, 2026.

Source: portabletext/editor on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.