playwright-repl 0.3.0 → 0.7.10

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/src/recorder.mjs DELETED
@@ -1,241 +0,0 @@
1
- /**
2
- * Session recorder and player.
3
- *
4
- * Records REPL commands to .pw files and replays them.
5
- *
6
- * File format (.pw):
7
- * - One command per line (exactly as typed in REPL)
8
- * - Comments start with #
9
- * - Blank lines are ignored
10
- * - First line is a metadata comment with timestamp
11
- *
12
- * Example:
13
- * # Login test
14
- * # recorded 2026-02-09T19:30:00Z
15
- *
16
- * open https://myapp.com
17
- * snapshot
18
- * click e5
19
- * fill e7 admin@test.com
20
- * fill e9 password123
21
- * click e12
22
- * verify-text Welcome back
23
- */
24
-
25
- import fs from 'node:fs';
26
- import path from 'node:path';
27
-
28
- // ─── Session Recorder ────────────────────────────────────────────────────────
29
-
30
- export class SessionRecorder {
31
- constructor() {
32
- this.commands = [];
33
- this.recording = false;
34
- this.filename = null;
35
- this.paused = false;
36
- }
37
-
38
- /**
39
- * Start recording commands.
40
- * @param {string} [filename] - Output file path. If not provided, uses a timestamp.
41
- */
42
- start(filename) {
43
- this.filename = filename || `session-${new Date().toISOString().replace(/[:.]/g, '-')}.pw`;
44
- this.commands = [];
45
- this.recording = true;
46
- this.paused = false;
47
- return this.filename;
48
- }
49
-
50
- /**
51
- * Record a command (called after each successful REPL command).
52
- * Skips meta-commands (lines starting with .).
53
- */
54
- record(line) {
55
- if (!this.recording || this.paused) return;
56
- const trimmed = line.trim();
57
- if (!trimmed || trimmed.startsWith('.')) return;
58
- this.commands.push(trimmed);
59
- }
60
-
61
- /**
62
- * Pause recording (toggle).
63
- */
64
- pause() {
65
- this.paused = !this.paused;
66
- return this.paused;
67
- }
68
-
69
- /**
70
- * Stop recording and save to file.
71
- * @returns {{ filename: string, count: number }}
72
- */
73
- save() {
74
- if (!this.recording) throw new Error('Not recording');
75
-
76
- const header = [
77
- `# Playwright REPL session`,
78
- `# recorded ${new Date().toISOString()}`,
79
- ``,
80
- ];
81
-
82
- const content = [...header, ...this.commands, ''].join('\n');
83
-
84
- // Ensure directory exists
85
- const dir = path.dirname(this.filename);
86
- if (dir && dir !== '.') {
87
- fs.mkdirSync(dir, { recursive: true });
88
- }
89
-
90
- fs.writeFileSync(this.filename, content, 'utf-8');
91
-
92
- const result = { filename: this.filename, count: this.commands.length };
93
-
94
- this.recording = false;
95
- this.commands = [];
96
- this.filename = null;
97
- this.paused = false;
98
-
99
- return result;
100
- }
101
-
102
- /**
103
- * Discard recording without saving.
104
- */
105
- discard() {
106
- this.recording = false;
107
- this.commands = [];
108
- this.filename = null;
109
- this.paused = false;
110
- }
111
-
112
- get status() {
113
- if (!this.recording) return 'idle';
114
- if (this.paused) return 'paused';
115
- return 'recording';
116
- }
117
-
118
- get commandCount() {
119
- return this.commands.length;
120
- }
121
- }
122
-
123
- // ─── Session Player ──────────────────────────────────────────────────────────
124
-
125
- export class SessionPlayer {
126
- /**
127
- * Load commands from a .pw file.
128
- * @param {string} filename
129
- * @returns {string[]} Array of command lines
130
- */
131
- static load(filename) {
132
- if (!fs.existsSync(filename)) {
133
- throw new Error(`File not found: ${filename}`);
134
- }
135
-
136
- const content = fs.readFileSync(filename, 'utf-8');
137
- return content
138
- .split('\n')
139
- .map(line => line.trim())
140
- .filter(line => line && !line.startsWith('#'));
141
- }
142
-
143
- /**
144
- * Create a player that yields commands one at a time.
145
- * Supports step-through mode where it pauses between commands.
146
- */
147
- constructor(filename) {
148
- this.filename = filename;
149
- this.commands = SessionPlayer.load(filename);
150
- this.index = 0;
151
- }
152
-
153
- get done() {
154
- return this.index >= this.commands.length;
155
- }
156
-
157
- get current() {
158
- return this.commands[this.index] || null;
159
- }
160
-
161
- get progress() {
162
- return `[${this.index}/${this.commands.length}]`;
163
- }
164
-
165
- next() {
166
- if (this.done) return null;
167
- return this.commands[this.index++];
168
- }
169
-
170
- reset() {
171
- this.index = 0;
172
- }
173
- }
174
-
175
- // ─── Session Manager (state machine) ────────────────────────────────────────
176
- //
177
- // States: idle → recording ⇄ paused → idle
178
- // idle → replaying → idle
179
- //
180
-
181
- export class SessionManager {
182
- #recorder = new SessionRecorder();
183
- #player = null;
184
- #step = false;
185
-
186
- /** Current mode: 'idle' | 'recording' | 'paused' | 'replaying' */
187
- get mode() {
188
- if (this.#player && !this.#player.done) return 'replaying';
189
- return this.#recorder.status;
190
- }
191
-
192
- // ── Recording ──────────────────────────────────────────────────
193
-
194
- startRecording(filename) {
195
- if (this.mode !== 'idle') throw new Error(`Cannot record while ${this.mode}`);
196
- return this.#recorder.start(filename);
197
- }
198
-
199
- save() {
200
- if (this.mode !== 'recording' && this.mode !== 'paused')
201
- throw new Error('Not recording');
202
- return this.#recorder.save();
203
- }
204
-
205
- togglePause() {
206
- if (this.mode !== 'recording' && this.mode !== 'paused')
207
- throw new Error('Not recording');
208
- return this.#recorder.pause();
209
- }
210
-
211
- discard() {
212
- if (this.mode !== 'recording' && this.mode !== 'paused')
213
- throw new Error('Not recording');
214
- this.#recorder.discard();
215
- }
216
-
217
- /** Called after each successful command — records if active. */
218
- record(line) {
219
- this.#recorder.record(line);
220
- }
221
-
222
- get recordingFilename() { return this.#recorder.filename; }
223
- get recordedCount() { return this.#recorder.commandCount; }
224
-
225
- // ── Playback ───────────────────────────────────────────────────
226
-
227
- startReplay(filename, step = false) {
228
- if (this.mode !== 'idle') throw new Error(`Cannot replay while ${this.mode}`);
229
- this.#player = new SessionPlayer(filename);
230
- this.#step = step;
231
- return this.#player;
232
- }
233
-
234
- endReplay() {
235
- this.#player = null;
236
- this.#step = false;
237
- }
238
-
239
- get player() { return this.#player; }
240
- get step() { return this.#step; }
241
- }