@capillarytech/cap-ui-dev-tools 0.0.4 → 1.2.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/CAPVISION_USAGE.md +488 -0
- package/README.md +217 -40
- package/package.json +52 -8
- package/src/LibraryWatcherPlugin.js +53 -0
- package/src/capvision-recorder/adapters/WebdriverIOAdapter.js +312 -0
- package/src/capvision-recorder/assets/capvision-player.css +1 -0
- package/src/capvision-recorder/assets/capvision-player.min.js +31 -0
- package/src/capvision-recorder/assets/capvision-plugins/console-record.min.js +93 -0
- package/src/capvision-recorder/assets/capvision-plugins/console-replay.min.js +85 -0
- package/src/capvision-recorder/assets/capvision-plugins/network-record.min.js +542 -0
- package/src/capvision-recorder/assets/capvision-plugins/network-replay.min.js +434 -0
- package/src/capvision-recorder/assets/capvision.min.js +19 -0
- package/src/capvision-recorder/core/CapVisionRecorder.js +1338 -0
- package/src/capvision-recorder/core/ReportEnhancer.js +506 -0
- package/src/capvision-recorder/index.js +58 -0
- package/src/index.js +32 -5
- package/architecture.png +0 -0
- package/capillarytech-cap-ui-dev-tools-0.0.4.tgz +0 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Report Enhancer Configuration
|
|
6
|
+
* @typedef {Object} ReportEnhancerConfig
|
|
7
|
+
* @property {boolean} [ENABLE_FEATURE] - Master feature flag - if false, replay enhancement is completely disabled (default: true)
|
|
8
|
+
* @property {string} [recordingsDir] - Directory where recordings are stored
|
|
9
|
+
* @property {string} [reportsDir] - Directory where HTML reports are located
|
|
10
|
+
* @property {string} [playerCSSPath] - Path to CapVision player CSS file
|
|
11
|
+
* @property {string} [playerScriptPath] - Path to CapVision player script file
|
|
12
|
+
* @property {string} [consoleReplayPluginPath] - Path to console replay plugin
|
|
13
|
+
* @property {string} [networkReplayPluginPath] - Path to network replay plugin
|
|
14
|
+
* @property {number} [playerWidth] - Player width in pixels
|
|
15
|
+
* @property {number} [playerHeight] - Player height in pixels
|
|
16
|
+
* @property {boolean} [autoPlay] - Auto-play recordings on load
|
|
17
|
+
* @property {string[]} [enabledClusters] - Only enhance for these clusters (empty = all)
|
|
18
|
+
* @property {string[]} [enabledModules] - Only enhance for these modules (empty = all)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Recording Data Interface
|
|
23
|
+
* @typedef {Object} RecordingData
|
|
24
|
+
* @property {string} filename - Recording filename
|
|
25
|
+
* @property {Object} data - Recording data object
|
|
26
|
+
* @property {string} data.testName - Test name
|
|
27
|
+
* @property {number} data.recordingNumber - Recording number/sequence
|
|
28
|
+
* @property {string} data.timestamp - ISO timestamp
|
|
29
|
+
* @property {number} data.totalEvents - Total events in recording
|
|
30
|
+
* @property {Array} data.events - Array of CapVision events
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Default Configuration
|
|
35
|
+
* @type {ReportEnhancerConfig}
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_ENHANCER_CONFIG = {
|
|
38
|
+
ENABLE_FEATURE: true,
|
|
39
|
+
recordingsDir: path.join(process.cwd(), 'reports', 'recordings'),
|
|
40
|
+
reportsDir: path.join(process.cwd(), 'reports', 'html-reports'),
|
|
41
|
+
playerCSSPath: path.join(__dirname, '../assets/capvision-player.css'),
|
|
42
|
+
playerScriptPath: path.join(__dirname, '../assets/capvision-player.min.js'),
|
|
43
|
+
consoleReplayPluginPath: path.join(__dirname, '../assets/capvision-plugins/console-replay.min.js'),
|
|
44
|
+
networkReplayPluginPath: path.join(__dirname, '../assets/capvision-plugins/network-replay.min.js'),
|
|
45
|
+
playerWidth: 800,
|
|
46
|
+
playerHeight: 460,
|
|
47
|
+
autoPlay: false,
|
|
48
|
+
enabledClusters: [],
|
|
49
|
+
enabledModules: []
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Report Enhancer Class
|
|
54
|
+
* Enhances HTML reports with CapVision player functionality
|
|
55
|
+
* @class
|
|
56
|
+
*/
|
|
57
|
+
class ReportEnhancer {
|
|
58
|
+
/**
|
|
59
|
+
* Create a new Report Enhancer instance
|
|
60
|
+
* @param {ReportEnhancerConfig} [config={}] - Enhancer configuration
|
|
61
|
+
*/
|
|
62
|
+
constructor(config = {}) {
|
|
63
|
+
/** @type {ReportEnhancerConfig} */
|
|
64
|
+
this.config = { ...DEFAULT_ENHANCER_CONFIG, ...config };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Update configuration
|
|
69
|
+
* @param {Partial<ReportEnhancerConfig>} config - Configuration updates
|
|
70
|
+
*/
|
|
71
|
+
updateConfig(config) {
|
|
72
|
+
this.config = { ...this.config, ...config };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get current configuration
|
|
77
|
+
* @returns {ReportEnhancerConfig} Current configuration object
|
|
78
|
+
*/
|
|
79
|
+
getConfig() {
|
|
80
|
+
return { ...this.config };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if enhancement should be enabled for current cluster
|
|
85
|
+
* @private
|
|
86
|
+
* @param {string} [cluster] - Override cluster (defaults to process.env.cluster)
|
|
87
|
+
* @returns {boolean} True if enabled for cluster
|
|
88
|
+
*/
|
|
89
|
+
isEnabledForCluster(cluster) {
|
|
90
|
+
if (this.config.enabledClusters.length === 0) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const currentCluster = cluster || process.env.cluster;
|
|
95
|
+
return currentCluster ? this.config.enabledClusters.includes(currentCluster) : false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if enhancement should be enabled for current module
|
|
100
|
+
* @private
|
|
101
|
+
* @param {string} [module] - Override module (defaults to process.env.module)
|
|
102
|
+
* @returns {boolean} True if enabled for module
|
|
103
|
+
*/
|
|
104
|
+
isEnabledForModule(module) {
|
|
105
|
+
if (this.config.enabledModules.length === 0) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const currentModule = module || process.env.module;
|
|
110
|
+
return currentModule ? this.config.enabledModules.includes(currentModule) : false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if enhancement is enabled
|
|
115
|
+
* @param {string} [cluster] - Override cluster check
|
|
116
|
+
* @param {string} [module] - Override module check
|
|
117
|
+
* @returns {boolean} True if enhancement enabled
|
|
118
|
+
*/
|
|
119
|
+
isEnhancementEnabled(cluster, module) {
|
|
120
|
+
return this.isEnabledForCluster(cluster) && this.isEnabledForModule(module);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get available recordings
|
|
125
|
+
* @private
|
|
126
|
+
* @returns {RecordingData[]} Array of recording data objects
|
|
127
|
+
*/
|
|
128
|
+
getAvailableRecordings() {
|
|
129
|
+
try {
|
|
130
|
+
if (!fs.existsSync(this.config.recordingsDir)) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const files = fs.readdirSync(this.config.recordingsDir);
|
|
135
|
+
const recordingFiles = files.filter(file => file.endsWith('.json'));
|
|
136
|
+
|
|
137
|
+
const recordings = [];
|
|
138
|
+
for (const file of recordingFiles) {
|
|
139
|
+
try {
|
|
140
|
+
const filePath = path.join(this.config.recordingsDir, file);
|
|
141
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
142
|
+
recordings.push({
|
|
143
|
+
filename: file,
|
|
144
|
+
data: data
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(`🔴 Failed to read recording file ${file}:`, error.message);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return recordings;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error('🔴 Failed to get recordings:', error.message);
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Add inline recording players to HTML content
|
|
160
|
+
* @private
|
|
161
|
+
* @param {string} htmlContent - Original HTML content
|
|
162
|
+
* @param {RecordingData[]} recordings - Array of recordings to add
|
|
163
|
+
* @returns {string} Modified HTML content with players
|
|
164
|
+
*/
|
|
165
|
+
addInlineRecordingPlayers(htmlContent, recordings) {
|
|
166
|
+
if (recordings.length === 0) {
|
|
167
|
+
return htmlContent;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const testHeaderRegex = /(<tr class="test-header">[\s\S]*?<\/tr>)/;
|
|
171
|
+
|
|
172
|
+
return htmlContent.replace(testHeaderRegex, (match) => {
|
|
173
|
+
const recordingPlayersHTML = recordings.map((recording, index) => {
|
|
174
|
+
const testName = recording.data.testName || 'Unknown Test';
|
|
175
|
+
const recordingNumber = recording.data.recordingNumber || (index + 1);
|
|
176
|
+
const displayName = recording.data.testName
|
|
177
|
+
? `${testName} (Recording #${recordingNumber})`
|
|
178
|
+
: recording.filename;
|
|
179
|
+
|
|
180
|
+
return `
|
|
181
|
+
<tr class="test-row recording">
|
|
182
|
+
<td colspan="2">
|
|
183
|
+
<div class="recordingWrapper" style="padding: 15px; background: #f8f9fa; margin: 10px 0;">
|
|
184
|
+
<h4 style="margin: 0 0 10px 0; color: #007bff; font-size: 16px;">
|
|
185
|
+
🎬 Session Recording: ${displayName}
|
|
186
|
+
</h4>
|
|
187
|
+
<div class="recording-info" style="margin-bottom: 10px; font-size: 12px; color: #666;">
|
|
188
|
+
Events: ${recording.data.totalEvents} | Timestamp: ${recording.data.timestamp} | File: ${recording.filename}
|
|
189
|
+
</div>
|
|
190
|
+
<div id="capvision-player-inline-${index}" class="capvision-player-inline" style="width: 100%; max-width: ${this.config.playerWidth + 200}px; height: ${this.config.playerHeight + 100}px; border: 1px solid #ddd; border-radius: 8px; background: #fff;">
|
|
191
|
+
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #666;">
|
|
192
|
+
Loading recording player...
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</td>
|
|
197
|
+
</tr>`;
|
|
198
|
+
}).join('');
|
|
199
|
+
|
|
200
|
+
return match + recordingPlayersHTML;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Generate CapVision player HTML and JavaScript
|
|
206
|
+
* @private
|
|
207
|
+
* @param {RecordingData[]} recordings - Array of recordings to embed
|
|
208
|
+
* @returns {string} HTML/JavaScript string to inject
|
|
209
|
+
*/
|
|
210
|
+
generateCapVisionPlayerHTML(recordings) {
|
|
211
|
+
// Read bundled assets
|
|
212
|
+
let playerCSS = '';
|
|
213
|
+
try {
|
|
214
|
+
playerCSS = fs.readFileSync(this.config.playerCSSPath, 'utf8');
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.warn('⚠️ Failed to load CapVision player CSS:', error.message);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let playerScript = '';
|
|
220
|
+
try {
|
|
221
|
+
playerScript = fs.readFileSync(this.config.playerScriptPath, 'utf8');
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.warn('⚠️ Failed to load CapVision player script:', error.message);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let consoleReplayPlugin = '';
|
|
227
|
+
try {
|
|
228
|
+
consoleReplayPlugin = fs.readFileSync(this.config.consoleReplayPluginPath, 'utf8');
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.warn('⚠️ Failed to load console replay plugin:', error.message);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Read bundled network replay plugin
|
|
234
|
+
let networkReplayPlugin = '';
|
|
235
|
+
try {
|
|
236
|
+
networkReplayPlugin = fs.readFileSync(this.config.networkReplayPluginPath, 'utf8');
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.warn('⚠️ Failed to load network replay plugin:', error.message);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return `
|
|
242
|
+
<!-- CapVision Player CSS (Bundled) -->
|
|
243
|
+
<style>
|
|
244
|
+
${playerCSS}
|
|
245
|
+
</style>
|
|
246
|
+
|
|
247
|
+
<!-- CapVision Player Script (Bundled) -->
|
|
248
|
+
<script>
|
|
249
|
+
${playerScript}
|
|
250
|
+
</script>
|
|
251
|
+
|
|
252
|
+
<!-- Console Replay Plugin (Bundled) -->
|
|
253
|
+
<script>
|
|
254
|
+
${consoleReplayPlugin}
|
|
255
|
+
</script>
|
|
256
|
+
|
|
257
|
+
<!-- Network Replay Plugin (Bundled) -->
|
|
258
|
+
<script>
|
|
259
|
+
${networkReplayPlugin}
|
|
260
|
+
</script>
|
|
261
|
+
|
|
262
|
+
<script>
|
|
263
|
+
// CapVision Player Integration Script for Inline Players
|
|
264
|
+
(function() {
|
|
265
|
+
const recordings = ${JSON.stringify(recordings)};
|
|
266
|
+
let players = [];
|
|
267
|
+
|
|
268
|
+
// Initialize all inline players
|
|
269
|
+
function initializeInlinePlayers() {
|
|
270
|
+
recordings.forEach((recording, index) => {
|
|
271
|
+
const playerContainer = document.getElementById(\`capvision-player-inline-\${index}\`);
|
|
272
|
+
if (playerContainer) {
|
|
273
|
+
try {
|
|
274
|
+
console.log('🟡 Initializing inline player for recording:', recording.filename);
|
|
275
|
+
|
|
276
|
+
// Clear loading message
|
|
277
|
+
playerContainer.innerHTML = '';
|
|
278
|
+
|
|
279
|
+
// Build plugins array for replay
|
|
280
|
+
const plugins = [];
|
|
281
|
+
|
|
282
|
+
// Add console replay plugin if available
|
|
283
|
+
if (window.rrwebPluginConsoleReplay) {
|
|
284
|
+
try {
|
|
285
|
+
const replayOptions = { level: ['log', 'info', 'warn', 'error'] };
|
|
286
|
+
let consolePlugin;
|
|
287
|
+
|
|
288
|
+
// Handle different export patterns
|
|
289
|
+
if (typeof window.rrwebPluginConsoleReplay === 'function') {
|
|
290
|
+
consolePlugin = window.rrwebPluginConsoleReplay(replayOptions);
|
|
291
|
+
} else if (window.rrwebPluginConsoleReplay.getReplayConsolePlugin) {
|
|
292
|
+
consolePlugin = window.rrwebPluginConsoleReplay.getReplayConsolePlugin(replayOptions);
|
|
293
|
+
} else if (window.rrwebPluginConsoleReplay.default) {
|
|
294
|
+
consolePlugin = window.rrwebPluginConsoleReplay.default(replayOptions);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (consolePlugin) {
|
|
298
|
+
plugins.push(consolePlugin);
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.warn('⚠️ Failed to initialize console replay plugin:', error.message);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Add network replay plugin if available
|
|
306
|
+
if (window.rrwebPluginNetworkReplay) {
|
|
307
|
+
try {
|
|
308
|
+
console.log('🟡 Initializing network replay plugin...');
|
|
309
|
+
const networkReplayOptions = {
|
|
310
|
+
showPanel: true,
|
|
311
|
+
maxEntries: 100,
|
|
312
|
+
position: 'bottom-right'
|
|
313
|
+
};
|
|
314
|
+
let networkPlugin;
|
|
315
|
+
|
|
316
|
+
// Handle different export patterns
|
|
317
|
+
if (typeof window.rrwebPluginNetworkReplay === 'function') {
|
|
318
|
+
networkPlugin = window.rrwebPluginNetworkReplay(networkReplayOptions);
|
|
319
|
+
console.log('🟢 Network replay plugin initialized (function pattern)');
|
|
320
|
+
} else if (window.rrwebPluginNetworkReplay.getReplayNetworkPlugin) {
|
|
321
|
+
networkPlugin = window.rrwebPluginNetworkReplay.getReplayNetworkPlugin(networkReplayOptions);
|
|
322
|
+
console.log('🟢 Network replay plugin initialized (getReplayNetworkPlugin pattern)');
|
|
323
|
+
} else if (window.rrwebPluginNetworkReplay.default) {
|
|
324
|
+
networkPlugin = window.rrwebPluginNetworkReplay.default(networkReplayOptions);
|
|
325
|
+
console.log('🟢 Network replay plugin initialized (default pattern)');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (networkPlugin) {
|
|
329
|
+
console.log('🟡 Network plugin structure:', {
|
|
330
|
+
hasHandler: typeof networkPlugin.handler === 'function',
|
|
331
|
+
hasOnBuild: typeof networkPlugin.onBuild === 'function',
|
|
332
|
+
keys: Object.keys(networkPlugin)
|
|
333
|
+
});
|
|
334
|
+
plugins.push(networkPlugin);
|
|
335
|
+
console.log('🟢 Network replay plugin added to plugins array');
|
|
336
|
+
} else {
|
|
337
|
+
console.warn('⚠️ Network replay plugin is null or undefined');
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error('🔴 Failed to initialize network replay plugin:', error);
|
|
341
|
+
console.error('🔴 Error stack:', error.stack);
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
console.warn('⚠️ window.rrwebPluginNetworkReplay not found');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Create RRWeb player
|
|
348
|
+
const player = new rrwebPlayer({
|
|
349
|
+
target: playerContainer,
|
|
350
|
+
props: {
|
|
351
|
+
events: recording.data.events,
|
|
352
|
+
width: ${this.config.playerWidth},
|
|
353
|
+
height: ${this.config.playerHeight},
|
|
354
|
+
autoPlay: ${this.config.autoPlay},
|
|
355
|
+
speed: 1,
|
|
356
|
+
showController: true,
|
|
357
|
+
mouseTail: true,
|
|
358
|
+
plugins: plugins.length > 0 ? plugins : undefined,
|
|
359
|
+
insertStyleRules: [
|
|
360
|
+
'.replayer-wrapper { border-radius: 8px; width: 100%; height: 100%; }',
|
|
361
|
+
'.replayer-mouse { z-index: 1000; }',
|
|
362
|
+
'.replayer-controller { background: rgba(0,0,0,0.8); border-radius: 4px; }',
|
|
363
|
+
'.replayer-controller button { color: white; }',
|
|
364
|
+
'.replayer-wrapper { background: white; }'
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
players[index] = player;
|
|
370
|
+
|
|
371
|
+
console.log('🟢 Successfully initialized inline player for recording:', recording.filename);
|
|
372
|
+
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error('🔴 Failed to initialize inline player for recording:', recording.filename, error);
|
|
375
|
+
playerContainer.innerHTML = \`
|
|
376
|
+
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #dc3545; font-size: 16px; flex-direction: column;">
|
|
377
|
+
<div>❌ Failed to load recording</div>
|
|
378
|
+
<div style="font-size: 12px; margin-top: 5px;">\${error.message}</div>
|
|
379
|
+
</div>
|
|
380
|
+
\`;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Initialize players when DOM is ready
|
|
387
|
+
if (document.readyState === 'loading') {
|
|
388
|
+
document.addEventListener('DOMContentLoaded', initializeInlinePlayers);
|
|
389
|
+
} else {
|
|
390
|
+
initializeInlinePlayers();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Cleanup function
|
|
394
|
+
window.addEventListener('beforeunload', function() {
|
|
395
|
+
players.forEach(player => {
|
|
396
|
+
if (player && player.destroy) {
|
|
397
|
+
player.destroy();
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
console.log('🟢 CapVision Player integration loaded with', recordings.length, 'recording(s)');
|
|
403
|
+
|
|
404
|
+
})();
|
|
405
|
+
</script>
|
|
406
|
+
`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Enhance a single HTML report with CapVision player
|
|
411
|
+
* @async
|
|
412
|
+
* @param {string} reportPath - Path to HTML report file
|
|
413
|
+
* @returns {Promise<void>}
|
|
414
|
+
*/
|
|
415
|
+
async enhanceReport(reportPath) {
|
|
416
|
+
try {
|
|
417
|
+
// Check master feature flag first
|
|
418
|
+
if (this.config.ENABLE_FEATURE === false) {
|
|
419
|
+
console.log('🟡 CapVision feature disabled (ENABLE_FEATURE=false), skipping report enhancement');
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
console.log('🟡 Enhancing HTML report with CapVision player:', reportPath);
|
|
424
|
+
|
|
425
|
+
if (!fs.existsSync(reportPath)) {
|
|
426
|
+
console.log('🟡 HTML report not found, skipping enhancement');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const recordings = this.getAvailableRecordings();
|
|
431
|
+
if (recordings.length === 0) {
|
|
432
|
+
console.log('🟡 No CapVision recordings found, skipping enhancement');
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
let htmlContent = fs.readFileSync(reportPath, 'utf8');
|
|
437
|
+
|
|
438
|
+
// Add inline players
|
|
439
|
+
htmlContent = this.addInlineRecordingPlayers(htmlContent, recordings);
|
|
440
|
+
|
|
441
|
+
// Add player scripts and CSS
|
|
442
|
+
const capVisionPlayerHTML = this.generateCapVisionPlayerHTML(recordings);
|
|
443
|
+
const bodyEndIndex = htmlContent.lastIndexOf('</body>');
|
|
444
|
+
|
|
445
|
+
if (bodyEndIndex === -1) {
|
|
446
|
+
console.log('🟡 Could not find </body> tag, appending to end');
|
|
447
|
+
htmlContent += capVisionPlayerHTML;
|
|
448
|
+
} else {
|
|
449
|
+
htmlContent = htmlContent.slice(0, bodyEndIndex) + capVisionPlayerHTML + htmlContent.slice(bodyEndIndex);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
fs.writeFileSync(reportPath, htmlContent);
|
|
453
|
+
|
|
454
|
+
console.log('🟢 Successfully enhanced HTML report with CapVision player');
|
|
455
|
+
console.log(`🟢 Found ${recordings.length} recording(s) to integrate`);
|
|
456
|
+
|
|
457
|
+
} catch (error) {
|
|
458
|
+
console.error('🔴 Failed to enhance HTML report:', error.message);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Enhance all HTML reports in the reports directory
|
|
464
|
+
* @async
|
|
465
|
+
* @returns {Promise<void>}
|
|
466
|
+
*/
|
|
467
|
+
async enhanceAllReports() {
|
|
468
|
+
try {
|
|
469
|
+
// Check master feature flag first
|
|
470
|
+
if (this.config.ENABLE_FEATURE === false) {
|
|
471
|
+
console.log('🟡 CapVision feature disabled (ENABLE_FEATURE=false), skipping report enhancement');
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!this.isEnhancementEnabled()) {
|
|
476
|
+
console.log('🟡 Skipping report enhancement (disabled for current cluster/module)');
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (!fs.existsSync(this.config.reportsDir)) {
|
|
481
|
+
console.log('🟡 Reports directory not found');
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const files = fs.readdirSync(this.config.reportsDir);
|
|
486
|
+
const htmlFiles = files.filter(file => file.endsWith('.html'));
|
|
487
|
+
|
|
488
|
+
for (const htmlFile of htmlFiles) {
|
|
489
|
+
const reportPath = path.join(this.config.reportsDir, htmlFile);
|
|
490
|
+
await this.enhanceReport(reportPath);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
console.log('🟢 Enhanced all HTML reports with CapVision player');
|
|
494
|
+
|
|
495
|
+
} catch (error) {
|
|
496
|
+
console.error('🔴 Failed to enhance reports:', error.message);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Export class and default config
|
|
502
|
+
module.exports = {
|
|
503
|
+
ReportEnhancer,
|
|
504
|
+
DEFAULT_ENHANCER_CONFIG
|
|
505
|
+
};
|
|
506
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapVision Recorder Module for @capillarytech/cap-ui-dev-tools
|
|
3
|
+
*
|
|
4
|
+
* Session recording and playback functionality for WebdriverIO tests
|
|
5
|
+
*
|
|
6
|
+
* @module capvision-recorder
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
// Import core modules
|
|
12
|
+
const { CapVisionRecorder, DEFAULT_CONFIG: CAPVISION_DEFAULT_CONFIG } = require('./core/CapVisionRecorder');
|
|
13
|
+
const { ReportEnhancer, DEFAULT_ENHANCER_CONFIG } = require('./core/ReportEnhancer');
|
|
14
|
+
const {
|
|
15
|
+
WebdriverIOBrowserExecutor,
|
|
16
|
+
WebdriverIOCapVisionRecorder,
|
|
17
|
+
createWDIOCapVisionHooks
|
|
18
|
+
} = require('./adapters/WebdriverIOAdapter');
|
|
19
|
+
|
|
20
|
+
// Update default asset paths to match cap-ui-dev-tools structure
|
|
21
|
+
const ASSET_BASE_PATH = path.join(__dirname, 'assets');
|
|
22
|
+
|
|
23
|
+
CAPVISION_DEFAULT_CONFIG.capVisionScriptPath = path.join(ASSET_BASE_PATH, 'capvision.min.js');
|
|
24
|
+
CAPVISION_DEFAULT_CONFIG.consoleRecordPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/console-record.min.js');
|
|
25
|
+
CAPVISION_DEFAULT_CONFIG.consoleReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/console-replay.min.js');
|
|
26
|
+
CAPVISION_DEFAULT_CONFIG.networkRecordPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/network-record.min.js');
|
|
27
|
+
CAPVISION_DEFAULT_CONFIG.networkReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/network-replay.min.js');
|
|
28
|
+
|
|
29
|
+
DEFAULT_ENHANCER_CONFIG.playerCSSPath = path.join(ASSET_BASE_PATH, 'capvision-player.css');
|
|
30
|
+
DEFAULT_ENHANCER_CONFIG.playerScriptPath = path.join(ASSET_BASE_PATH, 'capvision-player.min.js');
|
|
31
|
+
DEFAULT_ENHANCER_CONFIG.consoleReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/console-replay.min.js');
|
|
32
|
+
DEFAULT_ENHANCER_CONFIG.networkReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/network-replay.min.js');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Export all CapVision recorder components
|
|
36
|
+
*/
|
|
37
|
+
module.exports = {
|
|
38
|
+
// Core classes
|
|
39
|
+
CapVisionRecorder,
|
|
40
|
+
ReportEnhancer,
|
|
41
|
+
|
|
42
|
+
// WebdriverIO adapters
|
|
43
|
+
WebdriverIOBrowserExecutor,
|
|
44
|
+
WebdriverIOCapVisionRecorder,
|
|
45
|
+
|
|
46
|
+
// Convenience helpers
|
|
47
|
+
createWDIOCapVisionHooks,
|
|
48
|
+
|
|
49
|
+
// Default configurations
|
|
50
|
+
CAPVISION_DEFAULT_CONFIG,
|
|
51
|
+
DEFAULT_ENHANCER_CONFIG
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Default export - most commonly used function
|
|
56
|
+
*/
|
|
57
|
+
module.exports.default = createWDIOCapVisionHooks;
|
|
58
|
+
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @capillarytech/cap-ui-dev-tools
|
|
3
|
+
*
|
|
4
|
+
* Development tools for Capillary UI projects
|
|
5
|
+
*
|
|
6
|
+
* @module cap-ui-dev-tools
|
|
7
|
+
*/
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
// Webpack Plugin for library hot-reloading
|
|
10
|
+
const LibraryWatcherPlugin = require('./LibraryWatcherPlugin');
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
// CapVision Recorder for test session recording
|
|
13
|
+
const capVisionRecorder = require('./capvision-recorder');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Main exports for cap-ui-dev-tools
|
|
17
|
+
*/
|
|
18
|
+
module.exports = {
|
|
19
|
+
// Webpack plugin (existing functionality)
|
|
20
|
+
LibraryWatcherPlugin,
|
|
21
|
+
|
|
22
|
+
// CapVision recorder module (new functionality)
|
|
23
|
+
capVisionRecorder,
|
|
24
|
+
|
|
25
|
+
// Convenience exports for CapVision (most commonly used)
|
|
26
|
+
createWDIOCapVisionHooks: capVisionRecorder.createWDIOCapVisionHooks,
|
|
27
|
+
CapVisionRecorder: capVisionRecorder.CapVisionRecorder,
|
|
28
|
+
ReportEnhancer: capVisionRecorder.ReportEnhancer
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Default export - LibraryWatcherPlugin for backward compatibility
|
|
33
|
+
*/
|
|
34
|
+
module.exports.default = LibraryWatcherPlugin;
|
package/architecture.png
DELETED
|
Binary file
|
|
Binary file
|