testblocks 0.1.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.
- package/LICENSE +21 -0
- package/README.md +333 -0
- package/dist/cli/executor.d.ts +32 -0
- package/dist/cli/executor.js +517 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +411 -0
- package/dist/cli/reporters.d.ts +62 -0
- package/dist/cli/reporters.js +451 -0
- package/dist/client/assets/index-4hbFPUhP.js +2087 -0
- package/dist/client/assets/index-4hbFPUhP.js.map +1 -0
- package/dist/client/assets/index-Dnk1ti7l.css +1 -0
- package/dist/client/index.html +25 -0
- package/dist/core/blocks/api.d.ts +2 -0
- package/dist/core/blocks/api.js +610 -0
- package/dist/core/blocks/data-driven.d.ts +2 -0
- package/dist/core/blocks/data-driven.js +245 -0
- package/dist/core/blocks/index.d.ts +15 -0
- package/dist/core/blocks/index.js +71 -0
- package/dist/core/blocks/lifecycle.d.ts +2 -0
- package/dist/core/blocks/lifecycle.js +199 -0
- package/dist/core/blocks/logic.d.ts +2 -0
- package/dist/core/blocks/logic.js +357 -0
- package/dist/core/blocks/playwright.d.ts +2 -0
- package/dist/core/blocks/playwright.js +764 -0
- package/dist/core/blocks/procedures.d.ts +5 -0
- package/dist/core/blocks/procedures.js +321 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +44 -0
- package/dist/core/plugins.d.ts +66 -0
- package/dist/core/plugins.js +118 -0
- package/dist/core/types.d.ts +153 -0
- package/dist/core/types.js +2 -0
- package/dist/server/codegenManager.d.ts +54 -0
- package/dist/server/codegenManager.js +259 -0
- package/dist/server/codegenParser.d.ts +17 -0
- package/dist/server/codegenParser.js +598 -0
- package/dist/server/executor.d.ts +37 -0
- package/dist/server/executor.js +672 -0
- package/dist/server/globals.d.ts +85 -0
- package/dist/server/globals.js +273 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +361 -0
- package/dist/server/plugins.d.ts +55 -0
- package/dist/server/plugins.js +206 -0
- package/package.json +103 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export interface TestFile {
|
|
2
|
+
version: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
variables?: Record<string, VariableDefinition>;
|
|
6
|
+
beforeAll?: TestStep[];
|
|
7
|
+
afterAll?: TestStep[];
|
|
8
|
+
beforeEach?: TestStep[];
|
|
9
|
+
afterEach?: TestStep[];
|
|
10
|
+
procedures?: Record<string, ProcedureDefinition>;
|
|
11
|
+
tests: TestCase[];
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface VariableDefinition {
|
|
15
|
+
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
16
|
+
default?: unknown;
|
|
17
|
+
description?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ProcedureDefinition {
|
|
20
|
+
name: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
params?: ProcedureParam[];
|
|
23
|
+
returnType?: string;
|
|
24
|
+
steps: TestStep[];
|
|
25
|
+
}
|
|
26
|
+
export interface ProcedureParam {
|
|
27
|
+
name: string;
|
|
28
|
+
type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'any';
|
|
29
|
+
default?: unknown;
|
|
30
|
+
description?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface TestCase {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
data?: TestDataSet[];
|
|
37
|
+
dataFile?: string;
|
|
38
|
+
beforeEach?: TestStep[];
|
|
39
|
+
afterEach?: TestStep[];
|
|
40
|
+
steps: TestStep[];
|
|
41
|
+
tags?: string[];
|
|
42
|
+
}
|
|
43
|
+
export interface TestDataSet {
|
|
44
|
+
name?: string;
|
|
45
|
+
values: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
export interface TestStep {
|
|
48
|
+
id: string;
|
|
49
|
+
type: string;
|
|
50
|
+
params: Record<string, unknown>;
|
|
51
|
+
children?: TestStep[];
|
|
52
|
+
}
|
|
53
|
+
export interface BlockDefinition {
|
|
54
|
+
type: string;
|
|
55
|
+
category: string;
|
|
56
|
+
color: string;
|
|
57
|
+
tooltip?: string;
|
|
58
|
+
helpUrl?: string;
|
|
59
|
+
inputs: BlockInput[];
|
|
60
|
+
output?: BlockOutput;
|
|
61
|
+
previousStatement?: boolean;
|
|
62
|
+
nextStatement?: boolean;
|
|
63
|
+
execute: (params: Record<string, unknown>, context: ExecutionContext) => Promise<unknown>;
|
|
64
|
+
}
|
|
65
|
+
export interface BlockInput {
|
|
66
|
+
name: string;
|
|
67
|
+
type: 'value' | 'statement' | 'field';
|
|
68
|
+
fieldType?: 'text' | 'number' | 'dropdown' | 'checkbox' | 'variable' | 'multiline';
|
|
69
|
+
options?: Array<[string, string]>;
|
|
70
|
+
check?: string | string[];
|
|
71
|
+
default?: unknown;
|
|
72
|
+
required?: boolean;
|
|
73
|
+
}
|
|
74
|
+
export interface BlockOutput {
|
|
75
|
+
type: string | string[];
|
|
76
|
+
}
|
|
77
|
+
export interface ExecutionContext {
|
|
78
|
+
variables: Map<string, unknown>;
|
|
79
|
+
results: TestResult[];
|
|
80
|
+
browser?: unknown;
|
|
81
|
+
page?: unknown;
|
|
82
|
+
apiClient?: unknown;
|
|
83
|
+
logger: Logger;
|
|
84
|
+
plugins: Map<string, Plugin>;
|
|
85
|
+
abortSignal?: AbortSignal;
|
|
86
|
+
procedures?: Map<string, ProcedureDefinition>;
|
|
87
|
+
currentData?: TestDataSet;
|
|
88
|
+
dataIndex?: number;
|
|
89
|
+
testIdAttribute?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface Logger {
|
|
92
|
+
info(message: string, data?: unknown): void;
|
|
93
|
+
warn(message: string, data?: unknown): void;
|
|
94
|
+
error(message: string, data?: unknown): void;
|
|
95
|
+
debug(message: string, data?: unknown): void;
|
|
96
|
+
}
|
|
97
|
+
export interface TestResult {
|
|
98
|
+
testId: string;
|
|
99
|
+
testName: string;
|
|
100
|
+
status: 'passed' | 'failed' | 'skipped' | 'error';
|
|
101
|
+
duration: number;
|
|
102
|
+
steps: StepResult[];
|
|
103
|
+
error?: ErrorInfo;
|
|
104
|
+
startedAt: string;
|
|
105
|
+
finishedAt: string;
|
|
106
|
+
dataIteration?: {
|
|
107
|
+
index: number;
|
|
108
|
+
name?: string;
|
|
109
|
+
data: Record<string, unknown>;
|
|
110
|
+
};
|
|
111
|
+
isLifecycle?: boolean;
|
|
112
|
+
lifecycleType?: 'beforeAll' | 'afterAll' | 'beforeEach' | 'afterEach';
|
|
113
|
+
}
|
|
114
|
+
export interface StepResult {
|
|
115
|
+
stepId: string;
|
|
116
|
+
stepType: string;
|
|
117
|
+
status: 'passed' | 'failed' | 'skipped' | 'error';
|
|
118
|
+
duration: number;
|
|
119
|
+
output?: unknown;
|
|
120
|
+
error?: ErrorInfo;
|
|
121
|
+
screenshot?: string;
|
|
122
|
+
}
|
|
123
|
+
export interface ErrorInfo {
|
|
124
|
+
message: string;
|
|
125
|
+
stack?: string;
|
|
126
|
+
code?: string;
|
|
127
|
+
}
|
|
128
|
+
export interface Plugin {
|
|
129
|
+
name: string;
|
|
130
|
+
version: string;
|
|
131
|
+
description?: string;
|
|
132
|
+
blocks: BlockDefinition[];
|
|
133
|
+
hooks?: PluginHooks;
|
|
134
|
+
}
|
|
135
|
+
export interface PluginHooks {
|
|
136
|
+
beforeAll?: (context: ExecutionContext) => Promise<void>;
|
|
137
|
+
afterAll?: (context: ExecutionContext) => Promise<void>;
|
|
138
|
+
beforeTest?: (context: ExecutionContext, test: TestCase) => Promise<void>;
|
|
139
|
+
afterTest?: (context: ExecutionContext, test: TestCase, result: TestResult) => Promise<void>;
|
|
140
|
+
beforeStep?: (context: ExecutionContext, step: TestStep) => Promise<void>;
|
|
141
|
+
afterStep?: (context: ExecutionContext, step: TestStep, result: StepResult) => Promise<void>;
|
|
142
|
+
}
|
|
143
|
+
export interface CLIConfig {
|
|
144
|
+
testFiles: string[];
|
|
145
|
+
outputDir?: string;
|
|
146
|
+
reporter?: 'console' | 'json' | 'junit' | 'html';
|
|
147
|
+
parallel?: number;
|
|
148
|
+
timeout?: number;
|
|
149
|
+
headless?: boolean;
|
|
150
|
+
baseUrl?: string;
|
|
151
|
+
variables?: Record<string, unknown>;
|
|
152
|
+
plugins?: string[];
|
|
153
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Codegen Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the lifecycle of Playwright codegen recording sessions.
|
|
5
|
+
*/
|
|
6
|
+
import { ChildProcess } from 'child_process';
|
|
7
|
+
import { TestStep } from '../core';
|
|
8
|
+
export interface CodegenSession {
|
|
9
|
+
id: string;
|
|
10
|
+
process: ChildProcess | null;
|
|
11
|
+
outputFile: string;
|
|
12
|
+
url: string;
|
|
13
|
+
status: 'running' | 'completed' | 'error';
|
|
14
|
+
error?: string;
|
|
15
|
+
startedAt: number;
|
|
16
|
+
testIdAttribute?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Manages Playwright codegen sessions
|
|
20
|
+
*/
|
|
21
|
+
export declare class CodegenManager {
|
|
22
|
+
private sessions;
|
|
23
|
+
/**
|
|
24
|
+
* Generate a unique session ID
|
|
25
|
+
*/
|
|
26
|
+
private generateSessionId;
|
|
27
|
+
/**
|
|
28
|
+
* Start a new recording session
|
|
29
|
+
*/
|
|
30
|
+
startRecording(url: string, options?: {
|
|
31
|
+
testIdAttribute?: string;
|
|
32
|
+
}): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Stop a recording session and get the recorded steps
|
|
35
|
+
*/
|
|
36
|
+
stopRecording(sessionId: string): Promise<TestStep[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Get the status of a session
|
|
39
|
+
*/
|
|
40
|
+
getStatus(sessionId: string): CodegenSession | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Clean up a session (remove temp file)
|
|
43
|
+
*/
|
|
44
|
+
cleanup(sessionId: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Clean up all sessions (call on server shutdown)
|
|
47
|
+
*/
|
|
48
|
+
cleanupAll(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Get all active sessions
|
|
51
|
+
*/
|
|
52
|
+
getActiveSessions(): CodegenSession[];
|
|
53
|
+
}
|
|
54
|
+
export declare const codegenManager: CodegenManager;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Playwright Codegen Manager
|
|
4
|
+
*
|
|
5
|
+
* Manages the lifecycle of Playwright codegen recording sessions.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.codegenManager = exports.CodegenManager = void 0;
|
|
42
|
+
const child_process_1 = require("child_process");
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const os = __importStar(require("os"));
|
|
46
|
+
const codegenParser_1 = require("./codegenParser");
|
|
47
|
+
/**
|
|
48
|
+
* Manages Playwright codegen sessions
|
|
49
|
+
*/
|
|
50
|
+
class CodegenManager {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.sessions = new Map();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Generate a unique session ID
|
|
56
|
+
*/
|
|
57
|
+
generateSessionId() {
|
|
58
|
+
return `rec-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Start a new recording session
|
|
62
|
+
*/
|
|
63
|
+
async startRecording(url, options) {
|
|
64
|
+
const sessionId = this.generateSessionId();
|
|
65
|
+
const outputFile = path.join(os.tmpdir(), `testblocks-codegen-${sessionId}.js`);
|
|
66
|
+
console.log(`[CodegenManager] Starting recording session ${sessionId}`);
|
|
67
|
+
console.log(`[CodegenManager] URL: ${url}`);
|
|
68
|
+
console.log(`[CodegenManager] Output file: ${outputFile}`);
|
|
69
|
+
if (options?.testIdAttribute) {
|
|
70
|
+
console.log(`[CodegenManager] Test ID Attribute: ${options.testIdAttribute}`);
|
|
71
|
+
}
|
|
72
|
+
// Create the session
|
|
73
|
+
const session = {
|
|
74
|
+
id: sessionId,
|
|
75
|
+
process: null,
|
|
76
|
+
outputFile,
|
|
77
|
+
url,
|
|
78
|
+
status: 'running',
|
|
79
|
+
startedAt: Date.now(),
|
|
80
|
+
testIdAttribute: options?.testIdAttribute,
|
|
81
|
+
};
|
|
82
|
+
this.sessions.set(sessionId, session);
|
|
83
|
+
try {
|
|
84
|
+
// Spawn Playwright codegen
|
|
85
|
+
// Use npx to ensure we use the local playwright installation
|
|
86
|
+
const args = [
|
|
87
|
+
'playwright',
|
|
88
|
+
'codegen',
|
|
89
|
+
'--output', outputFile,
|
|
90
|
+
];
|
|
91
|
+
// Add test-id-attribute if specified
|
|
92
|
+
if (options?.testIdAttribute) {
|
|
93
|
+
args.push('--test-id-attribute', options.testIdAttribute);
|
|
94
|
+
}
|
|
95
|
+
args.push(url);
|
|
96
|
+
const codegenProcess = (0, child_process_1.spawn)('npx', args, {
|
|
97
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
98
|
+
detached: false,
|
|
99
|
+
});
|
|
100
|
+
session.process = codegenProcess;
|
|
101
|
+
// Handle process exit
|
|
102
|
+
codegenProcess.on('exit', (code) => {
|
|
103
|
+
console.log(`[CodegenManager] Session ${sessionId} codegen process exited with code ${code}`);
|
|
104
|
+
const currentSession = this.sessions.get(sessionId);
|
|
105
|
+
if (currentSession && currentSession.status === 'running') {
|
|
106
|
+
currentSession.status = 'completed';
|
|
107
|
+
currentSession.process = null;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// Handle errors
|
|
111
|
+
codegenProcess.on('error', (err) => {
|
|
112
|
+
console.error(`[CodegenManager] Session ${sessionId} process error:`, err);
|
|
113
|
+
const currentSession = this.sessions.get(sessionId);
|
|
114
|
+
if (currentSession) {
|
|
115
|
+
currentSession.status = 'error';
|
|
116
|
+
currentSession.error = err.message;
|
|
117
|
+
currentSession.process = null;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Log stderr for debugging
|
|
121
|
+
codegenProcess.stderr?.on('data', (data) => {
|
|
122
|
+
console.log(`[CodegenManager] Session ${sessionId} stderr:`, data.toString());
|
|
123
|
+
});
|
|
124
|
+
return sessionId;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error(`[CodegenManager] Failed to start recording:`, error);
|
|
128
|
+
session.status = 'error';
|
|
129
|
+
session.error = error.message;
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Stop a recording session and get the recorded steps
|
|
135
|
+
*/
|
|
136
|
+
async stopRecording(sessionId) {
|
|
137
|
+
const session = this.sessions.get(sessionId);
|
|
138
|
+
if (!session) {
|
|
139
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
140
|
+
}
|
|
141
|
+
console.log(`[CodegenManager] Stopping session ${sessionId}`);
|
|
142
|
+
// If process is still running, terminate it
|
|
143
|
+
if (session.process) {
|
|
144
|
+
console.log(`[CodegenManager] Terminating codegen process for session ${sessionId}`);
|
|
145
|
+
// Send SIGTERM to gracefully close the browser
|
|
146
|
+
session.process.kill('SIGTERM');
|
|
147
|
+
// Wait for process to exit (with timeout)
|
|
148
|
+
await new Promise((resolve) => {
|
|
149
|
+
const timeout = setTimeout(() => {
|
|
150
|
+
// Force kill if not exited
|
|
151
|
+
if (session.process) {
|
|
152
|
+
session.process.kill('SIGKILL');
|
|
153
|
+
}
|
|
154
|
+
resolve();
|
|
155
|
+
}, 5000);
|
|
156
|
+
if (session.process) {
|
|
157
|
+
session.process.on('exit', () => {
|
|
158
|
+
clearTimeout(timeout);
|
|
159
|
+
resolve();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
clearTimeout(timeout);
|
|
164
|
+
resolve();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
session.process = null;
|
|
168
|
+
}
|
|
169
|
+
// Wait a bit for file to be written
|
|
170
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
171
|
+
// Read and parse the generated file
|
|
172
|
+
let steps = [];
|
|
173
|
+
try {
|
|
174
|
+
if (fs.existsSync(session.outputFile)) {
|
|
175
|
+
const code = fs.readFileSync(session.outputFile, 'utf-8');
|
|
176
|
+
console.log(`[CodegenManager] Read generated code (${code.length} bytes)`);
|
|
177
|
+
// Pass the testIdAttribute for correct selector conversion
|
|
178
|
+
steps = (0, codegenParser_1.parsePlaywrightCode)(code, session.testIdAttribute || 'data-testid');
|
|
179
|
+
console.log(`[CodegenManager] Parsed ${steps.length} steps`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
console.log(`[CodegenManager] Output file not found: ${session.outputFile}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error(`[CodegenManager] Failed to read/parse output file:`, error);
|
|
187
|
+
}
|
|
188
|
+
// Update session status
|
|
189
|
+
session.status = 'completed';
|
|
190
|
+
return steps;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get the status of a session
|
|
194
|
+
*/
|
|
195
|
+
getStatus(sessionId) {
|
|
196
|
+
const session = this.sessions.get(sessionId);
|
|
197
|
+
if (!session) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
// Check if process has exited
|
|
201
|
+
if (session.process && session.status === 'running') {
|
|
202
|
+
// Check if the process is still alive
|
|
203
|
+
try {
|
|
204
|
+
// kill(0) checks if process exists without killing it
|
|
205
|
+
process.kill(session.process.pid, 0);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Process no longer exists
|
|
209
|
+
session.status = 'completed';
|
|
210
|
+
session.process = null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return session;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Clean up a session (remove temp file)
|
|
217
|
+
*/
|
|
218
|
+
cleanup(sessionId) {
|
|
219
|
+
const session = this.sessions.get(sessionId);
|
|
220
|
+
if (!session) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
console.log(`[CodegenManager] Cleaning up session ${sessionId}`);
|
|
224
|
+
// Kill process if still running
|
|
225
|
+
if (session.process) {
|
|
226
|
+
session.process.kill('SIGKILL');
|
|
227
|
+
}
|
|
228
|
+
// Remove temp file
|
|
229
|
+
try {
|
|
230
|
+
if (fs.existsSync(session.outputFile)) {
|
|
231
|
+
fs.unlinkSync(session.outputFile);
|
|
232
|
+
console.log(`[CodegenManager] Removed temp file: ${session.outputFile}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
console.error(`[CodegenManager] Failed to remove temp file:`, error);
|
|
237
|
+
}
|
|
238
|
+
// Remove session from map
|
|
239
|
+
this.sessions.delete(sessionId);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Clean up all sessions (call on server shutdown)
|
|
243
|
+
*/
|
|
244
|
+
cleanupAll() {
|
|
245
|
+
console.log(`[CodegenManager] Cleaning up all sessions`);
|
|
246
|
+
for (const sessionId of this.sessions.keys()) {
|
|
247
|
+
this.cleanup(sessionId);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get all active sessions
|
|
252
|
+
*/
|
|
253
|
+
getActiveSessions() {
|
|
254
|
+
return Array.from(this.sessions.values()).filter(s => s.status === 'running');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
exports.CodegenManager = CodegenManager;
|
|
258
|
+
// Singleton instance
|
|
259
|
+
exports.codegenManager = new CodegenManager();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Codegen Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses Playwright-generated JavaScript code and converts it to TestBlocks TestStep format.
|
|
5
|
+
* Converts Playwright locators to CSS selectors where possible.
|
|
6
|
+
*/
|
|
7
|
+
import { TestStep } from '../core';
|
|
8
|
+
/**
|
|
9
|
+
* Parse Playwright-generated code and convert to TestBlocks TestStep array
|
|
10
|
+
* @param code - The Playwright-generated code
|
|
11
|
+
* @param testIdAttribute - The attribute used for test IDs (default: 'data-testid')
|
|
12
|
+
*/
|
|
13
|
+
export declare function parsePlaywrightCode(code: string, testIdAttribute?: string): TestStep[];
|
|
14
|
+
/**
|
|
15
|
+
* Check if the code looks like valid Playwright test code
|
|
16
|
+
*/
|
|
17
|
+
export declare function isValidPlaywrightCode(code: string): boolean;
|