diff --git a/changelog.d/3113-cli-test-verbose-tree.fixed.md b/changelog.d/3113-cli-test-verbose-tree.fixed.md new file mode 100644 index 0000000000..361f0e95b1 --- /dev/null +++ b/changelog.d/3113-cli-test-verbose-tree.fixed.md @@ -0,0 +1 @@ +- `wheels test --verbose` (and `-v`, in any flag position) now actually prints the per-spec bundle → suite → spec tree: the test command honors the runtime `verboseEnabled` signal LuCLI passes through `init()`, since the launcher consumes the `--verbose`/`-v` token globally and it never reaches the module's own parser; requires a CLI binary built on a LuCLI runtime that preserves the flag across the module-shortcut re-dispatch (#3113) diff --git a/cli/lucli/Module.cfc b/cli/lucli/Module.cfc index 567a2dd618..1832497f6f 100644 --- a/cli/lucli/Module.cfc +++ b/cli/lucli/Module.cfc @@ -741,7 +741,7 @@ component extends="modules.BaseModule" { var filter = opts.filter; var reporter = opts.reporter; var format = opts.format; - var verboseOutput = opts.verbose; + var verboseOutput = $resolveTestVerbosity(opts.verbose); var ciMode = opts.ci; var coreTests = opts.core; var db = opts.db; @@ -769,6 +769,25 @@ component extends="modules.BaseModule" { return runTests(filter, reporter, format, verboseOutput, coreTests, db, ciMode, useTestDB, dbExplicit, basePath); } + /** + * Resolve the effective verbose flag for `wheels test`. The LuCLI picocli + * root defines `-v`/`--verbose` as GLOBAL options and consumes them + * wherever they appear on the command line — `wheels test --verbose` + * forwards only `test` to the module (verified live, issue #3113), so + * `parseTestArgs()` can never see the token on a normal install. The + * runtime conveys the flag through `init(verboseEnabled=...)` instead + * (LuCLI's executeModule.cfs passes `verboseEnabled=verbose`), which + * BaseModule stores as `variables.verboseEnabled`. Honor both sources: + * the parsed token still wins for direct/programmatic invocations that + * deliver it. + * + * Public `$`-prefixed so specs can exercise it (cli/CLAUDE.md carve-out); + * hidden from MCP by the `mcpHiddenTools()` structural sweep. + */ + public boolean function $resolveTestVerbosity(boolean parsedVerbose = false) { + return arguments.parsedVerbose || (variables.verboseEnabled ?: false); + } + /** * Normalize a short filter name to a path the test runner's directory * regex will accept. App mode prepends `tests.specs.`; core mode diff --git a/cli/lucli/tests/specs/commands/TestCommandSpec.cfc b/cli/lucli/tests/specs/commands/TestCommandSpec.cfc index 88bba43551..809dd78e70 100644 --- a/cli/lucli/tests/specs/commands/TestCommandSpec.cfc +++ b/cli/lucli/tests/specs/commands/TestCommandSpec.cfc @@ -428,6 +428,29 @@ component extends="wheels.wheelstest.system.BaseSpec" { }); + // The LuCLI picocli root defines -v/--verbose as GLOBAL options and + // consumes them wherever they sit on the command line, so the token + // never reaches parseTestArgs() ("wheels test --verbose" forwards only + // "test" to the module — verified live in issue 3113). The runtime + // conveys the flag through init(verboseEnabled=...) instead, and + // test() must honor it for --verbose to have any observable effect. + describe("$resolveTestVerbosity (--verbose / -v consumed by the LuCLI root, issue 3113)", () => { + + it("is false for a plain run", () => { + expect(mod.$resolveTestVerbosity(false)).toBeFalse(); + }); + + it("honors the runtime verboseEnabled flag set by a root-consumed --verbose / -v", () => { + var vmod = new cli.lucli.Module(cwd = variables.tempRoot, verboseEnabled = true); + expect(vmod.$resolveTestVerbosity(false)).toBeTrue(); + }); + + it("honors a --verbose token that does reach the module's own parser", () => { + expect(mod.$resolveTestVerbosity(true)).toBeTrue(); + }); + + }); + } /** diff --git a/web/sites/guides/src/content/docs/v4-0-0/command-line-tools/wheels-commands/testing.mdx b/web/sites/guides/src/content/docs/v4-0-0/command-line-tools/wheels-commands/testing.mdx index 8dcf8a80e1..be4ae24edd 100644 --- a/web/sites/guides/src/content/docs/v4-0-0/command-line-tools/wheels-commands/testing.mdx +++ b/web/sites/guides/src/content/docs/v4-0-0/command-line-tools/wheels-commands/testing.mdx @@ -58,7 +58,7 @@ A bare positional argument is treated as the filter directory — `wheels test m | `--directory=` | Alias for `--filter` (tutorial chapter 7). When both are supplied, `--directory` wins. | | `--db=` | Database engine for `--core` matrix runs only. Ignored for app tests (with a warning) — see [below](#testing-against-different-engines). | | `--reporter=` | `simple` (default, colourful), `json` (raw runner JSON), `tap` (TAP v13 for CI consumers). | -| `--verbose`, `-v` | Accepted but currently inert — output is identical to a plain run; no per-spec output is printed for passing specs. Wiring tracked in [#3113](https://github.com/wheels-dev/wheels/issues/3113). | +| `--verbose`, `-v` | Print the full bundle → suite → spec tree (one `[PASS]`/`[FAIL]` line per spec) instead of only the summary line. Applies to the default `simple` reporter. The launcher consumes the flag globally, so any position works (`wheels test --verbose`, `wheels test -v`, `wheels -v test`). CLI binaries built on a LuCLI runtime that predates the [#3113](https://github.com/wheels-dev/wheels/issues/3113) launcher fix drop the signal before it reaches the test command and print the plain summary. | | `--ci` | CI mode: emits one GitHub Actions `::error` workflow-command annotation per failed or errored spec, so failures surface inline in CI logs and PR-check panels. Exit code is non-zero on failure regardless. | | `--core` | Run framework self-tests (`vendor/wheels/tests/specs/`) instead of your app suite. App tests are the default; `--core` is the explicit opt-in. | | `--no-test-db` | Disable the auto-swap to `_test`. App tests run against your dev datasource, with whatever data is already in it. | diff --git a/web/sites/guides/src/content/docs/v4-0-0/testing/ci-integration.mdx b/web/sites/guides/src/content/docs/v4-0-0/testing/ci-integration.mdx index 79981981e5..54524b52e4 100644 --- a/web/sites/guides/src/content/docs/v4-0-0/testing/ci-integration.mdx +++ b/web/sites/guides/src/content/docs/v4-0-0/testing/ci-integration.mdx @@ -79,7 +79,7 @@ The `wheels test` command accepts a `--reporter=` flag. The CLI always req | `--reporter=json` | Emits the raw JSON result document — pipe it to `jq` or a post-processor | | `--reporter=tap` | Emits TAP version 13 (`1..N`, `ok` / `not ok` lines) for TAP-consuming CI tooling | | `--ci` | Emits one GitHub Actions `::error` workflow-command annotation per failed or errored spec, so failures appear inline in CI logs and PR-check panels. Exit code is non-zero on failure regardless. | -| `--verbose` / `-v` | Accepted but currently inert — output is identical to a plain run; the per-spec tree wiring is tracked in [#3113](https://github.com/wheels-dev/wheels/issues/3113) | +| `--verbose` / `-v` | Prints the full bundle → suite → spec tree (one `[PASS]`/`[FAIL]` line per spec) with the default `simple` reporter; flag position doesn't matter (`wheels -v test` works too). Binaries built on a LuCLI runtime that predates the [#3113](https://github.com/wheels-dev/wheels/issues/3113) launcher fix drop the signal and print the plain summary | For machine-readable results you can also call the test runner URL directly and post-process the JSON. That is exactly what `tools/ci/run-tests.sh` does in this repo: it `curl`s `/wheels/core/tests?db=sqlite&format=json`, parses the totals in Python, emits a JUnit XML file that `actions/upload-artifact` ingests for the GitHub summary, and fails the build when the payload reports a rejected `directory=` scope or a 0-bundle discovery.