@@ -6,39 +6,44 @@ export class MenuDropdownCO {
66 private readonly menuButtonAdmin : Locator ;
77 private readonly workspaceMenu : Locator ;
88 private readonly adminPanelLink : Locator ;
9- private readonly signOutLink : Locator ;
9+ private readonly workspaceSignOutLink : Locator ;
10+ private readonly page : Page ;
1011
1112 constructor ( page : Page ) {
13+ this . page = page ;
1214 this . menuButton = page . locator ( '#workspace-user-menu' ) ;
1315 this . menuButtonAdmin = page . getByRole ( 'button' , { name : 'Playwright Admin profile' } ) ;
1416 this . workspaceMenu = page . locator ( '#workspace-user-menu-dropdown' ) ;
1517 this . adminPanelLink = this . workspaceMenu . getByRole ( 'link' , { name : 'Admin Panel' } ) ;
16- this . signOutLink = page . getByRole ( 'link' , { name : 'Sign out' } ) ;
18+ this . workspaceSignOutLink = this . workspaceMenu . getByRole ( 'link' , { name : 'Sign out' } ) ;
1719 }
1820
1921 async open ( isAdminScreen = false ) {
2022 if ( isAdminScreen ) {
2123 await this . menuButtonAdmin . click ( ) ;
24+ await this . waitForAdminSignOutControl ( ) ;
2225 } else {
2326 await this . menuButton . click ( ) ;
27+ await Waiter . waitFor ( this . workspaceMenu , 'visible' ) ;
2428 }
2529 }
2630
2731 async goToAdminPanel ( ) {
28- await this . adminPanelLink . click ( ) ;
32+ await Waiter . waitFor ( this . adminPanelLink , 'visible' ) ;
33+ await this . adminPanelLink . click ( { force : true } ) ;
2934 }
3035
3136 async signOut ( isAdminScreen = false ) {
3237 const menuButton = isAdminScreen ? this . menuButtonAdmin : this . menuButton ;
3338
3439 // Try to open the dropdown (two attempts in case of stale click)
35- for ( let i = 0 ; i < 2 ; i ++ ) {
40+ for ( let i = 0 ; i < 2 && ! ( await this . isSignOutMenuVisible ( isAdminScreen ) ) ; i ++ ) {
3641 await menuButton . click ( ) ;
37- const visible = await this . workspaceMenu . waitFor ( { state : 'visible' , timeout : 1000 } ) . catch ( ( ) => false ) ;
42+ const visible = await this . waitForSignOutMenu ( isAdminScreen ) ;
3843 if ( visible ) break ;
3944 }
4045
41- const link = this . workspaceMenu . getByRole ( 'link' , { name : 'Sign out' } ) ;
46+ const link = isAdminScreen ? await this . getAdminSignOutControl ( ) : this . workspaceSignOutLink ;
4247
4348 // Preferred path: click the visible sign-out link
4449 const clicked = await link
@@ -49,8 +54,70 @@ export class MenuDropdownCO {
4954 if ( clicked ) return ;
5055
5156 // Fallback: clear session cookies and reload
52- const page = this . signOutLink . page ( ) ;
57+ const page = this . menuButton . page ( ) ;
5358 await page . context ( ) . clearCookies ( ) ;
5459 await page . goto ( '/' ) ;
5560 }
61+
62+ private async isSignOutMenuVisible ( isAdminScreen : boolean ) {
63+ if ( isAdminScreen ) {
64+ return ( await this . findVisibleAdminSignOutControl ( ) ) !== undefined ;
65+ }
66+
67+ return await this . workspaceMenu . isVisible ( ) ;
68+ }
69+
70+ private async waitForSignOutMenu ( isAdminScreen : boolean ) {
71+ if ( isAdminScreen ) {
72+ return await this . waitForAdminSignOutControl ( )
73+ . then ( ( ) => true )
74+ . catch ( ( ) => false ) ;
75+ }
76+
77+ return await this . workspaceMenu
78+ . waitFor ( { state : 'visible' , timeout : 1000 } )
79+ . then ( ( ) => true )
80+ . catch ( ( ) => false ) ;
81+ }
82+
83+ private async waitForAdminSignOutControl ( ) {
84+ const deadline = Date . now ( ) + 1000 ;
85+
86+ while ( Date . now ( ) < deadline ) {
87+ const control = await this . findVisibleAdminSignOutControl ( ) ;
88+
89+ if ( control ) {
90+ return control ;
91+ }
92+
93+ await this . page . waitForTimeout ( 100 ) ;
94+ }
95+
96+ throw new Error ( 'Admin sign-out control was not visible' ) ;
97+ }
98+
99+ private async getAdminSignOutControl ( ) {
100+ const visibleControl = await this . findVisibleAdminSignOutControl ( ) ;
101+
102+ return visibleControl ?? ( await this . waitForAdminSignOutControl ( ) ) ;
103+ }
104+
105+ private async findVisibleAdminSignOutControl ( ) {
106+ for ( const controls of [
107+ this . page . getByRole ( 'button' , { name : 'Sign out' } ) ,
108+ this . page . getByRole ( 'link' , { name : 'Sign out' } ) ,
109+ ] ) {
110+ const count = await controls . count ( ) ;
111+
112+ for ( let i = 0 ; i < count ; i ++ ) {
113+ const control = controls . nth ( i ) ;
114+
115+ if ( await control . isVisible ( ) . catch ( ( ) => false ) ) {
116+ return control ;
117+ }
118+ }
119+ }
120+
121+ return undefined ;
122+ }
56123}
0 commit comments