testdriverai 5.2.2 → 5.3.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.
Files changed (107) hide show
  1. package/.github/workflows/test-install.yml +1 -1
  2. package/README.md +5 -11
  3. package/agent.js +135 -99
  4. package/docs/30x30.mdx +84 -0
  5. package/docs/action/browser.mdx +129 -0
  6. package/docs/action/os.mdx +157 -0
  7. package/docs/action/output.mdx +98 -0
  8. package/docs/action/performance.mdx +71 -0
  9. package/docs/action/prerun.mdx +80 -0
  10. package/docs/action/secrets.mdx +103 -0
  11. package/docs/action/setup.mdx +115 -0
  12. package/docs/bugs/jira.mdx +208 -0
  13. package/docs/cli/overview.mdx +65 -0
  14. package/docs/commands/assert.mdx +31 -0
  15. package/docs/commands/exec.mdx +42 -0
  16. package/docs/commands/focus-application.mdx +29 -0
  17. package/docs/commands/hover-image.mdx +32 -0
  18. package/docs/commands/hover-text.mdx +37 -0
  19. package/docs/commands/if.mdx +43 -0
  20. package/docs/commands/match-image.mdx +41 -0
  21. package/docs/commands/press-keys.mdx +30 -0
  22. package/docs/commands/run.mdx +30 -0
  23. package/docs/commands/scroll-until-image.mdx +33 -0
  24. package/docs/commands/scroll-until-text.mdx +37 -0
  25. package/docs/commands/scroll.mdx +33 -0
  26. package/docs/commands/type.mdx +29 -0
  27. package/docs/commands/wait-for-image.mdx +31 -0
  28. package/docs/commands/wait-for-text.mdx +35 -0
  29. package/docs/commands/wait.mdx +30 -0
  30. package/docs/docs.json +226 -0
  31. package/docs/exporting/playwright.mdx +159 -0
  32. package/docs/features/auto-healing.mdx +124 -0
  33. package/docs/features/cross-platform.mdx +106 -0
  34. package/docs/features/generation.mdx +180 -0
  35. package/docs/features/github.mdx +161 -0
  36. package/docs/features/parallel-testing.mdx +130 -0
  37. package/docs/features/reusable-snippets.mdx +124 -0
  38. package/docs/features/selectorless.mdx +62 -0
  39. package/docs/features/visual-assertions.mdx +123 -0
  40. package/docs/getting-started/ci.mdx +196 -0
  41. package/docs/getting-started/generating.mdx +210 -0
  42. package/docs/getting-started/running.mdx +67 -0
  43. package/docs/getting-started/setup.mdx +133 -0
  44. package/docs/getting-started/writing.mdx +99 -0
  45. package/docs/guide/assertions.mdx +195 -0
  46. package/docs/guide/authentication.mdx +150 -0
  47. package/docs/guide/code.mdx +169 -0
  48. package/docs/guide/locating.mdx +136 -0
  49. package/docs/guide/setup-teardown.mdx +161 -0
  50. package/docs/guide/variables.mdx +218 -0
  51. package/docs/guide/waiting.mdx +199 -0
  52. package/docs/importing/csv.mdx +196 -0
  53. package/docs/importing/gherkin.mdx +142 -0
  54. package/docs/importing/jira.mdx +172 -0
  55. package/docs/importing/testrail.mdx +161 -0
  56. package/docs/integrations/electron.mdx +152 -0
  57. package/docs/integrations/netlify.mdx +98 -0
  58. package/docs/integrations/vercel.mdx +177 -0
  59. package/docs/interactive/assert.mdx +51 -0
  60. package/docs/interactive/generate.mdx +41 -0
  61. package/docs/interactive/run.mdx +36 -0
  62. package/docs/interactive/save.mdx +53 -0
  63. package/docs/interactive/undo.mdx +47 -0
  64. package/docs/issues.mdx +9 -0
  65. package/docs/overview/comparison.mdx +82 -0
  66. package/docs/overview/faq.mdx +122 -0
  67. package/docs/overview/quickstart.mdx +66 -0
  68. package/docs/overview/what-is-testdriver.mdx +73 -0
  69. package/docs/quickstart.mdx +66 -0
  70. package/docs/reference/commands/scroll.mdx +0 -0
  71. package/docs/reference/interactive/assert.mdx +0 -0
  72. package/docs/security/action.mdx +62 -0
  73. package/docs/security/agent.mdx +62 -0
  74. package/docs/security/dashboard.mdx +0 -0
  75. package/docs/security/platform.mdx +54 -0
  76. package/docs/tutorials/advanced-test.mdx +79 -0
  77. package/docs/tutorials/basic-test.mdx +41 -0
  78. package/electron/icon.png +0 -0
  79. package/electron/overlay.html +7 -3
  80. package/electron/overlay.js +75 -15
  81. package/electron/tray-buffered.png +0 -0
  82. package/electron/tray.png +0 -0
  83. package/index.js +75 -34
  84. package/lib/commander.js +22 -1
  85. package/lib/commands.js +87 -19
  86. package/lib/config.js +10 -1
  87. package/lib/focus-application.js +30 -23
  88. package/lib/generator.js +58 -7
  89. package/lib/init.js +48 -19
  90. package/lib/ipc.js +50 -0
  91. package/lib/logger.js +19 -6
  92. package/lib/overlay.js +82 -36
  93. package/lib/parser.js +9 -7
  94. package/lib/resources/prerun.yaml +17 -0
  95. package/lib/sandbox.js +2 -3
  96. package/lib/sdk.js +0 -2
  97. package/lib/session.js +3 -1
  98. package/lib/speak.js +0 -2
  99. package/lib/subimage/opencv.js +0 -4
  100. package/lib/system.js +56 -39
  101. package/lib/upload-secrets.js +65 -0
  102. package/lib/validation.js +175 -0
  103. package/package.json +2 -1
  104. package/postinstall.js +0 -24
  105. package/lib/websockets.js +0 -85
  106. package/test.md +0 -8
  107. package/test.yml +0 -18
package/lib/system.js CHANGED
@@ -19,15 +19,14 @@ if (!config.TD_VM) {
19
19
  }
20
20
 
21
21
  const screenshot = async (options) => {
22
-
23
22
  if (config.TD_VM) {
24
- let {base64} = await sandbox.send({type: 'screenshot'});
25
- let image = Buffer.from(base64, 'base64');
23
+ let { base64 } = await sandbox.send({ type: "screenshot" });
24
+ let image = Buffer.from(base64, "base64");
26
25
  fs.writeFileSync(options.filename, image);
27
26
  } else {
28
27
  return await scshotdesk({ filename: options.filename, format: "png" });
29
28
  }
30
- }
29
+ };
31
30
 
32
31
  let primaryDisplay = null;
33
32
 
@@ -35,26 +34,20 @@ let primaryDisplay = null;
35
34
  // this is the only display we ever target, because fuck it
36
35
  // the vm only has one and most people only have one
37
36
  const getPrimaryDisplay = async () => {
38
-
39
37
  if (!primaryDisplay) {
40
-
41
38
  // calculate scaling resolution
42
39
  let graphics = await si.graphics();
43
- primaryDisplay = graphics.displays.find(
44
- (display) => display.main == true,
45
- );
46
-
40
+ primaryDisplay = graphics.displays.find((display) => display.main == true);
47
41
  }
48
-
42
+
49
43
  return primaryDisplay;
50
-
51
44
  };
52
45
 
53
46
  const getSystemInformationOsInfo = async () => {
54
47
  if (config.TD_VM) {
55
48
  return {
56
- os: 'linux'
57
- }
49
+ os: "linux",
50
+ };
58
51
  } else {
59
52
  return await si.osInfo();
60
53
  }
@@ -68,7 +61,6 @@ const tmpFilename = () => {
68
61
 
69
62
  const captureAndResize = async (scale = 1, silent = false, mouse = false) => {
70
63
  try {
71
-
72
64
  if (!silent) {
73
65
  emitter.emit(events.screenCapture.start, {
74
66
  scale,
@@ -82,35 +74,32 @@ const captureAndResize = async (scale = 1, silent = false, mouse = false) => {
82
74
 
83
75
  await screenshot({ filename: step1, format: "png" });
84
76
 
77
+ // Location of cursor image
78
+ const cursorPath = path.join(__dirname, "resources", "cursor.png");
79
+
85
80
  let sharpInstance;
86
- if (config.TD_VM) {
81
+ const mousePos = await getMousePosition();
87
82
 
83
+ if (config.TD_VM) {
88
84
  // resize to 1:1 px ratio
89
85
  sharpInstance = sharp(step1).resize(
90
- Math.floor(1024 * scale),
91
- Math.floor(768 * scale),
86
+ Math.floor(config.TD_VM_RESOLUTION[0] * scale),
87
+ Math.floor(config.TD_VM_RESOLUTION[1] * scale),
92
88
  );
93
-
94
89
  } else {
95
-
96
90
  const primaryDisplay = await getPrimaryDisplay();
97
-
91
+
98
92
  sharpInstance = sharp(step1).resize(
99
93
  Math.floor(primaryDisplay.currentResX * scale),
100
94
  Math.floor(primaryDisplay.currentResY * scale),
101
95
  );
96
+ }
102
97
 
103
- // Fetch the mouse position
104
- const mousePos = robot.getMousePos();
105
-
106
- // Location of cursor image
107
- const cursorPath = path.join(__dirname, "resources", "cursor.png");
108
-
109
- if (mouse) {
110
- // composite the mouse image ontop
111
- sharpInstance.composite([{ input: cursorPath, left: mousePos.x, top: mousePos.y }]);
112
- }
113
-
98
+ if (mouse) {
99
+ // composite the mouse image ontop
100
+ sharpInstance.composite([
101
+ { input: cursorPath, left: mousePos.x, top: mousePos.y },
102
+ ]);
114
103
  }
115
104
 
116
105
  await sharpInstance.toFile(step2);
@@ -134,7 +123,11 @@ const captureAndResize = async (scale = 1, silent = false, mouse = false) => {
134
123
  };
135
124
 
136
125
  // our handy screenshot function
137
- const captureScreenBase64 = async (scale = 1, silent = false, mouse = false) => {
126
+ const captureScreenBase64 = async (
127
+ scale = 1,
128
+ silent = false,
129
+ mouse = false,
130
+ ) => {
138
131
  let step2 = await captureAndResize(scale, silent, mouse);
139
132
  return fs.readFileSync(step2, "base64");
140
133
  };
@@ -145,6 +138,11 @@ const captureScreenPNG = async (scale = 1, silent = false, mouse = false) => {
145
138
 
146
139
  const platform = () => {
147
140
  let platform = process.platform;
141
+
142
+ if (config.TD_VM) {
143
+ platform = "linux";
144
+ }
145
+
148
146
  if (platform === "darwin") {
149
147
  platform = "mac";
150
148
  } else if (platform === "win32") {
@@ -161,7 +159,10 @@ const platform = () => {
161
159
  let activeWindowFn = null;
162
160
  const initializeActiveWindow = async () => {
163
161
  if (!activeWindowFn && !config.TD_VM) {
164
- const { activeWindow } = await import('get-windows');
162
+ // For some reason, the import fails in certain situations
163
+ const activeWindow = await import("get-windows")
164
+ .then(({ activeWindow }) => activeWindow)
165
+ .catch(() => null);
165
166
  activeWindowFn = activeWindow;
166
167
  }
167
168
  return activeWindowFn;
@@ -169,18 +170,34 @@ const initializeActiveWindow = async () => {
169
170
 
170
171
  // this is the focused window
171
172
  const activeWin = async () => {
172
-
173
- if (config.TD_VM) {
173
+ if (config.TD_VM) {
174
174
  return "error getting active window, proceed normally";
175
175
  } else {
176
176
  const activeWindow = await initializeActiveWindow();
177
- return await activeWindow();
177
+ return await activeWindow?.();
178
178
  }
179
179
  };
180
180
 
181
181
  const getMousePosition = async () => {
182
- // @todo vm does expose mouse position, add it back
183
- return !config.TD_VM ? await robot.getMousePos() : null;
182
+ if (config.TD_VM) {
183
+ // Get Mouse Position from command line
184
+ let response = await sandbox.send({
185
+ type: "commands.run",
186
+ command:
187
+ 'eval $(xdotool getmouselocation --shell) && echo "{\\"x\\":$X,\\"y\\":$Y}"',
188
+ });
189
+
190
+ if (response.out.stderr) {
191
+ return {
192
+ x: 0,
193
+ y: 0,
194
+ };
195
+ }
196
+
197
+ return JSON.parse(response.out.stdout);
198
+ } else {
199
+ return await robot.getMousePos();
200
+ }
184
201
  };
185
202
 
186
203
  module.exports = {
@@ -0,0 +1,65 @@
1
+ const fs = require('fs');
2
+ const { execSync } = require('child_process');
3
+ const dotenv = require('dotenv');
4
+ const prompts = require('prompts');
5
+
6
+ module.exports = async () => {
7
+
8
+ // 1. Check for GitHub CLI
9
+ try {
10
+ execSync('gh --version', { stdio: 'ignore' });
11
+ } catch (err) {
12
+ console.log(err)
13
+ console.error('❌ GitHub CLI (gh) is not installed.\n');
14
+ console.log('👉 Install it from https://cli.github.com/');
15
+ console.log(' macOS: brew install gh');
16
+ console.log(' Windows: winget install GitHub.cli');
17
+ console.log(' Ubuntu: sudo apt install gh\n');
18
+ process.exit(1);
19
+ }
20
+
21
+ // 2. Load and parse .env file
22
+ const envPath = '.env';
23
+ if (!fs.existsSync(envPath)) {
24
+ console.error('❌ .env file not found.');
25
+ process.exit(1);
26
+ }
27
+
28
+ const env = dotenv.parse(fs.readFileSync(envPath));
29
+ const tdSecrets = Object.entries(env).filter(([key]) => key.startsWith('TD_'));
30
+
31
+ if (tdSecrets.length === 0) {
32
+ console.log('ℹ️ No secrets found in .env that start with TD_');
33
+ process.exit(0);
34
+ }
35
+
36
+ // 3. Prompt user for confirmation
37
+ (async () => {
38
+ console.log('\n🔐 The following TD_ secrets will be uploaded:');
39
+ tdSecrets.forEach(([key]) => console.log(`- ${key}`));
40
+
41
+ const response = await prompts({
42
+ type: 'confirm',
43
+ name: 'confirm',
44
+ message: '\nAre you sure you want to upload these secrets to the current GitHub repo?',
45
+ initial: false,
46
+ });
47
+
48
+ if (!response.confirm) {
49
+ console.log('❌ Upload cancelled.');
50
+ process.exit(0);
51
+ }
52
+
53
+ // 4. Upload secrets using GitHub CLI
54
+ for (const [key, value] of tdSecrets) {
55
+ try {
56
+ const cmd = `gh secret set ${key} --body "${value}"`;
57
+ execSync(cmd, { stdio: 'inherit' });
58
+ } catch (err) {
59
+ console.error(`❌ Failed to upload ${key}: ${err.message}`);
60
+ }
61
+ }
62
+
63
+ })();
64
+
65
+ }
@@ -0,0 +1,175 @@
1
+ const types = () =>
2
+ import("arktype").then(({ scope }) =>
3
+ scope({
4
+ // - command: press-keys # Types a keyboard combination. Repeat the command to repeat the keypress.
5
+ // keys: [command, space]
6
+ PressKeysCommand: {
7
+ command: '"press-keys"',
8
+ keys: "string[]",
9
+ },
10
+ // - command: hover-text # Hovers text matching the \`description\`. The text must be visible. This will also handle clicking or right clicking on the text if required.
11
+ // text: Sign Up # The text to find on screen. The longer and more unique the better.
12
+ // description: registration in the top right of the header # Describe the element so it can be identified in the future. Do not include the text itself here. Make sure to include the unique traits of this element.
13
+ // action: click # What to do when text is found. Available actions are: click, right-click, double-click, hover
14
+ // method: ai # Optional. Only try this if text match is not working.
15
+ HoverTextCommand: {
16
+ command: '"hover-text"',
17
+ text: "string",
18
+ description: "string",
19
+ action: '"click" | "right-click" | "double-click" | "hover"',
20
+ "method?": '"ai"',
21
+ },
22
+ // - command: type # Types the string into the active application. You must focus the correct field before typing.
23
+ // text: Hello World
24
+ TypeCommand: {
25
+ command: '"type"',
26
+ text: "string",
27
+ },
28
+ // - command: wait # Waits a number of miliseconds before continuing.
29
+ // timeout: 5000
30
+ WaitCommand: {
31
+ command: '"wait"',
32
+ timeout: "number",
33
+ },
34
+ // - command: hover-image # Hovers an icon, button, or image matching \`description\`. This will also handle handle clicking or right clicking on the icon or image if required.
35
+ // description: search icon in the webpage content # Describe the icon or image and what it represents. Describe the element so it can be identified in the future. Do not include the image or icon itself here. Make sure to include the unique traits of this element.
36
+ // action: click # What to do when text is found. Available actions are: click, right-click, double-click, hover
37
+ HoverImageCommand: {
38
+ command: '"hover-image"',
39
+ description: "string",
40
+ action: '"click" | "right-click" | "double-click" | "hover"',
41
+ },
42
+ // - command: focus-application # Focus an application by name.
43
+ // name: Google Chrome # The name of the application to focus.
44
+ FocusApplicationCommand: {
45
+ command: '"focus-application"',
46
+ name: "string",
47
+ },
48
+ // - command: remember # Remember a string value without needing to interact with the desktop.
49
+ // description: My dog's name # The key of the memory value to store.
50
+ // value: Roofus # The value of the memory to store
51
+ RememberCommand: {
52
+ command: '"remember"',
53
+ description: "string",
54
+ value: "string",
55
+ },
56
+ // - command: get-email-url # Retrieves the URL from a sign-up confirmation email in the background.
57
+ // # This retrieves an email confirmation URL without opening an email client. Do not view the screen, just run this command when dealing with emails
58
+ // username: testdriver # The username of the email address to check.
59
+ GetEmailUrlCommand: {
60
+ command: '"get-email-url"',
61
+ username: "string",
62
+ },
63
+ // - command: scroll # Scroll up or down. Make sure the correct portion of the page is focused before scrolling.
64
+ // direction: down # Available directions are: up, down, left, right
65
+ // method: keyboard # Optional. Available methods are: keyboard (default), mouse. Use mouse only if the prompt explicitly asks for it.
66
+ // amount: 300 # Optional. The amount of pixels to scroll. Defaults to 300 for keyboard and 200 for mouse.
67
+ ScrollCommand: {
68
+ command: '"scroll"',
69
+ direction: '"up" | "down" | "left" | "right"',
70
+ "method?": '"keyboard" | "mouse"',
71
+ "amount?": "number",
72
+ },
73
+ // - command: scroll-until-text # Scroll until text is found
74
+ // text: Sign Up # The text to find on screen. The longer and more unique the better.
75
+ // direction: down # Available directions are: up, down, left, right
76
+ // method: keyboard # Optional. Available methods are: keyboard (default), mouse. Use mouse only if the prompt explicitly asks for it.
77
+ ScrollUntilTextCommand: {
78
+ command: '"scroll-until-text"',
79
+ text: "string",
80
+ direction: '"up" | "down" | "left" | "right"',
81
+ "method?": '"keyboard" | "mouse"',
82
+ },
83
+ // - command: scroll-until-image # Scroll until icon or image is found
84
+ // description: Submit at the bottom of the form
85
+ // direction: down # Available directions are: up, down, left, rights
86
+ // method: keyboard # Optional. Available methods are: keyboard (default), mouse. Use mouse only if the prompt explicitly asks for it.
87
+ ScrollUntilImageCommand: {
88
+ command: '"scroll-until-image"',
89
+ description: "string",
90
+ direction: '"up" | "down" | "left" | "right"',
91
+ "method?": '"keyboard" | "mouse"',
92
+ },
93
+ // - command: wait-for-text # Wait until text is seen on screen. Not recommended unless explicitly requested by user.
94
+ // text: Copyright 2024 # The text to find on screen.
95
+ WaitForTextCommand: {
96
+ command: '"wait-for-text"',
97
+ text: "string",
98
+ },
99
+ // - command: wait-for-image # Wait until icon or image is seen on screen. Not recommended unless explicitly requested by user.
100
+ // description: trash icon
101
+ WaitForImageCommand: {
102
+ command: '"wait-for-image"',
103
+ description: "string",
104
+ },
105
+ // - command: assert # Assert that a condition is true. This is used to validate that a task was successful. Only use this when the user asks to "assert", "check," or "make sure" of something.
106
+ // expect: the video is playing # The condition to check. This should be a string that describes what you see on screen.
107
+ AssertCommand: {
108
+ command: '"assert"',
109
+ expect: "string",
110
+ },
111
+ // - command: if # Conditional block. If the condition is true, run the commands in the block. Otherwise, run the commands in the else block. Only use this if the user explicitly asks for a condition.
112
+ // condition: the active window is "Google Chrome"
113
+ // then:
114
+ // - command: hover-text
115
+ // text: Search Google or type a URL
116
+ // description: main google search
117
+ // action: click
118
+ // - command: type
119
+ // text: monster trucks
120
+ // description: search for monster trucks
121
+ // else:
122
+ // - command: focus-application
123
+ // name: Google Chrome
124
+ IfCommand: {
125
+ command: '"if"',
126
+ condition: "string",
127
+ // The unknown[] instead of Command[] is because of a weird error, although it does work
128
+ // on the other typescript repo
129
+ then: "unknown[]",
130
+ "else?": "unknown[]",
131
+ },
132
+
133
+ Command:
134
+ "PressKeysCommand | HoverTextCommand | TypeCommand | WaitCommand | HoverImageCommand | FocusApplicationCommand | RememberCommand | GetEmailUrlCommand | ScrollCommand | ScrollUntilTextCommand | ScrollUntilImageCommand | WaitForTextCommand | WaitForImageCommand | AssertCommand | IfCommand",
135
+
136
+ CommandList: "Command[]",
137
+
138
+ Step: {
139
+ prompt: "string",
140
+ commands: "Command[]",
141
+ },
142
+
143
+ StepList: "Step[]",
144
+
145
+ File: {
146
+ version: "string",
147
+ session: "string | undefined",
148
+ steps: "Step[]",
149
+ },
150
+ }).export(),
151
+ );
152
+
153
+ /**
154
+ *
155
+ * @param {unknown} data
156
+ */
157
+ const getType = async (data) => {
158
+ const { type } = await import("arktype");
159
+ const t = await types();
160
+
161
+ /**
162
+ * @type {["File", "Step", "StepList", "Command", "CommandList"]}
163
+ */
164
+ const options = ["File", "Step", "StepList", "Command", "CommandList"];
165
+
166
+ for (const option of options) {
167
+ const result = t[option](data);
168
+ if (!(result instanceof type.errors)) {
169
+ return option;
170
+ }
171
+ }
172
+ return null;
173
+ };
174
+
175
+ module.exports = { getType };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "5.2.2",
3
+ "version": "5.3.0",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -18,6 +18,7 @@
18
18
  "dependencies": {
19
19
  "@e2b/desktop": "^1.6.0",
20
20
  "@electerm/strip-ansi": "^1.0.0",
21
+ "arktype": "^2.1.19",
21
22
  "axios": "^1.7.7",
22
23
  "chalk": "^4.1.2",
23
24
  "cli-progress": "^3.12.0",
package/postinstall.js CHANGED
@@ -1,7 +1,5 @@
1
1
  let platform = require("os").platform();
2
2
  let exec = require("child_process").exec;
3
- const { execSync } = require("child_process");
4
- const readline = require("readline");
5
3
 
6
4
  if (platform !== "darwin") {
7
5
  console.log("TestDriver Setup: Skipping codesign becasue not on Mac");
@@ -20,25 +18,3 @@ exec(signScript, (error, stdout, stderr) => {
20
18
  console.log(`stdout: ${stdout}`);
21
19
  console.error(`stderr: ${stderr}`);
22
20
  });
23
-
24
- const rl = readline.createInterface({
25
- input: process.stdin,
26
- output: process.stdout
27
- });
28
-
29
- rl.question("Do you want to install Vue support? (y/n) ", (answer) => {
30
- if (answer.toLowerCase() === "y") {
31
- console.log("Installing Vue dependencies...");
32
- execSync("npm install -g vue vue-router", { stdio: "inherit" });
33
- }
34
-
35
- rl.question("Do you want to install React support? (y/n) ", (answer2) => {
36
- if (answer2.toLowerCase() === "y") {
37
- console.log("Installing React dependencies...");
38
- execSync("npm install -g react react-dom", { stdio: "inherit" });
39
- }
40
-
41
- console.log("Setup complete!");
42
- rl.close();
43
- });
44
- });
package/lib/websockets.js DELETED
@@ -1,85 +0,0 @@
1
- const WebSocket = require('ws');
2
- const config = require('./config');
3
-
4
- let instance;
5
- class WebSocketServerSingleton {
6
- constructor() {
7
-
8
- if (!WebSocketServerSingleton.instance) {
9
- this.wss = new WebSocket.Server({ port: 8080 }, () => {
10
- });
11
-
12
- this.clients = new Set();
13
- this.eventListeners = new Map();
14
-
15
- this.wss.on('error', (error) => {
16
- console.error('WebSocket server error:', error);
17
- });
18
-
19
- this.wss.on('connection', (ws) => {
20
-
21
- console.log('Client connected');
22
- this.clients.add(ws);
23
-
24
- ws.on('message', (message) => {
25
- try {
26
- const parsedMessage = JSON.parse(message);
27
- if (parsedMessage.event) {
28
- this.triggerEvent(parsedMessage.event, parsedMessage);
29
- } else {
30
- console.error('Parsed message does not contain an event property');
31
- }
32
- } catch (error) {
33
- console.error('Error parsing message:', error);
34
- }
35
- });
36
-
37
- ws.on('close', () => {
38
- this.clients.delete(ws);
39
- console.log('Client disconnected');
40
- });
41
-
42
- ws.on('error', (error) => {
43
- console.error('WebSocket error:', error);
44
- });
45
- });
46
-
47
- WebSocketServerSingleton.instance = this;
48
- }
49
-
50
- return WebSocketServerSingleton.instance;
51
- }
52
-
53
- addEventListener(event, listener) {
54
- if (!this.eventListeners.has(event)) {
55
- this.eventListeners.set(event, []);
56
- }
57
- this.eventListeners.get(event).push(listener);
58
- }
59
-
60
- triggerEvent(event, data) {
61
- if (this.eventListeners.has(event)) {
62
- for (const listener of this.eventListeners.get(event)) {
63
- listener(data);
64
- }
65
- }
66
- }
67
-
68
- sendToClients(event, message) {
69
- const data = JSON.stringify({ event, message });
70
- for (const client of this.clients) {
71
- if (client.readyState === WebSocket.OPEN) {
72
- client.send(data);
73
- }
74
- }
75
- }
76
- }
77
-
78
-
79
- module.exports = {
80
- create: () => {
81
- instance = new WebSocketServerSingleton();
82
- Object.freeze(instance);
83
- return instance;
84
- }
85
- };
package/test.md DELETED
@@ -1,8 +0,0 @@
1
- # Easy Math
2
-
3
- 2 + 2 = 4 // this test will pass
4
- 2 + 2 = 5 // this test will fail
5
-
6
- # Harder Math
7
-
8
- 230230 + 5819123 = 6049353
package/test.yml DELETED
@@ -1,18 +0,0 @@
1
- version: 4.1.40
2
- steps:
3
- - prompt: Enter Password n stuff
4
- commands:
5
- - command: focus-application
6
- name: Google Chrome
7
- - command: hover-text
8
- text: Username
9
- description: username field
10
- action: click
11
- - command: type
12
- text: ${TD_PASSWORD}
13
- - command: hover-text
14
- text: Password
15
- description: Password field
16
- action: click
17
- - command: type
18
- text: ${TD_PASSWORD}