test: add Playwright tests for metronome functionality
Add comprehensive test suite for metronome feature including: - Opening metronome from Open menu - Displaying metronome track with VU meter and gain controls - Closing metronome from main window - Helper function for opening metronome in tests - Documentation for test suite Tests verify core metronome UI behavior and user interactions. WindowPortal popup tests are skipped due to complexity (manual testing recommended). Test results: - 3 tests passing - 2 tests skipped (popup window controls) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ad12d4030a
commit
746cfd72fa
|
|
@ -0,0 +1,127 @@
|
|||
# Metronome Test Suite
|
||||
|
||||
This directory contains Playwright tests for the metronome functionality in jam-ui.
|
||||
|
||||
## Test Coverage
|
||||
|
||||
### metronome-controls.spec.ts
|
||||
|
||||
Tests the metronome feature including:
|
||||
|
||||
1. **Opening Metronome** (`should open metronome controls`)
|
||||
- Opens metronome via Open menu → Metronome
|
||||
- Verifies metronome track appears in session
|
||||
- Checks for metronome heading and close button
|
||||
|
||||
2. **Adjusting BPM in Popup** (`should allow adjusting BPM with slider in popup`)
|
||||
- Tests BPM slider in popup control window
|
||||
- Currently skipped due to WindowPortal complexity
|
||||
- Requires popup window handling with context.waitForEvent('page')
|
||||
|
||||
3. **Closing Metronome** (`should close metronome from main window`)
|
||||
- Clicks close button on metronome track
|
||||
- Verifies metronome track is removed
|
||||
- Ensures user stays in session
|
||||
|
||||
4. **Metronome Display** (`should display metronome track with VU meter and gain controls`)
|
||||
- Verifies metronome icon displays
|
||||
- Checks for VU meter component
|
||||
- Validates session track structure
|
||||
|
||||
5. **Popup Controls** (`should adjust metronome settings in popup window`)
|
||||
- Comprehensive test for all metronome settings
|
||||
- Currently skipped - manual testing recommended
|
||||
- Would test: BPM, sound, meter, cricket toggle, apply button
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Start the jam-ui development server:
|
||||
```bash
|
||||
cd jam-ui
|
||||
npm start
|
||||
```
|
||||
|
||||
2. Ensure the Rails backend is running:
|
||||
```bash
|
||||
cd web
|
||||
bundle exec rails s
|
||||
```
|
||||
|
||||
### Run All Metronome Tests
|
||||
|
||||
```bash
|
||||
cd jam-ui
|
||||
npx playwright test test/metronome --config=playwright.chrome.config.ts
|
||||
```
|
||||
|
||||
### Run Individual Test
|
||||
|
||||
```bash
|
||||
npx playwright test test/metronome/metronome-controls.spec.ts --config=playwright.chrome.config.ts
|
||||
```
|
||||
|
||||
### Run in UI Mode (Interactive)
|
||||
|
||||
```bash
|
||||
npx playwright test test/metronome --ui
|
||||
```
|
||||
|
||||
### Run in Headed Mode (Watch Browser)
|
||||
|
||||
```bash
|
||||
npx playwright test test/metronome --headed
|
||||
```
|
||||
|
||||
## Test Helpers
|
||||
|
||||
### openMetronome(page)
|
||||
|
||||
Helper function to open metronome in a session:
|
||||
|
||||
```typescript
|
||||
async function openMetronome(page: Page) {
|
||||
await page.locator('button:has-text("Open")').click();
|
||||
await page.waitForSelector('.dropdown-menu.show');
|
||||
await page.locator('button.dropdown-item:has-text("Metronome")').click();
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **WindowPortal Popup Testing**: The metronome controls open in a separate popup window (WindowPortal), which requires special handling in Playwright. Some tests are skipped due to this complexity.
|
||||
|
||||
2. **VU Meter Validation**: VU meters require actual audio data from the native client. Tests verify the VU component is present but don't test actual meter movement.
|
||||
|
||||
3. **Native Client Calls**: Tests cannot directly verify `jamClient.SessionSetMetronome()` calls. Backend verification would require additional instrumentation.
|
||||
|
||||
## Test Data Flow
|
||||
|
||||
```
|
||||
User Action → UI Component → jamClient → Native Audio Engine
|
||||
↓
|
||||
Backend API
|
||||
↓
|
||||
Session State
|
||||
```
|
||||
|
||||
Tests primarily verify UI behavior and component rendering. Backend and audio engine behavior should be tested separately.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
- [ ] Add tests for metronome settings persistence
|
||||
- [ ] Test metronome behavior during recording (should be blocked)
|
||||
- [ ] Test metronome with unstable clocks warning
|
||||
- [ ] Add visual regression tests for metronome UI
|
||||
- [ ] Test metronome audio output (if feasible)
|
||||
- [ ] Add unit tests for JKSessionMetronomePlayer component
|
||||
- [ ] Test metronome state synchronization across popup and main window
|
||||
|
||||
## Related Components
|
||||
|
||||
- `src/components/client/JKSessionMetronome.js` - Metronome track display
|
||||
- `src/components/client/JKSessionMetronomePlayer.js` - Metronome controls popup
|
||||
- `src/components/client/JKSessionOpenMenu.js` - Open menu with metronome option
|
||||
- `src/components/client/JKSessionScreen.js` - Session screen integration
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
import { test, expect, Page } from '@playwright/test';
|
||||
import { loginToJamUI, createAndJoinSession } from '../utils/test-helpers';
|
||||
import { APIInterceptor } from '../utils/api-interceptor';
|
||||
|
||||
/**
|
||||
* Metronome Controls Test Suite
|
||||
*
|
||||
* Tests metronome functionality including:
|
||||
* - Opening metronome controls
|
||||
* - Adjusting BPM, sound, meter settings
|
||||
* - Applying settings to backend
|
||||
* - Visual metronome (cricket) toggle
|
||||
* - Closing metronome
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper function to open metronome in session
|
||||
*/
|
||||
async function openMetronome(page: Page) {
|
||||
// Click the "Open" button
|
||||
await page.locator('button:has-text("Open")').click();
|
||||
|
||||
// Wait for dropdown menu
|
||||
await page.waitForSelector('.dropdown-menu.show');
|
||||
|
||||
// Click "Metronome..." in dropdown
|
||||
await page.locator('button.dropdown-item:has-text("Metronome")').click();
|
||||
|
||||
// Wait for metronome to open (either popup window or modal)
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
test.describe('Metronome Controls', () => {
|
||||
let apiInterceptor: APIInterceptor;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Set up API interceptor
|
||||
apiInterceptor = new APIInterceptor();
|
||||
await apiInterceptor.intercept(page);
|
||||
|
||||
// Login and join a session
|
||||
await loginToJamUI(page);
|
||||
await createAndJoinSession(page);
|
||||
});
|
||||
|
||||
test('should open metronome controls', async ({ page }) => {
|
||||
// Open metronome
|
||||
await openMetronome(page);
|
||||
|
||||
// Wait longer for metronome to initialize and mixers to be ready
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// Check that metronome track is displayed in session
|
||||
const metronomeTrack = page.locator('.metronomeTrack');
|
||||
await expect(metronomeTrack).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// Verify metronome heading is present
|
||||
await expect(metronomeTrack.locator('h5:has-text("Metronome")')).toBeVisible();
|
||||
|
||||
// Verify close button is present
|
||||
await expect(metronomeTrack.locator('a:has-text("Close")')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should allow adjusting BPM with slider in popup', async ({ page, context }) => {
|
||||
// Open metronome
|
||||
await openMetronome(page);
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Wait for popup window to open
|
||||
const popupPromise = context.waitForEvent('page');
|
||||
// The popup should open automatically
|
||||
const popup = await popupPromise.catch(() => null);
|
||||
|
||||
if (!popup) {
|
||||
console.log('Metronome popup did not open, test may need adjustment');
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for popup to load
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Find BPM slider in popup
|
||||
const bpmSlider = popup.locator('input[type="range"].metronome-slider');
|
||||
await bpmSlider.waitFor({ state: 'visible', timeout: 5000 });
|
||||
|
||||
// Change BPM to 140
|
||||
await bpmSlider.fill('140');
|
||||
|
||||
// Verify BPM input shows 140
|
||||
const bpmInput = popup.locator('input[type="number"].metronome-input');
|
||||
await expect(bpmInput).toHaveValue('140');
|
||||
});
|
||||
|
||||
test('should close metronome from main window', async ({ page }) => {
|
||||
// Open metronome
|
||||
await openMetronome(page);
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// Verify metronome track is visible
|
||||
const metronomeTrack = page.locator('.metronomeTrack');
|
||||
await expect(metronomeTrack).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// Click close button
|
||||
await metronomeTrack.locator('a:has-text("Close")').click();
|
||||
|
||||
// Wait for close operation
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Metronome track should no longer be visible
|
||||
await expect(metronomeTrack).not.toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Verify we're still in session (not navigated away)
|
||||
// Session URLs look like: /client/s/{session-id}
|
||||
expect(page.url()).toContain('/client/s/');
|
||||
});
|
||||
|
||||
test('should display metronome track with VU meter and gain controls', async ({ page }) => {
|
||||
// Open metronome
|
||||
await openMetronome(page);
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// Verify metronome track is visible
|
||||
const metronomeTrack = page.locator('.metronomeTrack');
|
||||
await expect(metronomeTrack).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// Check for metronome icon within the metronome track
|
||||
const metronomeIcon = metronomeTrack.locator('img[alt="metronome"]');
|
||||
await expect(metronomeIcon).toBeVisible();
|
||||
|
||||
// Note: VU meters require audio data from native client
|
||||
// We can verify the VU component is present but not test actual meter movement
|
||||
// Use .metronomeTrack to scope the selector and avoid strict mode violations
|
||||
const vuMeter = metronomeTrack.locator('.vu-and-gain');
|
||||
await expect(vuMeter).toBeVisible();
|
||||
|
||||
// Verify the track has a session-track class
|
||||
const sessionTrack = metronomeTrack.locator('.session-track');
|
||||
await expect(sessionTrack).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip('should adjust metronome settings in popup window', async ({ page, context }) => {
|
||||
// Note: This test is skipped because WindowPortal popup testing is complex
|
||||
// and requires special configuration. Manual testing recommended.
|
||||
|
||||
// Open metronome
|
||||
await openMetronome(page);
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Wait for popup window
|
||||
const popup = await context.waitForEvent('page');
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Set BPM
|
||||
const bpmInput = popup.locator('input[type="number"].metronome-input');
|
||||
await bpmInput.fill('150');
|
||||
|
||||
// Change sound
|
||||
const soundSelect = popup.locator('select.metronome-select').first();
|
||||
await soundSelect.selectOption({ value: '3' }); // Click
|
||||
|
||||
// Change meter
|
||||
const meterSelect = popup.locator('select.metronome-select').nth(1);
|
||||
await meterSelect.selectOption({ value: '4' });
|
||||
|
||||
// Toggle cricket
|
||||
const cricketCheckbox = popup.locator('input[type="checkbox"]');
|
||||
await cricketCheckbox.check();
|
||||
|
||||
// Apply settings
|
||||
const applyButton = popup.locator('button:has-text("Apply")');
|
||||
await applyButton.click();
|
||||
|
||||
// Wait for apply to complete
|
||||
await popup.waitForTimeout(1000);
|
||||
|
||||
// Verify settings were applied (would need native client verification)
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue