Skip to content

BrowserTest: plain describe() blocks don't wire this.browser — require browserDescribe() #2778

Closed

Description

@bpamiri

Summary

wheels.wheelstest.BrowserTest wires this.browser (the BrowserClient) only inside browserDescribe() blocks. Plain describe() blocks inherited from wheels.WheelsTest look like they should work (since BrowserTest extends WheelsTest) but cause runtime failures.

Repro

A spec like this — the form shown in most internal samples and what a newcomer would write by analogy with TestBox BDD examples — fails:

```cfml
component extends="wheels.wheelstest.BrowserTest" {
function run() {
describe("my flow", () => {
it("does the thing", () => {
this.browser.visitUrl("data:text/html,

Hi

").assertSee("Hi");
});
});
}
}
```

Error: `function [visitUrl] does not exist in the String` (because `this.browser` is a String — its uninitialized default — inside a non-browserDescribe block).

Working version requires `browserDescribe()`:

```cfml
browserDescribe("my flow", () => {
it("does the thing", () => { this.browser.visitUrl(...).assertSee(...); });
});
```

Impact

Anyone writing their first BrowserTest hits this on iteration 1. The error message doesn't point at the cause (says `this.browser` is a String, not "you need browserDescribe").

Suggested fixes (any of)

  1. Doc fix: prominently note the `browserDescribe()` requirement in the docstring at the top of `vendor/wheels/wheelstest/BrowserTest.cfc` (the existing sample uses `describe()`, which compounds the issue).
  2. Better error message: when `this.browser` is a String at the moment a BrowserClient method is called, throw with a hint: "BrowserTest specs must use `browserDescribe()` blocks, not `describe()`. The framework wires `this.browser` only inside `browserDescribe()`."
  3. Make it transparent: auto-wire `this.browser` for any `describe()` block inside a BrowserTest subclass.

Repo / version

  • CLI: `wheels 4.0.1`
  • Wheels Core: `4.0.0-SNAPSHOT+1779`
  • File: `vendor/wheels/wheelstest/BrowserTest.cfc`

Where found

Surfaced while dogfooding the BrowserTest infrastructure in Titan (DataPAI portal Phase 0).

🤖 Found while executing a Claude-Code-driven implementation plan

Activity

  1. wheels-bot commented on May 21, 2026

    @wheels-bot
    Contributor

    Wheels Bot — Triage

    Classified as bug.

    Suspected layer

    testing — see .ai/wheels/testing/browser-testing.md for canonical patterns. The defect lives in vendor/wheels/wheelstest/BrowserTest.cfc, which only wires this.browser inside browserDescribe() callbacks (see lines 38–41, 145–181) and leaves it as the empty-string default elsewhere, producing a misleading function [visitUrl] does not exist in the String error inside plain describe() blocks.

    Fix sketch

    Primary target: vendor/wheels/wheelstest/BrowserTest.cfc. The minimum-viable fix is option 2 from the report — when a spec uses describe() instead of browserDescribe(), detect that this.browser is still the uninitialized empty string and throw a clear "use browserDescribe() here" error (a guard in the existing browser-setup path, or a check-before-use in BrowserClient-bound methods). The docstring at the top of the file and the sample in .ai/wheels/testing/browser-testing.md should be updated in the same change to call out the requirement prominently. Auto-wiring this.browser for plain describe() blocks (option 3) is a larger design decision and not the right scope for the bug-fix path.

    Confidence: medium

    The reproduction and target file are unambiguous, but the report deliberately presents three different fix shapes (doc-only / better error / transparent auto-wire) and choosing between them is a judgment call rather than a mechanical change.

    <!-- wheels-bot:triage:2778 -->
    <!-- wheels-bot:triage-class:bug -->
    <!-- wheels-bot:triage-confidence:medium -->

  2. wheels-bot commented on May 21, 2026

    @wheels-bot
    Contributor

    Wheels Bot — Fix proposed

    Draft PR: #2782

    Spec: vendor/wheels/tests/specs/wheelstest/BrowserTestNotWiredSpec.cfc — failing → passing
    Implementation: vendor/wheels/wheelstest/UnwiredBrowserGuard.cfc (new sentinel) + vendor/wheels/wheelstest/BrowserTest.cfc (install guard at this.browser before/after browserDescribe() wiring; tighten $captureFailureArtifacts guard check)

    Picked the better error message path from the triage (option 2 in the report). Option 3 (auto-wire this.browser for plain describe() blocks) would have been a larger design decision and out of scope for the bug-fix path.

    A human review is required before merge. Reviewer A and Reviewer B will weigh in shortly. Doc updates (MDX guides, .ai/wheels/, CLAUDE.md) are handled separately by bot-update-docs.yml.

  3. added a commit that references this issue on May 22, 2026
    93ba712
  4. added a commit that references this issue on May 27, 2026
    7b2ca56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions