LearnNewsExamplesServices
Frontmatter
id15422
titleAdd unit coverage for Neo.util.Function
stateClosed
labels
enhancementaitesting
assigneesneo-opus-ada
createdAtJul 18, 2026, 6:41 AM
updatedAtJul 18, 2026, 11:04 AM
githubUrlhttps://github.com/neomjs/neo/issues/15422
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 18, 2026, 11:04 AM

Add unit coverage for Neo.util.Function

Closed Backlog/active-chunk-7 enhancementaitesting
neo-opus-ada
neo-opus-ada commented on Jul 18, 2026, 6:41 AM

Context

src/util/Function.mjs (230 lines, 9 exported functions) carries zero unit coverage while its siblings just gained it: Neo.util.Array (#15124), Neo.util.String (#15118), Neo.util.Json.extract (#15117). It is a high-reuse module — buffer/debounce/throttle back every rate-limited handler in the framework, and createSequence/intercept/bindAppend are core to the observable + method-wrapping machinery — so an untested regression here is broad-blast and silent.

The Problem

The module is entirely testable in isolation (zero imports; only the Neo.isNumber/Neo.isString/Neo.emptyFn globals the unit harness already provides), yet has no spec — so the timer semantics (leading-edge debounce, trailing throttle, buffer cancellation) and the method-wrapping lifecycle (createSequenceunSequence restore-original) are unverified. These are exactly the behaviors most prone to a subtle regression: an off-by-one in a timer branch, or a wrapper that fails to restore the original method.

The Architectural Reality

src/util/Function.mjs exports: bindAppend, buffer, createInterceptor, createSequence, debounce, intercept, resolveCallback, throttle, unSequence. Timer functions (buffer/debounce/throttle) use setTimeout/Date.now — deterministically testable by stubbing them (the test/playwright/unit/app/devindex/GitHubService.spec.mjs retry tests are the in-repo precedent for setTimeout/Date.now stubbing). createSequence/unSequence use private Symbols (sequencedFns, originalMethod) — witnessed via observable behavior (call-order, original-method restoration), never by reaching the Symbols. Spec lands at test/playwright/unit/util/Function.spec.mjs, the sibling of Array.spec.mjs / String.spec.mjs / Json.spec.mjs.

The Fix

One spec: test/playwright/unit/util/Function.spec.mjs covering each exported function's contract — the pure ones by direct assertion (bindAppend arg-append; createInterceptor/intercept value-transform + prevent-on-false; resolveCallback string / up.-parent-chain resolution; createSequence/unSequence call-order + restore) and the timer ones with stubbed setTimeout/Date.now (leading-edge debounce fires immediately then coalesces; throttle rate-limits + trailing-fires; buffer coalesces + .cancel()/.isPending). Uses the test/playwright/setup.mjs harness like the sibling util specs.

Acceptance Criteria

  • test/playwright/unit/util/Function.spec.mjs exists and covers all 9 exported functions.
  • Timer semantics witnessed deterministically (stubbed setTimeout/Date.now), never real waits.
  • createSequence/unSequence verified via observable behavior (call order + original-method restoration), not by reaching private Symbols.
  • Green under the unit config (UNIT_TEST_MODE=true), --workers=1 and parallel.

Out of Scope

  • Any change to src/util/Function.mjs itself (coverage only; if a test surfaces a real defect, its fix is a separate ticket).
  • The other src/util coverage gaps (Date, Matrix, Performance, KeyNavigation, …) — each its own leaf per the #15117/#15118/#15124 standalone precedent.

Related

Sibling precedents: #15117 (Json.extract), #15118 (String), #15124 (Array). Target: src/util/Function.mjs.

Origin Session ID: 3e5f61a5-35d0-4f3d-8805-54f63bebed70

Retrieval Hint: "Neo.util.Function unit coverage debounce throttle buffer createSequence intercept spec setTimeout stub"