fix(test): resolve 20 browser-spec errors with root-cause fixes#2134
Merged
fix(test): resolve 20 browser-spec errors with root-cause fixes#2134
Conversation
Restores all 119 browser specs (BrowserDialog/Integration/Launcher/
Login/Route/TestLifecycle) to green when running
`bash tools/test-local.sh` on Lucee 7 + SQLite. Prior state: 3063 pass,
0 fail, 20 error. New state: 3067 pass, 0 fail, 0 error.
Three distinct root causes, fixed independently:
1. Lucee 7 rejects struct-based createDynamicProxy targets.
`BrowserClient.\$requireDialogSupport()` and `\$registerDialogListener()`
passed an inline struct with an `accept` closure as the proxy target.
Lucee 7 tightened the first-argument type to require a Component —
structs now fail with "Can't cast Complex Object Type Struct to
String" as Lucee tries the CFC-path-string overload first.
Fix: new `wheels.wheelstest.DialogConsumer` CFC that encapsulates
the Consumer<Dialog> handler logic, parameterized with the caller's
`state` and `action` structs. Both the probe and the real handler
now pass a CFC instance; closures live inside the CFC. Probe also
switched from `java.lang.Runnable` (method: `run()`) to
`java.util.function.Consumer` (method: `accept(T)`) — matches the
interface used in real handling. Error messages now include the
underlying Lucee exception in `extendedInfo` for future debugging.
2. Named fixture routes (browserTestHome, browserTestLogin, etc.) are
not visible during core tests.
The core test runner includes `vendor/wheels/tests/routes.cfm`
before test execution, but prior to this PR that file did not
declare the `_browser/*` fixture routes — only the app's
`config/routes.cfm` did. Additionally, other specs in the core
suite (mapperSpec, security/PaginationXssSpec, view/linksSpec,
view/formsSpec) legitimately call `\$clearRoutes()` to test
route-registration behavior and do not restore the table after.
Since bundles run alphabetically, those specs wipe the routes
before browser specs execute.
Fix A: register `_browser/*` fixture routes in
`vendor/wheels/tests/routes.cfm` so core tests can resolve them.
Fix B: `BrowserTest.beforeAll()` re-includes the test routes file
and calls `\$setNamedRoutePositions()`. This makes browser specs
self-contained regardless of earlier specs' route-wiping.
3. Fixture controllers + views live only in the app tree.
The core test runner swaps `controllerPath` to
`/wheels/tests/_assets/controllers`, so app-tree controllers
(`BrowserTestHome.cfc`, `BrowserTestLogin.cfc`,
`BrowserTestSessions.cfc`) and their views are invisible during
core tests. Playwright-driven test requests returned empty
responses, failing assertions like "Expected page to contain
'alice@example.com'".
Fix: copy the three fixture controllers + six view templates into
`vendor/wheels/tests/_assets/controllers/` and
`vendor/wheels/tests/_assets/views/browsertest*/`. The loginAs
env-gate in the app copy references `application.\$wheels` which is
cleared post-init; the vendor copy omits the gate since the test
routes are only loaded by the core test runner.
Also:
- `tools/test-local.sh` exports `WHEELS_BROWSER_TEST_BASE_URL` to
match the local `\$PORT` so Playwright hits the running server
instead of the default localhost:8080. CI's own export is preserved
via the `\${VAR:-default}` pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
132e8d0 to
37dcb33
Compare
Documents the three root causes fixed in this PR so future contributors
don't hit the same traps:
- .ai/wheels/cross-engine-compatibility.md gains a new section on
Lucee 7's tightened `createDynamicProxy` signature — structs no
longer accepted, must be CFC instances. Includes the misleading
error message ("Can't cast Struct to String") and the CFC-with-init
pattern from DialogConsumer.
- .ai/wheels/testing/browser-testing.md updates two existing gotchas:
- `createDynamicProxy` entry now distinguishes engine support
(Lucee-only) from the Lucee 6→7 argument-type tightening.
- Fixture routes entry explains the two-place requirement (app tree
for app tests + vendor tree for core tests) and the same split
for controllers and views.
Adds a new gotcha on sibling specs (`mapperSpec`, `PaginationXssSpec`,
`view/linksSpec`, `view/formsSpec`) that call `$clearRoutes()` in
`beforeEach`/`beforeAll` without restoring — bundles run
alphabetically, so later specs that need named routes find an empty
table. The defensive fix (`beforeAll` re-include) is documented as
the pattern for route-dependent specs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves the 20 browser-test errors that surface when running
bash tools/test-local.shon Lucee 7 + SQLite. Prior state: 3063 pass / 0 fail / 20 error. New state: 3067 pass / 0 fail / 0 error. All 119 browser specs (BrowserDialog/Integration/Launcher/Login/Route/TestLifecycle) are now green.Three distinct root causes, each fixed directly — no skip/gate workarounds.
1. Lucee 7 rejects struct-based
createDynamicProxytargets (8 dialog errors)BrowserClient.$requireDialogSupport()and$registerDialogListener()passed an inline struct with anacceptclosure as the proxy target. Lucee 7 tightened the first-argument type to require a Component — structs now fail with"Can't cast Complex Object Type Struct to String"as Lucee tries the CFC-path-string overload first.Fix: new
wheels.wheelstest.DialogConsumerCFC that encapsulates theConsumer<Dialog>handler logic, parameterized with the caller'sstateandactionstructs. Both the probe and the real handler now pass a CFC instance. Probe also corrected fromjava.lang.Runnable(methodrun()) tojava.util.function.Consumer(methodaccept(T)) to match the interface actually used in dialog handling.2. Named fixture routes not visible during core tests (5 route errors)
Two sub-problems:
a)
vendor/wheels/tests/routes.cfm(the core test runner's route file) did not declare the_browser/*fixture routes — only the app'sconfig/routes.cfmdid.b) Other specs in the core suite (
mapperSpec,security/PaginationXssSpec,view/linksSpec,view/formsSpec) legitimately call$clearRoutes()to test route-registration behavior and do not restore afterwards. Bundles run alphabetically, so those specs wipe the route table before browser specs execute.Fix A: register
_browser/*fixture routes invendor/wheels/tests/routes.cfm.Fix B:
BrowserTest.beforeAll()re-includes the test routes file and calls$setNamedRoutePositions(), making browser specs self-contained regardless of earlier specs' route-wiping.3. Fixture controllers + views only existed in the app tree (7 login errors)
The core test runner swaps
controllerPathto/wheels/tests/_assets/controllers, so the app-treeBrowserTestHome.cfc,BrowserTestLogin.cfc,BrowserTestSessions.cfcand their views are invisible during core tests. Playwright requests returned empty responses, failing assertions like "Expected page to contain 'alice@example.com'".Fix: copied the three fixture controllers + six view templates into
vendor/wheels/tests/_assets/. TheloginAsenv-gate in the app copy referencesapplication.$wheelswhich is cleared post-init — the vendor copy omits the gate since these routes are only loaded by the core test runner.Also
tools/test-local.shexportsWHEELS_BROWSER_TEST_BASE_URL=http://localhost:${PORT}so Playwright hits the running server instead of the defaultlocalhost:8080. CI's own export is preserved via the${VAR:-default}pattern.Test plan
PORT=9090 bash tools/test-local.shon Lucee 7 + SQLite: 3067 pass / 0 fail / 0 error (was 3063 pass / 0 fail / 20 error).BrowserTest.beforeAll— full suite passes cleanly.$dialogSupported=trueresult on first call).Why not just skip?
Skipping via
WHEELS_CI=truemasked three real bugs:Each root-cause fix unlocks the actual capability rather than hiding the symptom.
🤖 Generated with Claude Code