Skip to content

fix: open issues sweep — resolve all 10 open issues (NOJS-295) - #310

Merged
ErickXavier merged 19 commits into
mainfrom
fix/NOJS-295
Jul 29, 2026
Merged

fix: open issues sweep — resolve all 10 open issues (NOJS-295)#310
ErickXavier merged 19 commits into
mainfrom
fix/NOJS-295

Conversation

@ErickXavier

Copy link
Copy Markdown
Collaborator

Open Issues Sweep (EPIC NOJS-295)

Fixes every open issue in the tracker. Issues intentionally left open (Refs, not Fixes) — each will receive a fix-report comment after merge; closing is maintainer-only.

Refs #213 #214 #269 #270 #274 #275 #276 #277 #278 #302

Framework fixes

Test infrastructure

Docs

Validation gate (merged branch)

  • Jest: 42 suites, 2225/2225 green; coverage 90.91% stmts / 93% lines
  • E2E: 1563 passed / 138 expected skips / 0 failed — chromium+firefox+webkit, --repeat-each=3
  • Bundle: 44,046 B gzip (≤ 44,500 bound; below 44,034 baseline), dist matches fresh build
  • grep waitForTimeout e2e/tests/ → empty; no unreferenced fixtures

No version bump or CHANGELOG here — owned by the v1.20.1 release EPIC (NOJS-283).

ErickXavier and others added 19 commits July 28, 2026 22:47
Allow overriding the NoJS-Elements build path via NOJS_ELEMENTS_PATH
so standalone clones, worktrees, and CI can point at the file without
requiring the sibling repo layout. Falls back to the existing relative
path when unset.

Refs #276
NoJS.i18n() called after init() (e.g. from defer/async scripts when
cdn.js auto-inits on DOM-ready) wrote _i18n._locale directly, bypassing
the setter that loads locale bundles and notifies listeners. The locale
was set but the bundle was never fetched.

Add a post-init guard at the end of i18n(): when _initPromise is set
(init has run), load the resolved locale's bundle via _loadI18nForLocale
if not already cached, then notify listeners. For inline locales (no
loadPath), call _notifyI18n synchronously.

Also remove the early return from the persisted-restore branch so the
guard runs on all exit paths, using a localeRestored flag to skip
browser detection when a persisted locale was restored.

Genesis: NOJS-297
Refs #213
…ive placement

The loops documentation explained self-repeating clone semantics but had no
explicit warning about the most common mistake: placing foreach/each/for on a
container element (ul, table, tbody) instead of the repeating child (li, tr).

- docs/md/loops.md: new "Common Mistakes" section with wrong/right examples
  for both list and table patterns, plus a rule-of-thumb blockquote
- docs/llms.txt: one-line warning appended to the loops bullet
- docs/llms-full.txt: new "Common Mistakes" section with wrong/right code block

Refs NOJS-269
Add 3 unit tests for animate-stagger when loop clones come from a
<template id> element instead of inline children:

1. Basic: 4-item loop with template="tpl" + animate + animate-stagger
   asserts class and delay on every clone (0ms..300ms)
2. Delta-append: push 2 items onto 3-item list, verify new clone
   delays continue from oldLen (300ms, 400ms)
3. Keyed loop: reorder items verifies no re-animation; insert one new
   item verifies only the new clone gets class + correct delay
Replace the triple-maintained locale list (config array, 5 desktop
dropdown buttons, 5 mobile pill buttons) with a single LOCALES var
exposed via NoJS.store('ui') and consumed by each="l in $store.ui.locales"
loops in both the desktop dropdown and mobile pill containers.
chore(test-server): support NOJS_ELEMENTS_PATH env var override
test(loops): cover animate-stagger with external template path
Remove the wrapper-clone rendering block that double-rendered pages in
append/prepend insert modes. The ctx.$set(asKey, accumulated) call
already triggers the loop directive's delta-append/reconcile, so the
manual clone+processTree was redundant and produced 6 rendered items
instead of 4 (2 duplicates per page).

The scroll-preservation logic for prepend mode is relocated to wrap the
$set call directly, relying on the synchronous delta-append coupling
with loops.js.

Refs #275
Resolve _findScrollContainer(el) inside _setupScrollObserver and the
visible-trigger observer so that IntersectionObserver uses the correct
root element. When the fetch element is inside an overflow-y:auto/scroll
container, root is set to that container; at document level, root is
null (viewport). Previously root was always omitted (viewport), causing
the sentinel to self-fire at scrollTop 0 inside scrollable containers.

The resolution happens inside each setup function (not cached at init)
so _resetPagination re-resolves correctly.

Refs #302
fix(i18n): load locale bundle when i18n() called after init()
Per QA review: repo convention places Common Mistakes immediately before
the See Also section, not mid-document. Moved in both docs/md/loops.md
and docs/llms-full.txt. No content changes.
docs(loops): add Common Mistakes callout for container-vs-item placement
docs(site): deduplicate 3x locale list in docs shell
Apply root option to the scroll-without-insert fallback observer (the
third IntersectionObserver that was still missing it). Fix the prepend
scroll-preservation comment: prepend takes the full-rebuild path, not
delta-append (which is append-only); both are synchronous.

Refs #302
fix(http): insert-mode deduplication and observer root
- Replace all 8 waitForTimeout calls with deterministic alternatives:
  waitForLoadState('networkidle'), waitForResponse, toPass, and
  auto-retrying Playwright assertions (toHaveText, toBeVisible)
- Replace non-retrying expect(count).toBe(2) snapshots in pagination
  tests with auto-retrying toHaveCount assertions
- Remove 100ms route-delay crutch in scroll pagination test (observer-root
  fix prevents sentinel rapid-fire)
- Fix pagination test 4 toHaveCount from 6 to 4 (dedup fix)
- Replace ordering-only assertions in insert-modes tests 1-2 with exact
  toHaveCount + unique-text assertions reflecting dedup behavior
- Add lang-selector.spec.ts availability guard: resolves NoJS-Elements
  build path like test-server.js, skips all 17 tests when absent
- Delete orphan e2e/examples/insert-prepend.html (zero spec references)
The CANCEL interceptor test (plugin-system test 4) was a false-positive:
toHaveText('idle') resolved at t=0 because the status was already 'idle'
before the click — the removed waitForTimeout was the only thing letting
the async abort chain settle.

Fix: add a hidden cancel-signal span ('pending') to the fixture that the
interceptor sets to 'cancelled' when the CANCEL branch fires. The test
now waits for toHaveText('cancelled') — a positive proof the interceptor
ran — before asserting the final idle state.
test(e2e): harden flaky waits, dedup assertions, Elements skip guard
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant