cookcli
CLAUDE.md1.4k starsChanged 5 days agoMITView on GitHub
CookCLI is a command-line interface for managing Cooklang recipes. It's written in Rust and includes a web server with server-side rendered HTML using Askama templates and Tailwind CSS. The project follows UNIX philosophy where each command does one thing well. build.rs stops the build until static/css/output.css and static/js/editor.bundle.js exist. They are gitignored, so a fresh clone or git worktree needs npm ci && npm run build-css && npm run build-js before cargo. In debug builds rust-embed reads static/ from the…
- Installs packages
We split this CLAUDE.md into the rules it gives an agent
32 rules. About 0 are common, 15 are distinctive, and 17 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈15
Distinctive · estimate
This author's own choices. Worth a look.
17
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 184
Every handler that changes something takes Extension<Viewer> and, once the change succeeded, calls server::activity::record — one stdout …
- Very specific: line 191
Never put e.to_string() in a response body by hand, and never put the recipe directory's absolute path in a page or API answer.
- Very specific: line 239
Every parser is built with Extensions::empty() (crates/core/src/parser.rs, crates/format/src/lib.rs): no @& references, no range values …
- Very specific: line 284
Add every new message to every locale.
- Distinctive: line 331
When a change alters output other pages share (URLs, templates, badges), run the whole Playwright project, not only the specs you touched — …
- Very specific: line 364
Use cook seed to create test recipes
- Distinctive: line 365
Test each command with various options
- Distinctive: line 367
Check error handling with invalid inputs
- Very specific: line 408
When Dependabot cannot fix an advisory because the fixed version raises other crates' minimums, cargo update -p <crate> --precise <version> …
- Distinctive: line 414
Always run these checks before creating a PR:
- Distinctive: line 421
All three must pass cleanly with no warnings or errors.
- Very specific: line 426
Add variant to Command enum in src/args.rs
- Very specific: line 427
Create module in src/ with XxxArgs struct and run() function
- Very specific: line 428
Add case in main.rs match statement
- Distinctive: line 429
Update help text and documentation
- Very specific: line 435
Create new template in templates/ directory
- Distinctive: line 438
Add route in the UI router
- Very specific: line 442
Run make css or npm run build-css to compile
- Very specific: line 448
Start CSS watch: npm run watch-css
- Very specific: line 456
Use RUST_LOG=trace to see detailed parsing information including:
- Very specific: line 693
Use beforeEach for setup
- Distinctive: line 697
Prefer semantic selectors (roles, labels)
- Distinctive: line 698
Use data attributes for test-specific targeting
- Distinctive: line 699
Avoid brittle CSS selectors
- Very specific: line 702
Use explicit waits (waitForLoadState)
- Distinctive: line 703
Check visibility before interaction
- Distinctive: line 704
Verify both positive and negative cases
- Distinctive: line 707
Run tests in parallel when possible
- Distinctive: line 708
Use page objects for reusability
- Very specific: line 752
Server not starting: Ensure make dev_server works locally
- Very specific: line 753
Browser installation: Run npx playwright install
- Distinctive: line 754
Port conflicts: Check port 9080 is available
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Repository Overview
CookCLI is a command-line interface for managing Cooklang recipes. It's written in Rust and includes a web server with server-side rendered HTML using Askama templates and Tailwind CSS. The project follows UNIX philosophy where each command does one thing well.
Build and Development Commands
Building
# Full release build (includes CSS compilation)
make release
# Development build
cargo build
make build # Includes CSS compilation
# Build CSS only
make css
npm run build-css
# Build JS only
make js
npm run build-js
# Build specific workspace member
cargo build -p cookcli
# Run without building
cargo run -- [command] [args]
# Docker image for local testing (thin-LTO `docker-dev` profile, much faster
# rebuilds than `make docker-build`, which uses `release`)
make docker-build-devbuild.rs stops the build until static/css/output.css and
static/js/editor.bundle.js exist. They are gitignored, so a fresh clone or git
worktree needs npm ci && npm run build-css && npm run build-js before cargo.
In debug builds rust-embed reads static/ from the source tree at runtime
(src/web/mod.rs), so a debug cook copied or mounted at a different path
serves pages without CSS or JS.
Testing
# Run all tests
cargo test
# Run specific test
cargo test test_name
# Run tests with output
cargo test -- --nocapture
# Run with debug logging
RUST_LOG=debug cargo run -- [command]
RUST_LOG=trace cargo run -- [command] # More verboseWeb UI Development
# Start development server with CSS built
make dev_server
# Start development with CSS watch mode
make css-watch # In one terminal
cargo run -- server ./seed # In another terminal
# Install dependencies (first time setup)
npm install
# Build CSS for production
npm run build-css
# Watch CSS changes during development
npm run watch-css
# Build JS for production
npm run build-js
# Watch JS changes during development
npm run watch-jsLinting and Formatting
cargo fmt # Format code
cargo clippy # Lint codeArchitecture
Workspace Structure
This is a Cargo workspace with multiple crates:
cookcli(this crate) - The CLI applicationcrates/core(cookcli-core) - Command logic as a librarycrates/format(cooklang-format) - Recipe output formatters, published for reuse../cooklang-rs- Core Cooklang parser and models../cooklang-find- Recipe discovery and search../cooklang-reports- Template-based report generation../cooklang-import- Recipe import from websites (external dependency)
Command Architecture
Each command is a module in src/ with a consistent pattern:
XxxArgsstruct insrc/args.rsor module file - Clap argument definitionspub fn run(ctx: &Context, args: XxxArgs) -> Result<()>- Entry point- Commands receive a
Contextwith base_path, aisle, and pantry configuration
Context System
The Context struct (in src/main.rs) provides:
base_path: Current working directory for recipesaisle(): Returns path to aisle.conf (local./config/or global~/.config/cook/)pantry(): Returns path to pantry.conf (same search pattern)
Configuration search order:
./config/[aisle.conf|pantry.conf]- Local to recipe directory- The global config directory, resolved by
cookcli_core::global_config_path:$COOK_CONFIG_DIRwhen set to a non-empty value- otherwise
~/.config/cook/(Linux),~/Library/Application Support/cook/(macOS),%APPDATA%\cook\config\(Windows)
global_config_path also resolves session.json (src/login.rs,
src/logout.rs, src/server/mod.rs) and sync.db (sync::sync_db_path), so
COOK_CONFIG_DIR moves the whole of CookCLI's global state at once.
Command Modules
Core Commands
recipe: Parse and display recipes (supports multiple output formats)shopping_list: Generate shopping lists with ingredient aggregationserver: Web server using Axum + Askama templates with Tailwind CSSsearch: Full-text recipe searchimport: Import from websites via cooklang-importdoctor: Validate recipes and check configurationsseed: Initialize with example recipesreport: Generate custom outputs using Jinja2 templatesupdate: Self-update the CookCLI binary to the latest version (can be disabled with --no-self-update feature)completions: Print a shell completion script (bash, zsh, fish, powershell, elvish) generated from the clap definition
Utility Modules
util/: Shared utilities for parsing, conversion, and output formatting- Recipe parsing and scaling
- Output format conversions (human, JSON, YAML, Markdown, Cooklang)
- Shopping list generation with aisle/pantry support
Web Server
- Backend: Axum web framework in
src/server/ - Frontend: Server-side rendered HTML using Askama templates
- Templates: Located in
templates/directory - Styling: Tailwind CSS with custom components
- API handlers in
src/server/handlers/ - Static files served from
static/directory - Shopping list stored as
.shopping-listand.shopping-checkedat the root of the recipe directory (ShoppingListStore);server/shopping_list_watcher.rspushes outside edits to open pages - Opt-in sign-in in
src/server/auth/: ausers.tomlin the config dir (or--users-file/COOK_USERS_FILE) turns it on. Each user has a role (reader<shopper<editor<admin; a bare hash isadmin).auth::middleware::required_capabilityis the one place deciding whichCapabilitya request needs — every non-GET route except an explicit allowlist, plus a few GETs (/edit,/new,/api/ws/lsp,/api/sync). A write route not listed in itsWRITEStable needsAdminister, so list a new one there at the capability it deserves. A GET handler must never change anything. Guests get401, signed-in users whose role falls short403; on/apiboth are translated and markedX-Cook-Refused, whichstatic/js/sign-in-notice.jsturns into a notice (with a sign-in link for401) for any page's write. Templates get aViewer(src/web/viewer.rs) and hide controls withviewer.can_edit_lists()/can_edit_recipes()/can_admin() --recipes-only/COOK_RECIPES_ONLY: whoever is not signed in (everyone without a users file) only reads recipes.auth::middleware::browses_recipesis an allowlist (RECIPE_READS) of what they may request; anything else, a new route included, asks them to sign in (404without sign-in). TheirViewercan do nothing andviewer.recipes_only()hides the rest in templates- Leaving a page with something unsaved asks first (
static/js/unsaved.js, loaded bybase.html). A write sent withfetchcounts on its own until it answers. Mark what holds typed data:data-unsavedon a form, inline editor or field (it counts while on screen and changed since first touched),data-dirtywhile a page holds changes it has not sent, orCookUnsaved.watch(fn). A page that moves on by itself after saving usesCookUnsaved.leave(url)/CookUnsaved.reload()instead oflocation - Every handler that changes something takes
Extension<Viewer>and, once the change succeeded, callsserver::activity::record— one stdout line saying who did what. Quote request text withactivity::quoted/activity::fileso it cannot forge a line. - Error text sent to a client goes through
handlers::common::json_errororui::error_page, which pass it toserver::private_paths::hide: paths come out relative to the recipe directory, or as…/filefor a config folder. Never pute.to_string()in a response body by hand, and never put the recipe directory's absolute path in a page or API answer. - Files from the recipe directory are served narrowly:
/api/staticgives only pictures and never a name starting with.(server/static_files.rs), and the recipe endpoints take only.cook/.menupaths (handlers::common::is_recipe_request). Serve new files through these rather than a freshServeDir.
Recipe Processing Pipeline
- Recipe discovery via
cooklang-find(handles paths and search) - Parsing via
cooklangparser (lenient mode for validation) - Scaling and conversion in
util/modules - Output formatting based on requested format
Error Handling
- Uses
anyhow::Resultthroughout for error propagation - User-friendly error messages with context
- Special handling for recipe validation (warnings vs errors)
Important Implementation Details
Recipe Scaling
- Scaling notation:
recipe.cook:2(uses colon) - Handled in
util::split_recipe_name_and_scaling_factor() - Applied during parsing before output
Shopping List Aggregation
- Ingredients with same name are automatically combined
- Unit conversion handled by cooklang crate
- Aisle categorization via aisle.conf
- Pantry filtering via pantry.conf (TOML format with quantities and dates)
Doctor Command
validate: Checks syntax, deprecated features, and recipe referencesaisle: Finds ingredients not in aisle configuration- Exit codes: Normal (0) or strict mode (1) for CI/CD
Report Command
- Uses cooklang-reports with Jinja2 templates
- Config builder pattern for scale, datastore, aisle, pantry
- Falls back to context configurations if not specified
File Formats
.cookfiles use Cooklang markup- Supports YAML frontmatter or
>>metadata (deprecated) - Output formats: human, json, yaml, markdown, cooklang
Parser and Config File Limits
- Every parser is built with
Extensions::empty()(crates/core/src/parser.rs,crates/format/src/lib.rs): no@&references, no range values (2-3stays text), no Unicode fractions. Don't turn extensions on in CookCLI; they need a spec change first (#219, #502). --and[-open a comment anywhere on a line,>notes included, and\escapes one character. The editor's highlighting (static/js/src/cooklang-mode.js) has to follow the same rules.- cooklang's
scale_to_targetrounds a servings target to a whole number, so the shopping list scales servings references itself (scale_to_servingsincrates/core/src/shopping_list/mod.rs). - Recipe reference paths keep their leading
./(/recipe/./Shared/X): strip./and the extension before comparing two of them. aisle.confonly has//comments;#or--lines become ingredients, whichcookcli_core::aisle::comment_lookalikeswarns about.pantry.confis TOML, edited withtoml_editincookcli-coreso comments survive; the server writes it through core too.
Frontend Architecture
Template System
The web UI uses server-side rendering with Askama templates:
- Templates located in
templates/directory - Base template (
base.html) provides common layout - Page-specific templates extend the base template
- Template data structures defined in
src/web/templates.rs - Templates are compiled into the binary: a template change needs a rebuild and a server restart
- In page scripts, DOM
replaceChildren/appendwritefalseandundefinedas text, so never pass themcond && node; filter first, likeel()/fill()intemplates/aisles.html
Styling
- Tailwind CSS for utility-first styling
- Custom components defined in
static/css/components.css - Compiled CSS output in
static/css/output.css - Tailwind is configured in
static/css/input.css(@source,@custom-variant,@theme); components live instatic/css/components.css
Translations
- UI strings are Fluent messages in
locales/<lang>/*.ftl(aisles,common,pantry,preferences,recipes,shopping), one folder per locale: de-DE, en-US, es-ES, eu-ES, fr-FR, it-IT, ja-JP, nl-NL, sv-SE - A literal
{in a message is written{"{"} - Templates call
tr.t("key"),tr.tn("key", count)(plurals) andtr.tf("key", factor), defined onTrinsrc/web/templates.rs; askama passes fields by reference, so helpers take&usize/&f64 - Add every new message to every locale.
tests/locales_test.rsfails when a locale lacks an en-US message id, or defines one twice — Fluent rejects a locale with a duplicate id, and every page in that language panics. Two branches adding the same key both pass on their own, so check again after a merge or rebase
Key Templates
base.html- Common layout with navigation and searchrecipes.html- Recipe listing with directory navigationrecipe.html- Individual recipe display with scalingmenu.html- Menu and meal plan displayedit.html/new.html- Recipe and menu editor, new file formshopping_list.html- Shopping list managementpantry.html/aisles.html- Pantry and aisle managementpreferences.html- User preferences and settings
Frontend Features
- Recipe Browsing: Directory-based navigation with breadcrumbs
- Recipe Display: Ingredients, steps, metadata with colorful badges
- Shopping List: Persistent storage in
.shopping-list/.shopping-checkedin the recipe directory - Recipe Scaling: Dynamic scaling with URL parameters
- Search: Real-time recipe search with dropdown results
- Responsive Design: Mobile-friendly layout with Tailwind
CSS Component Classes
Component classes live in static/css/components.css and resolve every colour through the tokens in static/css/input.css:
.card/.card-head- Bordered surface with hairline and 6px radius.btn,.btn-primary,.btn-danger,.btn-sm- 40px controls (32px compact); one accent-filled primary per view.icon-btn- 36px icon-only control.nav-card,.nav-pill,.menu-item- Navigation card, pills, and small-screen menu rows.recipe-card,.recipe-card-icon,.recipe-card-title- Index cards with a flat icon disc.ingredient-badge,.cookware-badge,.timer-badge- Inline recipe entities (tint, dotted underline, chip).metadata-pill,.tag- Neutral bordered pills.step-box,.step-number,.step-body,.step-refs- Boxed recipe steps.ingredient-row,.row,.row-value,.row-note- List rows.pantry-row,.pantry-editor,.pantry-expiry,.pantry-actions,.item-status-dot- Pantry item rows, their edit panel, expiry tags and stock state.stepper,.select,.search-input- Form controls.editor-toolbar,.editor-toolbar-group,.editor-toolbar-glyph- Cooklang insert toolbar above the editor (static/js/src/toolbar.js)
Testing Approach
Automated tests live in two places:
- Rust integration tests in
tests/*.rs(cargo test), withinstasnapshots intests/snapshots/ - Playwright end-to-end specs in
tests/e2e/(npm test; see UI Testing below)
Things to know when running them:
- When a change alters output other pages share (URLs, templates, badges), run the whole Playwright project, not only the specs you touched — other specs assert on that output too.
- The pantry e2e specs add and edit items in
./seed, which can leaveseed/config/pantry.confmodified; checkgit statusafter a run. - Two
cookcli-corepantry tests check file permissions and cannot pass when cargo runs as root (e.g. in a root container). Their failure message says so; it is not a regression. cargo teststops at the first failing test binary; add--no-fail-fastto see them all.- Build the expected value of a path in a test with the platform separator
(
Path::join, or.replace('/', std::path::MAIN_SEPARATOR_STR)as intests/doctor_characterization_test.rs). CookCLI prints native paths, so a literal"config/pantry.conf"fails only on the Windows CI job. URLs and API paths the code normalizes to/are the exception. docs/api.mdis generated from the API docs: regenerate it withUPDATE_API_DOCS=1 cargo test --test api_docs_md_test.- The Docker image builds with
--no-default-features --features server,lsp(nosync). Anything the server needs must not sit under#[cfg(feature = "sync")]; check with that build when touchingAppState. - Two checkouts must not share a
CARGO_TARGET_DIR: the artifacts have the same names, so one checkout's build replaces thecookthe other's tests run. - Playwright is fully parallel locally but uses one worker on CI. The
shopping-list and pantry specs share files in
./seed, so retry a local failure there with--workers=1before blaming the change, and restoreseed/config/aisle.confas well aspantry.confafterwards. - A spec that fakes ticks through the
POST /api/shopping_listanswer must also stubGET /api/shopping_list/checked: the page's event stream refetches the ticks when it opens. - The Docker image's
docker-entrypoint.shrefuses a/recipesit cannot write; mount a writable copy to try it.
For manual testing:
- Use
cook seedto create test recipes - Test each command with various options
- Validate output formats
- Check error handling with invalid inputs
Isolating spawned processes from the real config
Any test that spawns cook — and every test that spawns cook server —
must route its Command through common::with_isolated_config
(tests/common/mod.rs), which sets COOK_CONFIG_DIR to a directory inside the
test's TempDir.
This is not about tidiness. cook server loads session.json on boot and, if
it finds one, starts syncing the directory it was given against the global
sync.db — the database tracking the developer's own recipe folder. Without
isolation, cargo test on a machine where someone has run cook login can
upload fixture recipes to their real cook.md account, or index a temp directory
into that database and propagate the deletions.
Setting HOME / XDG_CONFIG_HOME is not sufficient: directories
resolves the Windows config directory through the Known Folder API and ignores
both. COOK_CONFIG_DIR is the only lever that works everywhere.
tests/config_dir_env_test.rs is the regression guard; the Playwright
webServer sets the same variable in playwright.config.ts.
Release Process
Uses semantic commit messages for automated releases:
feat:- New featuresfix:- Bug fixesdocs:- Documentation changeschore:- Maintenance tasks
Packaging scripts live in packaging/: debian/ (.deb), fedora/ (.rpm) and
source-tarball.sh, which builds the source tarball with the compiled front-end
assets (shared by the release workflow and the RPM build).
Release workflow (.github/workflows/release.yaml) traps:
- The Linux glibc binary is built in PyPA's
manylinux_2_28image (Cross.toml), and a release step fails when the binary needs a glibc aboveFLOOR=2.28. Building it natively onubuntu-latestwould raise that floor to the runner's glibc (#420). Bump the image's dated tag, neverlatest. - The
Swatinem/rust-cachekey includesmatrix.target: a shared key once fed build scripts linked against a newer glibc to older cross images (#656). - When Dependabot cannot fix an advisory because the fixed version raises
other crates' minimums,
cargo update -p <crate> --precise <version>can (#640).
Before Creating a PR
Always run these checks before creating a PR:
cargo fmt # Fix formatting
cargo clippy # Check for lint warnings
cargo test # Run testsAll three must pass cleanly with no warnings or errors.
Common Development Tasks
Adding a New Command
- Add variant to
Commandenum insrc/args.rs - Create module in
src/withXxxArgsstruct andrun()function - Add case in
main.rsmatch statement - Update help text and documentation
Modifying Output Formats
Output formatting is centralized in src/util/ modules. Each format has its own module with consistent interface.
Working with Templates
- Create new template in
templates/directory - Define data structure in
src/web/templates.rs - Implement handler in
src/server/ui.rs - Add route in the UI router
Modifying Styles
- Edit component classes in
static/css/components.css - Run
make cssornpm run build-cssto compile - For development, use
npm run watch-cssfor auto-rebuild - Custom colors and utilities can be added to
static/css/input.css(@theme) and component classes tostatic/css/components.css
Frontend Development Workflow
- Install dependencies:
npm install - Start CSS watch:
npm run watch-css - Run server:
cargo run -- server ./seed - Make changes to templates or styles
- Rebuild and restart the server for template changes (Askama compiles them
in); CSS and JS changes only need a browser refresh in a debug build, once
watch-css/watch-jshas rebuilt them
Debugging Recipe Parsing
Use RUST_LOG=trace to see detailed parsing information including:
- File discovery paths
- Configuration loading
- Recipe reference resolution
- Static file serving paths
UI Testing Documentation
This document describes the comprehensive UI testing setup for CookCLI using Playwright.
Overview
The testing suite provides end-to-end (E2E) testing for the CookCLI web interface, covering:
- Navigation and routing
- Recipe display and scaling
- Shopping list functionality
- Search capabilities
- Pantry management
- User preferences
- Accessibility compliance
- Performance metrics
Setup
Installation
# Install dependencies
npm install
# Install Playwright browsers
npx playwright installRunning Tests
# Run all tests
npm test
# Run tests with UI mode (interactive)
npm run test:ui
# Run tests in debug mode
npm run test:debug
# Run tests with browser visible
npm run test:headed
# Run specific browser tests
npm run test:chrome
npm run test:firefox
npm run test:webkit
# Run mobile tests
npm run test:mobile
# Show test report
npm run test:report
# Generate tests interactively
npm run test:codegenTest Structure
Directory Layout
tests/
├── e2e/ # End-to-end tests
│ ├── navigation.spec.ts # Navigation and routing tests
│ ├── recipe-display.spec.ts # Recipe rendering tests
│ ├── recipe-scaling.spec.ts # Recipe scaling functionality
│ ├── search.spec.ts # Search functionality
│ ├── shopping-list.spec.ts # Shopping list management
│ ├── preferences.spec.ts # User preferences
│ ├── pantry.spec.ts # Pantry management
│ ├── accessibility.spec.ts # WCAG compliance tests
│ └── performance.spec.ts # Performance metrics
└── fixtures/ # Test utilities
└── test-helpers.ts # Reusable helper functionsTest Helpers
The test-helpers.ts file provides reusable utilities:
- TestHelpers: Common navigation and interaction methods
- RecipePage: Recipe-specific page object model
- ShoppingListPage: Shopping list page object model
Test Coverage
Navigation Tests (navigation.spec.ts)
- Home page display
- Recipe navigation
- Breadcrumb navigation
- Directory browsing
- Navigation menu consistency
- Browser history handling
Recipe Display (recipe-display.spec.ts)
- Recipe title and description
- Ingredients list
- Cooking steps
- Ingredient/cookware/timer highlighting
- Recipe metadata
- Responsive layout
Recipe Scaling (recipe-scaling.spec.ts)
- Scale input functionality
- URL parameter scaling
- Decimal scaling support
- Scaling persistence
- Shopping list integration
Search (search.spec.ts)
- Search input functionality
- Search results display
- No results handling
- Special character support
- Case-insensitive search
- Search persistence
Shopping List (shopping-list.spec.ts)
- Empty state display
- Adding ingredients
- Item completion toggling
- List clearing
- Ingredient aggregation
- Aisle organization
- Session persistence
Preferences (preferences.spec.ts)
- Preference display
- Pantry configuration
- Aisle configuration
- File upload handling
- Configuration validation
- Settings persistence
Pantry Management (pantry.spec.ts)
- Pantry navigation
- Item display
- Adding/editing/removing items
- Shopping list filtering
- Recipe integration
- Import/export functionality
Accessibility (accessibility.spec.ts)
- WCAG 2.0 AA compliance
- Keyboard navigation
- Screen reader support
- ARIA labels
- Color contrast
- Focus indicators
- Form labels
Performance (performance.spec.ts)
- Page load times
- Search performance
- Scaling responsiveness
- Memory usage
- Asset caching
- Cumulative Layout Shift (CLS)
Configuration
Playwright Configuration (playwright.config.ts)
Key settings:
- Base URL:
http://localhost:9080 - Web Server: Automatically starts dev server
- Browsers: Chrome, Firefox, Safari, Mobile
- Parallel Execution: Enabled
- Retries: 2 on CI, 0 locally
- Artifacts: Screenshots, videos, traces on failure
CI/CD Integration
The UI Tests job of .github/workflows/test.yml:
- Runs on push/PR to main branch, alongside the Rust test jobs
- Chromium only, against the pre-built debug binary
- Uploads the Playwright report, plus videos when a test fails
Changes under .github/ are checked by workflow-lint.yml (actionlint and
zizmor). To run the same checks locally:
docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.12
docker run --rm -v "$PWD:/repo" -w /repo ghcr.io/zizmorcore/zizmor:1.30.1 --min-severity=low .github/workflowsWriting New Tests
Basic Test Structure
import { test, expect } from '@playwright/test';
import { TestHelpers } from '../fixtures/test-helpers';
test.describe('Feature Name', () => {
let helpers: TestHelpers;
test.beforeEach(async ({ page }) => {
helpers = new TestHelpers(page);
await helpers.navigateTo('/');
});
test('should do something', async ({ page }) => {
// Test implementation
await expect(page.locator('selector')).toBeVisible();
});
});Using Test Helpers
// Navigate to a page
await helpers.navigateTo('/recipes');
// Search for a recipe
await helpers.searchRecipe('pasta');
// Scale a recipe
await helpers.scaleRecipe(2);
// Add to shopping list
await helpers.addToShoppingList();Best Practices
Test Isolation
- Each test should be independent
- Use
beforeEachfor setup - Clean up after tests when necessary
Selectors
- Prefer semantic selectors (roles, labels)
- Use data attributes for test-specific targeting
- Avoid brittle CSS selectors
Assertions
- Use explicit waits (
waitForLoadState) - Check visibility before interaction
- Verify both positive and negative cases
Performance
- Run tests in parallel when possible
- Use page objects for reusability
- Minimize test data setup
Debugging
Visual Debugging
# Run with UI mode
npm run test:ui
# Run with browser visible
npm run test:headed
# Debug specific test
npm run test:debugTrace Viewer
Traces are automatically captured on failure:
# View trace
npx playwright show-trace trace.zipScreenshots and Videos
Available in test-results/ directory after failures.
Continuous Integration
Tests run automatically on:
- Push to main branch
- Pull requests
- Manual workflow dispatch
Results available as GitHub Actions artifacts.
Troubleshooting
Common Issues
- Server not starting: Ensure
make dev_serverworks locally - Browser installation: Run
npx playwright install - Port conflicts: Check port 9080 is available
- Slow tests: Increase timeouts in config
Environment Variables
CI: Set in CI environment for different behaviorDEBUG: Enable Playwright debug output
Future Improvements
- Visual regression testing
- API mocking for edge cases
- Load testing with multiple concurrent users
- Internationalization testing
- Cross-browser compatibility matrix
Trending
Steady: 0 new projects took it in the last 4 weeks, none the 4 weeks before.thin data
Who's talking about cooklang/cookcli
- NixOS/nixpkgs“Package request: cookcli”GitHub · Jan 12, 2024 · 3 reactions
- NixOS/nixpkgs“cook-cli: 0.32.1 -> 0.33.0”GitHub · Aug 11, 2026 · 2 reactions
- cooklang/cooklang-rs“Add Typst Package so .cook files can be imported to Typst”GitHub · Dec 15, 2025 · 2 reactions
- A Hacker News story“Show HN: CookCLI New Big Release”Hacker News · Aug 28, 2025 · 2 points
- NixOS/nixpkgs“cook-cli: re-add server”GitHub · Aug 13, 2026 · 1 reactions
The biggest 5 of 52 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 52
Talked about on GitHub, Hacker News and Bluesky (52 times).
On GitHub · 50
- Jan 12, 2024Package request: cookcli — NixOS/nixpkgs3 reactions · 1 comment
- Aug 11, 2026cook-cli: 0.32.1 -> 0.33.0 — NixOS/nixpkgs2 reactions · 1 comment
- Dec 15, 2025Add Typst Package so .cook files can be imported to Typst — cooklang/cooklang-rs2 reactions · 2 comments
- Aug 13, 2026cook-cli: re-add server — NixOS/nixpkgs1 reaction · 16 comments
- Jun 18, 2026cook-cli: 0.31.0 -> 0.32.0 — NixOS/nixpkgs1 reaction · 1 comment
45 more on GitHub
- Mar 5, 2026cook-cli: 0.20.0 -> 0.24.0 — NixOS/nixpkgs1 reaction · 4 comments
- Nov 10, 2025minor(seed.md): update Lamb Chops recipe file path — cooklang/cooklang.org1 reaction
- Sep 25, 2025cook-cli: 0.14.0 -> 0.18.1 — NixOS/nixpkgs1 reaction · 3 comments
- May 23, 2025Update Request: cook-cli 0.10.0 → 0.12.1 — NixOS/nixpkgs1 reaction
- Nov 16, 2021Update comment syntax highlighting — luizribeiro/vim-cooklang1 reaction
- Oct 28, 2026Guides: fix CookCLI download links for the 0.39 archive names — cooklang/cooklang.org0 reactions
- Oct 27, 2026fix(grammar-receipts): restore receipts for LFS and extracted corpora — odvcencio/gotreesitter0 reactions
- Oct 24, 2026cookcli 0.39.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Oct 10, 2026cook-cli: 0.37.0 -> 0.38.1 — NixOS/nixpkgs0 reactions · 1 comment
- Oct 9, 2026cookcli 0.38.1 — Homebrew/homebrew-core0 reactions · 1 comment
- Oct 8, 2026cookcli 0.38.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Sep 29, 2026cook-cli: 0.36.0 -> 0.37.0 — NixOS/nixpkgs0 reactions · 1 comment
- Sep 25, 2026Auto update failing for cookcli — termux/termux-packages0 reactions · 1 comment
- Sep 25, 2026cookcli 0.37.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Sep 19, 2026cook-cli: 0.35.0 -> 0.36.0 — NixOS/nixpkgs0 reactions · 3 comments
- Sep 18, 2026cookcli 0.36.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Sep 8, 2026cook-cli: 0.34.0 -> 0.35.0 — NixOS/nixpkgs0 reactions
- Aug 30, 2026cookcli 0.35.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Aug 29, 2026cook-cli: 0.33.1 -> 0.34.0 — NixOS/nixpkgs0 reactions · 3 comments
- Aug 23, 2026cookcli 0.34.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Aug 19, 2026cook-cli: 0.33.0 -> 0.33.1 — NixOS/nixpkgs0 reactions · 1 comment
- Aug 12, 2026cookcli 0.33.1 — Homebrew/homebrew-core0 reactions · 1 comment
- Aug 10, 2026Auto update failing for cookcli — zrsx/termux-ng0 reactions
- Aug 10, 2026Auto update failing for cookcli — termux/termux-packages0 reactions
- Aug 10, 2026cookcli 0.33.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Jun 27, 2026cook-cli: 0.32.0 -> 0.32.1 — NixOS/nixpkgs0 reactions · 4 comments
- Jun 26, 2026cookcli 0.32.1 — Homebrew/homebrew-core0 reactions · 1 comment
- Jun 13, 2026cookcli 0.32.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Jun 9, 2026cook-cli: 0.30.0 -> 0.31.0 — NixOS/nixpkgs0 reactions · 8 comments
- Jun 9, 2026cookcli 0.31.0 — Homebrew/homebrew-core0 reactions · 1 comment
- May 23, 2026cook-cli: 0.29.1 -> 0.30.0 — NixOS/nixpkgs0 reactions · 2 comments
- May 18, 2026cook-cli: 0.29.1 -> 0.30.0 — NixOS/nixpkgs0 reactions · 4 comments
- May 18, 2026cookcli 0.30.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Apr 24, 2026cookcli 0.29.1 — Homebrew/homebrew-core0 reactions · 1 comment
- Apr 14, 2026cookcli 0.29.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Apr 9, 2026cookcli 0.28.1 — Homebrew/homebrew-core0 reactions · 1 comment
- Apr 8, 2026Auto update failing for cookcli — termux/termux-packages0 reactions
- Apr 8, 2026cookcli 0.28.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Apr 2, 2026cookcli 0.27.2 — Homebrew/homebrew-core0 reactions · 1 comment
- Apr 2, 2026cook-cli: 0.27.0 -> 0.27.1 — NixOS/nixpkgs0 reactions · 3 comments
- Mar 24, 2026cookcli 0.27.1 — Homebrew/homebrew-core0 reactions · 1 comment
- Mar 23, 2026cook-cli: 0.26.0 -> 0.27.0 — NixOS/nixpkgs0 reactions · 3 comments
- Mar 23, 2026cookcli 0.27.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Mar 10, 2026cookcli 0.26.0 — Homebrew/homebrew-core0 reactions · 1 comment
- Mar 9, 2026cookcli 0.25.1 — Homebrew/homebrew-core0 reactions · 1 comment
On Hacker News · 1
- Aug 28, 2025Show HN: CookCLI New Big Release2 points
On Bluesky · 1
- Aug 29, 2025Open source. No accounts. Your data. Docs: cooklang.org GitHub: github.com/cooklang/cookcli Breaking: scaling now uses : instead of @… — @cooklang.bsky.social1 likes and repost · 1 reply
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

