Skip to content

Commit 4eca3c8

Browse files
committed
Refactor: Extract duplicated runLogic helper to shared test-utils module
- Created src/test-utils/test-helpers.ts with shared test utilities - Moved identical runLogic function from 11 UI automation test files to shared module - Also exported createMockToolHandlerContext and allText helpers - Eliminates ~30 lines of duplicated code across 11 test files - Reduces maintenance burden and ensures consistent test behavior
1 parent 46fdf65 commit 4eca3c8

File tree

12 files changed

+116
-374
lines changed

12 files changed

+116
-374
lines changed

src/mcp/tools/ui-automation/__tests__/button.test.ts

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,7 @@ import {
88
import { schema, handler, buttonLogic } from '../button.ts';
99
import type { CommandExecutor } from '../../../../utils/execution/index.ts';
1010
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
11-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
12-
13-
const runLogic = async (logic: () => Promise<unknown>) => {
14-
const { result, run } = createMockToolHandlerContext();
15-
const response = await run(logic);
16-
17-
if (
18-
response &&
19-
typeof response === 'object' &&
20-
'content' in (response as Record<string, unknown>)
21-
) {
22-
return response as {
23-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
24-
isError?: boolean;
25-
nextStepParams?: unknown;
26-
};
27-
}
28-
29-
const text = result.text();
30-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
31-
const imageContent = result.attachments.map((attachment) => ({
32-
type: 'image' as const,
33-
data: attachment.data,
34-
mimeType: attachment.mimeType,
35-
}));
36-
37-
return {
38-
content: [...textContent, ...imageContent],
39-
isError: result.isError() ? true : undefined,
40-
nextStepParams: result.nextStepParams,
41-
attachments: result.attachments,
42-
text,
43-
};
44-
};
11+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
4512

4613
describe('Button Plugin', () => {
4714
describe('Export Field Validation (Literal)', () => {

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

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,7 @@ import {
88
import { sessionStore } from '../../../../utils/session-store.ts';
99
import { schema, handler, gestureLogic } from '../gesture.ts';
1010
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
11-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
12-
13-
const runLogic = async (logic: () => Promise<unknown>) => {
14-
const { result, run } = createMockToolHandlerContext();
15-
const response = await run(logic);
16-
17-
if (
18-
response &&
19-
typeof response === 'object' &&
20-
'content' in (response as Record<string, unknown>)
21-
) {
22-
return response as {
23-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
24-
isError?: boolean;
25-
nextStepParams?: unknown;
26-
};
27-
}
28-
29-
const text = result.text();
30-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
31-
const imageContent = result.attachments.map((attachment) => ({
32-
type: 'image' as const,
33-
data: attachment.data,
34-
mimeType: attachment.mimeType,
35-
}));
36-
37-
return {
38-
content: [...textContent, ...imageContent],
39-
isError: result.isError() ? true : undefined,
40-
nextStepParams: result.nextStepParams,
41-
attachments: result.attachments,
42-
text,
43-
};
44-
};
11+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
4512

4613
describe('Gesture Plugin', () => {
4714
beforeEach(() => {

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

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,7 @@ import {
99
import { sessionStore } from '../../../../utils/session-store.ts';
1010
import { schema, handler, key_pressLogic } from '../key_press.ts';
1111
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
12-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
13-
14-
const runLogic = async (logic: () => Promise<unknown>) => {
15-
const { result, run } = createMockToolHandlerContext();
16-
const response = await run(logic);
17-
18-
if (
19-
response &&
20-
typeof response === 'object' &&
21-
'content' in (response as Record<string, unknown>)
22-
) {
23-
return response as {
24-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
25-
isError?: boolean;
26-
nextStepParams?: unknown;
27-
};
28-
}
29-
30-
const text = result.text();
31-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
32-
const imageContent = result.attachments.map((attachment) => ({
33-
type: 'image' as const,
34-
data: attachment.data,
35-
mimeType: attachment.mimeType,
36-
}));
37-
38-
return {
39-
content: [...textContent, ...imageContent],
40-
isError: result.isError() ? true : undefined,
41-
nextStepParams: result.nextStepParams,
42-
attachments: result.attachments,
43-
text,
44-
};
45-
};
12+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
4613

4714
function createDefaultMockAxeHelpers() {
4815
return {

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

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,7 @@ import {
88
import { sessionStore } from '../../../../utils/session-store.ts';
99
import { schema, handler, key_sequenceLogic } from '../key_sequence.ts';
1010
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
11-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
12-
13-
const runLogic = async (logic: () => Promise<unknown>) => {
14-
const { result, run } = createMockToolHandlerContext();
15-
const response = await run(logic);
16-
17-
if (
18-
response &&
19-
typeof response === 'object' &&
20-
'content' in (response as Record<string, unknown>)
21-
) {
22-
return response as {
23-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
24-
isError?: boolean;
25-
nextStepParams?: unknown;
26-
};
27-
}
28-
29-
const text = result.text();
30-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
31-
const imageContent = result.attachments.map((attachment) => ({
32-
type: 'image' as const,
33-
data: attachment.data,
34-
mimeType: attachment.mimeType,
35-
}));
36-
37-
return {
38-
content: [...textContent, ...imageContent],
39-
isError: result.isError() ? true : undefined,
40-
nextStepParams: result.nextStepParams,
41-
attachments: result.attachments,
42-
text,
43-
};
44-
};
11+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
4512

4613
describe('Key Sequence Tool', () => {
4714
beforeEach(() => {

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

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,7 @@ import { createMockExecutor, mockProcess } from '../../../../test-utils/mock-exe
44
import { sessionStore } from '../../../../utils/session-store.ts';
55
import { schema, handler, long_pressLogic } from '../long_press.ts';
66
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
7-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
8-
9-
const runLogic = async (logic: () => Promise<unknown>) => {
10-
const { result, run } = createMockToolHandlerContext();
11-
const response = await run(logic);
12-
13-
if (
14-
response &&
15-
typeof response === 'object' &&
16-
'content' in (response as Record<string, unknown>)
17-
) {
18-
return response as {
19-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
20-
isError?: boolean;
21-
nextStepParams?: unknown;
22-
};
23-
}
24-
25-
const text = result.text();
26-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
27-
const imageContent = result.attachments.map((attachment) => ({
28-
type: 'image' as const,
29-
data: attachment.data,
30-
mimeType: attachment.mimeType,
31-
}));
32-
33-
return {
34-
content: [...textContent, ...imageContent],
35-
isError: result.isError() ? true : undefined,
36-
nextStepParams: result.nextStepParams,
37-
attachments: result.attachments,
38-
text,
39-
};
40-
};
7+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
418

429
describe('Long Press Plugin', () => {
4310
beforeEach(() => {

src/mcp/tools/ui-automation/__tests__/screenshot.test.ts

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,7 @@ import {
1414
detectLandscapeMode,
1515
rotateImage,
1616
} from '../screenshot.ts';
17-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
18-
19-
const runLogic = async (logic: () => Promise<unknown>) => {
20-
const { result, run } = createMockToolHandlerContext();
21-
const response = await run(logic);
22-
23-
if (
24-
response &&
25-
typeof response === 'object' &&
26-
'content' in (response as Record<string, unknown>)
27-
) {
28-
return response as {
29-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
30-
isError?: boolean;
31-
nextStepParams?: unknown;
32-
};
33-
}
34-
35-
const text = result.text();
36-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
37-
const imageContent = result.attachments.map((attachment) => ({
38-
type: 'image' as const,
39-
data: attachment.data,
40-
mimeType: attachment.mimeType,
41-
}));
42-
43-
return {
44-
content: [...textContent, ...imageContent],
45-
isError: result.isError() ? true : undefined,
46-
nextStepParams: result.nextStepParams,
47-
attachments: result.attachments,
48-
text,
49-
};
50-
};
17+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
5118

5219
describe('Screenshot Plugin', () => {
5320
beforeEach(() => {

src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,7 @@ import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/m
44
import type { CommandExecutor } from '../../../../utils/execution/index.ts';
55
import { schema, handler, snapshot_uiLogic } from '../snapshot_ui.ts';
66
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
7-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
8-
9-
const runLogic = async (logic: () => Promise<unknown>) => {
10-
const { result, run } = createMockToolHandlerContext();
11-
const response = await run(logic);
12-
13-
if (
14-
response &&
15-
typeof response === 'object' &&
16-
'content' in (response as Record<string, unknown>)
17-
) {
18-
return response as {
19-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
20-
isError?: boolean;
21-
nextStepParams?: unknown;
22-
};
23-
}
24-
25-
const text = result.text();
26-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
27-
const imageContent = result.attachments.map((attachment) => ({
28-
type: 'image' as const,
29-
data: attachment.data,
30-
mimeType: attachment.mimeType,
31-
}));
32-
33-
return {
34-
content: [...textContent, ...imageContent],
35-
isError: result.isError() ? true : undefined,
36-
nextStepParams: result.nextStepParams,
37-
attachments: result.attachments,
38-
text,
39-
};
40-
};
7+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
418

429
describe('Snapshot UI Plugin', () => {
4310
describe('Export Field Validation (Literal)', () => {

src/mcp/tools/ui-automation/__tests__/swipe.test.ts

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,7 @@ import { sessionStore } from '../../../../utils/session-store.ts';
66

77
import { schema, handler, type AxeHelpers, swipeLogic, type SwipeParams } from '../swipe.ts';
88
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
9-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
10-
11-
const runLogic = async (logic: () => Promise<unknown>) => {
12-
const { result, run } = createMockToolHandlerContext();
13-
const response = await run(logic);
14-
15-
if (
16-
response &&
17-
typeof response === 'object' &&
18-
'content' in (response as Record<string, unknown>)
19-
) {
20-
return response as {
21-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
22-
isError?: boolean;
23-
nextStepParams?: unknown;
24-
};
25-
}
26-
27-
const text = result.text();
28-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
29-
const imageContent = result.attachments.map((attachment) => ({
30-
type: 'image' as const,
31-
data: attachment.data,
32-
mimeType: attachment.mimeType,
33-
}));
34-
35-
return {
36-
content: [...textContent, ...imageContent],
37-
isError: result.isError() ? true : undefined,
38-
nextStepParams: result.nextStepParams,
39-
attachments: result.attachments,
40-
text,
41-
};
42-
};
9+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
4310

4411
function createMockAxeHelpers(): AxeHelpers {
4512
return {

src/mcp/tools/ui-automation/__tests__/tap.test.ts

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,7 @@ import { sessionStore } from '../../../../utils/session-store.ts';
55

66
import { schema, handler, type AxeHelpers, tapLogic } from '../tap.ts';
77
import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts';
8-
import { allText, createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts';
9-
10-
const runLogic = async (logic: () => Promise<unknown>) => {
11-
const { result, run } = createMockToolHandlerContext();
12-
const response = await run(logic);
13-
14-
if (
15-
response &&
16-
typeof response === 'object' &&
17-
'content' in (response as Record<string, unknown>)
18-
) {
19-
return response as {
20-
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
21-
isError?: boolean;
22-
nextStepParams?: unknown;
23-
};
24-
}
25-
26-
const text = result.text();
27-
const textContent = text.length > 0 ? [{ type: 'text' as const, text }] : [];
28-
const imageContent = result.attachments.map((attachment) => ({
29-
type: 'image' as const,
30-
data: attachment.data,
31-
mimeType: attachment.mimeType,
32-
}));
33-
34-
return {
35-
content: [...textContent, ...imageContent],
36-
isError: result.isError() ? true : undefined,
37-
nextStepParams: result.nextStepParams,
38-
attachments: result.attachments,
39-
text,
40-
};
41-
};
8+
import { allText, runLogic } from '../../../../test-utils/test-helpers.ts';
429

4310
function createMockAxeHelpers(): AxeHelpers {
4411
return {

0 commit comments

Comments
 (0)