Tests
A package can ship unit tests (vitest) and end-to-end tests (Playwright). Each member carries its own vitest.config.ts and playwright.config.ts that inherit the app shell’s canonical config, and the tinycld-pkg CLI runs them - scoped to one member, or across every present member with --all. You don’t need to wire global config; place files in the right location and follow the naming conventions below.
Directory and naming
Put tests under a tests/ folder at the root of the package. Unit tests end in .test.ts; e2e specs live under tests/e2e/ and end in .spec.ts:
example/
tests/
manifest.test.ts
e2e/
example.spec.ts
Declare the directory in the manifest:
tests: { directory: 'tests' },
How discovery works
Each member ships two small config files that defer to the app shell’s canonical setup:
vitest.config.tsmerges the app’svitest.config(so@tinycld/core/*and other aliases resolve identically), adds the package’s own~/*source alias, and scopes the run to this package’stests/**/*.test.{ts,tsx}.playwright.config.tsspreads the app’splaywright.config(its webServer + browser setup) and pointstestDirat this package’stests/e2e(routed through the workspacenode_modules/@tinycld/<slug>symlink so node resolution finds@playwright/testin the hoisted install).
The tinycld-pkg CLI runs the right config for whichever member you’re in:
# from any member directory
pnpm exec tinycld-pkg test # vitest for this member
pnpm exec tinycld-pkg test:e2e # playwright for this member
pnpm exec tinycld-pkg check # typecheck + unit for this member
From anywhere, add --all to run across every present member:
pnpm exec tinycld-pkg test --all
pnpm exec tinycld-pkg test:e2e --all
pnpm exec tinycld-pkg check --all
The app shell also exposes pnpm run test, pnpm run test:e2e, and pnpm run check (single member = the app), plus pkg:test:unit / pkg:check / pkg:test:e2e wrappers that run --all.
What you can import
Package tests inherit the app shell’s resolver context, so the same aliases work:
~/...resolves to your package’s own nested source (~/tinycld/<slug>/...).@tinycld/core/...resolves to thecoremember. Import@tinycld/core/lib/pocketbase,@tinycld/core/ui/form, etc.@tinycld/<other-pkg>/...resolves to another present feature member - useful only if your package legitimately depends on another (it usually shouldn’t).- Relative imports resolve within your package.
Use the app shell’s test helpers (@tinycld/core test helpers, Playwright page objects under the app’s tests/) rather than reinventing fixtures. If a helper you need doesn’t exist yet and is broadly reusable, it belongs in @tinycld/core; package-specific helpers belong in the package.
A minimal e2e spec
import { test, expect } from '@playwright/test'
test.describe('Example', () => {
test('list screen renders', async ({ page }) => {
await page.goto('/a/test-org/example')
await expect(page.getByText('Example')).toBeVisible()
})
})
Playwright boots the app shell against the seeded dev org, so the URL above works if the package is a present member and has a screen at tinycld/example/screens/index.tsx.
Tests for packages that aren’t present
tinycld-pkg <verb> --all only runs the members that are present in the workspace. A feature that isn’t cloned in simply contributes no tests - cloning or removing a member flips its tests in and out of the run with no config changes on your part.