@iflow-mcp/appiumtestdistribution-mcp-appium-gestures 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ [2026-02-09 21:48:23] [✓] 步骤2: 阅读代码完成 - 项目确认为Node.js MCP服务端项目,支持stdio和SSE协议
2
+ [2026-02-09 21:48:48] [✓] 步骤3: 本地测试完成 - 成功检测到5个工具
3
+ [2026-02-09 21:48:56] [✓] 步骤4: 创建并推送分支完成 - 成功推送到iflow分支
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 AppiumTestDistribution
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/appiumtestdistribution-mcp-appium-gestures-badge.png)](https://mseep.ai/app/appiumtestdistribution-mcp-appium-gestures)
2
+
3
+ # MCP Appium Gestures
4
+
5
+ An MCP (Model Context Protocol) server providing resources and tools for Appium mobile gestures.
6
+
7
+ ## Features
8
+
9
+ - Documentation resources for common Appium mobile gestures:
10
+
11
+ - Tap
12
+ - Swipe
13
+ - Scroll
14
+ - Pinch/Zoom
15
+ - Long Press
16
+ - Drag and Drop
17
+ - Double Tap
18
+
19
+ - Tools to generate code for these gestures in different languages:
20
+ - JavaScript (WebdriverIO v9+ and below)
21
+ - Java (Appium Java Client)
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ # Clone the repository
27
+ git clone https://github.com/yourusername/mcp-appium-gestures.git
28
+ cd mcp-appium-gestures
29
+
30
+ # Install dependencies
31
+ npm install
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Start with stdio transport (for local use)
37
+
38
+ ```bash
39
+ # Start the server with stdio transport
40
+ npm start
41
+ # or
42
+ npm run start:stdio
43
+ ```
44
+
45
+ ### Start with SSE transport (for remote use)
46
+
47
+ ```bash
48
+ # Start the server with SSE transport on default port (8080)
49
+ npm run start:sse
50
+
51
+ # Start the server with SSE transport on a custom port
52
+ npm run start:sse:port 3000
53
+ ```
54
+
55
+ ### Development and Testing
56
+
57
+ ```bash
58
+ # Test with mcp-cli
59
+ npm run dev
60
+
61
+ # Inspect with MCP Inspector
62
+ npm run inspect
63
+ ```
64
+
65
+ ## Usage with Claude
66
+
67
+ To use this MCP server with Claude, you need to add it to your MCP settings configuration file. The location of this file depends on your platform:
68
+
69
+ - For Cursor: `/Users/[username]/Library/Application Support/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
70
+ - For Claude Desktop: `/Users/[username]/Library/Application Support/Claude/claude_desktop_config.json`
71
+ - For Cline or other MCP clients: Check your client's documentation for the configuration file location
72
+
73
+ Add the following configuration to the `mcpServers` object in the settings file:
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "appium-gestures": {
79
+ "command": "npx",
80
+ "disabled": false,
81
+ "args": ["mcp-appium-gestures"],
82
+ "autoApprove": [],
83
+ "timeout": 300,
84
+ "transportType": "stdio"
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ Once configured, you can use the MCP server's tools and resources directly in Claude:
91
+
92
+ ```
93
+ <use_mcp_tool>
94
+ <server_name>appium-gestures</server_name>
95
+ <tool_name>generate-tap-code</tool_name>
96
+ <arguments>
97
+ {
98
+ "language": "javascript",
99
+ "useElement": true,
100
+ "elementId": "login-button"
101
+ }
102
+ </arguments>
103
+ </use_mcp_tool>
104
+ ```
105
+
106
+ Or access resources:
107
+
108
+ ```
109
+ <access_mcp_resource>
110
+ <server_name>appium-gestures</server_name>
111
+ <uri>gesture://tap</uri>
112
+ </access_mcp_resource>
113
+ ```
114
+
115
+ ## Resources
116
+
117
+ The server provides documentation resources for the following gestures:
118
+
119
+ - `gesture://tap` - Tap gesture documentation
120
+ - `gesture://swipe` - Swipe gesture documentation
121
+ - `gesture://scroll` - Scroll gesture documentation
122
+ - `gesture://pinch-zoom` - Pinch and zoom gestures documentation
123
+ - `gesture://long-press` - Long press gesture documentation
124
+ - `gesture://drag-drop` - Drag and drop gesture documentation
125
+ - `gesture://double-tap` - Double tap gesture documentation
126
+
127
+ You can also access a specific gesture by name using the template: `gesture://{name}`
128
+
129
+ ## Tools
130
+
131
+ The server provides the following tools to generate code for Appium gestures:
132
+
133
+ ### generate-tap-code
134
+
135
+ Generates code for tap gesture.
136
+
137
+ Parameters:
138
+
139
+ - `language`: 'javascript' or 'java'
140
+ - `useElement`: boolean - whether to tap on an element or at coordinates
141
+ - `elementId`: string (required if useElement is true) - the element ID to tap on
142
+ - `x`: number (required if useElement is false) - x coordinate to tap at
143
+ - `y`: number (required if useElement is false) - y coordinate to tap at
144
+
145
+ ### generate-swipe-code
146
+
147
+ Generates code for swipe gesture.
148
+
149
+ Parameters:
150
+
151
+ - `language`: 'javascript' or 'java'
152
+ - `startX`: number - starting x coordinate
153
+ - `startY`: number - starting y coordinate
154
+ - `endX`: number - ending x coordinate
155
+ - `endY`: number - ending y coordinate
156
+ - `duration`: number (optional, default: 500) - duration of the swipe in milliseconds
157
+
158
+ ### generate-scroll-code
159
+
160
+ Generates code for scroll gesture.
161
+
162
+ Parameters:
163
+
164
+ - `language`: 'javascript' or 'java'
165
+ - `direction`: 'up', 'down', 'left', or 'right'
166
+ - `useElement`: boolean (optional, default: false) - whether to scroll to an element
167
+ - `elementId`: string (optional) - the element ID to scroll to
168
+ - `distance`: number (optional, default: 300) - distance to scroll
169
+
170
+ ### generate-long-press-code
171
+
172
+ Generates code for long press gesture.
173
+
174
+ Parameters:
175
+
176
+ - `language`: 'javascript' or 'java'
177
+ - `useElement`: boolean - whether to long press on an element or at coordinates
178
+ - `elementId`: string (required if useElement is true) - the element ID to long press on
179
+ - `x`: number (required if useElement is false) - x coordinate to long press at
180
+ - `y`: number (required if useElement is false) - y coordinate to long press at
181
+ - `duration`: number (optional, default: 2000) - duration of the long press in milliseconds
182
+
183
+ ### generate-double-tap-code
184
+
185
+ Generates code for double tap gesture.
186
+
187
+ Parameters:
188
+
189
+ - `language`: 'javascript' or 'java'
190
+ - `useElement`: boolean - whether to double tap on an element or at coordinates
191
+ - `elementId`: string (required if useElement is true) - the element ID to double tap on
192
+ - `x`: number (required if useElement is false) - x coordinate to double tap at
193
+ - `y`: number (required if useElement is false) - y coordinate to double tap at
194
+ - `pauseDuration`: number (optional, default: 200) - pause duration between taps in milliseconds
195
+
196
+ ## License
197
+
198
+ MIT
package/language.json ADDED
@@ -0,0 +1 @@
1
+ nodejs
package/package.json ADDED
@@ -0,0 +1 @@
1
+ {"name": "@iflow-mcp/appiumtestdistribution-mcp-appium-gestures", "version": "1.0.0", "type": "module", "main": "src/index.js", "bin": {"iflow-mcp-appiumtestdistribution-mcp-appium-gestures": "src/index.js"}, "scripts": {"start": "node src/index.js", "start:stdio": "node src/index.js", "start:sse": "node src/index.js --sse", "start:sse:port": "node src/index.js --sse --port=", "dev": "npx fastmcp dev src/index.js", "inspect": "npx fastmcp inspect src/index.js", "test": "echo \"Error: no test specified\" && exit 1"}, "author": "", "license": "MIT", "description": "MCP server providing resources and tools for Appium mobile gestures", "dependencies": {"@modelcontextprotocol/sdk": "^1.11.0", "fastmcp": "^1.23.2", "zod": "^3.24.3"}}
package/package_name ADDED
@@ -0,0 +1 @@
1
+ @iflow-mcp/appiumtestdistribution-mcp-appium-gestures
package/push_info.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "push_platform": "github",
3
+ "fork_url": "https://github.com/iflow-mcp/appiumtestdistribution-mcp-appium-gestures",
4
+ "fork_branch": "iflow"
5
+ }
package/src/index.js ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+
3
+ import server from './server.js';
4
+
5
+ // Parse command line arguments
6
+ const args = process.argv.slice(2);
7
+ const useSSE = args.includes('--sse');
8
+ const port = args.find((arg) => arg.startsWith('--port='))?.split('=')[1] || '8080';
9
+
10
+ // Start the server with the appropriate transport
11
+ async function startServer() {
12
+ console.log("Starting Appium Gestures MCP Server...");
13
+
14
+ try {
15
+ if (useSSE) {
16
+ // Start with SSE transport
17
+ server.start({
18
+ transportType: 'sse',
19
+ sse: {
20
+ endpoint: '/sse',
21
+ port: parseInt(port, 10),
22
+ },
23
+ });
24
+
25
+ console.log(`Server started with SSE transport on http://localhost:${port}/sse`);
26
+ console.log("Waiting for client connections...");
27
+ } else {
28
+ // Start with stdio transport
29
+ server.start({
30
+ transportType: 'stdio',
31
+ });
32
+
33
+ console.log("Server started with stdio transport");
34
+ console.log("Waiting for client connections...");
35
+ }
36
+ } catch (error) {
37
+ console.error("Error starting server:", error);
38
+ process.exit(1);
39
+ }
40
+ }
41
+
42
+ // Start the server
43
+ startServer();
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Resource for double tap gesture documentation
3
+ */
4
+ export default function doubleTapGesture(server) {
5
+ server.addResource({
6
+ uri: 'gesture://double-tap',
7
+ name: 'Double Tap Gesture',
8
+ mimeType: 'text/markdown',
9
+ async load() {
10
+ return {
11
+ text: `
12
+ # Double Tap Gesture in Appium
13
+
14
+ The double tap gesture simulates tapping twice in quick succession on the same location, commonly used for zooming in on maps or images.
15
+
16
+ ## WebdriverIO Example (v9+)
17
+ \`\`\`javascript
18
+ // Double tap on an element
19
+ // Note: In WebdriverIO v9+, you can use the doubleClick method
20
+ const element = $('~element-id');
21
+ await element.doubleClick();
22
+ \`\`\`
23
+
24
+ ## WebdriverIO Example (below v9)
25
+ \`\`\`javascript
26
+ // Double tap at specific coordinates using touchAction
27
+ await driver.touchAction([
28
+ { action: 'tap', x: 100, y: 200 },
29
+ { action: 'wait', ms: 100 },
30
+ { action: 'tap', x: 100, y: 200 }
31
+ ]);
32
+
33
+ // Using the Gestures helper class
34
+ const { x, y } = await element.getLocation();
35
+ const centerX = x + (await element.getSize()).width / 2;
36
+ const centerY = y + (await element.getSize()).height / 2;
37
+
38
+ // Execute double tap gesture
39
+ await driver
40
+ .action('pointer')
41
+ .move(centerX, centerY)
42
+ .down()
43
+ .up()
44
+ .pause(100)
45
+ .down()
46
+ .up()
47
+ .perform();
48
+ \`\`\`
49
+
50
+ ## Appium Java Client Example
51
+ \`\`\`java
52
+ // Implementation from VodQaAdvancedAppium repository
53
+ @Test
54
+ public void doubleTap() throws InterruptedException {
55
+ // Setup
56
+ login();
57
+ Thread.sleep(5000);
58
+ driver.findElement(AppiumBy.accessibilityId("doubleTap")).click();
59
+
60
+ // Get the element to double tap
61
+ WebElement element = new WebDriverWait(driver, Duration.ofMillis(30))
62
+ .until(elementToBeClickable(AppiumBy.accessibilityId("doubleTapMe")));
63
+
64
+ // Get element location
65
+ Point source = element.getLocation();
66
+
67
+ // Create finger gesture with touch pointer
68
+ PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
69
+ Sequence tap = new Sequence(finger, 1);
70
+
71
+ // First tap
72
+ tap.addAction(finger.createPointerMove(Duration.ofMillis(0),
73
+ PointerInput.Origin.viewport(), source.x, source.y));
74
+ tap.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
75
+ tap.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
76
+
77
+ // Small pause between taps
78
+ tap.addAction(new Pause(finger, Duration.ofMillis(200)));
79
+
80
+ // Second tap
81
+ tap.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
82
+ tap.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
83
+
84
+ // Perform the action
85
+ driver.perform(Collections.singletonList(tap));
86
+
87
+ // Wait to see the result
88
+ Thread.sleep(4000);
89
+ }
90
+ \`\`\`
91
+
92
+ This implementation uses the W3C Actions API which provides more precise control over pointer movements and timing, and is the recommended approach in newer versions of Appium.
93
+ `,
94
+ };
95
+ },
96
+ });
97
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Resource for drag and drop gesture documentation
3
+ */
4
+ export default function dragDropGesture(server) {
5
+ server.addResource({
6
+ uri: 'gesture://drag-drop',
7
+ name: 'Drag and Drop Gesture',
8
+ mimeType: 'text/markdown',
9
+ async load() {
10
+ return {
11
+ text: `
12
+ # Drag and Drop Gesture in Appium
13
+
14
+ The drag and drop gesture simulates dragging an element from one location to another, commonly used in UI testing for moving items between containers.
15
+
16
+ ## WebdriverIO Example (v9+)
17
+ \`\`\`javascript
18
+ // Drag and drop element to another element
19
+ const elem = $('#someElem');
20
+ const target = $('#someTarget');
21
+ await elem.dragAndDrop(target);
22
+
23
+ // Drag and drop relative from current position
24
+ await elem.dragAndDrop({ x: 100, y: 200 });
25
+
26
+ // Drag and drop with custom duration
27
+ await elem.dragAndDrop(target, { duration: 2000 }); // 2 seconds
28
+ \`\`\`
29
+
30
+ ## WebdriverIO Example (below v9)
31
+ \`\`\`javascript
32
+ // Drag and drop using coordinates with touchAction
33
+ await driver.touchAction([
34
+ { action: 'press', x: 100, y: 200 },
35
+ { action: 'wait', ms: 500 },
36
+ { action: 'moveTo', x: 300, y: 400 },
37
+ { action: 'release' }
38
+ ]);
39
+
40
+ // Using the Gestures helper class
41
+ const { x: sourceX, y: sourceY } = await sourceElem.getLocation();
42
+ const { x: targetX, y: targetY } = await targetElem.getLocation();
43
+
44
+ await driver
45
+ .action('pointer')
46
+ .move(sourceX, sourceY)
47
+ .down()
48
+ .pause(500)
49
+ .move({ duration: 1000, x: targetX, y: targetY })
50
+ .up()
51
+ .perform();
52
+ \`\`\`
53
+
54
+ ## Appium Java Client Example
55
+ \`\`\`java
56
+ // Implementation from VodQaAdvancedAppium repository
57
+ // Find the element to drag
58
+ WebElement dragMe = new WebDriverWait(driver, Duration.ofSeconds(30))
59
+ .until(elementToBeClickable(AppiumBy.accessibilityId("dragMe")));
60
+
61
+ // Get source location
62
+ Point source = dragMe.getLocation();
63
+
64
+ // Find the drop target
65
+ Point target = driver.findElement(AppiumBy.accessibilityId("dropzone")).getLocation();
66
+
67
+ // Create finger gesture with touch pointer
68
+ PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
69
+ Sequence dragNDrop = new Sequence(finger, 1);
70
+
71
+ // Press, move and release
72
+ dragNDrop.addAction(finger.createPointerMove(Duration.ofMillis(0),
73
+ PointerInput.Origin.viewport(), source.x, source.y));
74
+ dragNDrop.addAction(finger.createPointerDown(PointerInput.MouseButton.MIDDLE.asArg()));
75
+ dragNDrop.addAction(finger.createPointerMove(Duration.ofMillis(600),
76
+ PointerInput.Origin.viewport(), target.x, target.y));
77
+ dragNDrop.addAction(finger.createPointerUp(PointerInput.MouseButton.MIDDLE.asArg()));
78
+
79
+ // Perform the action
80
+ driver.perform(Collections.singletonList(dragNDrop));
81
+
82
+ // Verify the drop was successful
83
+ assertEquals(driver.findElements(AppiumBy.accessibilityId("success")).size(), 1);
84
+ \`\`\`
85
+
86
+ This implementation uses the W3C Actions API which provides more precise control over pointer movements and is the recommended approach in newer versions of Appium.
87
+ `,
88
+ };
89
+ },
90
+ });
91
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Dynamic resource for gesture documentation by name
3
+ */
4
+ export default function gestureByName(server) {
5
+ server.addResourceTemplate({
6
+ uriTemplate: 'gesture://{name}',
7
+ name: 'Gesture by Name',
8
+ mimeType: 'text/markdown',
9
+ arguments: [
10
+ {
11
+ name: 'name',
12
+ description: 'Name of the gesture',
13
+ required: true,
14
+ enum: ['tap', 'swipe', 'scroll', 'pinch-zoom', 'long-press', 'drag-drop', 'double-tap'],
15
+ },
16
+ ],
17
+ async load({ name }) {
18
+ // Map of supported gestures
19
+ const gestures = {
20
+ tap: 'gesture://tap',
21
+ swipe: 'gesture://swipe',
22
+ scroll: 'gesture://scroll',
23
+ 'pinch-zoom': 'gesture://pinch-zoom',
24
+ 'long-press': 'gesture://long-press',
25
+ 'drag-drop': 'gesture://drag-drop',
26
+ 'double-tap': 'gesture://double-tap',
27
+ };
28
+
29
+ if (!gestures[name]) {
30
+ return {
31
+ text: `Gesture "${name}" not found. Available gestures: ${Object.keys(
32
+ gestures
33
+ ).join(', ')}`,
34
+ };
35
+ }
36
+
37
+ // Redirect to the specific gesture resource
38
+ return {
39
+ text: `Redirecting to ${gestures[name]}...`,
40
+ };
41
+ },
42
+ });
43
+ }
@@ -0,0 +1,21 @@
1
+ // Export all resources
2
+ import tapGesture from './tap-gesture.js';
3
+ import swipeGesture from './swipe-gesture.js';
4
+ import scrollGesture from './scroll-gesture.js';
5
+ import pinchZoomGesture from './pinch-zoom-gesture.js';
6
+ import longPressGesture from './long-press-gesture.js';
7
+ import dragDropGesture from './drag-drop-gesture.js';
8
+ import doubleTapGesture from './double-tap-gesture.js';
9
+ import gestureByName from './gesture-by-name.js';
10
+
11
+ export default function registerResources(server) {
12
+ // Register all resources
13
+ tapGesture(server);
14
+ swipeGesture(server);
15
+ scrollGesture(server);
16
+ pinchZoomGesture(server);
17
+ longPressGesture(server);
18
+ dragDropGesture(server);
19
+ doubleTapGesture(server);
20
+ gestureByName(server);
21
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Resource for long press gesture documentation
3
+ */
4
+ export default function longPressGesture(server) {
5
+ server.addResource({
6
+ uri: 'gesture://long-press',
7
+ name: 'Long Press Gesture',
8
+ mimeType: 'text/markdown',
9
+ async load() {
10
+ return {
11
+ text: `
12
+ # Long Press Gesture in Appium
13
+
14
+ The long press gesture simulates pressing and holding a finger on a specific element or at specific coordinates.
15
+
16
+ ## WebdriverIO Example (v9+)
17
+ \`\`\`javascript
18
+ // Long press on an element
19
+ const contacts = $('~Contacts');
20
+ // Long press with default duration (1500ms)
21
+ await contacts.longPress();
22
+ // Long press with custom duration and offset
23
+ await contacts.longPress({ duration: 5000 }); // 5 seconds
24
+ // Long press with offset from center point of element
25
+ await contacts.longPress({ x: 30, y: 10 });
26
+ \`\`\`
27
+
28
+ ## WebdriverIO Example (below v9)
29
+ \`\`\`javascript
30
+ // Long press on an element
31
+ await $('~element-id').touchAction('longPress');
32
+
33
+ // Long press at specific coordinates
34
+ await driver.touchAction([
35
+ { action: 'longPress', x: 100, y: 200, ms: 2000 },
36
+ { action: 'release' }
37
+ ]);
38
+ \`\`\`
39
+
40
+ ## Appium Java Client Example (W3C Actions API)
41
+ \`\`\`java
42
+ // Long press on an element
43
+ WebElement element = driver.findElement(AppiumBy.accessibilityId("elementId"));
44
+ Point source = element.getLocation();
45
+ PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
46
+ Sequence sequence = new Sequence(finger, 1);
47
+
48
+ // Move to element, press down, wait, then release
49
+ sequence.addAction(finger.createPointerMove(Duration.ofMillis(0),
50
+ PointerInput.Origin.viewport(), source.x, source.y));
51
+ sequence.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
52
+ // Hold for 2 seconds (2000 milliseconds)
53
+ sequence.addAction(new Pause(finger, Duration.ofMillis(2000)));
54
+ sequence.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
55
+
56
+ // Perform the action
57
+ driver.perform(Collections.singletonList(sequence));
58
+
59
+ // Long press at specific coordinates
60
+ PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
61
+ Sequence sequence = new Sequence(finger, 1);
62
+
63
+ sequence.addAction(finger.createPointerMove(Duration.ofMillis(0),
64
+ PointerInput.Origin.viewport(), 100, 200));
65
+ sequence.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
66
+ // Hold for 2 seconds (2000 milliseconds)
67
+ sequence.addAction(new Pause(finger, Duration.ofMillis(2000)));
68
+ sequence.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
69
+
70
+ // Perform the action
71
+ driver.perform(Collections.singletonList(sequence));
72
+ \`\`\`
73
+ `,
74
+ };
75
+ },
76
+ });
77
+ }