testchimp-runner-core 0.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 (74) hide show
  1. package/dist/auth-config.d.ts +33 -0
  2. package/dist/auth-config.d.ts.map +1 -0
  3. package/dist/auth-config.js +69 -0
  4. package/dist/auth-config.js.map +1 -0
  5. package/dist/env-loader.d.ts +20 -0
  6. package/dist/env-loader.d.ts.map +1 -0
  7. package/dist/env-loader.js +83 -0
  8. package/dist/env-loader.js.map +1 -0
  9. package/dist/execution-service.d.ts +61 -0
  10. package/dist/execution-service.d.ts.map +1 -0
  11. package/dist/execution-service.js +822 -0
  12. package/dist/execution-service.js.map +1 -0
  13. package/dist/file-handler.d.ts +59 -0
  14. package/dist/file-handler.d.ts.map +1 -0
  15. package/dist/file-handler.js +75 -0
  16. package/dist/file-handler.js.map +1 -0
  17. package/dist/index.d.ts +46 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +196 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/llm-facade.d.ts +101 -0
  22. package/dist/llm-facade.d.ts.map +1 -0
  23. package/dist/llm-facade.js +289 -0
  24. package/dist/llm-facade.js.map +1 -0
  25. package/dist/playwright-mcp-service.d.ts +42 -0
  26. package/dist/playwright-mcp-service.d.ts.map +1 -0
  27. package/dist/playwright-mcp-service.js +167 -0
  28. package/dist/playwright-mcp-service.js.map +1 -0
  29. package/dist/prompts.d.ts +34 -0
  30. package/dist/prompts.d.ts.map +1 -0
  31. package/dist/prompts.js +237 -0
  32. package/dist/prompts.js.map +1 -0
  33. package/dist/scenario-service.d.ts +25 -0
  34. package/dist/scenario-service.d.ts.map +1 -0
  35. package/dist/scenario-service.js +119 -0
  36. package/dist/scenario-service.js.map +1 -0
  37. package/dist/scenario-worker-class.d.ts +30 -0
  38. package/dist/scenario-worker-class.d.ts.map +1 -0
  39. package/dist/scenario-worker-class.js +263 -0
  40. package/dist/scenario-worker-class.js.map +1 -0
  41. package/dist/script-utils.d.ts +44 -0
  42. package/dist/script-utils.d.ts.map +1 -0
  43. package/dist/script-utils.js +100 -0
  44. package/dist/script-utils.js.map +1 -0
  45. package/dist/types.d.ts +171 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +28 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/utils/browser-utils.d.ts +13 -0
  50. package/dist/utils/browser-utils.d.ts.map +1 -0
  51. package/dist/utils/browser-utils.js +269 -0
  52. package/dist/utils/browser-utils.js.map +1 -0
  53. package/dist/utils/page-info-utils.d.ts +16 -0
  54. package/dist/utils/page-info-utils.d.ts.map +1 -0
  55. package/dist/utils/page-info-utils.js +77 -0
  56. package/dist/utils/page-info-utils.js.map +1 -0
  57. package/env.prod +1 -0
  58. package/env.staging +1 -0
  59. package/package.json +38 -0
  60. package/src/auth-config.ts +84 -0
  61. package/src/env-loader.ts +91 -0
  62. package/src/execution-service.ts +999 -0
  63. package/src/file-handler.ts +104 -0
  64. package/src/index.ts +205 -0
  65. package/src/llm-facade.ts +413 -0
  66. package/src/playwright-mcp-service.ts +203 -0
  67. package/src/prompts.ts +247 -0
  68. package/src/scenario-service.ts +138 -0
  69. package/src/scenario-worker-class.ts +330 -0
  70. package/src/script-utils.ts +109 -0
  71. package/src/types.ts +202 -0
  72. package/src/utils/browser-utils.ts +272 -0
  73. package/src/utils/page-info-utils.ts +93 -0
  74. package/tsconfig.json +19 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * File Handler Interface
3
+ * Defines how different platforms handle file operations
4
+ */
5
+
6
+ export interface FileHandler {
7
+ /**
8
+ * Read test file content
9
+ * @param filePath Path to the test file
10
+ * @returns File content as string
11
+ */
12
+ readTestFile(filePath: string): Promise<string>;
13
+
14
+ /**
15
+ * Resolve a relative path to an absolute path based on the current working directory
16
+ * @param relativePath Relative path to resolve
17
+ * @returns Absolute path
18
+ */
19
+ resolvePath(relativePath: string): string;
20
+
21
+ /**
22
+ * Check if a file exists
23
+ * @param filePath Path to check
24
+ * @returns True if file exists
25
+ */
26
+ fileExists(filePath: string): Promise<boolean>;
27
+ }
28
+
29
+ /**
30
+ * Local File Handler - Direct file system operations
31
+ * Used by VS Code extension for local development
32
+ */
33
+ export class LocalFileHandler implements FileHandler {
34
+ private fs = require('fs');
35
+ private path = require('path');
36
+ private basePath: string;
37
+
38
+ constructor(basePath?: string) {
39
+ this.basePath = basePath || process.cwd();
40
+ }
41
+
42
+ async readTestFile(filePath: string): Promise<string> {
43
+ return this.fs.readFileSync(filePath, 'utf8');
44
+ }
45
+
46
+ resolvePath(relativePath: string): string {
47
+ return this.path.resolve(this.basePath, relativePath);
48
+ }
49
+
50
+ async fileExists(filePath: string): Promise<boolean> {
51
+ try {
52
+ return this.fs.existsSync(filePath);
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * CI/CD File Handler - For CI/CD environments
61
+ * Used by GitHub Actions for CI/CD environments
62
+ */
63
+ export class CIFileHandler implements FileHandler {
64
+ private fs = require('fs');
65
+ private path = require('path');
66
+ private basePath: string;
67
+
68
+ constructor(basePath?: string) {
69
+ this.basePath = basePath || process.cwd();
70
+ }
71
+
72
+ async readTestFile(filePath: string): Promise<string> {
73
+ return this.fs.readFileSync(filePath, 'utf8');
74
+ }
75
+
76
+ resolvePath(relativePath: string): string {
77
+ return this.path.resolve(this.basePath, relativePath);
78
+ }
79
+
80
+ async fileExists(filePath: string): Promise<boolean> {
81
+ try {
82
+ return this.fs.existsSync(filePath);
83
+ } catch {
84
+ return false;
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * No-op File Handler - For testing or when file operations are not needed
91
+ */
92
+ export class NoOpFileHandler implements FileHandler {
93
+ async readTestFile(filePath: string): Promise<string> {
94
+ return '';
95
+ }
96
+
97
+ resolvePath(relativePath: string): string {
98
+ return relativePath;
99
+ }
100
+
101
+ async fileExists(filePath: string): Promise<boolean> {
102
+ return false;
103
+ }
104
+ }
package/src/index.ts ADDED
@@ -0,0 +1,205 @@
1
+ /**
2
+ * TestChimp Runner Core
3
+ * Shared functionality for VS Code extension and GitHub Actions
4
+ */
5
+
6
+ // Core services
7
+ import { ExecutionService } from './execution-service';
8
+ import { ScenarioService } from './scenario-service';
9
+ import { ScenarioWorker } from './scenario-worker-class';
10
+ import { PlaywrightMCPService } from './playwright-mcp-service';
11
+ import { LLMFacade } from './llm-facade';
12
+ import { AuthConfig } from './auth-config';
13
+
14
+ export { ExecutionService, ScenarioService, ScenarioWorker, PlaywrightMCPService, LLMFacade };
15
+
16
+ // File handlers
17
+ import { FileHandler, LocalFileHandler, CIFileHandler, NoOpFileHandler } from './file-handler';
18
+ export { FileHandler, LocalFileHandler, CIFileHandler, NoOpFileHandler };
19
+
20
+ // Types
21
+ export * from './types';
22
+
23
+ // Authentication
24
+ export * from './auth-config';
25
+
26
+ // Environment configuration
27
+ export { loadEnvConfig } from './env-loader';
28
+
29
+ // Script utilities
30
+ export * from './script-utils';
31
+
32
+ // Main TestChimp service class
33
+ export class TestChimpService {
34
+ private executionService: ExecutionService;
35
+ public scenarioService: ScenarioService; // Make public for event listening
36
+ private playwrightService: PlaywrightMCPService;
37
+ private llmFacade: LLMFacade;
38
+ private fileHandler: FileHandler;
39
+ private authConfig: AuthConfig | null;
40
+ private backendUrl: string;
41
+
42
+ constructor(fileHandler?: FileHandler, authConfig?: AuthConfig, backendUrl?: string) {
43
+ this.fileHandler = fileHandler || new NoOpFileHandler();
44
+ this.authConfig = authConfig || null;
45
+ this.backendUrl = backendUrl || 'https://featureservice.testchimp.io'; // Default to production
46
+ this.playwrightService = new PlaywrightMCPService();
47
+ this.llmFacade = new LLMFacade(this.authConfig || undefined, this.backendUrl);
48
+ this.executionService = new ExecutionService(this.authConfig || undefined);
49
+ this.scenarioService = new ScenarioService(2, this.fileHandler, this.authConfig || undefined);
50
+ }
51
+
52
+ /**
53
+ * Set authentication configuration for the service
54
+ */
55
+ async setAuthConfig(authConfig: AuthConfig): Promise<void> {
56
+ this.authConfig = authConfig;
57
+ this.llmFacade.setAuthConfig(authConfig);
58
+
59
+ // Recreate services with new auth config to ensure all workers get the updated config
60
+ this.executionService = new ExecutionService(this.authConfig);
61
+ this.scenarioService = new ScenarioService(2, this.fileHandler, this.authConfig);
62
+
63
+ // Reinitialize the services
64
+ await this.executionService.initialize();
65
+ await this.scenarioService.initialize();
66
+ }
67
+
68
+ /**
69
+ * Set backend URL for the service
70
+ */
71
+ setBackendUrl(backendUrl: string): void {
72
+ this.backendUrl = backendUrl;
73
+ // Recreate LLM facade with new backend URL
74
+ this.llmFacade = new LLMFacade(this.authConfig || undefined, this.backendUrl);
75
+ }
76
+
77
+ /**
78
+ * Get current authentication configuration
79
+ */
80
+ getAuthConfig(): AuthConfig | null {
81
+ return this.authConfig;
82
+ }
83
+
84
+ async initialize(): Promise<void> {
85
+ await this.playwrightService.initialize();
86
+ await this.executionService.initialize();
87
+ await this.scenarioService.initialize();
88
+ }
89
+
90
+ async shutdown(): Promise<void> {
91
+ // Cleanup resources
92
+ }
93
+
94
+ // Scenario generation
95
+ async generateScript(scenario: string, testName?: string, config?: string, model?: string, scenarioFileName?: string): Promise<string> {
96
+ return this.scenarioService.processScenario(scenario, testName, config, model, scenarioFileName);
97
+ }
98
+
99
+ // Test execution
100
+ async executeScript(request: any): Promise<any> {
101
+ // Read script content if not provided but scriptFilePath is
102
+ if (!request.script && request.scriptFilePath) {
103
+ try {
104
+ const resolvedPath = this.fileHandler.resolvePath(request.scriptFilePath);
105
+ request.script = await this.fileHandler.readTestFile(resolvedPath);
106
+ console.log(`Read script content from file: ${resolvedPath}`);
107
+ } catch (error) {
108
+ throw new Error(`Failed to read script file: ${error}`);
109
+ }
110
+ }
111
+
112
+ // Read Playwright config content if not provided but playwrightConfigFilePath is
113
+ if (!request.playwrightConfig && request.playwrightConfigFilePath) {
114
+ try {
115
+ const resolvedPath = this.fileHandler.resolvePath(request.playwrightConfigFilePath);
116
+ request.playwrightConfig = await this.fileHandler.readTestFile(resolvedPath);
117
+ console.log(`Read Playwright config content from file: ${resolvedPath}`);
118
+ } catch (error) {
119
+ console.warn(`Failed to read Playwright config file: ${error}. Using default configuration.`);
120
+ // Don't throw error, just use default config
121
+ }
122
+ }
123
+
124
+ // Log content status
125
+ if (request.script) {
126
+ console.log(`Using provided script content (${request.script.length} characters)`);
127
+ } else {
128
+ throw new Error('Script content is required. Provide either script or scriptFilePath.');
129
+ }
130
+
131
+ if (request.playwrightConfig) {
132
+ console.log(`Using provided Playwright config (${request.playwrightConfig.length} characters)`);
133
+ } else {
134
+ console.log(`Using default Playwright configuration`);
135
+ }
136
+
137
+ const result = await this.executionService.executeScript(request);
138
+
139
+ // Return result - consumer decides what to do with updated_script
140
+ return result;
141
+ }
142
+
143
+ // Test execution with AI repair
144
+ async executeScriptWithAIRepair(request: any): Promise<any> {
145
+ const repairRequest = { ...request, mode: 'RUN_WITH_AI_REPAIR' };
146
+ return this.executeScript(repairRequest);
147
+ }
148
+
149
+ // Find TestChimp managed tests
150
+ findTestChimpTests(directory: string, recursive: boolean = true): string[] {
151
+ const fs = require('fs');
152
+ const path = require('path');
153
+
154
+ const testFiles: string[] = [];
155
+
156
+ function scanDir(dir: string) {
157
+ try {
158
+ const items = fs.readdirSync(dir);
159
+
160
+ for (const item of items) {
161
+ const fullPath = path.join(dir, item);
162
+ const stat = fs.statSync(fullPath);
163
+
164
+ if (stat.isDirectory() && recursive) {
165
+ scanDir(fullPath);
166
+ } else if (stat.isFile() && isTestChimpManaged(fullPath)) {
167
+ testFiles.push(fullPath);
168
+ }
169
+ }
170
+ } catch (error) {
171
+ // Skip directories that can't be read
172
+ }
173
+ }
174
+
175
+ function isTestChimpManaged(filePath: string): boolean {
176
+ // Check if file is a test file
177
+ if (!filePath.match(/\.(spec|test)\.(js|ts)$/)) {
178
+ return false;
179
+ }
180
+
181
+ try {
182
+ // Read file content
183
+ const content = fs.readFileSync(filePath, 'utf8');
184
+
185
+ // Check for TestChimp markers
186
+ const testChimpMarkers = [
187
+ /\/\/ TestChimp:.*step/i,
188
+ /\/\* TestChimp:.*step \*\//i,
189
+ /\/\/ Step \d+:/i,
190
+ /\/\* Step \d+: \*\//i,
191
+ /testchimp.*step/i,
192
+ /\/\/ AI.*repair/i,
193
+ /\/\* AI.*repair \*\//i
194
+ ];
195
+
196
+ return testChimpMarkers.some(marker => marker.test(content));
197
+ } catch (error) {
198
+ return false;
199
+ }
200
+ }
201
+
202
+ scanDir(directory);
203
+ return testFiles;
204
+ }
205
+ }