diff --git a/AGENTS.md b/AGENTS.md index 21d7922..09049b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,7 +52,7 @@ Everything is in `src/` (entry point `src/index.js` re-exports `WebComponent` an Any change to observable behavior ships as one unit — code alone is an incomplete change. Land all four together: -1. **Tests** — unit specs in `test/` (or colocated `*.test.mjs`) covering the new contract *and* the old behavior it replaces. Add a `test/e2e/` spec whenever the behavior depends on something happy-dom cannot model faithfully — CSS selector matching, computed styles, custom-element upgrade timing. +1. **Tests** — unit specs in `test/` covering the new contract *and* the old behavior it replaces. Add a `test/e2e/` spec whenever the behavior depends on something happy-dom cannot model faithfully — CSS selector matching, computed styles, custom-element upgrade timing. 2. **Demo examples** — a runnable example under `demo/examples/` that demonstrates the behavior, linked from a card in `demo/index.html`. Update any existing example the change affects, including ones that now emit a console warning or model a discouraged pattern. 3. **Documentation** — the guide under `docs/src/content/docs/guides/` that doubles as the behavioral spec. For a breaking change, also update the `README.md` banner with the migration consumers have to perform. 4. **Size budget** — `pnpm size-limit` stays green. If an addition genuinely needs more headroom, raise the budget in `package.json` deliberately and say so in the change description; never let it drift silently. Whenever the change moves the base-class bundle, record it as a new row in `size-change-log.md` **and** update the headline size in `docs/src/content/docs/guides/library-size.md` to match that row's new min+brotli figure — the two must never disagree. @@ -62,7 +62,7 @@ Verify with `pnpm test:all` (unit + types + template + e2e across all engines) b ## Testing notes - Environment is **happy-dom** (set in `vitest.config.mjs`), so real custom-element registration works. Any component under test must be registered with `customElements.define(...)` before instantiation, or the browser throws. -- Tests live both in `test/` and colocated next to source as `*.test.mjs` (e.g. `src/utils/serialize.test.mjs`). Coverage is restricted to `src`. +- All specs live under `test/` (`test/utils/` mirrors `src/utils/`); never colocate one next to source. `copy:source` globs `./src/*.js` and `./src/utils/*`, so a spec in `src/` is minified into the published `dist/` — `test/exports.test.mjs` guards against that. Coverage is restricted to `src`. - happy-dom does not perfectly match browser semantics (notably custom-element upgrade ordering); be skeptical of behaviors that depend on real-browser timing. ## State-model invariants (easy to regress) diff --git a/src/utils/serialize.test.mjs b/src/utils/serialize.test.mjs deleted file mode 100644 index 570455a..0000000 --- a/src/utils/serialize.test.mjs +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, test } from 'vitest' -import { serialize } from './serialize.mjs' - -describe('serialize', () => { - test('should stringify number', () => { - const result = serialize(3) - expect(result).toBeTypeOf('string') - expect(result).toEqual('3') - }) - - test('should stringify boolean', () => { - const result = serialize(false) - expect(result).toBeTypeOf('string') - expect(result).toEqual('false') - }) - - test('should stringify object', () => { - const result = serialize({ hello: 'world' }) - expect(result).toBeTypeOf('string') - expect(result).toEqual('{"hello":"world"}') - }) - - test('should return undefined', () => { - const result = serialize(undefined) - expect(result).toBeUndefined() - }) -}) diff --git a/test/exports.test.mjs b/test/exports.test.mjs index c8e6e9c..a23175e 100644 --- a/test/exports.test.mjs +++ b/test/exports.test.mjs @@ -1,3 +1,5 @@ +import { readdirSync } from 'node:fs' +import { resolve } from 'node:path' import { describe, expect, it } from 'vitest' import * as main from '../src/index.js' import * as utils from '../src/utils/index.js' @@ -27,6 +29,17 @@ describe('cem-plugin entry', () => { }) }) +describe('published source shape', () => { + it('has no spec files under src', () => { + // `copy:source` globs ./src/*.js and ./src/utils/*, so anything living in + // src ends up in the published dist — specs belong in test/ + const strays = readdirSync(resolve('src'), { recursive: true }).filter( + (entry) => /\.(test|spec)\./.test(String(entry)) + ) + expect(strays).toEqual([]) + }) +}) + describe('utils exports', () => { it('exposes every documented utility', () => { for (const name of [