@democratize-quality/mcp-server 1.2.0 → 1.2.1
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.
- package/cli.js +248 -0
- package/package.json +7 -5
- package/src/chatmodes//360/237/214/220 api-generator.chatmode.md" +409 -0
- package/src/chatmodes//360/237/214/220 api-healer.chatmode.md" +494 -0
- package/src/chatmodes//360/237/214/220 api-planner.chatmode.md" +954 -0
- package/src/config/environments/api-only.js +72 -0
- package/src/config/environments/development.js +73 -0
- package/src/config/environments/production.js +88 -0
- package/src/config/index.js +360 -0
- package/src/config/server.js +60 -0
- package/src/config/tools/api.js +86 -0
- package/src/config/tools/browser.js +109 -0
- package/src/config/tools/default.js +51 -0
- package/src/docs/Agent_README.md +310 -0
- package/src/docs/QUICK_REFERENCE.md +111 -0
- package/src/server.ts +234 -0
- package/src/services/browserService.js +344 -0
- package/src/skills/api-planning/SKILL.md +224 -0
- package/src/skills/test-execution/SKILL.md +777 -0
- package/src/skills/test-generation/SKILL.md +309 -0
- package/src/skills/test-healing/SKILL.md +405 -0
- package/src/tools/api/api-generator.js +1884 -0
- package/src/tools/api/api-healer.js +636 -0
- package/src/tools/api/api-planner.js +2617 -0
- package/src/tools/api/api-project-setup.js +332 -0
- package/src/tools/api/api-request.js +660 -0
- package/src/tools/api/api-session-report.js +1297 -0
- package/src/tools/api/api-session-status.js +414 -0
- package/src/tools/api/prompts/README.md +293 -0
- package/src/tools/api/prompts/generation-prompts.js +722 -0
- package/src/tools/api/prompts/healing-prompts.js +214 -0
- package/src/tools/api/prompts/index.js +44 -0
- package/src/tools/api/prompts/orchestrator.js +353 -0
- package/src/tools/api/prompts/validation-rules.js +358 -0
- package/src/tools/base/ToolBase.js +249 -0
- package/src/tools/base/ToolRegistry.js +288 -0
- package/src/tools/browser/advanced/browser-console.js +403 -0
- package/src/tools/browser/advanced/browser-dialog.js +338 -0
- package/src/tools/browser/advanced/browser-evaluate.js +356 -0
- package/src/tools/browser/advanced/browser-file.js +499 -0
- package/src/tools/browser/advanced/browser-keyboard.js +362 -0
- package/src/tools/browser/advanced/browser-mouse.js +351 -0
- package/src/tools/browser/advanced/browser-network.js +440 -0
- package/src/tools/browser/advanced/browser-pdf.js +426 -0
- package/src/tools/browser/advanced/browser-tabs.js +516 -0
- package/src/tools/browser/advanced/browser-wait.js +397 -0
- package/src/tools/browser/click.js +187 -0
- package/src/tools/browser/close.js +79 -0
- package/src/tools/browser/dom.js +89 -0
- package/src/tools/browser/launch.js +86 -0
- package/src/tools/browser/navigate.js +289 -0
- package/src/tools/browser/screenshot.js +370 -0
- package/src/tools/browser/type.js +193 -0
- package/src/tools/index.js +114 -0
- package/src/utils/agentInstaller.js +437 -0
- package/src/utils/browserHelpers.js +102 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 Democratize Quality
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Democratize Quality MCP Server.
|
|
5
|
+
*
|
|
6
|
+
* Democratize Quality MCP Server is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* Democratize Quality MCP Server is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const ToolBase = require('../base/ToolBase');
|
|
21
|
+
const browserService = require('../../services/browserService');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Browser DOM Tool
|
|
25
|
+
* Interacts with the DOM of the current browser page.
|
|
26
|
+
*/
|
|
27
|
+
class BrowserDOMTool extends ToolBase {
|
|
28
|
+
static definition = {
|
|
29
|
+
name: "browser_dom",
|
|
30
|
+
description: "Interacts with the DOM of the current browser page.",
|
|
31
|
+
input_schema: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
browserId: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "The ID of the browser instance."
|
|
37
|
+
},
|
|
38
|
+
action: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "The DOM action to perform (e.g., 'click', 'type')."
|
|
41
|
+
},
|
|
42
|
+
selector: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "The CSS selector of the element to interact with."
|
|
45
|
+
},
|
|
46
|
+
text: {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "The text to type into the element (if applicable)."
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
required: ["browserId", "action", "selector"]
|
|
52
|
+
},
|
|
53
|
+
output_schema: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
success: { type: "boolean", description: "Indicates if the DOM action was successful." },
|
|
57
|
+
browserId: { type: "string", description: "The browser instance ID that was used." }
|
|
58
|
+
},
|
|
59
|
+
required: ["success", "browserId"]
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
async execute(parameters) {
|
|
64
|
+
const { browserId, action, selector, text } = parameters;
|
|
65
|
+
|
|
66
|
+
console.error(`[BrowserDOMTool] Performing ${action} in browser ${browserId} on element: ${selector}`);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
switch (action) {
|
|
70
|
+
case 'click':
|
|
71
|
+
await browserService.clickElement(browserId, selector);
|
|
72
|
+
break;
|
|
73
|
+
case 'type':
|
|
74
|
+
await browserService.typeText(browserId, selector, text);
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Unknown action: ${action}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.error(`[BrowserDOMTool] Successfully performed ${action} in browser: ${browserId}`);
|
|
81
|
+
return { success: true, browserId: browserId };
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`[BrowserDOMTool] Failed to perform ${action}:`, error.message);
|
|
84
|
+
throw new Error(`Failed to perform ${action} in browser ${browserId}: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = BrowserDOMTool;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 Democratize Quality
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Democratize Quality MCP Server.
|
|
5
|
+
*
|
|
6
|
+
* Democratize Quality MCP Server is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* Democratize Quality MCP Server is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const ToolBase = require('../base/ToolBase');
|
|
21
|
+
const browserService = require('../../services/browserService');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Browser Launch Tool
|
|
25
|
+
* Launches a new web browser instance and returns a unique browserId
|
|
26
|
+
*/
|
|
27
|
+
class BrowserLaunchTool extends ToolBase {
|
|
28
|
+
static definition = {
|
|
29
|
+
name: "browser_launch",
|
|
30
|
+
description: "Launches a new web browser instance. Returns a unique browserId. Use this before any other browser actions.",
|
|
31
|
+
input_schema: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
headless: {
|
|
35
|
+
type: "boolean",
|
|
36
|
+
description: "Whether to launch the browser in headless mode (no UI). Defaults to true. Set to false for manual login.",
|
|
37
|
+
default: true
|
|
38
|
+
},
|
|
39
|
+
userDataDir: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Optional. A path (relative to the server) to a directory to store persistent user data (e.g., login sessions, cookies). Use for authenticated sessions. If not provided, a temporary profile is used."
|
|
42
|
+
},
|
|
43
|
+
port: {
|
|
44
|
+
type: "number",
|
|
45
|
+
description: "Optional. The port for remote debugging. If not provided, Chrome will choose an available port."
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
output_schema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
browserId: { type: "string", description: "The unique ID of the launched browser instance." },
|
|
53
|
+
port: { type: "number", description: "The port the browser instance is running on for remote debugging." },
|
|
54
|
+
userDataDir: { type: "string", description: "The absolute path to the user data directory used." }
|
|
55
|
+
},
|
|
56
|
+
required: ["browserId", "port"]
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
async execute(parameters) {
|
|
61
|
+
// Set defaults
|
|
62
|
+
const headless = parameters.headless !== undefined ? parameters.headless : true;
|
|
63
|
+
const port = parameters.port || undefined; // Let chrome-launcher choose if not specified
|
|
64
|
+
const userDataDir = parameters.userDataDir || null;
|
|
65
|
+
|
|
66
|
+
console.error(`[BrowserLaunchTool] Launching browser with headless=${headless}, port=${port}, userDataDir=${userDataDir}`);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const result = await browserService.launchBrowser(headless, port, userDataDir);
|
|
70
|
+
|
|
71
|
+
console.error(`[BrowserLaunchTool] Successfully launched browser: ${result.browserId}`);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
browserId: result.browserId,
|
|
75
|
+
port: result.port,
|
|
76
|
+
userDataDir: result.userDataDir || null
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error(`[BrowserLaunchTool] Failed to launch browser:`, error.message);
|
|
81
|
+
throw new Error(`Failed to launch browser: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = BrowserLaunchTool;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 Democratize Quality
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Democratize Quality MCP Server.
|
|
5
|
+
*
|
|
6
|
+
* Democratize Quality MCP Server is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* Democratize Quality MCP Server is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const ToolBase = require('../base/ToolBase');
|
|
21
|
+
const browserService = require('../../services/browserService');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Enhanced Browser Navigate Tool
|
|
25
|
+
* Navigates a specific browser instance to a given URL with history navigation support
|
|
26
|
+
* Inspired by Playwright MCP navigate capabilities
|
|
27
|
+
*/
|
|
28
|
+
class BrowserNavigateTool extends ToolBase {
|
|
29
|
+
static definition = {
|
|
30
|
+
name: "browser_navigate",
|
|
31
|
+
description: "Navigate browser pages with full history support including go to URL, back, forward, and refresh operations.",
|
|
32
|
+
input_schema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
browserId: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "The ID of the browser instance to navigate."
|
|
38
|
+
},
|
|
39
|
+
action: {
|
|
40
|
+
type: "string",
|
|
41
|
+
enum: ["goto", "back", "forward", "refresh", "reload"],
|
|
42
|
+
default: "goto",
|
|
43
|
+
description: "Navigation action to perform"
|
|
44
|
+
},
|
|
45
|
+
url: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "The URL to navigate to (required for 'goto' action). Must include protocol (http:// or https://)."
|
|
48
|
+
},
|
|
49
|
+
waitForNavigation: {
|
|
50
|
+
type: "boolean",
|
|
51
|
+
default: true,
|
|
52
|
+
description: "Whether to wait for navigation to complete"
|
|
53
|
+
},
|
|
54
|
+
timeout: {
|
|
55
|
+
type: "number",
|
|
56
|
+
default: 30000,
|
|
57
|
+
description: "Navigation timeout in milliseconds"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
required: ["browserId"]
|
|
61
|
+
},
|
|
62
|
+
output_schema: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
success: { type: "boolean", description: "Whether the navigation was successful" },
|
|
66
|
+
action: { type: "string", description: "The navigation action that was performed" },
|
|
67
|
+
message: { type: "string", description: "Confirmation message of the navigation result" },
|
|
68
|
+
url: { type: "string", description: "The current URL after navigation" },
|
|
69
|
+
previousUrl: { type: "string", description: "The previous URL (for back/forward actions)" },
|
|
70
|
+
canGoBack: { type: "boolean", description: "Whether browser can navigate back" },
|
|
71
|
+
canGoForward: { type: "boolean", description: "Whether browser can navigate forward" },
|
|
72
|
+
browserId: { type: "string", description: "The browser instance ID that was used" }
|
|
73
|
+
},
|
|
74
|
+
required: ["success", "action", "message", "browserId"]
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
async execute(parameters) {
|
|
79
|
+
const {
|
|
80
|
+
browserId,
|
|
81
|
+
action = "goto",
|
|
82
|
+
url,
|
|
83
|
+
waitForNavigation = true,
|
|
84
|
+
timeout = 30000
|
|
85
|
+
} = parameters;
|
|
86
|
+
|
|
87
|
+
// Validate required parameters
|
|
88
|
+
if (action === "goto" && !url) {
|
|
89
|
+
throw new Error("URL is required for 'goto' action");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (url && !url.startsWith('http://') && !url.startsWith('https://')) {
|
|
93
|
+
throw new Error("URL must include protocol (http:// or https://)");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const browser = browserService.getBrowserInstance(browserId);
|
|
97
|
+
if (!browser) {
|
|
98
|
+
throw new Error(`Browser instance '${browserId}' not found. Please launch a browser first using browser_launch.`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const client = browser.cdpClient;
|
|
102
|
+
|
|
103
|
+
console.error(`[BrowserNavigateTool] Performing ${action} action on browser ${browserId}`);
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
let result = {
|
|
107
|
+
success: false,
|
|
108
|
+
action: action,
|
|
109
|
+
browserId: browserId
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Get current URL and navigation state before action
|
|
113
|
+
const currentInfo = await this.getCurrentNavigationState(client);
|
|
114
|
+
|
|
115
|
+
switch (action) {
|
|
116
|
+
case 'goto':
|
|
117
|
+
await this.performGoto(client, url, waitForNavigation, timeout);
|
|
118
|
+
result.success = true;
|
|
119
|
+
result.message = `Successfully navigated to ${url}`;
|
|
120
|
+
result.url = url;
|
|
121
|
+
result.previousUrl = currentInfo.url;
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
case 'back':
|
|
125
|
+
if (!currentInfo.canGoBack) {
|
|
126
|
+
throw new Error("Cannot go back - no previous page in history");
|
|
127
|
+
}
|
|
128
|
+
await this.performBack(client, waitForNavigation);
|
|
129
|
+
const backInfo = await this.getCurrentNavigationState(client);
|
|
130
|
+
result.success = true;
|
|
131
|
+
result.message = "Successfully navigated back";
|
|
132
|
+
result.url = backInfo.url;
|
|
133
|
+
result.previousUrl = currentInfo.url;
|
|
134
|
+
break;
|
|
135
|
+
|
|
136
|
+
case 'forward':
|
|
137
|
+
if (!currentInfo.canGoForward) {
|
|
138
|
+
throw new Error("Cannot go forward - no next page in history");
|
|
139
|
+
}
|
|
140
|
+
await this.performForward(client, waitForNavigation);
|
|
141
|
+
const forwardInfo = await this.getCurrentNavigationState(client);
|
|
142
|
+
result.success = true;
|
|
143
|
+
result.message = "Successfully navigated forward";
|
|
144
|
+
result.url = forwardInfo.url;
|
|
145
|
+
result.previousUrl = currentInfo.url;
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 'refresh':
|
|
149
|
+
case 'reload':
|
|
150
|
+
await this.performRefresh(client, waitForNavigation);
|
|
151
|
+
result.success = true;
|
|
152
|
+
result.message = "Successfully refreshed page";
|
|
153
|
+
result.url = currentInfo.url;
|
|
154
|
+
break;
|
|
155
|
+
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`Unsupported navigation action: ${action}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get final navigation state
|
|
161
|
+
const finalInfo = await this.getCurrentNavigationState(client);
|
|
162
|
+
result.canGoBack = finalInfo.canGoBack;
|
|
163
|
+
result.canGoForward = finalInfo.canGoForward;
|
|
164
|
+
|
|
165
|
+
if (!result.url) {
|
|
166
|
+
result.url = finalInfo.url;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.error(`[BrowserNavigateTool] Successfully completed ${action} action`);
|
|
170
|
+
return result;
|
|
171
|
+
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error(`[BrowserNavigateTool] Navigation failed:`, error.message);
|
|
174
|
+
throw new Error(`Failed to perform ${action}: ${error.message}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Navigate to a specific URL
|
|
180
|
+
*/
|
|
181
|
+
async performGoto(client, url, waitForNavigation, timeout) {
|
|
182
|
+
await client.Page.enable();
|
|
183
|
+
|
|
184
|
+
if (waitForNavigation) {
|
|
185
|
+
// Set up navigation promise
|
|
186
|
+
const navigationPromise = new Promise((resolve, reject) => {
|
|
187
|
+
const timeoutId = setTimeout(() => {
|
|
188
|
+
reject(new Error(`Navigation timeout after ${timeout}ms`));
|
|
189
|
+
}, timeout);
|
|
190
|
+
|
|
191
|
+
client.Page.loadEventFired(() => {
|
|
192
|
+
clearTimeout(timeoutId);
|
|
193
|
+
resolve();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Navigate and wait
|
|
198
|
+
await client.Page.navigate({ url });
|
|
199
|
+
await navigationPromise;
|
|
200
|
+
} else {
|
|
201
|
+
await client.Page.navigate({ url });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Navigate back in history
|
|
207
|
+
*/
|
|
208
|
+
async performBack(client, waitForNavigation) {
|
|
209
|
+
const history = await client.Page.getNavigationHistory();
|
|
210
|
+
|
|
211
|
+
if (history.currentIndex > 0) {
|
|
212
|
+
const previousEntry = history.entries[history.currentIndex - 1];
|
|
213
|
+
|
|
214
|
+
if (waitForNavigation) {
|
|
215
|
+
const navigationPromise = new Promise((resolve) => {
|
|
216
|
+
client.Page.loadEventFired(resolve);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
await client.Page.navigateToHistoryEntry({ entryId: previousEntry.id });
|
|
220
|
+
await navigationPromise;
|
|
221
|
+
} else {
|
|
222
|
+
await client.Page.navigateToHistoryEntry({ entryId: previousEntry.id });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Navigate forward in history
|
|
229
|
+
*/
|
|
230
|
+
async performForward(client, waitForNavigation) {
|
|
231
|
+
const history = await client.Page.getNavigationHistory();
|
|
232
|
+
|
|
233
|
+
if (history.currentIndex < history.entries.length - 1) {
|
|
234
|
+
const nextEntry = history.entries[history.currentIndex + 1];
|
|
235
|
+
|
|
236
|
+
if (waitForNavigation) {
|
|
237
|
+
const navigationPromise = new Promise((resolve) => {
|
|
238
|
+
client.Page.loadEventFired(resolve);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
await client.Page.navigateToHistoryEntry({ entryId: nextEntry.id });
|
|
242
|
+
await navigationPromise;
|
|
243
|
+
} else {
|
|
244
|
+
await client.Page.navigateToHistoryEntry({ entryId: nextEntry.id });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Refresh/reload the current page
|
|
251
|
+
*/
|
|
252
|
+
async performRefresh(client, waitForNavigation) {
|
|
253
|
+
if (waitForNavigation) {
|
|
254
|
+
const navigationPromise = new Promise((resolve) => {
|
|
255
|
+
client.Page.loadEventFired(resolve);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
await client.Page.reload();
|
|
259
|
+
await navigationPromise;
|
|
260
|
+
} else {
|
|
261
|
+
await client.Page.reload();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get current navigation state
|
|
267
|
+
*/
|
|
268
|
+
async getCurrentNavigationState(client) {
|
|
269
|
+
await client.Page.enable();
|
|
270
|
+
|
|
271
|
+
// Get current URL
|
|
272
|
+
const urlResult = await client.Runtime.evaluate({
|
|
273
|
+
expression: 'window.location.href'
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Get navigation history
|
|
277
|
+
const history = await client.Page.getNavigationHistory();
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
url: urlResult.result.value,
|
|
281
|
+
canGoBack: history.currentIndex > 0,
|
|
282
|
+
canGoForward: history.currentIndex < history.entries.length - 1,
|
|
283
|
+
historyLength: history.entries.length,
|
|
284
|
+
currentIndex: history.currentIndex
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = BrowserNavigateTool;
|