lexxit-automation-framework 2.0.0 → 2.0.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.
Files changed (136) hide show
  1. package/README.md +93 -0
  2. package/bin/lexxit-automation-framework.js +427 -0
  3. package/dist/app.d.ts +2 -0
  4. package/dist/app.js +26 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/controllers/controller.d.ts +57 -0
  7. package/dist/controllers/controller.js +263 -0
  8. package/dist/controllers/controller.js.map +1 -0
  9. package/dist/core/BrowserManager.d.ts +46 -0
  10. package/dist/core/BrowserManager.js +377 -0
  11. package/dist/core/BrowserManager.js.map +1 -0
  12. package/dist/core/PlaywrightEngine.d.ts +16 -0
  13. package/dist/core/PlaywrightEngine.js +246 -0
  14. package/dist/core/PlaywrightEngine.js.map +1 -0
  15. package/dist/core/ScreenshotManager.d.ts +10 -0
  16. package/dist/core/ScreenshotManager.js +28 -0
  17. package/dist/core/ScreenshotManager.js.map +1 -0
  18. package/dist/core/TestData.d.ts +12 -0
  19. package/dist/core/TestData.js +29 -0
  20. package/dist/core/TestData.js.map +1 -0
  21. package/dist/core/TestExecutor.d.ts +16 -0
  22. package/dist/core/TestExecutor.js +355 -0
  23. package/dist/core/TestExecutor.js.map +1 -0
  24. package/dist/core/handlers/AllHandlers.d.ts +116 -0
  25. package/dist/core/handlers/AllHandlers.js +648 -0
  26. package/dist/core/handlers/AllHandlers.js.map +1 -0
  27. package/dist/core/handlers/BaseHandler.d.ts +16 -0
  28. package/dist/core/handlers/BaseHandler.js +27 -0
  29. package/dist/core/handlers/BaseHandler.js.map +1 -0
  30. package/dist/core/handlers/ClickHandler.d.ts +34 -0
  31. package/dist/core/handlers/ClickHandler.js +359 -0
  32. package/dist/core/handlers/ClickHandler.js.map +1 -0
  33. package/dist/core/handlers/CustomCodeHandler.d.ts +35 -0
  34. package/dist/core/handlers/CustomCodeHandler.js +102 -0
  35. package/dist/core/handlers/CustomCodeHandler.js.map +1 -0
  36. package/dist/core/handlers/DropdownHandler.d.ts +43 -0
  37. package/dist/core/handlers/DropdownHandler.js +304 -0
  38. package/dist/core/handlers/DropdownHandler.js.map +1 -0
  39. package/dist/core/handlers/InputHandler.d.ts +24 -0
  40. package/dist/core/handlers/InputHandler.js +197 -0
  41. package/dist/core/handlers/InputHandler.js.map +1 -0
  42. package/dist/core/registry/ActionRegistry.d.ts +8 -0
  43. package/dist/core/registry/ActionRegistry.js +35 -0
  44. package/dist/core/registry/ActionRegistry.js.map +1 -0
  45. package/dist/installer/frameworkLauncher.d.ts +31 -0
  46. package/dist/installer/frameworkLauncher.js +198 -0
  47. package/dist/installer/frameworkLauncher.js.map +1 -0
  48. package/dist/queue/ExecutionQueue.d.ts +52 -0
  49. package/dist/queue/ExecutionQueue.js +175 -0
  50. package/dist/queue/ExecutionQueue.js.map +1 -0
  51. package/dist/routes/api.routes.d.ts +2 -0
  52. package/dist/routes/api.routes.js +16 -0
  53. package/dist/routes/api.routes.js.map +1 -0
  54. package/dist/server.d.ts +1 -0
  55. package/dist/server.js +30 -0
  56. package/dist/server.js.map +1 -0
  57. package/dist/types/types.d.ts +135 -0
  58. package/dist/types/types.js +4 -0
  59. package/dist/types/types.js.map +1 -0
  60. package/dist/utils/elementHighlight.d.ts +35 -0
  61. package/dist/utils/elementHighlight.js +136 -0
  62. package/dist/utils/elementHighlight.js.map +1 -0
  63. package/dist/utils/locatorHelper.d.ts +7 -0
  64. package/dist/utils/locatorHelper.js +53 -0
  65. package/dist/utils/locatorHelper.js.map +1 -0
  66. package/dist/utils/logger.d.ts +12 -0
  67. package/dist/utils/logger.js +35 -0
  68. package/dist/utils/logger.js.map +1 -0
  69. package/dist/utils/response.d.ts +4 -0
  70. package/dist/utils/response.js +25 -0
  71. package/dist/utils/response.js.map +1 -0
  72. package/dist/utils/responseFormatter.d.ts +78 -0
  73. package/dist/utils/responseFormatter.js +123 -0
  74. package/dist/utils/responseFormatter.js.map +1 -0
  75. package/dist/utils/sseManager.d.ts +32 -0
  76. package/dist/utils/sseManager.js +122 -0
  77. package/dist/utils/sseManager.js.map +1 -0
  78. package/lexxit-automation-framework-2.0.0.tgz +0 -0
  79. package/npmignore +5 -0
  80. package/package.json +36 -45
  81. package/scripts/postinstall.js +52 -0
  82. package/src/app.ts +27 -0
  83. package/src/controllers/controller.ts +282 -0
  84. package/src/core/BrowserManager.ts +398 -0
  85. package/src/core/PlaywrightEngine.ts +371 -0
  86. package/src/core/ScreenshotManager.ts +25 -0
  87. package/src/core/TestData.ts +25 -0
  88. package/src/core/TestExecutor.ts +436 -0
  89. package/src/core/handlers/AllHandlers.ts +626 -0
  90. package/src/core/handlers/BaseHandler.ts +41 -0
  91. package/src/core/handlers/ClickHandler.ts +482 -0
  92. package/src/core/handlers/CustomCodeHandler.ts +123 -0
  93. package/src/core/handlers/DropdownHandler.ts +438 -0
  94. package/src/core/handlers/InputHandler.ts +192 -0
  95. package/src/core/registry/ActionRegistry.ts +31 -0
  96. package/src/installer/frameworkLauncher.ts +242 -0
  97. package/src/installer/install.sh +107 -0
  98. package/src/public/dashboard.html +540 -0
  99. package/src/public/queue-monitor.html +190 -0
  100. package/src/queue/ExecutionQueue.ts +200 -0
  101. package/src/routes/api.routes.ts +16 -0
  102. package/src/server.ts +29 -0
  103. package/src/types/types.ts +169 -0
  104. package/src/utils/elementHighlight.ts +174 -0
  105. package/src/utils/locatorHelper.ts +49 -0
  106. package/src/utils/logger.ts +40 -0
  107. package/src/utils/response.ts +27 -0
  108. package/src/utils/responseFormatter.ts +167 -0
  109. package/src/utils/sseManager.ts +127 -0
  110. package/tsconfig.json +18 -0
  111. package/videos/fb1b94b6-6639-4c9a-82bb-63572606f403/page@5bd5c6c8b62baa700e9810cdd64f5c49.webm +0 -0
  112. package/dist-obf/app.js +0 -1
  113. package/dist-obf/controllers/controller.js +0 -1
  114. package/dist-obf/core/BrowserManager.js +0 -1
  115. package/dist-obf/core/PlaywrightEngine.js +0 -1
  116. package/dist-obf/core/ScreenshotManager.js +0 -1
  117. package/dist-obf/core/TestData.js +0 -1
  118. package/dist-obf/core/TestExecutor.js +0 -1
  119. package/dist-obf/core/handlers/AllHandlers.js +0 -1
  120. package/dist-obf/core/handlers/BaseHandler.js +0 -1
  121. package/dist-obf/core/handlers/ClickHandler.js +0 -1
  122. package/dist-obf/core/handlers/CustomCodeHandler.js +0 -1
  123. package/dist-obf/core/handlers/DropdownHandler.js +0 -1
  124. package/dist-obf/core/handlers/InputHandler.js +0 -1
  125. package/dist-obf/core/registry/ActionRegistry.js +0 -1
  126. package/dist-obf/installer/frameworkLauncher.js +0 -1
  127. package/dist-obf/queue/ExecutionQueue.js +0 -1
  128. package/dist-obf/routes/api.routes.js +0 -1
  129. package/dist-obf/server.js +0 -1
  130. package/dist-obf/types/types.js +0 -1
  131. package/dist-obf/utils/elementHighlight.js +0 -1
  132. package/dist-obf/utils/locatorHelper.js +0 -1
  133. package/dist-obf/utils/logger.js +0 -1
  134. package/dist-obf/utils/response.js +0 -1
  135. package/dist-obf/utils/responseFormatter.js +0 -1
  136. package/dist-obf/utils/sseManager.js +0 -1
@@ -0,0 +1,282 @@
1
+ import { Request, Response } from "express";
2
+ import { executionQueue } from "../queue/ExecutionQueue";
3
+ import { sseManager } from "../utils/sseManager";
4
+ import { formatExecutionResult, formatScriptResult } from "../utils/responseFormatter";
5
+ import { Logger } from "../utils/logger";
6
+
7
+ const log = Logger.create("ApiController");
8
+
9
+ export class ApiController {
10
+
11
+ /**
12
+ * POST /api/run-test
13
+ * ------------------
14
+ * Enqueues execution, returns 202 + executionId immediately.
15
+ * The caller opens the dashboard and subscribes to SSE for live updates.
16
+ * Call GET /api/result/:executionId when done to get the full formatted result.
17
+ */
18
+ static async runTest(req: Request, res: Response): Promise<void> {
19
+ const payload = req.body;
20
+ if (!payload?.scripts?.length) {
21
+ res.status(400).json({ error: "'scripts' array is required." });
22
+ return;
23
+ }
24
+
25
+ // Validate that steps have step_script
26
+ const missingSteps: string[] = [];
27
+ for (const script of payload.scripts) {
28
+ for (const step of (script.steps || [])) {
29
+ if (!step.step_script || step.step_script.trim() === "") {
30
+ missingSteps.push(step.step_name || "unnamed step");
31
+ }
32
+ }
33
+ }
34
+ if (missingSteps.length > 0) {
35
+ res.status(400).json({
36
+ error: "Steps are missing step_script field.",
37
+ detail: `Steps without step_script: ${missingSteps.slice(0, 5).join(", ")}`,
38
+ fix: "Call prepareSteps(uid) in your main app to resolve obj_uid → step_script before sending to the framework.",
39
+ });
40
+ return;
41
+ }
42
+
43
+ const executionId = executionQueue.enqueue(payload);
44
+ const dashboardURL = `http://localhost:${process.env.PORT || 3000}/dashboard/${executionId}`;
45
+ const resultURL = `http://localhost:${process.env.PORT || 3000}/api/result/${executionId}`;
46
+ const streamURL = `http://localhost:${process.env.PORT || 3000}/api/stream/${executionId}`;
47
+ // const videoURL = `http://localhost:${process.env.PORT || 3000}/api/video/${executionId}`;
48
+
49
+ log.info(`Enqueued: ${executionId} | scripts=${payload.scripts.length}`);
50
+
51
+ res.status(202).json({
52
+ executionId,
53
+ dashboardURL,
54
+ resultURL, // Poll this for final result
55
+ streamURL, // Subscribe to this for live events
56
+ // videoURL,
57
+ status: "queued",
58
+ message: "Execution queued. Dashboard opening. Subscribe to streamURL for live updates, or poll resultURL for final result.",
59
+ });
60
+ }
61
+
62
+ /**
63
+ * GET /api/stream/:executionId
64
+ * ----------------------------
65
+ * SSE endpoint — dashboard subscribes here for live step events.
66
+ * Replays all past events immediately for late-connecting clients.
67
+ */
68
+ static stream(req: Request, res: Response): void {
69
+ const { executionId } = req.params;
70
+ sseManager.addClient(executionId, res);
71
+ }
72
+
73
+ /**
74
+ * GET /api/result/:executionId
75
+ * ----------------------------
76
+ * Returns the FULL formatted result once execution is complete.
77
+ * Returns 202 if still running (poll until 200).
78
+ *
79
+ * Response matches the standard format:
80
+ * {
81
+ * "status": "pass|fail",
82
+ * "results": [{ "test_script_uid": "...", "status": "...", "results": [...] }]
83
+ * }
84
+ */
85
+ static getResult(req: Request, res: Response): void {
86
+ const { executionId } = req.params;
87
+ const job = executionQueue.getJob(executionId);
88
+
89
+ if (!job) {
90
+ res.status(404).json({ error: `Execution ${executionId} not found` });
91
+ return;
92
+ }
93
+
94
+ if (job.status === "queued" || job.status === "running") {
95
+ res.status(202).json({
96
+ executionId,
97
+ status: job.status,
98
+ message: "Execution still in progress",
99
+ progress: job.progress,
100
+ });
101
+ return;
102
+ }
103
+
104
+ if (job.status === "cancelled") {
105
+ res.status(200).json({
106
+ executionId,
107
+ status: "cancelled",
108
+ message: "Execution was cancelled",
109
+ });
110
+ return;
111
+ }
112
+
113
+ if (!job.result) {
114
+ res.status(500).json({
115
+ executionId,
116
+ status: "failed",
117
+ error: job.error || "Execution failed with no result",
118
+ });
119
+ return;
120
+ }
121
+
122
+ // Return formatted result matching the standard API format
123
+ res.status(200).json(formatExecutionResult(job.result));
124
+ // console.log(formatExecutionResult(job.result));
125
+ }
126
+
127
+ /**
128
+ * GET /api/status/:executionId
129
+ * ----------------------------
130
+ * Lightweight status check (progress only, no full results).
131
+ */
132
+ static getStatus(req: Request, res: Response): void {
133
+ const { executionId } = req.params;
134
+ const job = executionQueue.getJob(executionId);
135
+
136
+ if (!job) {
137
+ res.status(404).json({ error: `Execution ${executionId} not found` });
138
+ return;
139
+ }
140
+
141
+ res.json({
142
+ executionId: job.executionId,
143
+ status: job.status,
144
+ queuedAt: job.queuedAt,
145
+ startedAt: job.startedAt,
146
+ completedAt: job.completedAt,
147
+ cancelledAt: job.cancelledAt,
148
+ progress: job.progress,
149
+ cancelRequested: job.cancelRequested,
150
+ error: job.error,
151
+ });
152
+ }
153
+
154
+ /**
155
+ * DELETE /api/cancel/:executionId
156
+ */
157
+ static cancelExecution(req: Request, res: Response): void {
158
+ const { executionId } = req.params;
159
+ const result = executionQueue.cancel(executionId);
160
+ res.status(result.success ? 200 : 400).json({ executionId, ...result });
161
+ }
162
+
163
+ /**
164
+ * DELETE /api/cancel-all
165
+ */
166
+ static cancelAll(_req: Request, res: Response): void {
167
+ res.json(executionQueue.cancelAll());
168
+ }
169
+
170
+ /**
171
+ * GET /api/queue
172
+ */
173
+ static getQueue(_req: Request, res: Response): void {
174
+ res.json(executionQueue.getQueueStats());
175
+ }
176
+
177
+ /**
178
+ * GET /api/health
179
+ */
180
+ static health(_req: Request, res: Response): void {
181
+ res.json({
182
+ status: "ok",
183
+ version: "2.0.0",
184
+ ts: new Date().toISOString(),
185
+ queue: executionQueue.getQueueStats(),
186
+ });
187
+ }
188
+
189
+ /**
190
+ * GET /api/actions
191
+ */
192
+ static listActions(_req: Request, res: Response): void {
193
+ res.json({ total: ALL_ACTIONS.length, actions: ALL_ACTIONS });
194
+ }
195
+ }
196
+
197
+ const ALL_ACTIONS = [
198
+ // Browser
199
+ { name: "openBrowser", category: "browser", example: "tSetup.openBrowser('chrome')" },
200
+ { name: "closeBrowser", category: "browser", example: "tSetup.closeBrowser()" },
201
+ { name: "navigateToURL", category: "browser", example: "tSetup.navigateToURL('https://example.com')" },
202
+ { name: "navigateBack", category: "browser", example: "tSetup.navigateBack()" },
203
+ { name: "navigateForward", category: "browser", example: "tSetup.navigateForward()" },
204
+ { name: "refreshPage", category: "browser", example: "tSetup.refreshPage()" },
205
+ { name: "maximizeWindow", category: "browser", example: "tSetup.maximizeWindow()" },
206
+ { name: "setWindowSize", category: "browser", example: "tSetup.setWindowSize('1920', '1080')" },
207
+ { name: "switchToTab", category: "browser", example: "tSetup.switchToTab('1')" },
208
+ { name: "openNewTab", category: "browser", example: "tSetup.openNewTab('https://example.com')" },
209
+ { name: "closeCurrentTab", category: "browser", example: "tSetup.closeCurrentTab()" },
210
+ { name: "acceptAlert", category: "browser", example: "tSetup.acceptAlert()" },
211
+ { name: "dismissAlert", category: "browser", example: "tSetup.dismissAlert()" },
212
+ { name: "getAlertText", category: "browser", example: "tSetup.getAlertText('storeKey')" },
213
+ { name: "switchToFrame", category: "browser", example: "tSetup.switchToFrame('frameName')" },
214
+ { name: "executeScript", category: "browser", example: "tSetup.executeScript('return document.title')" },
215
+ { name: "scrollToBottom", category: "browser", example: "tSetup.scrollToBottom()" },
216
+ { name: "scrollToTop", category: "browser", example: "tSetup.scrollToTop()" },
217
+ { name: "deleteAllCookies", category: "browser", example: "tSetup.deleteAllCookies()" },
218
+ { name: "clearLocalStorage", category: "browser", example: "tSetup.clearLocalStorage()" },
219
+ { name: "clearSessionStorage", category: "browser", example: "tSetup.clearSessionStorage()" },
220
+ { name: "takeScreenshot", category: "browser", example: "tSetup.takeScreenshot()" },
221
+ { name: "getTitle", category: "browser", example: "tSetup.getTitle('storeKey')" },
222
+ { name: "verifyTitle", category: "browser", example: "tSetup.verifyTitle('Expected Title')" },
223
+ { name: "verifyPartialTitle", category: "browser", example: "tSetup.verifyPartialTitle('Expected')" },
224
+ { name: "getCurrentURL", category: "browser", example: "tSetup.getCurrentURL()" },
225
+ // Input
226
+ { name: "enterText", category: "input", example: "tSetup.enterText('xpath', '//input[...]', 'value')" },
227
+ { name: "typeText", category: "input", example: "tSetup.typeText('xpath', '//input[...]', 'value', '50')" },
228
+ { name: "clearText", category: "input", example: "tSetup.clearText('xpath', '//input[...]')" },
229
+ { name: "appendText", category: "input", example: "tSetup.appendText('xpath', '//input[...]', ' extra')" },
230
+ { name: "getInputValue", category: "input", example: "tSetup.getInputValue('xpath', '//input', 'storeKey')" },
231
+ { name: "setInputValue", category: "input", example: "tSetup.setInputValue('xpath', '//input', 'value')" },
232
+ { name: "uploadFile", category: "input", example: "tSetup.uploadFile('xpath', '//input[@type=\"file\"]', '/path/file.pdf')" },
233
+ // Click
234
+ { name: "clickElement", category: "click", example: "tSetup.clickElement('xpath', '//button[...]')" },
235
+ { name: "doubleClick", category: "click", example: "tSetup.doubleClick('xpath', '//div[...]')" },
236
+ { name: "rightClick", category: "click", example: "tSetup.rightClick('xpath', '//div[...]')" },
237
+ { name: "hover", category: "click", example: "tSetup.hover('xpath', '//div[...]')" },
238
+ { name: "clickByJS", category: "click", example: "tSetup.clickByJS('xpath', '//button[...]')" },
239
+ { name: "clickIfPresent", category: "click", example: "tSetup.clickIfPresent('xpath', '//button[...]')" },
240
+ { name: "dragAndDrop", category: "click", example: "tSetup.dragAndDrop('xpath', '//src', 'xpath', '//target')" },
241
+ { name: "clickByText", category: "click", example: "tSetup.clickByText('Sign In')" },
242
+ { name: "clickNthElement", category: "click", example: "tSetup.clickNthElement('xpath', '//li', '2')" },
243
+ { name: "scrollAndClick", category: "click", example: "tSetup.scrollAndClick('xpath', '//button[...]')" },
244
+ // Dropdown
245
+ { name: "selectDropdown", category: "dropdown", example: "tSetup.selectDropdown('xpath', '//select', 'Option Label')" },
246
+ { name: "selectByIndex", category: "dropdown", example: "tSetup.selectByIndex('xpath', '//select', '2')" },
247
+ { name: "selectByValue", category: "dropdown", example: "tSetup.selectByValue('xpath', '//select', 'optValue')" },
248
+ { name: "selectCustomDropdown", category: "dropdown", example: "tSetup.selectCustomDropdown('xpath', '//div.dd', 'xpath', '//li')" },
249
+ { name: "selectAutocomplete", category: "dropdown", example: "tSetup.selectAutocomplete('xpath', '//input', 'New York', 'xpath', '//li')" },
250
+ // Assertion
251
+ { name: "verifyText", category: "assertion", example: "tSetup.verifyText('xpath', '//h1', 'Welcome')" },
252
+ { name: "verifyTextContains", category: "assertion", example: "tSetup.verifyTextContains('xpath', '//p', 'success')" },
253
+ { name: "verifyVisible", category: "assertion", example: "tSetup.verifyVisible('xpath', '//div[...]')" },
254
+ { name: "verifyHidden", category: "assertion", example: "tSetup.verifyHidden('xpath', '//div[...]')" },
255
+ { name: "verifyEnabled", category: "assertion", example: "tSetup.verifyEnabled('xpath', '//button[...]')" },
256
+ { name: "verifyDisabled", category: "assertion", example: "tSetup.verifyDisabled('xpath', '//button[...]')" },
257
+ { name: "verifyURL", category: "assertion", example: "tSetup.verifyURL('https://example.com/dashboard')" },
258
+ { name: "verifyPartialURL", category: "assertion", example: "tSetup.verifyPartialURL('dashboard')" },
259
+ { name: "verifyElementCount", category: "assertion", example: "tSetup.verifyElementCount('xpath', '//li', '5')" },
260
+ { name: "verifyAttribute", category: "assertion", example: "tSetup.verifyAttribute('xpath', '//input', 'placeholder', 'Email')" },
261
+ { name: "getText", category: "assertion", example: "tSetup.getText('xpath', '//h1', 'storeKey')" },
262
+ // Wait
263
+ { name: "waitForElement", category: "wait", example: "tSetup.waitForElement('xpath', '//div', '10000')" },
264
+ { name: "waitForElementHidden", category: "wait", example: "tSetup.waitForElementHidden('xpath', '//div[@id=\"loader\"]', '10000')" },
265
+ { name: "waitForURL", category: "wait", example: "tSetup.waitForURL('dashboard', '10000')" },
266
+ { name: "waitForPageLoad", category: "wait", example: "tSetup.waitForPageLoad('10000')" },
267
+ { name: "waitForNetworkIdle", category: "wait", example: "tSetup.waitForNetworkIdle('10000')" },
268
+ { name: "sleep", category: "wait", example: "tSetup.sleep('2000')" },
269
+ // Keyboard
270
+ { name: "pressKey", category: "keyboard", example: "tSetup.pressKey('Enter')" },
271
+ { name: "pressKeyOn", category: "keyboard", example: "tSetup.pressKeyOn('xpath', '//input', 'Enter')" },
272
+ { name: "keyCombo", category: "keyboard", example: "tSetup.keyCombo('Control+A')" },
273
+ // Checkbox
274
+ { name: "checkCheckbox", category: "checkbox", example: "tSetup.checkCheckbox('xpath', '//input[@type=\"checkbox\"]')" },
275
+ { name: "uncheckCheckbox", category: "checkbox", example: "tSetup.uncheckCheckbox('xpath', '//input[@type=\"checkbox\"]')" },
276
+ { name: "selectRadio", category: "checkbox", example: "tSetup.selectRadio('xpath', '//input[@type=\"radio\"][@value=\"opt1\"]')" },
277
+ // Table
278
+ { name: "getTableRowCount", category: "table", example: "tSetup.getTableRowCount('xpath', '//table/tbody/tr', 'rowCount')" },
279
+ { name: "verifyTableCell", category: "table", example: "tSetup.verifyTableCell('xpath', '//table/tbody/tr[1]/td[2]', 'Expected')" },
280
+ { name: "clickTableRow", category: "table", example: "tSetup.clickTableRow('xpath', '//table/tbody/tr', '3')" },
281
+ { name: "runCustomCode", category: "customcode", example: "tSetup.runCustomCode('const result = \"LN\" + Math.floor(Math.random()*1000);', 'result', '//input[@id=\"ref\"]')" },,
282
+ ];