Skip to content
10 changes: 8 additions & 2 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)}
>
<img
className={styles.moreIcon}
Expand Down
69 changes: 69 additions & 0 deletions packages/scratch-gui/test/unit/components/action-menu.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import {fireEvent, act} from '@testing-library/react';
import {renderWithIntl} from '../../helpers/intl-helpers.jsx';
import ActionMenu from '../../../src/components/action-menu/action-menu';

// Mock ReactTooltip
const ReactTooltip = props => <div className="mock-tooltip">{props.children}</div>;
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(<ActionMenu {...defaultProps} />);
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();
});
});
Loading