Files
buzz/desktop/playwright.config.ts
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell 0f32f49045 fix(desktop): single-owner anchored scroll for dynamic loading
Replaces the two competing scroll writers (useTimelineScrollManager +
useLoadOlderOnScroll) with one anchor-based primitive, useAnchoredScroll,
owned by both the main timeline and the thread panel.

The prior design had two hooks both mutating scrollTop on prepend, which
is the root of the timeline-jumping bug: a fetch-older restore and a
scroll-anchor adjustment would fight over the same frame. The new hook
keeps a single anchor (the row the reader's eye is on, picked by a
top-crossing walk) and restores it relative to its prior top offset after
every render — prepends, appends, image loads, and embed expansions all
flow through that one path.

Also fixes three concrete bugs surfaced by the ported E2E suite:

- Deep-link targets in older history: the post-mount target effect bailed
  once when the row wasn't loaded yet and never retried. It now keys on
  `messages` and re-runs until the spliced-in row commits, then centers.
- Root-message deep-link reopen: the initial-mount target path scrolled
  and marked the target handled but never fired `onTargetReached`, so the
  `messageId` URL param stuck and re-clicking the same link was a no-op.
  The initial path now fires the callback too, matching the post-mount one.
- Inline image height reservation: images never read their NIP-92 `dim`
  tag, so a tall image grew from ~0 on load and shoved the timeline. We
  now stamp intrinsic width/height so the row reserves space before decode.

Verification: tsc clean, biome (764 files) clean, 989/989 unit tests,
scroll-history 6/6, full e2e smoke 253 passed (3 failures all reproduce
on a clean main checkout — pre-existing, unrelated to this change).

Co-authored-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
2026-06-18 12:36:44 -04:00

87 lines
2.5 KiB
TypeScript

import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./tests/e2e",
timeout: 30_000,
retries: process.env.CI ? 2 : 0,
workers: 1,
reporter: [
["list"],
["html", { open: "never", outputFolder: "playwright-report" }],
],
use: {
baseURL: "http://127.0.0.1:4173",
screenshot: "only-on-failure",
trace: "on-first-retry",
video: "retain-on-failure",
},
projects: [
{
name: "smoke",
testMatch: [
"**/smoke.spec.ts",
"**/navigation.spec.ts",
"**/channels.spec.ts",
"**/badge.spec.ts",
"**/channel-browser.spec.ts",
"**/messaging.spec.ts",
"**/custom-emoji.spec.ts",
"**/profile-custom-emoji-status.spec.ts",
"**/custom-emoji-screenshots.spec.ts",
"**/channel-mute-screenshots.spec.ts",
"**/channel-star-screenshots.spec.ts",
"**/channel-controls-screenshots.spec.ts",
"**/team-management-screenshots.spec.ts",
"**/active-turn-screenshots.spec.ts",
"**/profile-active-turn-screenshots.spec.ts",
"**/file-attachment.spec.ts",
"**/video-attachment.spec.ts",
"**/spoiler.spec.ts",
"**/mentions.spec.ts",
"**/relay-reconnect.spec.ts",
"**/relay-reconnect-screenshots.spec.ts",
"**/workflows.spec.ts",
"**/identity-archive.spec.ts",
"**/identity-archive-hide.spec.ts",
"**/relay-connectivity-screenshots.spec.ts",
"**/unread-pill-screenshots.spec.ts",
"**/thread-unread-screenshots.spec.ts",
"**/animated-avatar-screenshots.spec.ts",
"**/reminders-screenshots.spec.ts",
"**/virtualization-screenshots.spec.ts",
"**/scroll-history.spec.ts",
],
use: {
...devices["Desktop Chrome"],
},
},
{
name: "integration",
testMatch: [
"**/agents.spec.ts",
"**/onboarding.spec.ts",
"**/stream.spec.ts",
"**/integration.spec.ts",
"**/profile.spec.ts",
"**/sidebar.spec.ts",
"**/sidebar-relay-card.spec.ts",
"**/tokens.spec.ts",
"**/persona-env-vars.spec.ts",
"**/mesh-compute.spec.ts",
],
use: {
...devices["Desktop Chrome"],
},
expect: {
timeout: process.env.CI ? 15_000 : 10_000,
},
},
],
webServer: {
command: "python3 -m http.server 4173 -d dist",
cwd: ".",
reuseExistingServer: !process.env.CI,
url: "http://127.0.0.1:4173",
},
});