Skip to content

Commit ca8dd8a

Browse files
committed
Add session-aware support to remaining UI tools
1 parent d0b5f79 commit ca8dd8a

17 files changed

Lines changed: 424 additions & 335 deletions

docs/session-aware-migration-todo.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ Reference: `docs/session_management_plan.md`
5151
- [x] `src/mcp/tools/logging/start_sim_log_cap.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
5252

5353
## AXe UI Testing Tools
54-
- [ ] `src/mcp/tools/ui-testing/button.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
55-
- [ ] `src/mcp/tools/ui-testing/describe_ui.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
56-
- [ ] `src/mcp/tools/ui-testing/gesture.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
57-
- [ ] `src/mcp/tools/ui-testing/key_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
58-
- [ ] `src/mcp/tools/ui-testing/key_sequence.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
59-
- [ ] `src/mcp/tools/ui-testing/long_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
60-
- [ ] `src/mcp/tools/ui-testing/screenshot.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
61-
- [ ] `src/mcp/tools/ui-testing/swipe.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
62-
- [ ] `src/mcp/tools/ui-testing/tap.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
63-
- [ ] `src/mcp/tools/ui-testing/touch.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
64-
- [ ] `src/mcp/tools/ui-testing/type_text.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
54+
- [x] `src/mcp/tools/ui-testing/button.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
55+
- [x] `src/mcp/tools/ui-testing/describe_ui.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
56+
- [x] `src/mcp/tools/ui-testing/gesture.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
57+
- [x] `src/mcp/tools/ui-testing/key_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
58+
- [x] `src/mcp/tools/ui-testing/key_sequence.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
59+
- [x] `src/mcp/tools/ui-testing/long_press.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
60+
- [x] `src/mcp/tools/ui-testing/screenshot.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
61+
- [x] `src/mcp/tools/ui-testing/swipe.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
62+
- [x] `src/mcp/tools/ui-testing/tap.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
63+
- [x] `src/mcp/tools/ui-testing/touch.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).
64+
- [x] `src/mcp/tools/ui-testing/type_text.ts` — session defaults: `simulatorId` (hydrate `simulatorUuid`).

src/mcp/tools/simulator/__tests__/screenshot.test.ts

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ import {
1212
createCommandMatchingMockExecutor,
1313
} from '../../../../test-utils/mock-executors.ts';
1414
import { SystemError } from '../../../../utils/responses/index.ts';
15+
import { sessionStore } from '../../../../utils/session-store.ts';
1516
import screenshotPlugin, { screenshotLogic } from '../../ui-testing/screenshot.ts';
1617

1718
describe('screenshot plugin', () => {
18-
// No mocks to clear since we use pure dependency injection
19+
beforeEach(() => {
20+
sessionStore.clear();
21+
});
1922

2023
describe('Export Field Validation (Literal)', () => {
2124
it('should have correct name field', () => {
@@ -35,19 +38,13 @@ describe('screenshot plugin', () => {
3538
it('should have correct schema validation', () => {
3639
const schema = z.object(screenshotPlugin.schema);
3740

38-
expect(
39-
schema.safeParse({
40-
simulatorId: '550e8400-e29b-41d4-a716-446655440000',
41-
}).success,
42-
).toBe(true);
43-
44-
expect(
45-
schema.safeParse({
46-
simulatorId: 123,
47-
}).success,
48-
).toBe(false);
41+
expect(schema.safeParse({}).success).toBe(true);
4942

50-
expect(schema.safeParse({}).success).toBe(false);
43+
const withSimId = schema.safeParse({
44+
simulatorId: '550e8400-e29b-41d4-a716-446655440000',
45+
});
46+
expect(withSimId.success).toBe(true);
47+
expect('simulatorId' in (withSimId.data as Record<string, unknown>)).toBe(false);
5148
});
5249
});
5350

@@ -282,18 +279,13 @@ describe('screenshot plugin', () => {
282279
});
283280

284281
it('should handle missing simulatorId via handler', async () => {
285-
// Test Zod validation by calling the handler with invalid params
286282
const result = await screenshotPlugin.handler({});
287283

288-
expect(result).toEqual({
289-
content: [
290-
{
291-
type: 'text',
292-
text: 'Error: Parameter validation failed\nDetails: Invalid parameters:\nsimulatorId: Required',
293-
},
294-
],
295-
isError: true,
296-
});
284+
expect(result.isError).toBe(true);
285+
const message = result.content[0].text;
286+
expect(message).toContain('Missing required session defaults');
287+
expect(message).toContain('simulatorId is required');
288+
expect(message).toContain('session-set-defaults');
297289
});
298290

299291
it('should handle command failure', async () => {

src/mcp/tools/ui-testing/__tests__/gesture.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22
* Tests for gesture tool plugin
33
*/
44

5-
import { describe, it, expect } from 'vitest';
5+
import { describe, it, expect, beforeEach } from 'vitest';
66
import { z } from 'zod';
77
import {
88
createMockExecutor,
99
createMockFileSystemExecutor,
1010
createNoopExecutor,
1111
} from '../../../../test-utils/mock-executors.ts';
12+
import { sessionStore } from '../../../../utils/session-store.ts';
1213
import gesturePlugin, { gestureLogic } from '../gesture.ts';
1314

1415
describe('Gesture Plugin', () => {
16+
beforeEach(() => {
17+
sessionStore.clear();
18+
});
19+
1520
describe('Export Field Validation (Literal)', () => {
1621
it('should have correct name', () => {
1722
expect(gesturePlugin.name).toBe('gesture');
@@ -55,6 +60,30 @@ describe('Gesture Plugin', () => {
5560
});
5661
});
5762

63+
describe('Handler Requirements', () => {
64+
it('should require simulatorId session default when not provided', async () => {
65+
const result = await gesturePlugin.handler({ preset: 'scroll-up' });
66+
67+
expect(result.isError).toBe(true);
68+
const message = result.content[0].text;
69+
expect(message).toContain('Missing required session defaults');
70+
expect(message).toContain('simulatorId is required');
71+
expect(message).toContain('session-set-defaults');
72+
});
73+
74+
it('should surface validation errors once simulator defaults exist', async () => {
75+
sessionStore.setDefaults({ simulatorId: '12345678-1234-1234-1234-123456789012' });
76+
77+
const result = await gesturePlugin.handler({});
78+
79+
expect(result.isError).toBe(true);
80+
const message = result.content[0].text;
81+
expect(message).toContain('Parameter validation failed');
82+
expect(message).toContain('preset: Required');
83+
expect(message).toContain('Tip: set session defaults via session-set-defaults');
84+
});
85+
});
86+
5887
describe('Command Generation', () => {
5988
it('should generate correct axe command for basic gesture', async () => {
6089
let capturedCommand: string[] = [];

src/mcp/tools/ui-testing/__tests__/key_press.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22
* Tests for key_press tool plugin
33
*/
44

5-
import { describe, it, expect } from 'vitest';
5+
import { describe, it, expect, beforeEach } from 'vitest';
66
import { z } from 'zod';
77
import {
88
createMockExecutor,
99
createMockFileSystemExecutor,
1010
createNoopExecutor,
1111
} from '../../../../test-utils/mock-executors.ts';
12+
import { sessionStore } from '../../../../utils/session-store.ts';
1213
import keyPressPlugin, { key_pressLogic } from '../key_press.ts';
1314

1415
describe('Key Press Plugin', () => {
16+
beforeEach(() => {
17+
sessionStore.clear();
18+
});
19+
1520
describe('Export Field Validation (Literal)', () => {
1621
it('should have correct name', () => {
1722
expect(keyPressPlugin.name).toBe('key_press');
@@ -47,6 +52,30 @@ describe('Key Press Plugin', () => {
4752
});
4853
});
4954

55+
describe('Handler Requirements', () => {
56+
it('should require simulatorId session default when not provided', async () => {
57+
const result = await keyPressPlugin.handler({ keyCode: 40 });
58+
59+
expect(result.isError).toBe(true);
60+
const message = result.content[0].text;
61+
expect(message).toContain('Missing required session defaults');
62+
expect(message).toContain('simulatorId is required');
63+
expect(message).toContain('session-set-defaults');
64+
});
65+
66+
it('should surface validation errors once simulator default exists', async () => {
67+
sessionStore.setDefaults({ simulatorId: '12345678-1234-1234-1234-123456789012' });
68+
69+
const result = await keyPressPlugin.handler({});
70+
71+
expect(result.isError).toBe(true);
72+
const message = result.content[0].text;
73+
expect(message).toContain('Parameter validation failed');
74+
expect(message).toContain('keyCode: Required');
75+
expect(message).toContain('Tip: set session defaults via session-set-defaults');
76+
});
77+
});
78+
5079
describe('Command Generation', () => {
5180
it('should generate correct axe command for basic key press', async () => {
5281
let capturedCommand: string[] = [];

src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
* Tests for key_sequence plugin
33
*/
44

5-
import { describe, it, expect } from 'vitest';
5+
import { describe, it, expect, beforeEach } from 'vitest';
66
import { z } from 'zod';
77
import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts';
8+
import { sessionStore } from '../../../../utils/session-store.ts';
89
import keySequencePlugin, { key_sequenceLogic } from '../key_sequence.ts';
910

1011
describe('Key Sequence Plugin', () => {
12+
beforeEach(() => {
13+
sessionStore.clear();
14+
});
15+
1116
describe('Export Field Validation (Literal)', () => {
1217
it('should have correct name', () => {
1318
expect(keySequencePlugin.name).toBe('key_sequence');
@@ -44,6 +49,30 @@ describe('Key Sequence Plugin', () => {
4449
});
4550
});
4651

52+
describe('Handler Requirements', () => {
53+
it('should require simulatorId session default when not provided', async () => {
54+
const result = await keySequencePlugin.handler({ keyCodes: [40] });
55+
56+
expect(result.isError).toBe(true);
57+
const message = result.content[0].text;
58+
expect(message).toContain('Missing required session defaults');
59+
expect(message).toContain('simulatorId is required');
60+
expect(message).toContain('session-set-defaults');
61+
});
62+
63+
it('should surface validation errors once simulator defaults exist', async () => {
64+
sessionStore.setDefaults({ simulatorId: '12345678-1234-1234-1234-123456789012' });
65+
66+
const result = await keySequencePlugin.handler({ keyCodes: [] });
67+
68+
expect(result.isError).toBe(true);
69+
const message = result.content[0].text;
70+
expect(message).toContain('Parameter validation failed');
71+
expect(message).toContain('keyCodes: At least one key code required');
72+
expect(message).toContain('Tip: set session defaults via session-set-defaults');
73+
});
74+
});
75+
4776
describe('Command Generation', () => {
4877
it('should generate correct axe command for basic key sequence', async () => {
4978
let capturedCommand: string[] = [];

src/mcp/tools/ui-testing/__tests__/long_press.test.ts

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
* Tests for long_press tool plugin
33
*/
44

5-
import { describe, it, expect } from 'vitest';
5+
import { describe, it, expect, beforeEach } from 'vitest';
66
import { z } from 'zod';
77
import { createMockExecutor } from '../../../../test-utils/mock-executors.ts';
8+
import { sessionStore } from '../../../../utils/session-store.ts';
89
import longPressPlugin, { long_pressLogic } from '../long_press.ts';
910

1011
describe('Long Press Plugin', () => {
11-
// Setup for each test - no vitest mocks to clear
12+
beforeEach(() => {
13+
sessionStore.clear();
14+
});
1215

1316
describe('Export Field Validation (Literal)', () => {
1417
it('should have correct name', () => {
@@ -28,65 +31,78 @@ describe('Long Press Plugin', () => {
2831
it('should validate schema fields with safeParse', () => {
2932
const schema = z.object(longPressPlugin.schema);
3033

31-
// Valid case
3234
expect(
3335
schema.safeParse({
34-
simulatorId: '12345678-1234-1234-1234-123456789012',
3536
x: 100,
3637
y: 200,
3738
duration: 1500,
3839
}).success,
3940
).toBe(true);
4041

41-
// Invalid simulatorId
42-
expect(
43-
schema.safeParse({
44-
simulatorId: 'invalid-uuid',
45-
x: 100,
46-
y: 200,
47-
duration: 1500,
48-
}).success,
49-
).toBe(false);
50-
51-
// Invalid x (not integer)
5242
expect(
5343
schema.safeParse({
54-
simulatorId: '12345678-1234-1234-1234-123456789012',
5544
x: 100.5,
5645
y: 200,
5746
duration: 1500,
5847
}).success,
5948
).toBe(false);
6049

61-
// Invalid y (not integer)
6250
expect(
6351
schema.safeParse({
64-
simulatorId: '12345678-1234-1234-1234-123456789012',
6552
x: 100,
6653
y: 200.5,
6754
duration: 1500,
6855
}).success,
6956
).toBe(false);
7057

71-
// Invalid duration (not positive)
7258
expect(
7359
schema.safeParse({
74-
simulatorId: '12345678-1234-1234-1234-123456789012',
7560
x: 100,
7661
y: 200,
7762
duration: 0,
7863
}).success,
7964
).toBe(false);
8065

81-
// Invalid duration (negative)
8266
expect(
8367
schema.safeParse({
84-
simulatorId: '12345678-1234-1234-1234-123456789012',
8568
x: 100,
8669
y: 200,
8770
duration: -100,
8871
}).success,
8972
).toBe(false);
73+
74+
const withSimId = schema.safeParse({
75+
simulatorId: '12345678-1234-1234-1234-123456789012',
76+
x: 100,
77+
y: 200,
78+
duration: 1500,
79+
});
80+
expect(withSimId.success).toBe(true);
81+
expect('simulatorId' in (withSimId.data as Record<string, unknown>)).toBe(false);
82+
});
83+
});
84+
85+
describe('Handler Requirements', () => {
86+
it('should require simulatorId session default', async () => {
87+
const result = await longPressPlugin.handler({ x: 100, y: 200, duration: 1500 });
88+
89+
expect(result.isError).toBe(true);
90+
const message = result.content[0].text;
91+
expect(message).toContain('Missing required session defaults');
92+
expect(message).toContain('simulatorId is required');
93+
expect(message).toContain('session-set-defaults');
94+
});
95+
96+
it('should surface validation errors once simulator default exists', async () => {
97+
sessionStore.setDefaults({ simulatorId: '12345678-1234-1234-1234-123456789012' });
98+
99+
const result = await longPressPlugin.handler({ x: 100, y: 200, duration: 0 });
100+
101+
expect(result.isError).toBe(true);
102+
const message = result.content[0].text;
103+
expect(message).toContain('Parameter validation failed');
104+
expect(message).toContain('duration: Duration of the long press in milliseconds');
105+
expect(message).toContain('Tip: set session defaults via session-set-defaults');
90106
});
91107
});
92108

0 commit comments

Comments
 (0)