diff --git a/.claude-stop-state.png b/.claude-stop-state.png new file mode 100644 index 00000000..11971d94 Binary files /dev/null and b/.claude-stop-state.png differ diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..bfd2ae86 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "superpowers-developing-for-claude-code@superpowers-marketplace": true + } +} diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..9c3c9735 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,13 @@ +{ + "permissions": { + "allow": [ + "Bash(ls:*)", + "Bash(git -C \"C:\\\\Users\\\\xgp\\\\projects\\\\rrweb\" stash pop:*)", + "Bash(node:*)", + "Bash(git -C \"C:\\\\Users\\\\xgp\\\\projects\\\\rrweb\" push:*)", + "Bash(git ls-remote:*)", + "Bash(git -C \"C:\\\\Users\\\\xgp\\\\projects\\\\rrweb\" status:*)", + "Bash(start:*)" + ] + } +} diff --git a/debug-404.js b/debug-404.js new file mode 100644 index 00000000..6eb575fa --- /dev/null +++ b/debug-404.js @@ -0,0 +1,38 @@ +const puppeteer = require('puppeteer-core'); + +(async () => { + const browser = await puppeteer.launch({ + headless: true, + executablePath: 'C:\\Users\\xgp\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe', + args: ['--allow-file-access-from-files', '--disable-web-security'] + }); + + const page = await browser.newPage(); + + page.on('response', async r => { + const s = r.status(); + if (s >= 400) { + console.log('HTTP', s, r.url()); + } + }); + + page.on('requestfailed', req => { + console.log('FAILED', req.failure() && req.failure().errorText, req.url()); + }); + + page.on('console', msg => { + console.log('CONSOLE', msg.type(), msg.text()); + }); + + await page.goto('file:///C:/Users/xgp/projects/rrweb/index.html', { + waitUntil: 'networkidle2', + timeout: 120000 + }); + + await new Promise(r => setTimeout(r, 3000)); + + await browser.close(); +})().catch(e => { + console.error(e); + process.exit(1); +}); \ No newline at end of file diff --git a/debug-all.js b/debug-all.js new file mode 100644 index 00000000..98367058 --- /dev/null +++ b/debug-all.js @@ -0,0 +1,47 @@ +const puppeteer = require('puppeteer-core'); + +(async () => { + const browser = await puppeteer.launch({ + headless: true, + executablePath: 'C:\\Users\\xgp\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe', + args: ['--allow-file-access-from-files', '--disable-web-security'] + }); + + const page = await browser.newPage(); + + page.on('response', async r => { + const s = r.status(); + const url = r.url(); + if (s >= 400) { + console.log('ERROR', s, url); + } else if (s >= 300) { + console.log('REDIRECT', s, url); + } else { + console.log('SUCCESS', s, url.split('/').pop()); + } + }); + + page.on('requestfailed', req => { + console.log('REQUEST FAILED', req.failure().errorText, req.url()); + }); + + page.on('request', req => { + console.log('REQUEST', req.url().split('/').pop()); + }); + + page.on('console', msg => { + console.log('CONSOLE', msg.type(), msg.text()); + }); + + await page.goto('file:///C:/Users/xgp/projects/rrweb/index.html', { + waitUntil: 'networkidle2', + timeout: 120000 + }); + + await new Promise(r => setTimeout(r, 3000)); + + await browser.close(); +})().catch(e => { + console.error(e); + process.exit(1); +}); \ No newline at end of file diff --git a/regression-test.js b/regression-test.js new file mode 100644 index 00000000..f83a4732 --- /dev/null +++ b/regression-test.js @@ -0,0 +1,151 @@ +const puppeteer = require('puppeteer-core'); + +(async () => { + const browser = await puppeteer.launch({ + headless: false, + executablePath: 'C:\\Users\\xgp\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe', + args: ['--allow-file-access-from-files', '--disable-web-security'] + }); + + const page = await browser.newPage(); + + // Override alert to capture messages + page.evaluateOnNewDocument(() => { + window.alert = (msg) => { + console.log('ALERT:', msg); + return true; + }; + }); + + const results = {}; + + // Wait for page load + await page.goto('file:///C:/Users/xgp/projects/rrweb/index.html', { + waitUntil: 'networkidle2', + timeout: 120000 + }); + + // Helper to get element state + const getState = async () => { + return await page.evaluate(() => { + const startBtn = document.getElementById('start-btn'); + const stopBtn = document.getElementById('stop-btn'); + const playBtn = document.getElementById('play-toggle-btn'); + const timeline = document.getElementById('timeline'); + const status = document.getElementById('status-bar'); + const replay = document.getElementById('replayer'); + const currentTime = document.getElementById('current-time'); + const totalTime = document.getElementById('total-time'); + const activeSpeed = document.querySelector('.speed-controls button.active'); + + return { + status: status.textContent.trim(), + startDisabled: startBtn.disabled, + stopDisabled: stopBtn.disabled, + playText: playBtn.textContent, + playDisabled: playBtn.disabled, + timelineDisabled: timeline.disabled, + timelineValue: timeline.value, + timelineMax: timeline.max, + currentTime: currentTime.textContent, + totalTime: totalTime.textContent, + activeSpeed: activeSpeed ? activeSpeed.textContent : 'none', + replayChildren: replay.children.length, + hasIframe: !!replay.querySelector('iframe') + }; + }); + }; + + // Initial state + results.initial = await getState(); + console.log('INITIAL:', JSON.stringify(results.initial, null, 2)); + + // Test 1: Start recording + await page.click('#start-btn'); + await new Promise(r => setTimeout(r, 1000)); + results.afterStart = await getState(); + console.log('AFTER START:', JSON.stringify(results.afterStart, null, 2)); + + // Test 2: Perform some actions + await page.evaluate(() => { + document.querySelector('button[onclick="changeColor()"]').click(); + document.querySelector('button[onclick="addCounter()"]').click(); + document.querySelector('button[onclick="addCounter()"]').click(); + }); + await new Promise(r => setTimeout(r, 1000)); + + // Test 3: Stop recording + await page.click('#stop-btn'); + await new Promise(r => setTimeout(r, 2000)); + results.afterStop = await getState(); + console.log('AFTER STOP:', JSON.stringify(results.afterStop, null, 2)); + + // Test 4: Test play/pause + if (results.afterStop.playDisabled === false) { + await page.click('#play-toggle-btn'); + await new Promise(r => setTimeout(r, 1000)); + results.afterPlay = await getState(); + console.log('AFTER PLAY:', JSON.stringify(results.afterPlay, null, 2)); + + await page.click('#play-toggle-btn'); + await new Promise(r => setTimeout(r, 1000)); + results.afterPause = await getState(); + console.log('AFTER PAUSE:', JSON.stringify(results.afterPause, null, 2)); + } + + // Test 5: Test timeline seek + if (results.afterStop.timelineDisabled === false) { + await page.evaluate(() => { + const timeline = document.getElementById('timeline'); + timeline.value = '50'; + timeline.dispatchEvent(new Event('input')); + timeline.dispatchEvent(new Event('change')); + }); + await new Promise(r => setTimeout(r, 1000)); + results.afterSeek = await getState(); + console.log('AFTER SEEK:', JSON.stringify(results.afterSeek, null, 2)); + } + + // Test 6: Test speed control + if (results.afterStop.timelineDisabled === false) { + const speedBtn = await page.$('.speed-controls button[data-speed="2"]'); + if (speedBtn) { + await speedBtn.click(); + await new Promise(r => setTimeout(r, 1000)); + results.afterSpeed2 = await getState(); + console.log('AFTER SPEED 2X:', JSON.stringify(results.afterSpeed2, null, 2)); + } + } + + // Test 7: Clear all + await page.click('button[onclick="clearAll()"]'); + await new Promise(r => setTimeout(r, 1000)); + results.afterClear = await getState(); + console.log('AFTER CLEAR:', JSON.stringify(results.afterClear, null, 2)); + + await browser.close(); + console.log('\n=== REGRESSION TEST RESULTS ==='); + console.log('✓ Initial state loaded'); + console.log('✓ Start recording works:', results.afterStart.status.includes('录制')); + console.log('✓ Stop recording works:', results.afterStop.status.includes('已录制')); + console.log('✓ Replay area populated:', results.afterStop.replayChildren > 0); + console.log('✓ Play/pause controls:', results.afterStop.playDisabled === false); + console.log('✓ Timeline control:', results.afterStop.timelineDisabled === false); + console.log('✓ Speed controls:', results.afterStop.activeSpeed !== 'none'); + console.log('✓ Clear function works:', results.afterClear.status.includes('等待录制')); + + // Check if all tests passed + const allPassed = + results.afterStart.status.includes('录制') && + results.afterStop.status.includes('已录制') && + results.afterStop.replayChildren > 0 && + results.afterStop.playDisabled === false && + results.afterStop.timelineDisabled === false && + results.afterClear.status.includes('等待录制'); + + console.log('\n🎯 OVERALL RESULT:', allPassed ? 'PASS' : 'FAIL'); + +})().catch(e => { + console.error(e); + process.exit(1); +}); \ No newline at end of file