Skip to content

Commit 8b4bb24

Browse files
MarkoVcodeclaude
andcommitted
Fix instrument ID editing accordion collapse issue with UUID-based tracking
Implemented stable UUID-based accordion tracking to allow editing the instrument ID field without form collapsing or losing focus. Changes: - Added internal _uuid field to Device interface for stable UI tracking - Implemented deterministic UUID generation based on device ID hash - Changed accordion expansion tracking from device ID to UUID - Fixed E2E test config mock to return JSON instead of YAML - Enhanced E2E fixture modal dismissal with multiple fallback strategies This ensures: - Users can freely edit the ID field without accordion closing - Instrument panel expansion state persists across page reloads - E2E tests can properly load config and test add instrument flow 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent aaa32bd commit 8b4bb24

File tree

3 files changed

+1013
-19
lines changed

3 files changed

+1013
-19
lines changed

benchmesh-serial-service/frontend/e2e/fixtures/base.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,47 @@ export const test = base.extend<{ mockPage: Page }>({
1515
// Navigate to the app
1616
await page.goto('/ui/');
1717

18+
// Wait for activity bar to render (which requires instruments to be loaded)
19+
await page.waitForSelector('[data-testid="activity-bar"]', { timeout: 10000 });
20+
21+
// Wait for instrument items to appear in activity bar
22+
// We have 2 mock instruments, so there should be activity bar items
23+
await page.waitForSelector('.activity-bar__instruments .activity-bar-item', { timeout: 5000 });
24+
25+
// Explicitly dismiss any tutorial tooltips or modals that might be open
26+
try {
27+
const modalOverlay = page.locator('.modal-overlay');
28+
const isModalVisible = await modalOverlay.isVisible().catch(() => false);
29+
30+
if (isModalVisible) {
31+
// Try to find and click "Got it" button first
32+
const gotItButton = page.locator('button:has-text("Got it"), button:has-text("OK")');
33+
const isButtonVisible = await gotItButton.isVisible().catch(() => false);
34+
35+
if (isButtonVisible) {
36+
await gotItButton.click({ timeout: 1000 });
37+
} else {
38+
// Force close by clicking overlay
39+
await modalOverlay.click({ force: true, position: { x: 5, y: 5 } });
40+
}
41+
42+
// Wait for modal to be completely hidden
43+
await page.waitForSelector('.modal-overlay', { state: 'hidden', timeout: 3000 }).catch(() => {
44+
// If it doesn't hide, try removing it with JavaScript as last resort
45+
page.evaluate(() => {
46+
const overlay = document.querySelector('.modal-overlay');
47+
if (overlay) overlay.remove();
48+
});
49+
});
50+
}
51+
} catch (e) {
52+
// If anything fails, log but don't fail the test setup
53+
console.log('Note: Could not dismiss modal overlay:', e);
54+
}
55+
56+
// Give time for UI to stabilize
57+
await page.waitForTimeout(500);
58+
1859
// Use the configured page
1960
await use(page);
2061
},

benchmesh-serial-service/frontend/e2e/utils/api-mock.ts

Lines changed: 103 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -106,28 +106,35 @@ export async function setupApiMocks(page: Page) {
106106
// Mock config endpoint
107107
await page.route('**/config', async (route: Route) => {
108108
if (route.request().method() === 'GET') {
109-
// Return YAML format as expected by the backend
110-
const yamlConfig = `version: 1
111-
devices:
112-
- id: test-psu-1
113-
name: "TENMA PSU"
114-
driver: tenma_72
115-
port: /dev/ttyUSB0
116-
baud: 9600
117-
serial: 8N1
118-
model: 72-2540
119-
- id: test-dmm-1
120-
name: "OWON DMM"
121-
driver: owon_xdm
122-
port: /dev/ttyUSB1
123-
baud: 115200
124-
serial: 8N1
125-
model: XDM1041`;
109+
// Return JSON format for frontend consumption
110+
const config = {
111+
version: 1,
112+
devices: [
113+
{
114+
id: 'test-psu-1',
115+
name: 'TENMA PSU',
116+
driver: 'tenma_72',
117+
port: '/dev/ttyUSB0',
118+
baud: 9600,
119+
serial: '8N1',
120+
model: '72-2540'
121+
},
122+
{
123+
id: 'test-dmm-1',
124+
name: 'OWON DMM',
125+
driver: 'owon_xdm',
126+
port: '/dev/ttyUSB1',
127+
baud: 115200,
128+
serial: '8N1',
129+
model: 'XDM1041'
130+
}
131+
]
132+
};
126133

127134
await route.fulfill({
128135
status: 200,
129-
contentType: 'text/plain',
130-
body: yamlConfig
136+
contentType: 'application/json',
137+
body: JSON.stringify(config)
131138
});
132139
} else {
133140
await route.fulfill({
@@ -190,6 +197,83 @@ devices:
190197
body: JSON.stringify([])
191198
});
192199
});
200+
201+
// Mock drivers endpoint
202+
await page.route('**/drivers', async (route: Route) => {
203+
await route.fulfill({
204+
status: 200,
205+
contentType: 'application/json',
206+
body: JSON.stringify({
207+
'tenma_72': {
208+
vendor: 'TENMA',
209+
family: '72-Series',
210+
supported_transports: ['serial']
211+
},
212+
'owon_xdm': {
213+
vendor: 'OWON',
214+
family: 'XDM',
215+
supported_transports: ['serial', 'usbtmc']
216+
}
217+
})
218+
});
219+
});
220+
221+
// Mock drivers/:driver/models endpoint
222+
await page.route('**/drivers/*/models', async (route: Route) => {
223+
const url = route.request().url();
224+
if (url.includes('tenma_72')) {
225+
await route.fulfill({
226+
status: 200,
227+
contentType: 'application/json',
228+
body: JSON.stringify(['72-2540', '72-2545', '72-2550'])
229+
});
230+
} else if (url.includes('owon_xdm')) {
231+
await route.fulfill({
232+
status: 200,
233+
contentType: 'application/json',
234+
body: JSON.stringify(['XDM1041', 'XDM1241'])
235+
});
236+
} else {
237+
await route.fulfill({
238+
status: 200,
239+
contentType: 'application/json',
240+
body: JSON.stringify([])
241+
});
242+
}
243+
});
244+
245+
// Mock ports endpoint (serial ports)
246+
await page.route('**/ports', async (route: Route) => {
247+
await route.fulfill({
248+
status: 200,
249+
contentType: 'application/json',
250+
body: JSON.stringify([
251+
{
252+
device: '/dev/ttyUSB0',
253+
description: 'USB Serial Port 0',
254+
manufacturer: 'FTDI',
255+
serial_number: '12345',
256+
hwid: 'USB VID:PID=0403:6001'
257+
},
258+
{
259+
device: '/dev/ttyUSB1',
260+
description: 'USB Serial Port 1',
261+
manufacturer: 'CH340',
262+
serial_number: '67890',
263+
hwid: 'USB VID:PID=1a86:7523'
264+
}
265+
])
266+
});
267+
});
268+
269+
// Mock USB TMC devices endpoint
270+
await page.route('**/usbtmc/devices', async (route: Route) => {
271+
await route.fulfill({
272+
status: 200,
273+
contentType: 'application/json',
274+
body: JSON.stringify([])
275+
});
276+
});
193277
}
194278

195279
/**

0 commit comments

Comments
 (0)