1- import { expect , test } from "@playwright/test" ;
1+ import { expect , test , type Page } from "@playwright/test" ;
22import {
33 DONOR_EMAIL ,
44 HOST_EMAIL ,
@@ -10,6 +10,19 @@ import {
1010 signIn ,
1111} from "./helpers" ;
1212
13+ async function expectMessageListAtBottom ( page : Page ) {
14+ await expect
15+ . poll ( async ( ) =>
16+ page . getByTestId ( "chat-message-list" ) . evaluate ( ( element ) => {
17+ return (
18+ Math . ceil ( element . scrollTop + element . clientHeight ) >=
19+ element . scrollHeight - 1
20+ ) ;
21+ } )
22+ )
23+ . toBe ( true ) ;
24+ }
25+
1326test ( "chat loads the seeded thread and composer for a signed-in donor" , async ( {
1427 page,
1528} ) => {
@@ -127,7 +140,11 @@ test("chat send disables the composer while pending and appends the new message"
127140 await expect ( sendButton ) . toHaveAttribute ( "aria-busy" , "true" ) ;
128141 await expect ( composerInput ) . toBeDisabled ( ) ;
129142 await expect ( messageVisible ) . toBeVisible ( ) ;
143+ await expectMessageListAtBottom ( page ) ;
130144 await expect ( composerInput ) . toHaveValue ( "" ) ;
145+ await expect (
146+ page . getByTestId ( "thread-list" ) . locator ( "a" ) . first ( )
147+ ) . toHaveAttribute ( "data-testid" , `thread-preview-${ SEEDED_THREAD_ID } ` ) ;
131148 await expect
132149 . poll ( async ( ) =>
133150 page . evaluate ( ( threadId ) => {
@@ -271,3 +288,69 @@ test("invalid chat thread ids redirect back to the chat index", async ({
271288 await expect ( page ) . toHaveURL ( / \/ c h a t s $ / ) ;
272289 await expect ( page . getByTestId ( "thread-list" ) ) . toBeVisible ( ) ;
273290} ) ;
291+
292+ test ( "mobile listing chat drawer keeps the composer visible as the draft grows" , async ( {
293+ page,
294+ } ) => {
295+ await page . setViewportSize ( { width : 390 , height : 844 } ) ;
296+ await signIn ( page , {
297+ email : DONOR_EMAIL ,
298+ redirectTo : "/listings/demo-marrickville-compost" ,
299+ } ) ;
300+
301+ await page . getByRole ( "button" , { name : "Contact Avery" } ) . click ( ) ;
302+ await expect ( page . getByTestId ( "chat-window" ) ) . toBeVisible ( ) ;
303+ await expect
304+ . poll ( async ( ) =>
305+ page . locator ( "[data-vaul-drawer]" ) . evaluate ( ( drawer ) => {
306+ const drawerRect = drawer . getBoundingClientRect ( ) ;
307+ return (
308+ getComputedStyle ( drawer ) . transform === "none" &&
309+ Math . ceil ( drawerRect . bottom ) <= window . innerHeight + 1
310+ ) ;
311+ } )
312+ )
313+ . toBe ( true ) ;
314+
315+ const composerInput = page . getByTestId ( "chat-composer-input" ) ;
316+ const sendButton = page . getByTestId ( "chat-composer-send" ) ;
317+ await composerInput . fill (
318+ [
319+ "Hi Avery, would it be possible to drop some scraps off tomorrow morning?" ,
320+ "About a lunchbox worth." ,
321+ "Sorry for the late notice, just leaving town tomorrow." ,
322+ "Please let me know what works best." ,
323+ ] . join ( "\n" )
324+ ) ;
325+
326+ await expect ( composerInput ) . toBeInViewport ( ) ;
327+ await expect ( sendButton ) . toBeInViewport ( ) ;
328+ const layoutMetrics = await page
329+ . getByTestId ( "chat-composer" )
330+ . evaluate ( ( composer ) => {
331+ const drawer = document . querySelector ( "[data-vaul-drawer]" ) ;
332+ const input = composer . querySelector ( "textarea" ) ;
333+ const composerRect = composer . getBoundingClientRect ( ) ;
334+ const drawerRect = drawer ?. getBoundingClientRect ( ) ;
335+ const inputRect = input ?. getBoundingClientRect ( ) ;
336+ const drawerStyles = drawer ? getComputedStyle ( drawer ) : null ;
337+
338+ return {
339+ bottomLeftRadius : parseFloat (
340+ drawerStyles ?. borderBottomLeftRadius ?? "0"
341+ ) ,
342+ composerBottom : Math . ceil ( composerRect . bottom ) ,
343+ drawerTop : Math . floor ( drawerRect ?. top ?? 0 ) ,
344+ inputHeight : Math . ceil ( inputRect ?. height ?? 0 ) ,
345+ viewportHeight : window . innerHeight ,
346+ } ;
347+ } ) ;
348+
349+ expect ( layoutMetrics . bottomLeftRadius ) . toBeGreaterThan ( 0 ) ;
350+ expect ( layoutMetrics . drawerTop ) . toBeGreaterThan ( 0 ) ;
351+ expect ( layoutMetrics . drawerTop ) . toBeLessThanOrEqual ( 24 ) ;
352+ expect ( layoutMetrics . composerBottom ) . toBeLessThanOrEqual (
353+ layoutMetrics . viewportHeight + 1
354+ ) ;
355+ expect ( layoutMetrics . inputHeight ) . toBeLessThanOrEqual ( 112 ) ;
356+ } ) ;
0 commit comments