Skip to content

feat(console): golden tests for non-TTY ProgressBar outputs + TTY/non-TTY comment clarification#47042

Merged
pelikhan merged 4 commits into
mainfrom
copilot/go-fan-review-bubbles-v2
Jul 21, 2026
Merged

feat(console): golden tests for non-TTY ProgressBar outputs + TTY/non-TTY comment clarification#47042
pelikhan merged 4 commits into
mainfrom
copilot/go-fan-review-bubbles-v2

Conversation

Copilot AI commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

From the Go Fan bubbles/v2 review: the non-TTY ProgressBar.Update branches are pure and deterministic — ideal candidates for golden coverage using the already-present charmbracelet/x/exp/golden dep. Also adds a comment clarifying the TTY vs non-TTY percentage rendering split.

Changes

  • Golden tests (pkg/console/golden_test.go): adds TestGolden_ProgressBarNonTTY with 6 sub-cases covering both output paths:

    Subtest Golden output
    determinate_0pct 0% (0B/1.0KB)
    determinate_50pct 50% (512.0MB/1.00GB)
    determinate_100pct 100% (1.0KB/1.0KB)
    determinate_zero_total 100% (0B/0B)
    indeterminate_no_data Processing...
    indeterminate_with_data Processing... (512.0MB)
  • Clarifying comment (pkg/console/progress.go): documents that in non-TTY mode the percentage is hand-built via fmt.Sprintf, while in TTY mode it is rendered by bubbles' ViewAs (where ShowPercentage defaults to true) — so the two branches aren't accidentally "unified" in a way that breaks either.

No new dependencies; no production logic changes.

…larify TTY/non-TTY comment

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Review usage of bubbles module in Go Fan feat(console): golden tests for non-TTY ProgressBar outputs + TTY/non-TTY comment clarification Jul 21, 2026
Copilot AI requested a review from pelikhan July 21, 2026 09:19
@pelikhan
pelikhan marked this pull request as ready for review July 21, 2026 09:49
Copilot AI review requested due to automatic review settings July 21, 2026 09:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds deterministic golden coverage for ProgressBar non-TTY rendering and clarifies TTY rendering behavior.

Changes:

  • Adds six golden test cases for determinate and indeterminate output.
  • Documents the TTY/non-TTY percentage-rendering split.
  • Makes no production logic or dependency changes.
Show a summary per file
File Description
pkg/console/golden_test.go Adds non-TTY progress golden tests.
pkg/console/progress.go Clarifies rendering responsibilities.
.../determinate_0pct.golden Captures 0% output.
.../determinate_50pct.golden Captures 50% output.
.../determinate_100pct.golden Captures 100% output.
.../determinate_zero_total.golden Captures zero-total output.
.../indeterminate_no_data.golden Captures no-data processing output.
.../indeterminate_with_data.golden Captures processing output with bytes.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 8/8 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #47042 does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (default_business_additions=50, threshold=100).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The golden test coverage and comment clarification look correct. All 6 non-TTY sub-cases are deterministic and the golden files match the code logic. The total==0 edge case is guarded before division. The struct literal for indeterminate mode is safe because the bubbles progress field is only accessed on the TTY path. No blocking issues.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 15.6 AIC · ⌖ 4.34 AIC · ⊞ 4.9K

@github-actions github-actions Bot mentioned this pull request Jul 21, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues need fixing before merge

The golden tests added here have two structural flaws that undermine their value:

  1. t.Skip() on TTY (line 325): silently skips the entire test for any local developer running in a terminal. Golden tests that are routinely skipped rot without anyone noticing — the value of golden coverage depends on them always running.

  2. Zero-value progress.Model (lines 350, 355): &ProgressBar{indeterminate: true} bypasses the constructor, leaving the embedded progress.Model uninitialized. The skip guard on line 325 is the only thing preventing ViewAs() from being called on a zero-value model, creating a fragile and implicit coupling.

The fix for both: inject/mock isTTY so tests always exercise the non-TTY path (removing the need for the skip guard), and use the constructor for indeterminate bars.

The progress.go comment clarification is fine as-is.

🔎 Code quality review by PR Code Quality Reviewer · 38.2 AIC · ⌖ 4.63 AIC · ⊞ 5.6K
Comment /review to run again

Comment thread pkg/console/golden_test.go Outdated
// The tests always exercise the non-TTY code path because isTTY() returns false in CI
// (stdout is not a terminal when running tests).
func TestGolden_ProgressBarNonTTY(t *testing.T) {
if isTTY() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t.Skip() on TTY makes these golden tests permanently invisible to local developers: any developer running tests in a terminal will silently skip the entire suite, allowing golden files to diverge undetected.

💡 Suggested fix

Inject a isTTYFunc func() bool field on ProgressBar (defaulting to the real isTTY) and override it in tests to always return false. This guarantees the non-TTY code path is exercised in every environment, removing the need for the skip guard:

// In test setup:
bar := NewProgressBar(1024)
bar.isTTYFunc = func() bool { return false } // force non-TTY
golden.RequireEqual(t, []byte(bar.Update(0)))

Golden tests that skip on developer machines will silently rot — by the time CI catches it, the golden files may have been regenerated without review.

Comment thread pkg/console/golden_test.go Outdated
})

t.Run("indeterminate_no_data", func(t *testing.T) {
bar := &ProgressBar{indeterminate: true}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&ProgressBar{indeterminate: true} bypasses the constructor, leaving progress.Model zero-valued: if isTTY() returns true (e.g. the skip guard is removed or mocked), p.progress.ViewAs() will be called on a zero-value model and will likely panic or produce garbage.

💡 Suggested fix

Use NewProgressBar(0) and set indeterminate: true separately, or add a constructor NewIndeterminateProgressBar():

// safe: model is properly initialized
bar := NewProgressBar(0)
bar.indeterminate = true
// or
bar := NewIndeterminateProgressBar()

Right now the only thing keeping these tests from hitting the zero-value model is the t.Skip() guard on line 325. That is a fragile coupling between two unrelated lines.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 75/100 — Acceptable

Analyzed 6 test(s): 6 design, 0 implementation, 0 violation(s).

📊 Metrics (6 tests)
Metric Value
Analyzed 6 (Go: 6, JS: 0)
✅ Design 6 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 3 (50%)
Duplicate clusters 0
Inflation YES (golden_test.go +40 vs progress.go +4)
🚨 Violations 0
Test File Classification Issues
determinate_0pct golden_test.go design_test / behavioral_contract 0% boundary edge case ✅
determinate_50pct golden_test.go design_test / behavioral_contract Happy-path
determinate_100pct golden_test.go design_test / behavioral_contract 100% boundary edge case ✅
determinate_zero_total golden_test.go design_test / behavioral_contract Zero-division edge case ✅
indeterminate_no_data golden_test.go design_test / behavioral_contract Happy-path
indeterminate_with_data golden_test.go design_test / behavioral_contract Happy-path
⚠️ Flagged Tests (1)

Test inflation (pkg/console/golden_test.go) — golden_test.go adds 40 lines while the paired production change (progress.go) adds only 4 lines (10:1 ratio, threshold 2:1). This is expected for a shared golden test file covering multiple components; the new tests provide genuine behavioral coverage. Score penalty applied per rules, but no code change is required.

Verdict

Passed. 0% implementation tests (threshold: 30%). All 6 tests are golden snapshot tests enforcing non-TTY output contracts for ProgressBar. Build tag //go:build !integration is present. The isTTY() guard ensures tests self-skip in TTY environments. Score penalty is solely from the inflation ratio (shared golden test file vs. small targeted production change).

✅ Test Quality Sentinel: 75/100. 0% implementation tests (threshold: 30%).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /tdd — one observation on test construction, no blocking issues.

📋 Key Themes & Highlights

Key Themes

  • Struct literal vs constructor: Indeterminate tests use &ProgressBar{indeterminate: true} directly because NewIndeterminateProgressBar() is wasm-only. This works, but promoting the constructor to the non-wasm build would decouple tests from unexported fields.
  • Golden file trailing newlines: All 6 golden files are missing a trailing newline (\ No newline at end of file). This is cosmetic but can cause noisy diffs with some editors/tools.

Positive Highlights

  • ✅ Clean, focused PR with zero production logic changes
  • ✅ Excellent coverage of edge cases: 0%, 50%, 100%, zero-total, and both indeterminate variants
  • ✅ The isTTY() skip guard is the right pattern — tests are reliable in CI and honest about their environment dependency
  • ✅ Clarifying comments in progress.go are accurate and prevent accidental "unification" of the TTY/non-TTY branches

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 30.6 AIC · ⌖ 4.54 AIC · ⊞ 6.6K
Comment /matt to run again

Comment thread pkg/console/golden_test.go Outdated
})

t.Run("indeterminate_no_data", func(t *testing.T) {
bar := &ProgressBar{indeterminate: true}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The indeterminate tests use a raw struct literal &ProgressBar{indeterminate: true} instead of a constructor. NewIndeterminateProgressBar() only exists in the wasm build, so the literal is currently the right choice — but this signals the constructor should be promoted to the non-wasm build so tests can use the public API and avoid coupling to unexported fields.

💡 Suggestion

Add NewIndeterminateProgressBar() to progress.go (non-wasm) alongside NewProgressBar(), then update both test cases:

bar := NewIndeterminateProgressBar()
golden.RequireEqual(t, []byte(bar.Update(0)))

This keeps tests decoupled from internal struct layout and makes the constructor discoverable for non-wasm callers.

@copilot please address this.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finishser skill

…rogressBar; remove t.Skip from golden test

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finishser skill

Done. Addressed both issues flagged by the PR Code Quality Reviewer in commit fix(console): inject ttyCheck into ProgressBar; add NewIndeterminateProgressBar; remove t.Skip from golden test:

  1. t.Skip() removed — added a ttyCheck func() bool field to ProgressBar (defaults to isTTY in both constructors). Test helpers nonTTYProgressBar / nonTTYIndeterminateProgressBar inject func() bool { return false }, so all 6 subtests run unconditionally regardless of the developer's terminal.

  2. Zero-value model fixed — added NewIndeterminateProgressBar() to non-wasm progress.go (was wasm-only). The test helpers use it, so the embedded progress.Model is fully initialized via the same progress.New(...) call as NewProgressBar.

make fmt, make lint, make test-unit all green locally.

@pelikhan
pelikhan merged commit 4556f8e into main Jul 21, 2026
23 checks passed
@pelikhan
pelikhan deleted the copilot/go-fan-review-bubbles-v2 branch July 21, 2026 12:18
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.0

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.

[go-fan] Go Module Review: charm.land/bubbles/v2

3 participants