@@ -63,24 +63,41 @@ export function getInlineActionEntryForFile(file: string, actionId: string) {
6363}
6464
6565/**
66- * Open the actions menu of a file row and wait until it is really open.
66+ * Whether the menu controlled by the given toggle is actually shown.
67+ * The toggle state (aria-expanded) is not enough: async row re-renders
68+ * (preview and share-status loading) can interrupt the popover while it
69+ * is positioning, leaving a "zombie" menu that the NcActions component
70+ * considers open but that is never displayed.
6771 *
68- * Async row re-renders (preview and share-status loading) re-mount the
69- * NcActions component, which silently closes a menu that was just opened.
70- * So click only while the toggle reports "closed" and poll aria-expanded,
71- * retrying until the open state sticks.
72+ * @param $toggle the actions menu toggle of the row
73+ */
74+ function isActionsMenuVisible ( $toggle : JQuery < HTMLElement > ) : boolean {
75+ const menuId = $toggle . attr ( 'aria-controls' )
76+ return Boolean ( menuId ) && Cypress . $ ( `#${ menuId } ` ) . is ( ':visible' )
77+ }
78+
79+ /**
80+ * Open the actions menu of a file row and wait until it is really shown.
81+ * Clicks the toggle, then polls the menu visibility: a hidden menu gets
82+ * closed and re-opened (recovering the zombie state described above),
83+ * retrying until the menu is displayed.
7284 *
7385 * @param getActionButton query for the actions menu toggle of the row
7486 * @param attempts remaining retries before giving up
7587 */
7688function openActionsMenu ( getActionButton : ( ) => Cypress . Chainable < JQuery < HTMLElement > > , attempts = 5 ) {
7789 getActionButton ( ) . then ( ( $toggle ) => {
78- if ( $toggle . attr ( 'aria-expanded' ) !== 'true' ) {
79- cy . wrap ( $toggle ) . click ( { force : true } ) // force to avoid issues with overlaying file list header
90+ if ( isActionsMenuVisible ( $toggle ) ) {
91+ return
92+ }
93+ if ( $toggle . attr ( 'aria-expanded' ) === 'true' ) {
94+ // Zombie state: close the menu first so the next click re-opens it cleanly
95+ cy . wrap ( $toggle ) . click ( { force : true } )
8096 }
97+ cy . wrap ( $toggle ) . click ( { force : true } ) // force to avoid issues with overlaying file list header
8198 } )
8299 getActionButton ( ) . then ( ( $toggle ) => {
83- if ( $toggle . attr ( 'aria-expanded' ) === 'true' ) {
100+ if ( isActionsMenuVisible ( $toggle ) ) {
84101 return
85102 }
86103 if ( attempts <= 0 ) {
0 commit comments