@@ -9,30 +9,38 @@ import {
99 SIGN_IN_BUTTON_SELECTOR ,
1010 SIGN_IN_CARD_SELECTOR ,
1111 SIGN_OUT_BUTTON_SELECTOR ,
12+ AUTH_BANNER_UNAUTHENTICATED_SELECTOR ,
13+ ERROR_BANNER_SELECTOR ,
14+ ERROR_BOUNDARY_SELECTOR ,
1215} from "./d5-auth.js" ;
1316
1417interface FakeOpts {
15- /** Whether the sign-in button is initially visible. Default true. */
18+ /** Whether the SignInCard's sign-in button is visible at preFill time
19+ * (idiomatic shape detection). Default true (langgraph-python). */
1620 signInButtonVisible ?: boolean ;
17- /** Whether the chat textarea mounts after sign-in. Default true. */
21+ /** Whether the chat textarea mounts after sign-in click . Default true. */
1822 textareaMountsAfterSignIn ?: boolean ;
1923 /** Whether the sign-out button is visible after authentication. Default true. */
2024 signOutButtonVisible ?: boolean ;
21- /** Whether the SignInCard re-mounts after sign-out. Default true . */
25+ /** Whether the SignInCard re-mounts after sign-out (idiomatic-shape pass) . */
2226 signInCardRemounts ?: boolean ;
27+ /** Whether the legacy banner flips to unauthenticated after sign-out. */
28+ legacyBannerFlipsToUnauth ?: boolean ;
29+ /** Whether the legacy error surface eventually appears. */
30+ legacyErrorSurfaceAppears ?: boolean ;
2331}
2432
2533function makePage ( opts : FakeOpts ) : {
2634 page : Page ;
2735 fakeClick : ( p : Page , sel : string ) => Promise < void > ;
2836} {
29- let signedIn = false ;
37+ let signedIn = ! opts . signInButtonVisible ; // legacy starts already-signed-in
3038 let signedOut = false ;
3139 const page : Page = {
3240 async waitForSelector ( selector : string ) {
3341 if ( selector === SIGN_IN_BUTTON_SELECTOR ) {
3442 if ( ! ( opts . signInButtonVisible ?? true ) ) {
35- throw new Error ( "waitForSelector timeout (sign-in button missing )" ) ;
43+ throw new Error ( "waitForSelector timeout (legacy shape — no sign-in button)" ) ;
3644 }
3745 return ;
3846 }
@@ -67,11 +75,22 @@ function makePage(opts: FakeOpts): {
6775 }
6876 return ;
6977 }
78+ if ( selector === AUTH_BANNER_UNAUTHENTICATED_SELECTOR ) {
79+ if ( ! signedOut || ! ( opts . legacyBannerFlipsToUnauth ?? false ) ) {
80+ throw new Error (
81+ "waitForSelector timeout (legacy banner never flipped)" ,
82+ ) ;
83+ }
84+ return ;
85+ }
7086 } ,
7187 async fill ( ) { } ,
7288 async press ( ) { } ,
7389 async evaluate ( ) {
74- return undefined as never ;
90+ // probeErrorSurfaceVisible asks for `[data-testid="auth-demo-error"]`
91+ // OR `[data-testid="auth-demo-chat-boundary"]` — return true after
92+ // sign-out if the legacy error surface should appear.
93+ return ( signedOut && ( opts . legacyErrorSurfaceAppears ?? false ) ) as never ;
7594 } ,
7695 } ;
7796 const fakeClick = async ( _p : Page , sel : string ) : Promise < void > => {
@@ -108,61 +127,121 @@ describe("d5-auth script", () => {
108127 expect ( SIGN_OUT_BUTTON_SELECTOR ) . toBe (
109128 '[data-testid="auth-sign-out-button"]' ,
110129 ) ;
130+ expect ( AUTH_BANNER_UNAUTHENTICATED_SELECTOR ) . toBe (
131+ '[data-testid="auth-banner"][data-authenticated="false"]' ,
132+ ) ;
133+ expect ( ERROR_BANNER_SELECTOR ) . toBe ( '[data-testid="auth-demo-error"]' ) ;
134+ expect ( ERROR_BOUNDARY_SELECTOR ) . toBe (
135+ '[data-testid="auth-demo-chat-boundary"]' ,
136+ ) ;
111137 } ) ;
112138
113139 describe ( "buildAuthPreFill" , ( ) => {
114- it ( "fails when sign-in button is not visible (demo loaded into wrong state)" , async ( ) => {
115- const { page, fakeClick } = makePage ( { signInButtonVisible : false } ) ;
116- const preFill = buildAuthPreFill ( { click : fakeClick } ) ;
117- await expect ( preFill ( page ) ) . rejects . toThrow (
118- / s i g n - i n b u t t o n .* n o t v i s i b l e / ,
119- ) ;
140+ it ( "on idiomatic shape: clicks sign-in then waits for chat textarea" , async ( ) => {
141+ const { page, fakeClick } = makePage ( {
142+ signInButtonVisible : true ,
143+ textareaMountsAfterSignIn : true ,
144+ } ) ;
145+ const preFill = buildAuthPreFill ( { click : fakeClick , detectTimeoutMs : 50 } ) ;
146+ await expect ( preFill ( page ) ) . resolves . toBeUndefined ( ) ;
120147 } ) ;
121148
122- it ( "fails when chat textarea does not mount after sign-in" , async ( ) => {
149+ it ( "on idiomatic shape: fails when chat textarea does not mount after sign-in" , async ( ) => {
123150 const { page, fakeClick } = makePage ( {
151+ signInButtonVisible : true ,
124152 textareaMountsAfterSignIn : false ,
125153 } ) ;
126- const preFill = buildAuthPreFill ( { click : fakeClick } ) ;
154+ const preFill = buildAuthPreFill ( { click : fakeClick , detectTimeoutMs : 50 } ) ;
127155 await expect ( preFill ( page ) ) . rejects . toThrow (
128156 / c h a t t e x t a r e a d i d n o t m o u n t / ,
129157 ) ;
130158 } ) ;
131159
132- it ( "succeeds when sign-in mounts the chat surface" , async ( ) => {
133- const { page, fakeClick } = makePage ( { } ) ;
134- const preFill = buildAuthPreFill ( { click : fakeClick } ) ;
160+ it ( "on legacy shape: returns immediately (no sign-in click needed)" , async ( ) => {
161+ // No sign-in button visible → legacy shape → preFill is a no-op.
162+ const { page, fakeClick } = makePage ( { signInButtonVisible : false } ) ;
163+ const preFill = buildAuthPreFill ( { click : fakeClick , detectTimeoutMs : 50 } ) ;
135164 await expect ( preFill ( page ) ) . resolves . toBeUndefined ( ) ;
136165 } ) ;
137166 } ) ;
138167
139168 describe ( "buildAuthAssertion" , ( ) => {
140- it ( "fails when sign-out button is not visible (sign-in path didn't authenticate) " , async ( ) => {
169+ it ( "fails when sign-out button is not visible" , async ( ) => {
141170 const { page, fakeClick } = makePage ( { signOutButtonVisible : false } ) ;
142171 const assertion = buildAuthAssertion ( {
143172 signOutTimeoutMs : 50 ,
173+ detectTimeoutMs : 50 ,
144174 click : fakeClick ,
145175 } ) ;
146176 await expect ( assertion ( page ) ) . rejects . toThrow (
147177 / s i g n - o u t b u t t o n .* n o t v i s i b l e / ,
148178 ) ;
149179 } ) ;
150180
151- it ( "fails when SignInCard does not re-mount after sign-out (tree didn't unmount)" , async ( ) => {
152- const { page, fakeClick } = makePage ( { signInCardRemounts : false } ) ;
181+ it ( "on idiomatic shape: succeeds when SignInCard re-mounts after sign-out" , async ( ) => {
182+ const { page, fakeClick } = makePage ( {
183+ signInButtonVisible : true ,
184+ signOutButtonVisible : true ,
185+ signInCardRemounts : true ,
186+ } ) ;
153187 const assertion = buildAuthAssertion ( {
154- signOutTimeoutMs : 50 ,
188+ signOutTimeoutMs : 5_000 ,
189+ detectTimeoutMs : 50 ,
155190 click : fakeClick ,
156191 } ) ;
157192 await expect ( assertion ( page ) ) . rejects . toThrow (
158193 / S i g n I n C a r d .* d i d n o t r e - m o u n t / ,
159194 ) ;
160195 } ) ;
161196
162- it ( "succeeds when SignInCard re-mounts after clicking sign-out" , async ( ) => {
163- const { page, fakeClick } = makePage ( { } ) ;
164- const assertion = buildAuthAssertion ( { click : fakeClick } ) ;
197+ it ( "on legacy shape: succeeds when banner flips and error surface appears" , async ( ) => {
198+ const { page, fakeClick } = makePage ( {
199+ signInButtonVisible : false , // legacy
200+ signOutButtonVisible : true ,
201+ signInCardRemounts : false , // legacy doesn't have SignInCard
202+ legacyBannerFlipsToUnauth : true ,
203+ legacyErrorSurfaceAppears : true ,
204+ } ) ;
205+ const assertion = buildAuthAssertion ( {
206+ signOutTimeoutMs : 5_000 ,
207+ detectTimeoutMs : 50 ,
208+ click : fakeClick ,
209+ } ) ;
165210 await expect ( assertion ( page ) ) . resolves . toBeUndefined ( ) ;
166211 } ) ;
212+
213+ it ( "fails when neither idiomatic re-mount nor legacy banner-flip happens" , async ( ) => {
214+ const { page, fakeClick } = makePage ( {
215+ signOutButtonVisible : true ,
216+ signInCardRemounts : false ,
217+ legacyBannerFlipsToUnauth : false ,
218+ } ) ;
219+ const assertion = buildAuthAssertion ( {
220+ signOutTimeoutMs : 100 ,
221+ detectTimeoutMs : 50 ,
222+ click : fakeClick ,
223+ } ) ;
224+ await expect ( assertion ( page ) ) . rejects . toThrow (
225+ / n e i t h e r i d i o m a t i c S i g n I n C a r d r e - m o u n t n o r l e g a c y b a n n e r - f l i p / ,
226+ ) ;
227+ } ) ;
228+
229+ it ( "fails when legacy banner flips but error surface never appears" , async ( ) => {
230+ const { page, fakeClick } = makePage ( {
231+ signInButtonVisible : false ,
232+ signOutButtonVisible : true ,
233+ signInCardRemounts : false ,
234+ legacyBannerFlipsToUnauth : true ,
235+ legacyErrorSurfaceAppears : false ,
236+ } ) ;
237+ const assertion = buildAuthAssertion ( {
238+ signOutTimeoutMs : 200 ,
239+ detectTimeoutMs : 50 ,
240+ click : fakeClick ,
241+ } ) ;
242+ await expect ( assertion ( page ) ) . rejects . toThrow (
243+ / l e g a c y s h a p e .* n e i t h e r .* a p p e a r e d / ,
244+ ) ;
245+ } ) ;
167246 } ) ;
168247} ) ;
0 commit comments