diff --git a/jam-ui/src/components/client/JKSessionMetronome.js b/jam-ui/src/components/client/JKSessionMetronome.js index ce75c9810..a2ae5b776 100644 --- a/jam-ui/src/components/client/JKSessionMetronome.js +++ b/jam-ui/src/components/client/JKSessionMetronome.js @@ -3,8 +3,6 @@ import { useMixersContext } from '../../context/MixersContext'; import { useJamClient } from '../../context/JamClientContext'; import SessionTrackVU from './SessionTrackVU'; import SessionTrackGain from './SessionTrackGain'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faTimes } from '@fortawesome/free-solid-svg-icons'; import './JKSessionMyTrack.css'; const JKSessionMetronome = ({ @@ -13,6 +11,7 @@ const JKSessionMetronome = ({ }) => { const mixerHelper = useMixersContext(); const jamClient = useJamClient(); + const [showMenu, setShowMenu] = React.useState(false); console.log('JKSessionMetronome mixers:', mixers); @@ -27,8 +26,12 @@ const JKSessionMetronome = ({ return (
-
+ {/* Metronome Title */} +
+ Metronome +
+ {/* Metronome Icon */}
metronome { // Fallback if metronome icon doesn't exist e.target.src = "/assets/content/icon_recording.png"; @@ -51,8 +60,14 @@ const JKSessionMetronome = ({ />
- {/* Controls */} + {/* 0db Label */} +
+ 0db +
+ + {/* Track Controls */}
+ {/* VU Meter and Gain Control - Horizontal */}
- {/* Close Button */} + {/* Track Buttons - Three Dots Menu */}
- +
setShowMenu(!showMenu)}> + ... +
+ {showMenu && ( +
+
{ + handleClose(); + setShowMenu(false); + }} + > + Close +
+
+ )}
+
- - {/* Track Info */} -
-
- Metronome -
-
+
); diff --git a/jam-ui/src/context/JamClientContext.js b/jam-ui/src/context/JamClientContext.js index f9cf5bca4..0b2d11b6e 100644 --- a/jam-ui/src/context/JamClientContext.js +++ b/jam-ui/src/context/JamClientContext.js @@ -22,25 +22,25 @@ export const JamClientProvider = ({ children }) => { const proxyRef = useRef(null); - // if (process.env.NODE_ENV === 'development') { - // const fakeJamClientMessages = new FakeJamClientMessages(); - // const proxy = new FakeJamClientProxy(app, fakeJamClientMessages); // Pass appropriate parameters - // proxyRef.current = proxy.init(); - // // For testing purposes, we can add some fake recordings - // const fakeJamClientRecordings = new FakeJamClientRecordings(app, proxyRef.current, fakeJamClientMessages); - // proxyRef.current.SetFakeRecordingImpl(fakeJamClientRecordings); - // } else { - // if (!proxyRef.current) { - // const proxy = new JamClientProxy(app, console, globalObject); - // proxyRef.current = proxy.init(); - // } - // } + if (process.env.NODE_ENV === 'test' || process.env.REACT_APP_USE_FAKE_CLIENT === 'true') { + const fakeJamClientMessages = new FakeJamClientMessages(); + const proxy = new FakeJamClientProxy(app, fakeJamClientMessages); // Pass appropriate parameters + proxyRef.current = proxy.init(); + // For testing purposes, we can add some fake recordings + const fakeJamClientRecordings = new FakeJamClientRecordings(app, proxyRef.current, fakeJamClientMessages); + proxyRef.current.SetFakeRecordingImpl(fakeJamClientRecordings); + } else { + if (!proxyRef.current) { + const proxy = new JamClientProxy(app, console, globalObject); + proxyRef.current = proxy.init(); + } + } if (!proxyRef.current) { const proxy = new JamClientProxy(app, console, globalObject); proxyRef.current = proxy.init(); - window.jamClient = proxyRef.current; // TODO: Expose jamClient globally for debugging. This is temporarily added. Remove it later. } + window.jamClient = proxyRef.current; // TODO: Expose jamClient globally for debugging. This is temporarily added. Remove it later. // Register metronome callback with jamClient when it's available useEffect(() => { diff --git a/jam-ui/test/config/global-setup.ts b/jam-ui/test/config/global-setup.ts index 1326d32da..e3e3eb799 100644 --- a/jam-ui/test/config/global-setup.ts +++ b/jam-ui/test/config/global-setup.ts @@ -23,19 +23,19 @@ async function globalSetup(config: FullConfig) { const browser = await chromium.launch(); const page1 = await browser.newPage(); - //signup user1 - await signup(page1, user1.email, user1.password, user1.first_name, user1.last_name) + // Skip signup - use existing user1 account + // await signup(page1, user1.email, user1.password, user1.first_name, user1.last_name) // ... log in user1 - await login(page1, user1.email, user1.password) + await login(page1, user1.email, user1.password) await page1.context().storageState({ path: 'test/storageState/user1.json' }); const page2 = await browser.newPage(); - //signup user2 - await signup(page2, user2.email, user2.password, user2.first_name, user2.last_name) + // Skip signup - use existing user2 account + // await signup(page2, user2.email, user2.password, user2.first_name, user2.last_name) // ... log in - await login(page2, user2.email, user2.password) - await page2.context().storageState({ path: 'test/storageState/user2.json' }); - + await login(page2, user2.email, user2.password) + await page2.context().storageState({ path: 'test/storageState/user2.json' }); + await browser.close(); } diff --git a/jam-ui/test/metronome/metronome-controls.spec.ts b/jam-ui/test/metronome/metronome-controls.spec.ts index 32feb10f9..76f10ce0c 100644 --- a/jam-ui/test/metronome/metronome-controls.spec.ts +++ b/jam-ui/test/metronome/metronome-controls.spec.ts @@ -13,27 +13,84 @@ import { APIInterceptor } from '../utils/api-interceptor'; * - Closing metronome */ +/** + * Helper function to mock jamClient methods + */ +async function mockJamClient(page: Page) { + await page.evaluate(() => { + // Create mock that logs and returns resolved promises + const mockMethods = { + SessionOpenMetronome: async (...args: any[]) => { + console.log('[MOCK] SessionOpenMetronome called', args); + return true; + }, + SessionSetMetronome: async (...args: any[]) => { + console.log('[MOCK] SessionSetMetronome called', args); + return true; + }, + SessionStopPlay: async (...args: any[]) => { + console.log('[MOCK] SessionStopPlay called', args); + return true; + }, + SessionCloseMetronome: async (...args: any[]) => { + console.log('[MOCK] SessionCloseMetronome called', args); + return true; + }, + SetVURefreshRate: async (...args: any[]) => { + console.log('[MOCK] SetVURefreshRate called', args); + return true; + }, + SessionRegisterCallback: async (...args: any[]) => { + console.log('[MOCK] SessionRegisterCallback called', args); + return true; + } + }; + + // Replace jamClient methods if it exists + if ((window as any).jamClient) { + Object.assign((window as any).jamClient, mockMethods); + console.log('[MOCK] jamClient methods replaced'); + } else { + // Create mock jamClient if it doesn't exist + (window as any).jamClient = mockMethods; + console.log('[MOCK] jamClient created'); + } + }); +} + /** * Helper function to open metronome in session */ async function openMetronome(page: Page) { + console.log('[TEST] Mocking jamClient...'); + // Mock jamClient before opening metronome + await mockJamClient(page); + + console.log('[TEST] Clicking Open button...'); // Click the "Open" button await page.locator('button:has-text("Open")').click(); + console.log('[TEST] Waiting for dropdown menu...'); // Wait for dropdown menu await page.waitForSelector('.dropdown-menu.show'); + console.log('[TEST] Clicking Metronome in dropdown...'); // Click "Metronome..." in dropdown await page.locator('button.dropdown-item:has-text("Metronome")').click(); + console.log('[TEST] Waiting for metronome to open...'); // Wait for metronome to open (either popup window or modal) await page.waitForTimeout(1000); + console.log('[TEST] Metronome open complete'); } test.describe('Metronome Controls', () => { let apiInterceptor: APIInterceptor; test.beforeEach(async ({ page }) => { + // Increase timeout for these tests + test.setTimeout(60000); + // Set up API interceptor apiInterceptor = new APIInterceptor(); await apiInterceptor.intercept(page); @@ -41,6 +98,9 @@ test.describe('Metronome Controls', () => { // Login and join a session await loginToJamUI(page); await createAndJoinSession(page); + + // Mock jamClient after session is created + await mockJamClient(page); }); test('should open metronome controls', async ({ page }) => { @@ -54,11 +114,11 @@ test.describe('Metronome Controls', () => { 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 metronome title is present + await expect(metronomeTrack.locator('.track-title:has-text("Metronome")')).toBeVisible(); - // Verify close button is present - await expect(metronomeTrack.locator('a:has-text("Close")')).toBeVisible(); + // Verify three dots menu button is present + await expect(metronomeTrack.locator('.track-menu-button')).toBeVisible(); }); test('should allow adjusting BPM with slider in popup', async ({ page, context }) => { @@ -101,8 +161,14 @@ test.describe('Metronome Controls', () => { const metronomeTrack = page.locator('.metronomeTrack'); await expect(metronomeTrack).toBeVisible({ timeout: 15000 }); - // Click close button - await metronomeTrack.locator('a:has-text("Close")').click(); + // Click three dots menu button to open menu + await metronomeTrack.locator('.track-menu-button').click(); + + // Wait for menu to appear + await page.waitForTimeout(500); + + // Click close in the menu + await metronomeTrack.locator('.track-menu div:has-text("Close")').click(); // Wait for close operation await page.waitForTimeout(2000);