LearnNewsExamplesServices
Frontmatter
id15196
titleAdd unit coverage for Neo.util.Style.compareStyles
stateClosed
labels
enhancementhelp wantedgood first issuecontributor-experienceaitestingcore
assigneesstantheman0128
createdAtJul 15, 2026, 5:09 PM
updatedAtJul 15, 2026, 9:52 PM
githubUrlhttps://github.com/neomjs/neo/issues/15196
authorneo-gpt-emmy
commentsCount2
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 15, 2026, 9:52 PM

Add unit coverage for Neo.util.Style.compareStyles

Closed Backlog/active-chunk-6 enhancementhelp wantedgood first issuecontributor-experienceaitestingcore
neo-gpt-emmy
neo-gpt-emmy commented on Jul 15, 2026, 5:09 PM

Context

GitHub's current beginner guidance explicitly points newcomers toward the good first issue label. Neo's only open ticket with that label, #15124, is already assigned, so there is no unassigned entry point for the next contributor.

This ticket creates another deliberately bounded, self-contained contribution outside the v13.2 release work.

[!IMPORTANT] Reserved for first-time Neo.mjs contributors. The Neo maintainer team will not claim, edit, or implement this ticket through 2026-07-22. A first-time contributor may claim it immediately; contributor questions and PR reviews remain welcome during the reservation window.

Please comment before starting. A maintainer will assign the ticket to one contributor to avoid duplicate work.

Problem

Neo.util.Style.compareStyles() is the pure utility that turns old and new style values into the compact style delta consumed by Neo's VDOM diffing path. It accepts object or string inputs, reports added and changed properties, represents removed properties with null, and returns null when no delta exists.

The method has no focused unit spec. This is not theoretical risk: the regression documented in #6859 broke style removal when the new style value became absent, affecting UI filtering and producing misleading VDOM symptoms. The runtime fix landed, but its edge remains unpinned.

Architectural reality

  • Runtime source: src/util/Style.mjs
  • String normalization source: src/core/Util.mjs#createStyleObject()
  • Direct runtime consumer: the style branch in src/vdom/Helper.mjs#compareAttributes()
  • Canonical test destination: test/playwright/unit/util/Style.spec.mjs
  • Established sibling pattern: test/playwright/unit/util/Json.spec.mjs and String.spec.mjs
  • Structural pre-flight: fast-path; the proposed spec exactly matches the existing test/playwright/unit/util/ sibling role and naming pattern.
  • This is pure single-thread utility coverage. It does not require a browser DOM, component instance, or VDOM integration fixture.

Fix

Add one focused Playwright unit spec for the existing Neo.util.Style.compareStyles() contract. Pin the current documented and runtime behavior without changing src/util/Style.mjs.

Contract ledger

Target surface Source of authority Existing behavior to pin Fallback / edge case Docs Evidence
Style.compareStyles(newStyle, oldStyle) with object inputs src/util/Style.mjs; src/vdom/Helper.mjs style-delta consumer Return only added or changed new properties and encode removed old properties as null, without mutating either input Return null when both objects express the same styles No change Focused object-delta and immutability assertions in Style.spec.mjs
Missing-side semantics src/util/Style.mjs; historical regression #6859 With no old style, return a shallow clone of the new style; with no new style, return a delta that maps every old key to null Return null when both inputs are absent No change Focused identity, absent-input, and #6859 regression assertions
String normalization src/util/Style.mjs; src/core/Util.mjs#createStyleObject() Normalize string inputs before comparison, including kebab-case property names and numeric values Object/string mixed inputs follow the same delta semantics No change String/string and mixed-input assertions

Acceptance criteria

  • Add test/playwright/unit/util/Style.spec.mjs using the existing util-spec setup and import pattern.
  • Import src/Neo.mjs, src/core/_export.mjs, and src/util/Style.mjs.
  • Cover added, changed, removed, and unchanged properties for object inputs.
  • Cover all missing-side branches: both absent, old absent, and new absent.
  • Preserve the #6859 regression edge: an absent new style maps every old style key to null.
  • Cover string/string and representative mixed object/string inputs.
  • Assert kebab-case string properties are normalized to camelCase and numeric values remain numeric.
  • Assert object inputs are not mutated.
  • Assert the old-absent branch returns an equal but different object reference from the new-style input.
  • Assert unchanged styles and two absent inputs return null.
  • Keep the change test-only. If documented behavior and runtime behavior appear to disagree, stop and ask on this ticket before changing src/util/Style.mjs.
  • Run the focused spec: npm run test-unit -- test/playwright/unit/util/Style.spec.mjs
  • Run the complete unit suite: npm run test-unit

Out of scope

  • Refactoring or changing Neo.util.Style
  • Changing Neo.core.Util#createStyleObject()
  • Browser CSS validation or computed-style assertions
  • Adding VDOM integration coverage
  • Removing unrelated imports or performing broad utility cleanup
  • Adding coverage for Neo.util.Date
  • Any v13.2 release, Agent OS, Fleet Manager, or docking-feature work

Avoided traps

  • Do not run default npx playwright test; Neo requires its custom unit configuration.
  • Do not use browser DOM or toHaveCSS() assertions for this pure delta utility.
  • Do not broaden the ticket into CSS parsing or normalization changes.
  • Do not change runtime behavior merely to make a test easier.

Prior art and duplicate sweep

  • Template precedents: #15117, #15118, and #15124.
  • Historical regression anchor: #6859.
  • Live latest-20-open, exact GitHub, Knowledge Base, local issue/discussion, and recent all-state A2A claim sweeps were refreshed immediately before creation at 2026-07-15T15:08:33.095Z; no equivalent ticket or active lane was found.

Decision Record impact

None. This ticket pins an existing public utility contract and introduces no architectural decision.

Origin Session ID: 2c46ce52-9c69-46a7-a8c9-4db937d2a341

stantheman0128
stantheman0128 Jul 15, 2026, 5:35 PM

Hi, I would like to work on this as a first-time Neo contributor. I reviewed the requested cases and plan to add focused Playwright unit coverage in test/playwright/unit/util/Style.spec.mjs, including object and string inputs, removals, immutability, clone identity, and unchanged-style behavior, without changing runtime behavior. Please assign this issue to me; I will begin the tracked-file changes once assigned.

@neo-gpt-emmy - 2026-07-15T15:50:54Z

Welcome to Neo, @stantheman0128 — and thank you for reading the scope so carefully. Your plan matches the ticket exactly, including the test-only boundary, the missing-side cases, immutability, and clone identity.

I’m assigning #15196 to you now. Please go ahead, and if the documented contract and runtime behavior diverge, pause and ask here before changing runtime code. Looking forward to your contribution!

  • 2026-07-15T15:51:08Z @neo-gpt-emmy assigned to @stantheman0128
stantheman0128 cross-referenced by PR #15198 on Jul 15, 2026, 6:35 PM
tobiu referenced in commit 2c25c43 - "test(util): cover Style.compareStyles contract (#15196) (#15198)" on Jul 15, 2026, 9:52 PM
tobiu closed this issue on Jul 15, 2026, 9:52 PM