Skip to content

feat: unify smartui-storybook into smartui-cli (built-in storybook command + TUI) - #527

Draft
chaitanyas-maker wants to merge 8 commits into
LambdaTest:stagefrom
chaitanyas-maker:feat/unify-storybook
Draft

feat: unify smartui-storybook into smartui-cli (built-in storybook command + TUI)#527
chaitanyas-maker wants to merge 8 commits into
LambdaTest:stagefrom
chaitanyas-maker:feat/unify-storybook

Conversation

@chaitanyas-maker

@chaitanyas-maker chaitanyas-maker commented Jul 10, 2026

Copy link
Copy Markdown

Unify @lambdatest/smartui-storybook into @lambdatest/smartui-cli

Folds the standalone Storybook CLI into the main CLI as a built-in storybook command, plus a terminal UI. Draft — opened for review/discussion; not merge-ready (see Open Questions). Tested live end-to-end.

Why

Both packages declare bin.smartui, so they collide on PATH — installing @lambdatest/smartui-storybook silently clobbers @lambdatest/smartui-cli (and vice-versa), and users need two installs for one tool. The storybook package also re-implements auth, git, HTTP, polling, and constants the main CLI already owns. This PR makes one binary own the name and ships Storybook as a first-class command.

Approach — faithful relocation (not a rewrite)

Rather than re-plumb Storybook onto the CLI's createBuild/finalizeBuild lifecycle (which depends on an unverified backend question — see Open Questions), this vendors the proven, shipping @lambdatest/smartui-storybook v1.1.32 engine into the CLI under src/storybookVendor/, hitting the same /storybook/* endpoints it already uses in production. Behaviour is byte-identical to the standalone tool; only the collision and the double-install are removed. The "elegant" shared-lifecycle refactor is the documented follow-up once the backend routing is confirmed.

What changed

  • New command smartui storybook <url-or-dir> — URL and DIR modes, grammar identical to the standalone package.
  • New generator smartui config:create-storybook + a storybook block in the ajv ConfigSchema (and anyOf, since the top level is additionalProperties:false).
  • Vendored engine src/storybookVendor/** (12 .cjs modules), bundled by tsup. Vendored .js.cjs so esbuild bundles them as CommonJS under the CLI's "type":"module".
  • Terminal UI src/lib/tui.cjs — gradient banner + boxed launch/summary (dashboard link, screenshot/approval/change counts). Degrades gracefully without a TTY (CI-safe).
  • Deps: puppeteer, jsdom, archiver, form-data, cli-table3, proxy agents (engine); figlet, gradient-string, boxen, log-symbols, ora (TUI). pnpm-lock.yaml updated.

Testing (live)

  • DIR mode — fully working. 5-component / 17-story demo → baseline (0 changes) → one-line rebrand → 24 changed / 9 unchanged, real builds on the dashboard.
  • Core SDK regression — passes. Built from source (pnpm i → build → pack), wired to @lambdatest/playwright-driver (SDK stage), ran smartui exec -- node test.js → build finalized. The merge does not break the exec/web-capture flow.
  • URL mode — runs end-to-end after fixes below, but returns Total Screenshots: 0 from the backend /storybook/render DOM-upload path (both 17-story and 1-story). Points at the render endpoint, not the CLI glue — see Open Questions.

Fixes from review (thanks @sushobhit-lt)

  • dom.cjsdom/clone/element were implicit globals (strict-mode ReferenceError once bundled); declared with const.
  • storybook.cjs — guarded the URL-mode resolutions loop with a viewports fallback so smartui storybook <url> with no -c doesn't crash on the default config.
  • storybook.ts — merged root global flags via optsWithGlobals() so --config actually reaches the engine (local -c/--config collided with the global one).

Open questions for maintainers

  1. Backend endpoint ownership. URL-mode /storybook/render returns 0 screenshots here; and the shared-lifecycle refactor needs confirmation that the CLI's visualui/1.0 client can reach /storybook/*. Biggest unknown.
  2. Versioning / deprecation. Proposed 5.0.0 (MAJOR) + a deprecation shim on @lambdatest/smartui-storybook. Not done here.
  3. Tests + @ts-nocheck. Needs the project's test suite; src/commander/storybook.ts uses // @ts-nocheck (vendored .cjs import lacks types) — to be scoped down as follow-up.

Not included (deliberately)

Live publish, official version bump, deprecation shim, docs updates.

🤖 Generated with Claude Code


Update, 24 Aug 2026: rebased onto stage, plus five bug fixes

Rebased onto current stage (v4.1.79)

The branch was cut from prod at c03c033 (5 Jun, v4.1.71) and had drifted 1350 commits behind. It is now rebased onto stage at 5451caf, so the PR is mergeable again. Only conflict was the version line in package.json; version is now 5.0.0-unified.2.

This is what caused the reported ignoreColors failure (TE-24908). QA saw smartuiSnapshot(page, name, { ignoreColors: { cssSelector: [...] } }) rejected by the local SmartUI server with HTTP 500 Invalid snapshot, turning the build into an Error-Build with 0 screenshots, while ignoreDOM and selectDOM succeeded.

The cause was not a gap in the SDK path. ignoreColors landed upstream in eed39d2 (PR #528, TE-20613) on 13 Jul, released in v4.1.72, which is after this branch was cut. Snapshot options uses additionalProperties: false, so the unknown key failed validation with the literal message Invalid snapshot. ignoreDOM and selectDOM predate the branch cut, which is why they passed. The shipped CLI was never affected.

Verified by running the compiled validator over the same payloads before and after the rebase:

snapshot option before rebase after rebase
ignoreColors with cssSelector, id, class, xpath, coordinates, entireScreenshot all rejected, Invalid snapshot all accepted
ignoreDOM, selectDOM, no options accepted accepted
7 malformed ignoreColors payloads rejected rejected, each with its specific message

Fixes in this update

Commit Ticket Fix
d6fc275 TE-24909 URL mode fetched stories.json only. Storybook 8 dropped that file and serves index.json, so discovery 404s and the run ends with 0 screenshots. Now tries index.json first and falls back to stories.json, accepting both payload shapes (entries vs stories) and both title fields (title vs kind). DIR mode already handled both.
bb31892 TE-24912 storybook accepted only a pre-issued PROJECT_TOKEN. It now resolves PROJECT_NAME through the same /visualui/1.0/token/verify call the core client uses, auto-creating the project, then continues on the resulting token so downstream code is unchanged.
bb31892 TE-24910 Outside a git repo, branch and commit are empty, the validate call is rejected, and the CLI printed Cannot fetch latest build of the project. Error: Request failed with status code 401, which reads like an auth failure. The duplicate-build check is now skipped with a message naming the real cause, matching how the core CLI skips git details when it is not a git repo.
bcac6a3 TE-24911 Caused by this branch's switch to pnpm: pnpm 10 blocks dependency install scripts, so puppeteer's postinstall never ran and its Chromium was never downloaded, leaving URL mode to die on a WS endpoint timeout that says nothing about the missing browser. Added pnpm.onlyBuiltDependencies for puppeteer, sharp, esbuild and the playwright browsers, and wrapped puppeteer.launch so a failure names the likely cause and the command that fixes it.
6f67b26 TE-23033 config:create and config:create-storybook both default to .smartui.json, so the second only reported "already exists". Since the schema lets one file carry both a web and a storybook block, the generator now adds the block to the existing file. An existing storybook block is still left alone and reported.

Verification

  • Storybook 8 discovery, against a real Storybook 8.6 index of 17 stories served two ways: with index.json only, before the fix the request 404s and after it resolves 17 stories; with stories.json only, both before and after resolve 17. Docs entries are still skipped and kind correctly falls back to title.
  • Storybook config schema, 26 cases covering browsers, viewport bounds and caps, waitForTimeout bounds, backgroundTheme, chunkSize, customViewports XOR rules, and unknown keys. All behave as expected, including 18 negative cases.
  • Merged .smartui.json carrying both a web and a storybook block validates against ConfigSchema.
  • pnpm i now runs the previously skipped install scripts, confirmed by puppeteer downloading Chromium and the playwright browsers installing.
  • pnpm build and npm pack succeed, and the packaged tarball contains both the ignoreColors schema and the new index resolution.
  • No capture regression risk from this branch: the whole diff against stage is 1541 insertions and 9 deletions, every deletion is an import rewrite or an adjacent-line edit, and SnapshotSchema and WebStaticConfigSchema are byte-identical to stage.

Still open

  1. Backend /storybook/render returning 0 screenshots. TE-24909 fixes story discovery on Storybook 8, which is a different failure. The earlier 0-screenshot result came from a run where discovery had already succeeded, so the render endpoint question stands until URL mode is re-run against the backend.
  2. Versioning and deprecation. Still proposing 5.0.0 MAJOR plus a deprecation shim on @lambdatest/smartui-storybook. Note LambdaTest/smartui-storybook#111 is still open and would need a decision.
  3. Tests and @ts-nocheck. Unchanged from the original description.

Minor observation, not a regression

ignoreColors: { selector: [...] } with an unknown nested key is silently accepted, because the nested object has no additionalProperties: false. ignoreDOM and selectDOM behave identically on stage, so this is pre-existing and at parity. Flagging it rather than changing it here.

Chaitanya Sharma and others added 8 commits August 24, 2026 19:53
…command

Vendor the proven @lambdatest/smartui-storybook v1.1.32 engine into the single
smartui binary (faithful relocation hitting the same /storybook/* backend), add
config:create-storybook, and resolve the colliding-bin problem. One install, one
binary. Version 5.0.0-unified.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CwQRZwr64F9fLQAW4V1bWy
…ent, log-symbols)

Gradient figlet banner + launch config box + end-of-run summary box (dashboard
link, screenshot/approval/change counts) around the storybook command. Summary
deferred to process 'beforeExit' so it lands after the engine's polling. Verified
with a live baseline build (11 stories -> 33 screenshots) on SmartUI cloud.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CwQRZwr64F9fLQAW4V1bWy
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CwQRZwr64F9fLQAW4V1bWy
Two bugs that broke the URL-mode capture path (surfaced by live URL testing,
confirmed by @sushobhit-lt review on PR #1):

- storybook.ts: merge root global flags via optsWithGlobals() so `--config`
  actually reaches the engine (local -c/--config collided with the global one),
  letting validateConfig normalize viewports->resolutions.
- dom.cjs: declare `dom`/`clone`/`element` with const — they were implicit
  globals that throw ReferenceError once bundled into strict mode (same class
  as the earlier res/filename/githubURL fixes; these three were missed).
- storybook.cjs: guard the URL-mode resolutions loop with a viewports fallback
  so `smartui storybook <url>` with no -c flag doesn't crash on the default
  config (per review).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CwQRZwr64F9fLQAW4V1bWy
…TE-24909)

URL mode fetched stories.json only. Storybook 8 dropped that file and serves
index.json instead, so the request 404s, discovery reports "Cannot fetch
stories" and the run ends with 0 screenshots. DIR mode already read either file.

Try index.json first and fall back to stories.json, accepting both payload
shapes (entries for v8, stories for v7) and both title fields (title for v8,
kind for v7). Verified against a real Storybook 8.6 index (17 stories) served
with index.json only and with stories.json only: before the change the first
case failed with 404, after it resolves 17 stories from either file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017LYhe6ASMZLy7LSuAJ8pqQ
…po (TE-24912, TE-24910)

Two auth-adjacent problems in the vendored engine:

PROJECT_NAME (TE-24912). The engine only accepted a pre-issued PROJECT_TOKEN,
while the rest of the CLI accepts PROJECT_NAME and auto-creates the project.
Resolve PROJECT_NAME through the same /visualui/1.0/token/verify call the core
client uses, then continue on the resulting token, so downstream code is
unchanged.

No git repo (TE-24910). The duplicate-build check keys off branch and commit.
Outside a git repo both are empty, the API rejects the call, and the CLI prints
"Cannot fetch latest build of the project. Error: Request failed with status
code 401", which reads like an auth failure. Skip the check with a message
naming the real cause, matching how the core CLI skips git details when it is
not a git repo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017LYhe6ASMZLy7LSuAJ8pqQ
…nch failures (TE-24911)

This branch switched the lockfile to pnpm, and pnpm 10 blocks dependency install
scripts by default. puppeteer's postinstall never runs, so its Chromium is never
downloaded and URL mode dies on a WS endpoint timeout that says nothing about the
missing browser.

Declare the packages whose install scripts are required in
pnpm.onlyBuiltDependencies, and wrap puppeteer.launch so a failure names the
likely cause and the command that fixes it.

The lockfile change is the playwright bump that came in from stage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017LYhe6ASMZLy7LSuAJ8pqQ
… refusing (TE-23033)

config:create and config:create-storybook both default to .smartui.json, so
running the second after the first only produced "SmartUI Storybook config
already exists". The schema already allows one file to carry both a web and a
storybook block, so write the block into the existing file instead. An existing
storybook block is still left alone and reported.

Also give the storybook browsers uniqueItems rule its own message, which the
enum catch-all was swallowing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017LYhe6ASMZLy7LSuAJ8pqQ
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.

1 participant