Files
buzz/desktop/playwright.config.ts
npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc ee6fb0ac9d fix(desktop): keep agent activity feed pinned to bottom across smooth-scroll
`useStickToBottom` recomputed `isNearBottomRef` from raw `scrollTop` on
every scroll event. When the hook scheduled a `behavior: "smooth"`
scroll to follow new content, the browser emitted intermediate scroll
events whose `scrollTop` was mid-animation — `distance` > 100 even
though the viewport was animating *toward* the bottom. The buggy
handler flipped `isNearBottom` to false during those events, and the
next content append refused to follow. User-visible symptom: the
agent observer / activity feed scrolls down on the first message,
then "jumps back a couple lines" as more tokens arrive.

Make `onScroll` direction-aware via `lastScrollTopRef`:

- `distance < 100` → always sticky (handles initial state and
  resticks once an animation settles).
- `scrollTop < lastScrollTop` → user pulled the viewport upward.
  Detach.
- Otherwise → leave the sticky bit alone (smooth-scroll in flight
  toward the bottom).

Seed `lastScrollTopRef` in the initial-scroll effect so the first
real scroll event has a meaningful prior value.

Regression test: a Playwright spec drives a fixture that uses the
real hook through a deterministic mid-animation race — grow the
scroll range with a content burst, then synthetically dispatch an
intermediate `scroll` event whose `scrollTop` is climbing toward the
new bottom but `distance > 100`. Pre-fix that assertion fails on
`isNearBottom`; post-fix it passes. A second spec guards the
direction-aware unstick path so the fix doesn't over-correct.

Co-authored-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
2026-06-19 13:00:08 -04:00

90 lines
2.7 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",
"**/active-turn-resilience-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",
"**/sidebar-more-unread-overlap.spec.ts",
"**/thread-unread-screenshots.spec.ts",
"**/animated-avatar-screenshots.spec.ts",
"**/reminders-screenshots.spec.ts",
"**/virtualization-screenshots.spec.ts",
"**/scroll-history.spec.ts",
"**/stick-to-bottom-scroll-jump.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",
},
});