chore(deps): upgrade leptos 0.7 -> 0.8 (resolves wasm-streams dup)#387
Open
intendednull wants to merge 14 commits intomainfrom
Open
chore(deps): upgrade leptos 0.7 -> 0.8 (resolves wasm-streams dup)#387intendednull wants to merge 14 commits intomainfrom
intendednull wants to merge 14 commits intomainfrom
Conversation
server_fn 0.8 uses wasm-streams 0.5, aligning with reqwest's wasm-streams 0.5 (pulled by iroh-relay). This kills the duplicate-symbol link error that has been silently breaking CI Browser tests under bash -e (no pipefail). After this lands, ui/phase-3a-composer rebases on main and its 39 browser tests will run on CI for the first time. Verified: cargo tree -i wasm-streams --target wasm32-unknown-unknown returns a single version (0.5.x). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Steps with `id:` set on this runner inherit a shell of `bash -e {0}`
(no pipefail), so `cmd 2>&1 | tee /tmp/log` masks the upstream exit
code — `tee` always returns 0 and the step is reported as success
even when the actual command died.
Discovered while auditing browser test runs: ui/phase-3a-composer
shows `error: linking with rust-lld failed` followed by a green
conclusion, and the same is true for recent main-branch runs. The
duplicate-symbol link error from two `wasm-streams` versions has
been live for some time but invisible because the step "passes."
Fix is mechanical: force `bash --noprofile --norc -eo pipefail {0}`
on every tee-piped step (Clippy, Test, Browser tests, Playwright
E2E) so the upstream exit code surfaces.
Note: this commit will likely turn Browser tests RED on this PR —
that's the correct outcome. The dep skew between
`wasm-streams 0.4.2` (Leptos -> server_fn) and
`wasm-streams 0.5.0` (iroh -> reqwest) needs a separate fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two latent issues surfaced once the wasm-pack test binary actually ran
to completion (CI was previously masking the link error so this whole
mod was effectively unmonitored).
1. `foundation.css` ships an `@import url('https://fonts.googleapis.com/...')`
for Fraunces/Plex/JetBrains. Headless Firefox under wasm-pack has no
network access and the @import stalls/cancels the entire stylesheet,
leaving every `:root` custom property unresolved. Strip @import lines
before injecting the CSS into `<style>` so token resolution works
offline. The fonts are irrelevant to token assertions.
2. `style.css` redeclared `--focus-ring: var(--focus-ring, …)` on the
same `:root` selector that already inherits the token from
`foundation.css`. A same-selector self-reference is a custom-property
dependency cycle, which CSS resolves to the guaranteed-invalid value
— blanking `--focus-ring` everywhere it's consumed (focus outlines,
button/state styling). Drop the redundant declaration; foundation.css
owns the token.
The bug also explains why focus rings on the deployed app may have looked
weaker than the spec since phase-0 — same-selector cycles are silent,
they just produce empty values.
Restores all three `foundation_tokens` browser tests:
- foundation_palette_tokens_defined
- legacy_bg_main_aliases_bg_0
- data_accent_swap_changes_moss_2
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…te contract Phase 2a (commit 77ce56e) shipped two ad-hoc spans for the message-row inline queue hint: <span class="queue-note queue-note--pending"> queued · will send on reconnect </span> <span class="queue-note queue-note--late"> sent earlier · arrived now </span> Phase 2b (commits 357e128 + 9535748) replaced both with a shared `<InlineQueueNote>` component that consumes spec-pinned copy from `sync_queue_copy` and renders `.inline-note.inline-note--{queued,inbound-held}`. The Phase 2a tests were never updated to follow that refactor; with the CI mask now lifted they fail because the old class names + the legacy `queued · will send on reconnect` copy no longer exist. Update both row tests to assert the live contract: - `queue_note_late_renders_hint` now queries `.inline-note.inline-note--inbound-held`. - `queue_note_pending_renders_hint_and_badge` queries `.inline-note.inline-note--queued` and asserts the spec copy from `sync_queue_copy::msg_note_queued("Mira")` (`queued · will send when Mira reachable`, per `docs/specs/2026-04-19-ui-design/sync-queue.md` §Copy / msg_note_queued_peer). `.queued-badge` + `.message--pending` checks are unchanged — those classes still flow from `message.rs` as before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n frames The 6 reconnection-toast / welcome-back-banner tests pre-set `device_online = false`, mount the component, then schedule the `device_online → true` flip inside `request_animation_frame`. They relied on `tick().await; tick().await;` (each a `setTimeout(0)`) being enough wall-clock time for that RAF callback to fire before the assertions ran. That assumption is wrong under wasm-pack's headless Firefox harness. Every `#[wasm_bindgen_test]` runs in the same browser tab, so prior tests leave RAF callbacks, gloo timers, and forgotten leptos owners behind. Under enough load the next test's RAF can sit in the queue past two `setTimeout(0)` resolutions, leaving the toast/banner unmounted when the test asserts. Locally this manifested as a ~⅓ flake on `reconnection_toast_dismiss_button_hides_toast` and `reconnection_toast_fires_after_60s_offline`; on CI the same flake sometimes hit the welcome-back-banner pair. Add `await_animation_frame()` — a Future that resolves when the browser actually dispatches the next RAF — and inject one between the two `tick()` calls in every transition-driving test. Now the sequence is deterministic: tick // initial Effect run with prev=true, online=false await_animation_frame // fence — the queued RAF closure has fired tick // reactive flush of the false→true transition Three sequential phase_2b runs and two full-suite runs (304 tests each) go green after this change. No production code touched — the bug is purely a test-harness synchronization gap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "server gear" button was renamed to the "grove header" during the UI redesign; production code in channel_sidebar.rs:266-270 renders the button with `class="grove-header sidebar-header"` and `aria-label="grove menu"`, with no `.server-gear-btn` anywhere in crates/web/src/. The e2e tests + helpers kept the old selector. CI hid the failure because Browser tests + E2E both ran under bash -e (no pipefail) and the | tee pipe masked the exit code. With the pipefail fix on this PR, 7 mobile-chrome E2E tests fail on the same locator timeout. Switch to the semantic [aria-label="grove menu"] selector — robust to class drift, works on both desktop and mobile shells. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`e2e/cross-browser-sync.spec.ts` launches both Chromium and Firefox to
verify cross-browser P2P connectivity, but `scripts/setup-e2e.sh` only
installed Chromium. Firefox-launching tests failed with:
browserType.launch: Executable doesn't exist at
/home/runner/.cache/ms-playwright/firefox-1509/firefox/firefox
Mirror the existing Chromium install block — same filesystem guard so
re-runs skip the download. Skip `--with-deps` for the same reason as
Chromium (sudo prompt is non-interactive in CI sandboxes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `.net-status-footer` selector no longer exists in production code — `grep -r net-status-footer crates/web/src` is empty. The mobile branch of the same OR-locator (`.mobile-top-bar`) still works, so only the desktop project (which renders the desktop shell) was failing. The test's intent is "the app shell rendered after server creation, proving the network came up enough for the client to join a server and mount the channel surface." On desktop the always-mounted equivalent is `.main-pane-header` (the channel header). Use it. Reachability and queue depth indicators (`.relay-signal-button`, `.offline-strip`) live inside the sync-queue panel which only mounts on demand, so neither is a stable "app loaded" proxy at this point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The members pane is closed by default. `setupTwoPeers` opens it only briefly to wait for display-name sync, then `closeMemberList` collapses it again — `.right-rail` switches `data-open` to "false" and the `match which.get()` branch unmounts MemberList entirely (right_rail.rs). Counting `.member-item` against the closed pane returned 0, failing the `toBeGreaterThanOrEqual(2)` assertion. Open the pane explicitly via `openMemberList` and poll the count instead of a fixed `waitForTimeout(1000)` so we don't race against member-sync completion. After `kickPeer` toggles the pane during its own flow, re-open it before re-counting. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three Playwright tests timed out waiting for channels to gossip through
the relay after a `joinViaInvite`:
- `multi-peer-sync.spec.ts:48` (desktop+mobile) — pre-existing channels
visible after join. Setup pre-creates two channels, then peer 2 joins.
Three sequential `toBeVisible({ timeout: 30_000 })` assertions on top
of fresh-start + invite + join can total > 120 s on CI.
- `multi-peer-mobile.spec.ts:43` — `setupTwoPeers` failed inside
`joinViaInvite` waiting for `.channel-item` (20 s). First-channel
arrival via gossip can take 30+ s when the relay is recovering from
the previous serial test's teardown.
Bump the post-join `.channel-item` wait inside `joinViaInvite` from 20 s
to 60 s so the helper itself doesn't flake. Bump per-describe timeout
on the two affected specs from 120 s to 180 s for the compounded budget.
These are timing-only fixes — no behavioural change. Selectors,
production code, and assertions stay identical. The fast happy path
(first-channel visible immediately) still resolves immediately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Owner
Author
|
e2e is still failing, plz fix |
…ation_frame `Closure::once_into_js` enforces FnOnce at the JS boundary (throws on second invocation), and the `js_sys::Promise` constructor body is synchronous, so the take-once guard around `resolve` is unreachable. Sibling helper `request_animation_frame` ten lines above already uses the simple form — this aligns the two. Verified via `cargo check --tests --target wasm32-unknown-unknown`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…bility assertion Deep-review of PR #387 identified two real regressions and one weakened test that the prior timeout-bump commit (7f88280) had been masking. Root cause 1 — strict-mode violation on grove-menu selector. Earlier commit a625105 swapped `.server-gear-btn` → `[aria-label="grove menu"]` but `e2e/cross-browser-sync.spec.ts:60,136` use the bare unscoped form. The grove header renders both in the desktop sidebar and inside `.mobile-home`, so the bare locator matches multiple elements and Playwright's strict mode throws instantly — no timeout absorbs that. Scope both call sites with `${visibleShell(page)}`. Root cause 2 — display name lost in setupTwoPeers. `getPeerId(page2)` auto-advanced past welcome step 1 with no name supplied, so the joiner broadcasts "anonymous" and downstream lookups for `.member-item` text "Bob" never resolve. Thread `displayName` through `getPeerId` → `advancePastNameStep` so step 1 commits with the intended name. Also flip the membership-sync wait in setupTwoPeers from warn-and-continue to throw — silent fallback hid the very regression we were debugging (per CLAUDE.md "don't swallow errors"). Worker-nodes test — restore real relay-reachability assertion. The `.net-status-footer` selector deleted in an earlier UI redesign was replaced (commit 99298c9) with `.main-pane-header`, which is the channel banner — it does not contain network indicators despite the comment claiming otherwise. Real reachability flows through `RelaySignalButton` (`relay_signal_button.rs:48-49` — `--ok` modifier is set only when `Network::relay_status` reports `Reachable` from the live iroh handshake), which is rendered only inside `<SyncQueueView>` when `app.queue.open == true`. Add an `openSyncQueue` helper that opens the panel via the command palette, then assert `.relay-signal-button.relay-signal-button--ok:visible`. Mobile shell does not yet expose the palette, so the relay assertion is desktop- only; the shell smoke check still runs in both projects. While wiring this up, found a latent UI bug: `app.rs` was passing no `on_open_sync_queue` callback to `<CommandPalette>`, so the palette catalog had the action but clicking it was a silent no-op. Add the one-line callback that flips `app_state.queue.open`. Revert the timeout bumps from 7f88280: - helpers.ts post-join channel waits 60_000 → 20_000 (×2) - multi-peer-{mobile,sync}.spec.ts test budget 180_000 → 120_000 The membership-sync `waitFor` in setupTwoPeers stays at 60_000 because it is the one assertion in the helper that genuinely depends on cross- peer gossip delivery — and now that it throws instead of warning, the larger ceiling protects CI cold-start without re-introducing silent flake. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…els test Same anti-pattern as the prior commit fixed in `setupTwoPeers`: `getPeerId(page2)` advances past welcome step 1 with no name, so the subsequent `joinViaInvite(page2, _, 'Bob')` cannot commit the name — step 1 has already been completed. The test's assertions only check channel names, so this didn't break the test's behaviour, but the 'Bob' arg to `joinViaInvite` was silently dead. Pass the name into `getPeerId` so the joiner actually identifies as Bob, matching the pattern in `setupTwoPeers`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intendednull
pushed a commit
that referenced
this pull request
Apr 30, 2026
This reverts commit b34d820. Reverting because the new test-hooks-guard CI job hits a pre-existing wasm-streams duplicate-symbol link error (issue tracked by PR #387): rust-lld: error: duplicate symbol: __wbg_intounderlyingbytesource_free rust-lld: error: duplicate symbol: __wbindgen_describe_intounderlyingbytesource_* ... Two transitive crates pull conflicting versions: wasm-streams 0.4.2 <- server_fn 0.7.8 <- leptos 0.7.8 wasm-streams 0.5.0 <- reqwest 0.13.2 <- iroh-relay Trunk's prod build (cargo build --release --target wasm32-unknown-unknown on the bin target) fails to link. The existing 'browser' CI job hits the same bug but masks it via 'wasm-pack test ... | tee' under bash -e without pipefail (tee's exit 0 swallows wasm-pack's failure). My new step is a single command with no pipe -> bash -e propagates the failure -> CI red. Won't mask via tee; per CLAUDE.md 'No swallowing errors'. #490 has to wait for #387 (leptos 0.7 -> 0.8 upgrade collapses the wasm-streams duplication) to land. Filing follow-up issue noting the dependency chain. Leaving the eslint CI wiring (#491) and the js_sys::eval safe-DOM swap (#425) in place; both are unaffected by the wasm-streams bug. Refs #490, #387
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
Bumps
leptosfrom 0.7.8 to 0.8.19 incrates/web/Cargo.toml.Why
CI Browser tests have been silently broken for weeks. Two transitive crates pulled different
wasm-streamsversions, causing a duplicate-symbol link error in the wasm test binary:wasm-streams 0.4.2<-server_fn 0.7.8<-leptos 0.7.8wasm-streams 0.5.0<-reqwest 0.13.2<-iroh-relayThe link error was masked by a missing
pipefailin CI'sbash -e {0}shell (a separate fix onui/phase-3a-composeraddspipefail, surfacing the failure). We can't easily drop the iroh side, so the fix is to bump Leptos:server_fn 0.8.xuseswasm-streams 0.5, aligning both sides.After this lands,
ui/phase-3a-composerwill rebase on main and its 39 browser tests will run on CI for the first time.Breaking changes from 0.7 -> 0.8
None applied to this crate. The Leptos 0.8 release notes flag a few areas (LocalResource no longer wrapping in
SendWrapper, server-fn error type changes, axum 0.8) but none affected our CSR-only web crate.cargo build,cargo check --target wasm32-unknown-unknown,cargo clippy, andcargo testall pass with zero source changes — onlycrates/web/Cargo.tomlandCargo.lockdiffer.Verification
wasm-streamsdeduplication confirmed:Single version 0.5.0, sourced by both
reqwestandserver_fn.Before this PR: two versions (0.4.2 and 0.5.0) pulled in for the wasm32 target.
Test plan
cargo build --workspacecargo check --target wasm32-unknown-unknown -p willow-webcargo check --target wasm32-unknown-unknown --workspace --exclude willow-relay --exclude willow-worker --exclude willow-replay --exclude willow-storage --exclude willow-agentcargo check --tests --target wasm32-unknown-unknown -p willow-web(test binary compiles)just clippy— zero warningsjust test— all native tests passjust fmt— no diffcargo tree -i wasm-streams --target wasm32-unknown-unknown -p willow-webreports a single version🤖 Generated with Claude Code