test(e2e): cover the client's lvt-* attribute handler registry - #344
test(e2e): cover the client's lvt-* attribute handler registry#344adnaan wants to merge 1 commit into
Conversation
The client is growing a public registerAttribute() API (livetemplate/client#159, phase 1 of livetemplate/livetemplate#473). Its failure mode is SILENCE: a handler that never runs, or one that warns on every page load, breaks nothing an existing assertion looks at. The existing suites cannot catch either, because they exercise attribute BEHAVIOUR through the built-ins, not the wiring. Assert the two things only a browser can show: 1. A page using lvt-* attributes produces no registry diagnostics -- the registry must not warn about the framework's own handlers. 2. A handler registered from a plain <script> AFTER the client tag takes effect immediately, against the DOM already on screen. That is the only path a third-party bundle has: under the documented `defer` pattern the core bundle has auto-initialized and connected before any second script evaluates. It also pins the global's spelling. --global-name over a module that also exports a class of the same name makes window.LiveTemplateClient the module NAMESPACE, so LiveTemplateClient.registerAttribute -- what every doc example writes -- resolves to a named export rather than the class static. Those are indistinguishable in a unit test and trivially confused in a refactor. Falsified before being trusted: against the currently published bundle it fails with "registerAttribute is not a function"; against a build of livetemplate/client#159 it passes. The console assertion is deliberately scoped to this feature's own diagnostics rather than demanding total silence -- the fixture serves no WebSocket endpoint, so the client legitimately reports a transport failure, and asserting on silence would make the test hostage to unrelated noise. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01N6hr7ZCG9o4pCmtnA8qhSq
ReviewNice test — the doc comment does real work explaining why this test exists (silent-failure mode, the module-namespace-vs-class-static trap, the "no pre-init window" constraint on third-party bundles), and the draft framing (falsified against Potential bug: unguarded read of
|
Important
Draft — do not merge until
@livetemplate/clientships the attribute registry.This test asserts an API that does not exist in the published client yet.
lvt/testingfetches@livetemplate/client@latestfrom the CDN, so onmaintoday this test fails withregisterAttribute is not a function— correctly. It goes green the moment the client releases with livetemplate/client#159.That failure is the point: it is what makes this a real test rather than a tautology.
The browser half of phase 1 of livetemplate/livetemplate#473. Pairs with livetemplate/client#159, which adds the registry and the public
registerAttribute()API.Why a new test, when the built-in attributes are already covered
The existing e2e suites exercise attribute behaviour — they drive
lvt-fx:scroll, modals, event delegation. They pass identically whether those attributes are dispatched by a hardcoded call sequence or by a registry, which is exactly what made them the right regression net for the refactor. It also means they cannot see the wiring at all.The registry's failure mode is silence. A handler that never runs, or one that warns on every page load, breaks nothing any existing assertion looks at. During development a severed array literal in the client left 4 of 18 handlers registered and all 861 unit tests green — only the bundle size gave it away.
What it asserts
<script>after the client tag reaches the DOM already on screen — no render is triggered afterwards on purpose. This is the only path available to a third-party bundle: under the documenteddeferpattern the core bundle has already auto-initialized and connected by the time a second script evaluates.ctx.valuereads the attribute from the already-rendered element, proving the catch-up scan actually matched rather than merely running.--global-name=LiveTemplateClientover a module that also exports a class of that name makeswindow.LiveTemplateClientthe module namespace — soLiveTemplateClient.registerAttribute(...), what every doc example writes, resolves to a module-level named export, and the class sits atLiveTemplateClient.LiveTemplateClient. Indistinguishable in a unit test, trivially confused in a refactor.Two deliberate choices worth reviewing
The console assertion is scoped, not absolute. It looks for this feature's own diagnostics (
AttributeRegistry,already claims this name,never both,has an empty value) rather than demanding zero console output. The fixture serves no WebSocket endpoint, so the client legitimately reports a transport failure; asserting on total silence would make the test hostage to unrelated noise and it would rot on the first unrelated log line.It was falsified before being trusted. Run against the published bundle: fails. Against a build of livetemplate/client#159: passes. A browser test asserting a new API is otherwise indistinguishable from one asserting nothing.
How it was verified locally
The client bundle was built from livetemplate/client#159 and dropped into lvt's test-client disk cache, then the suite run with
-count=1:19/19 PASS (18 pre-existing + this one).
-count=1is mandatory: without itgo testreplays a cached result and the suite "passes" against a bundle it never loaded — which happened once during development and was caught only by checking the byte count in[lvt/testing] Using cached client library (…).🤖 Generated with Claude Code
https://claude.ai/code/session_01N6hr7ZCG9o4pCmtnA8qhSq