diff --git a/GEMINI.md b/GEMINI.md index 2f23e5e2958..01f433675aa 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -83,11 +83,17 @@ docker compose run --rm app bash -c "cd packages/scratch-vm && npm run tap:unit" ### `scratch-gui` ```bash -# Run GUI unit tests +# Run GUI unit tests (runs ALL unit tests in the package) docker compose run --rm app bash -c "cd packages/scratch-gui && npm run test:unit" -# Run GUI integration tests (requires build:dev first) +# Run a specific unit test +docker compose run --rm app bash -c "cd packages/scratch-gui && npx jest test/unit/components/action-menu.test.jsx" + +# Run GUI integration tests (requires build:dev first; runs ALL integration tests) docker compose run --rm app bash -c "cd packages/scratch-gui && npm run build:dev && npm run test:integration" + +# Run a specific integration test (requires build:dev first) +docker compose run --rm app bash -c "cd packages/scratch-gui && npx jest test/integration/your-test.test.js" ``` ## Package-Specific Details diff --git a/packages/scratch-gui/src/components/action-menu/action-menu.jsx b/packages/scratch-gui/src/components/action-menu/action-menu.jsx index a09fd906819..0c7905a69a0 100644 --- a/packages/scratch-gui/src/components/action-menu/action-menu.jsx +++ b/packages/scratch-gui/src/components/action-menu/action-menu.jsx @@ -81,9 +81,10 @@ class ActionMenu extends React.Component { // Blur the button so it does not keep focus after being clicked // This prevents keyboard events from triggering the button this.buttonRef.blur(); - this.setState({forceHide: true, isOpen: false}, () => { - setTimeout(() => this.setState({forceHide: false})); - }); + this.setState({forceHide: true, isOpen: false}); + // Don't reset forceHide automatically - wait for user to move mouse away and back + // This prevents tooltip from reappearing while modal is open + // forceHide will be reset in handleToggleOpenState when user hovers again }; } handleTouchStart (e) { @@ -156,7 +157,7 @@ class ActionMenu extends React.Component { })} data-for={tooltipId} data-tip={title} - onClick={hasFileInput ? handleClick : this.clickDelayer(handleClick)} + onClick={this.clickDelayer(handleClick)} >
{props.children}
; +ReactTooltip.hide = jest.fn(); +ReactTooltip.show = jest.fn(); +jest.mock('react-tooltip', () => ({ + __esModule: true, + default: ReactTooltip, + hide: ReactTooltip.hide, + show: ReactTooltip.show +})); + +// Mock styles +jest.mock('../../../src/components/action-menu/action-menu.css', () => ({ + menuContainer: 'menu-container', + forceHidden: 'force-hidden', + button: 'button', + mainButton: 'main-button' +})); + +describe('ActionMenu Component', () => { + let defaultProps; + + beforeEach(() => { + defaultProps = { + img: 'main-img.png', + onClick: jest.fn(), + title: 'Main Title', + moreButtons: [ + { + img: 'more-img.png', + title: 'More Title', + onClick: jest.fn() + } + ] + }; + jest.clearAllMocks(); + }); + + test('clickDelayer sets forceHide and keeps it until re-hover', () => { + jest.useFakeTimers(); + const {container} = renderWithIntl(); + const mainButton = container.querySelector('.main-button'); + const menuContainer = container.firstChild; + + // 1. Initial state + expect(menuContainer.classList.contains('force-hidden')).toBe(false); + + // 2. Click button + fireEvent.click(mainButton); + expect(menuContainer.classList.contains('force-hidden')).toBe(true); + + // 3. Should stay force-hidden even after long delay + act(() => { + jest.advanceTimersByTime(1000); + }); + expect(menuContainer.classList.contains('force-hidden')).toBe(true); + + // 4. Re-hover should reset force-hidden + fireEvent.mouseEnter(menuContainer); + expect(menuContainer.classList.contains('force-hidden')).toBe(false); + + jest.useRealTimers(); + }); +});