Uh oh!
There was an error while loading. Please reload this page.
test: cover router, forms, and reactive collections - #15
Merged
joaodibba merged 5 commits intoJun 15, 2026
Merged
Conversation
Adds 130 new tests across 4 new test files, bringing the suite total
from 32 tests (5 files) to 162 tests (9 files).
tests/router.test.ts (14 tests)
- Router.history, Router.pathname accessors
- Router.go() in memory mode — single and multi-hop navigation
- Router.go() void return type
- Router.props documents {} behaviour for routes without path params
- Redirect route resolves to target after async microtask flush
- Route title assertion scoped to a string (full pipeline caveat documented)
- Router.create() throws on second call
- Router.reload() no-throw
- Router.back() / Router.forward() — console.warn in memory mode
tests/forms.test.ts (39 tests)
- TextField: DOM mounting, inputElement tag, placeholder, value, name,
disabled, type, label, input/change event forwarding
- CheckBox: DOM mounting, input type, checked default/prop/setter, value,
name, label, disabled, variant radio/checkbox
- CheckBoxGroup: DOM mounting, children, selected null, onChange callback
- SwitchPanel: DOM mounting, input type, checked default/prop/setter, name,
label, change forwarding, toggle cycle
tests/reactive-collections.test.ts (57 tests)
- RefList: construction, id, valueOf, value, Symbol.toStringTag
- RefList mutations: push, pop, shift, unshift, splice, sort, reverse,
fill, clear, insert, removeItem, remove, value setter, update()
- RefList reactivity: subscribe (emit), listen (no-emit), unsubscribe
- RefList.syncComponentWithList: add, remove, reorder
- RefMap: construction, id, valueOf; mutations: set, delete, clear,
value setter, update(), toJSON; reactivity: subscribe, listen, unsubscribe
- RefObject: creation, subscribe, listen, unsubscribe, update, toJSON
- ref() factory: dispatches to RefList for arrays, RefMap for Maps
tests/lifecycle.test.ts (20 tests)
- DOM cleanup: remove, parent removal, re-append
- Re-render: swap components in container
- Reactive DOM updates: ref → textContent, style, boolean visibility,
multi-change propagation, unsubscribe stops updates
- RefList.syncComponentWithList: init, push, remove, clear flows
- Multi-subscriber fan-out and selective unsubscribe
- Component child management: append, innerHTML clear, deep nesting,
querySelectorAll
Caveats / APIs skipped:
- Router queryParams in memory mode: extras.queryParams are overwritten by
buildRoutePage(); documented as {} behaviour in the props test.
- Router document.title via full rendering pipeline: App.setPage() +
Component constructor may silently reject in happy-dom; title test
asserts typeof string only.
- RefObject deep-proxy reactive updates: console.log side-effect present
in the current source (RefObject.ts:296); not patched here.
- ForEach / LazyLoad / VirtualScroll / DropDown: async or layout-dependent
APIs; deferred to a future PR.
No production files were modified.
Roadmap alignment: Testing utilities and example tests.Uh oh!
There was an error while loading. Please reload this page.
This was referenced Jun 15, 2026
joaodibba added a commit
that referenced
this pull request
Jun 15, 2026
## Summary
Removes a debug `console.log("RefObject: emit", ...)` that was
accidentally left in the `createObservador()` Proxy `set` trap inside
`src/core/ref/RefObject.ts`.
The log fired on **every nested object property mutation**, leaking
internal reactive state (`{ key, value, target, receiver }`) to
production consoles.
**File changed:** `src/core/ref/RefObject.ts`
```diff
- console.log("RefObject: emit", {
- key, value, target, receiver
- });
container.emitAll('value', this.value);
```
---
## Regression tests added
Three new tests in `tests/reactive-collections.test.ts` (describe:
*RefObject – no console.log in production*):
| Test | Asserts |
|---|---|
| `nested property mutation does NOT call console.log` | proxy setter
fires without calling `console.log` |
| `update() does NOT call console.log` | `obj.update({...})` fires
without calling `console.log` |
| `subscriber is still notified after property mutation without
console.log` | reactivity still works (listen callback fires), no log |
---
## Validation
```
npm run test:run → 9 test files, 165 tests passed ✓
npx tsc --noEmit → no errors ✓
```
---
## Roadmap alignment
Addresses **audit item 1** from the PR #15 review. Small, focused, no
architectural change.
Reviewer: @zico15joaodibba pushed a commit
that referenced
this pull request
Jun 15, 2026
Replaces four weak toBeTruthy / typeof-only assertions from the RefObject (refObject) describe block (introduced in PR #15) with exact behavioral assertions: - 'creates a reactive object wrapper': now verifies id is a non-empty string and that subscribe/update are functions (not just truthy). - 'value exposes the wrapped plain object': now asserts the actual key value (x === 1) rather than just that the value is truthy. - 'update with new plain object replaces content': now counts subscriber call invocations to confirm notification fires, and checks lastValue is a defined object. - 'toJSON serializes the object value': now checks the serialized shape contains the original key/value pairs (key === 'value', count === 42). All 162 tests pass. Addresses audit item 8.
joaodibba added a commit
that referenced
this pull request
Jun 15, 2026
…#23) ## Summary Follow-up to PR #15 — replaces four weak `toBeTruthy()` / `typeof`-only assertions in the `RefObject (refObject)` describe block with exact behavioral assertions that would catch real regressions. **File changed:** `tests/reactive-collections.test.ts` (test-only change, no production code modified) | Old assertion (weak) | New assertion (exact) | |---|---| | `expect(obj).toBeTruthy()` | `expect(typeof obj.id).toBe('string')` + `id.length > 0` + `subscribe`/`update` are functions | | `expect(obj.value).toBeTruthy()` | `expect((obj.value as any).x).toBe(1)` — checks actual wrapped value | | `expect(lastValue).toBeTruthy()` after update | counts subscriber calls (> baseCalls) and checks `lastValue` is a defined object | | `expect(typeof json).toBe('object')` | checks `json.key === 'value'` and `json.count === 42` — verifies serialized shape | --- ## Validation ``` npm run test:run → 9 test files, 162 tests passed ✓ npx tsc --noEmit → no errors ✓ ``` --- ## Roadmap alignment Addresses **audit item 8** (strengthen weak RefObject test assertions). No production code changed. Reviewer: @zico15
joaodibba added a commit
to TypeComposer/docs
that referenced
this pull request
Jun 15, 2026
## Summary Marks the **Component Testing Framework** roadmap item as `completed` (was `in-progress`). ### Why The testing milestone is fully done across three merged PRs in `TypeComposer/typecomposer`: | PR | What it added | |---|---| | [#13](TypeComposer/typecomposer#13) | Testing utilities (`tests/utils.ts` render helper) + CI integration (GitHub Actions workflow) | | [#14](TypeComposer/typecomposer#14) | Example tests: computed properties, composition patterns, event handling | | [#15](TypeComposer/typecomposer#15) | Expanded tests: router, forms, reactive-collections, lifecycle hooks | All three roadmap sub-items are now satisfied: - ✅ Add testing utilities - ✅ Create example tests - ✅ Integrate with CI ### Change **File:** `src/assets/roadmap.json` **Diff:** 1-line change — `"status": "in-progress"` → `"status": "completed"` on the `Component Testing Framework` card (October 2025 milestone). ### Validation ``` npm run build → ✓ 3487 modules transformed, built in 16.82s (no errors) ``` cc @zico15
joaodibba added a commit
that referenced
this pull request
Jun 15, 2026
…ualScroll tests (#24) ## Summary Closes two deferred roadmap items from PR #15: 1. **`tests/README.md`** — contributor documentation for the test suite. 2. **`tests/async-components.test.ts`** — stable tests for `ForEach`, `LazyLoad`, and `VirtualScroll`. --- ## What's in each file ### `tests/README.md` | Section | What it covers | |---|---| | Running tests | `npm run test` (watch) vs `npm run test:run` (CI) | | Test structure | Every file in `tests/`, what each one tests | | `tests/utils.ts` render helper | `render()` signature, auto-cleanup, example | | `tests/setup.ts` | `MemoryStorage` polyfill, `beforeEach` storage clear | | Assertion patterns | DOM structure, styles, reactivity, async flush | | happy-dom layout limitations | Which APIs return 0 and how to avoid brittle tests | | `vitest.config.ts` reference | Environment, globals, setupFiles | | Adding new tests | Step-by-step checklist | ### `tests/async-components.test.ts` **ForEach (11 tests):** custom element tag, empty innerHTML, `item` getter default, `of` setter/getAttribute round-trip, `apply()` no-throw and innerHTML-clear contract. **LazyLoad (11 tests):** custom element tag, `loader`/`loaderProps` stored correctly, `Component.lazyLoad` spy asserts it is called with correct `fallback`/`props` on `onConnected`, not called when no loader provided. **VirtualScroll (13 tests, 1 skipped):** custom element tag, inner container presence and `position: relative`, total height = `items.length × itemHeight`, `overflow-y: auto`, initial render slice under happy-dom (`scrollTop=0` → `buffer` items), custom `buffer`, empty items, absolute positioning + px `top` + `100% width`, `renderItem` factory call count, `top = index × itemHeight`, scroll event no-throw. Scroll-position windowing skipped with a note pointing to E2E. --- ## Validation ``` npm run test:run npx tsc --noEmit ``` ``` Test Files 10 passed (10) Tests 207 passed | 1 skipped (208) Duration ~9s tsc --noEmit: no errors ``` All pre-existing tests continue to pass. --- ## Reviewers @zico15
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 freeto 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.
Summary
Follows up on the merged PR #14 (computed chains, composition, events) by adding test coverage for Router, form components, reactive collections, and component lifecycle/DOM-update patterns.
What changed
tests/router.test.ts(14 tests — new file)Router.historyreturns configured mode ("memory")Router.pathnameat creation (root ="") and aftergo()Router.go()— single and multi-hop navigation in memory modeRouter.go()resolvesundefined(void return type)Router.props— documents{}behaviour for routes without path params (known API nuance)/old → /new) resolves to target after async microtask flushtypeof stringonly (full rendering pipeline limitation documented)Router.create()throws on second call (singleton guard)Router.reload()no-throwRouter.back()/Router.forward()—console.warnin memory mode, never throwtests/forms.test.ts(39 tests — new file)inputElementtag, placeholder, value, name, disabled get/set, type, label element presence,input/changeevent forwardinginput[type=checkbox], checked default/prop/setter, value, name, label,disabled(Component level), variantradio/checkboxselected === nullinitially,onChangecallback fires on change eventtests/reactive-collections.test.ts(57 tests — new file)valueOf(),value,Symbol.toStringTag; all mutation methods (push,pop,shift,unshift,splice,sort,reverse,fill,clear,insert,removeItem,remove, value setter,update()); reactivity (subscribe/listen/unsubscribe);RefList.syncComponentWithList(add, remove, reorder)valueOf(); mutations (set,delete,clear, value setter,update(),toJSON()); reactivity (subscribe/listen/unsubscribe)refObject()): creation, subscribe, listen, unsubscribe, update, toJSONref()factory: dispatches toRefListfor arrays,RefMapfor Mapstests/lifecycle.test.ts(20 tests — new file)remove(), parent removal cascades to children, re-appendtextContent, style, boolean visibility, multi-change propagation, unsubscribe stops DOM updatesRefList.syncComponentWithListdriving DOM children: init, push settle, remove, clearinnerHTML = ''clear, deep nesting,querySelectorAllValidation
✅ Build passes (tsc + Sass + dist assembly).
Caveats / APIs skipped
Router.propswithqueryParamsin memory modeextras.queryParamsare stored in#propsthen immediately overwritten bybuildRoutePage(match) → match.params || {}. Documented as a known API nuance in the test.document.titlevia full rendering pipelineApp.setPage(new Component())may silently reject in happy-dom when custom element constructors reference unregistered elements; title test assertstypeof stringonly.RefObjectdeep proxy reactive updatesconsole.logside-effect atRefObject.ts:296; not patched here per the "no production code changes" constraint.ForEach,LazyLoad,VirtualScroll,DropDownNo production files modified
This PR is tests only.
Roadmap alignment: Testing utilities and example tests.
cc @zico15