create-walle 0.9.3 → 0.9.4
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/README.md +2 -1
- package/package.json +1 -1
- package/template/claude-task-manager/db.js +5 -1
- package/template/claude-task-manager/public/css/walle.css +317 -0
- package/template/claude-task-manager/public/index.html +404 -101
- package/template/claude-task-manager/public/js/walle.js +1256 -86
- package/template/claude-task-manager/server.js +189 -14
- package/template/docs/site/api/README.md +146 -0
- package/template/docs/site/skills/README.md +99 -5
- package/template/package.json +1 -1
- package/template/wall-e/agent.js +54 -0
- package/template/wall-e/api-walle.js +452 -3
- package/template/wall-e/brain.js +45 -1
- package/template/wall-e/channels/telegram-channel.js +96 -0
- package/template/wall-e/chat.js +61 -2
- package/template/wall-e/coding-context.js +252 -0
- package/template/wall-e/coding-orchestrator.js +625 -0
- package/template/wall-e/coding-review.js +189 -0
- package/template/wall-e/core-tasks.js +12 -3
- package/template/wall-e/deploy.sh +4 -4
- package/template/wall-e/fly.toml +2 -2
- package/template/wall-e/package.json +4 -1
- package/template/wall-e/skills/_bundled/coding-agent/SKILL.md +17 -0
- package/template/wall-e/skills/_bundled/coding-agent/run.js +142 -0
- package/template/wall-e/skills/_bundled/email-sync/SKILL.md +12 -7
- package/template/wall-e/skills/_bundled/email-sync/mail-reader.jxa +76 -46
- package/template/wall-e/skills/_bundled/email-sync/run.js +42 -2
- package/template/wall-e/skills/_bundled/glean-team-sync/SKILL.md +57 -0
- package/template/wall-e/skills/_bundled/glean-team-sync/run.js +254 -0
- package/template/wall-e/skills/_bundled/slack-mentions/SKILL.md +1 -1
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +268 -121
- package/template/wall-e/skills/_templates/data-fetcher.md +27 -0
- package/template/wall-e/skills/_templates/manual-action.md +19 -0
- package/template/wall-e/skills/_templates/periodic-checker.md +29 -0
- package/template/wall-e/skills/_templates/script-runner.md +21 -0
- package/template/wall-e/skills/claude-code-reader.js +16 -4
- package/template/wall-e/skills/skill-executor.js +23 -1
- package/template/wall-e/skills/skill-validator.js +73 -0
- package/template/wall-e/tests/brain.test.js +3 -3
- package/template/wall-e/tests/coding-agent-integration.test.js +240 -0
- package/template/wall-e/tests/coding-context.test.js +212 -0
- package/template/wall-e/tests/coding-orchestrator.test.js +303 -0
- package/template/wall-e/tests/coding-review.test.js +141 -0
- package/template/claude-task-manager/package-lock.json +0 -1607
- package/template/claude-task-manager/tests/test-ai-search.js +0 -61
- package/template/claude-task-manager/tests/test-editor-ux.js +0 -76
- package/template/claude-task-manager/tests/test-editor-ux2.js +0 -51
- package/template/claude-task-manager/tests/test-features-v2.js +0 -127
- package/template/claude-task-manager/tests/test-insights-cached.js +0 -78
- package/template/claude-task-manager/tests/test-insights.js +0 -124
- package/template/claude-task-manager/tests/test-permissions-v2.js +0 -127
- package/template/claude-task-manager/tests/test-permissions.js +0 -122
- package/template/claude-task-manager/tests/test-pin.js +0 -51
- package/template/claude-task-manager/tests/test-prompts.js +0 -164
- package/template/claude-task-manager/tests/test-recent-sessions.js +0 -96
- package/template/claude-task-manager/tests/test-review.js +0 -104
- package/template/claude-task-manager/tests/test-send-dropdown.js +0 -76
- package/template/claude-task-manager/tests/test-send-final.js +0 -30
- package/template/claude-task-manager/tests/test-send-fixes.js +0 -76
- package/template/claude-task-manager/tests/test-send-integration.js +0 -107
- package/template/claude-task-manager/tests/test-send-visual.js +0 -34
- package/template/claude-task-manager/tests/test-session-create.js +0 -147
- package/template/claude-task-manager/tests/test-sidebar-ux.js +0 -83
- package/template/claude-task-manager/tests/test-url-hash.js +0 -68
- package/template/claude-task-manager/tests/test-ux-crop.js +0 -34
- package/template/claude-task-manager/tests/test-ux-review.js +0 -130
- package/template/claude-task-manager/tests/test-zoom-card.js +0 -76
- package/template/claude-task-manager/tests/test-zoom.js +0 -92
- package/template/claude-task-manager/tests/test-zoom2.js +0 -67
- package/template/docs/openclaw-vs-walle-comparison.md +0 -103
- package/template/docs/ux-improvement-plan.md +0 -84
- package/template/wall-e/docs/specs/2026-04-01-publish-plan.md +0 -112
- package/template/wall-e/docs/specs/SKILL-FORMAT.md +0 -326
- package/template/wall-e/package-lock.json +0 -533
- package/template/wall-e/skills/_bundled/slack-mentions/.watermark.json +0 -4
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const puppeteer = require('puppeteer');
|
|
3
|
-
const TOKEN = process.env.CTM_TEST_TOKEN || 'test-token-placeholder';
|
|
4
|
-
const BASE = `http://localhost:3456/prompts.html?token=${TOKEN}`;
|
|
5
|
-
|
|
6
|
-
async function runTest() {
|
|
7
|
-
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
8
|
-
const page = await browser.newPage();
|
|
9
|
-
page.on('pageerror', err => console.error('[PAGE ERROR]', err.message));
|
|
10
|
-
await page.setViewport({ width: 1920, height: 1080 });
|
|
11
|
-
|
|
12
|
-
await page.goto(BASE + '#prompt=3', { waitUntil: 'networkidle2', timeout: 15000 });
|
|
13
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
14
|
-
|
|
15
|
-
// Test 1: Send button guard - clicking multiple times should not create multiple sessions
|
|
16
|
-
// Simulate clicking Send twice quickly
|
|
17
|
-
const sendBtn = await page.$('.send-dropdown-wrap .btn-main');
|
|
18
|
-
console.log('=== Fix 1: Debounce Send ===');
|
|
19
|
-
|
|
20
|
-
// First click — should go through
|
|
21
|
-
await sendBtn.click();
|
|
22
|
-
await new Promise(r => setTimeout(r, 200));
|
|
23
|
-
const btnText1 = await page.$eval('.send-dropdown-wrap .btn-main', el => el.textContent);
|
|
24
|
-
const btnDisabled1 = await page.$eval('.send-dropdown-wrap .btn-main', el => el.disabled);
|
|
25
|
-
console.log('1. After first click - button text:', btnText1.trim());
|
|
26
|
-
console.log(' Button disabled:', btnDisabled1);
|
|
27
|
-
|
|
28
|
-
// Second click — should be blocked by guard
|
|
29
|
-
await sendBtn.click();
|
|
30
|
-
await new Promise(r => setTimeout(r, 200));
|
|
31
|
-
// Check pendingPromptSend is still set (not reset/replaced)
|
|
32
|
-
const pending = await page.evaluate(() => !!state.pendingPromptSend);
|
|
33
|
-
console.log('2. Pending prompt send exists:', pending);
|
|
34
|
-
|
|
35
|
-
// Wait for the safety timeout or session creation, then check button resets
|
|
36
|
-
// (We won't wait 15s, just verify the guard works)
|
|
37
|
-
|
|
38
|
-
// Reset for next test
|
|
39
|
-
await page.evaluate(() => { state.pendingPromptSend = null; resetSendButton(); });
|
|
40
|
-
|
|
41
|
-
// Test 2: Image in plain text
|
|
42
|
-
console.log('\n=== Fix 2: Images in Copy ===');
|
|
43
|
-
|
|
44
|
-
// Insert a fake image into the editor
|
|
45
|
-
await page.evaluate(() => {
|
|
46
|
-
const editor = document.getElementById('editor');
|
|
47
|
-
const img = document.createElement('img');
|
|
48
|
-
img.src = '/api/images/file/test-image-abc123.png?token=xyz';
|
|
49
|
-
img.dataset.imageId = '99';
|
|
50
|
-
editor.appendChild(img);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const plainText = await page.evaluate(() => getPromptPlainText());
|
|
54
|
-
console.log('3. Plain text includes image ref:', plainText.includes('[Image: test-image-abc123.png]'));
|
|
55
|
-
console.log(' Image ref text:', plainText.match(/\[Image: [^\]]+\]/)?.[0] || '(not found)');
|
|
56
|
-
|
|
57
|
-
// Test markdown copy
|
|
58
|
-
const mdResult = await page.evaluate(() => {
|
|
59
|
-
const editor = document.getElementById('editor');
|
|
60
|
-
return htmlToMarkdown(editor.innerHTML);
|
|
61
|
-
});
|
|
62
|
-
console.log('4. Markdown includes image:', mdResult.includes('![Image]'));
|
|
63
|
-
console.log(' Image md:', mdResult.match(/!\[Image\]\([^)]+\)/)?.[0] || '(not found)');
|
|
64
|
-
|
|
65
|
-
// Clean up the fake image
|
|
66
|
-
await page.evaluate(() => {
|
|
67
|
-
const editor = document.getElementById('editor');
|
|
68
|
-
const img = editor.querySelector('img[data-image-id="99"]');
|
|
69
|
-
if (img) img.remove();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
await browser.close();
|
|
73
|
-
console.log('\nAll fix tests complete.');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
runTest();
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const puppeteer = require('puppeteer');
|
|
3
|
-
const TOKEN = process.env.CTM_TEST_TOKEN || 'test-token-placeholder';
|
|
4
|
-
const BASE_PROMPTS = `http://localhost:3456/prompts.html?token=${TOKEN}`;
|
|
5
|
-
const BASE_MAIN = `http://localhost:3456/?token=${TOKEN}`;
|
|
6
|
-
|
|
7
|
-
async function runTest() {
|
|
8
|
-
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
9
|
-
|
|
10
|
-
// --- Test 1: API - Track prompt usage ---
|
|
11
|
-
console.log('=== API Tests ===');
|
|
12
|
-
const http = require('http');
|
|
13
|
-
const testSessionId = 'test-session-' + Date.now();
|
|
14
|
-
|
|
15
|
-
// Track usage for prompt 3
|
|
16
|
-
const trackRes = await fetch(`http://localhost:3456/api/prompts/3/usage?token=${TOKEN}`, {
|
|
17
|
-
method: 'POST',
|
|
18
|
-
headers: { 'Content-Type': 'application/json' },
|
|
19
|
-
body: JSON.stringify({ session_id: testSessionId }),
|
|
20
|
-
});
|
|
21
|
-
const trackData = await trackRes.json();
|
|
22
|
-
console.log('1. Track usage:', trackData);
|
|
23
|
-
|
|
24
|
-
// Get usage stats
|
|
25
|
-
const statsRes = await fetch(`http://localhost:3456/api/prompts/3/usage?token=${TOKEN}`);
|
|
26
|
-
const stats = await statsRes.json();
|
|
27
|
-
console.log('2. Usage stats:', stats);
|
|
28
|
-
console.log(' use_count > 0:', stats.use_count > 0);
|
|
29
|
-
|
|
30
|
-
// Get usage sessions
|
|
31
|
-
const sessionsRes = await fetch(`http://localhost:3456/api/prompts/3/usage/sessions?token=${TOKEN}`);
|
|
32
|
-
const sessions = await sessionsRes.json();
|
|
33
|
-
console.log('3. Usage sessions:', sessions.length, 'entries');
|
|
34
|
-
console.log(' Latest session:', sessions[0]?.session_id);
|
|
35
|
-
|
|
36
|
-
// Get session prompts (reverse lookup)
|
|
37
|
-
const sessionPromptsRes = await fetch(`http://localhost:3456/api/session-prompts?session_id=${testSessionId}&token=${TOKEN}`);
|
|
38
|
-
const sessionPrompts = await sessionPromptsRes.json();
|
|
39
|
-
console.log('4. Session prompts:', sessionPrompts.length, 'entries');
|
|
40
|
-
console.log(' First prompt title:', sessionPrompts[0]?.title);
|
|
41
|
-
|
|
42
|
-
// --- Test 2: UI - Dropdown on prompts page ---
|
|
43
|
-
console.log('\n=== UI Tests ===');
|
|
44
|
-
const page = await browser.newPage();
|
|
45
|
-
page.on('pageerror', err => console.error('[PAGE ERROR]', err.message));
|
|
46
|
-
await page.setViewport({ width: 1920, height: 1080 });
|
|
47
|
-
await page.goto(BASE_PROMPTS, { waitUntil: 'networkidle2', timeout: 15000 });
|
|
48
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
49
|
-
|
|
50
|
-
// Open prompt 3
|
|
51
|
-
await page.goto(BASE_PROMPTS + '#prompt=3', { waitUntil: 'networkidle2', timeout: 15000 });
|
|
52
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
53
|
-
|
|
54
|
-
// Check usage badge appears (since we just tracked usage)
|
|
55
|
-
const badgeText = await page.$eval('#usage-badge-wrap', el => el.textContent.trim()).catch(() => '');
|
|
56
|
-
console.log('5. Usage badge text:', badgeText || '(empty)');
|
|
57
|
-
console.log(' Badge shows usage:', badgeText.includes('Used'));
|
|
58
|
-
|
|
59
|
-
// Open dropdown and screenshot
|
|
60
|
-
const caret = await page.$('.send-dropdown-wrap .btn-caret');
|
|
61
|
-
if (caret) await caret.click();
|
|
62
|
-
await new Promise(r => setTimeout(r, 300));
|
|
63
|
-
|
|
64
|
-
// Crop screenshot of just the dropdown area
|
|
65
|
-
const dropWrap = await page.$('.send-dropdown-wrap');
|
|
66
|
-
if (dropWrap) {
|
|
67
|
-
await dropWrap.screenshot({ path: 'tests/screenshots/send-04-dropdown-detail.png' });
|
|
68
|
-
}
|
|
69
|
-
await page.screenshot({ path: 'tests/screenshots/send-05-with-badge.png' });
|
|
70
|
-
|
|
71
|
-
// Test copy as text
|
|
72
|
-
// We can't easily verify clipboard in headless, but we can verify the function exists
|
|
73
|
-
const hasCopyFn = await page.evaluate(() => typeof copyPromptText === 'function');
|
|
74
|
-
console.log('6. copyPromptText function exists:', hasCopyFn);
|
|
75
|
-
|
|
76
|
-
const hasCopyMdFn = await page.evaluate(() => typeof copyPromptMarkdown === 'function');
|
|
77
|
-
console.log('7. copyPromptMarkdown function exists:', hasCopyMdFn);
|
|
78
|
-
|
|
79
|
-
// Test htmlToMarkdown
|
|
80
|
-
const md = await page.evaluate(() => htmlToMarkdown('<h1>Title</h1><p>Hello <strong>world</strong></p><ul><li>Item 1</li><li>Item 2</li></ul>'));
|
|
81
|
-
console.log('8. HTML to Markdown:', JSON.stringify(md));
|
|
82
|
-
|
|
83
|
-
// --- Test 3: Main page shows linked prompts ---
|
|
84
|
-
console.log('\n=== Main Page Tests ===');
|
|
85
|
-
const mainPage = await browser.newPage();
|
|
86
|
-
mainPage.on('pageerror', err => console.error('[MAIN PAGE ERROR]', err.message));
|
|
87
|
-
await mainPage.setViewport({ width: 1920, height: 1080 });
|
|
88
|
-
await mainPage.goto(BASE_MAIN, { waitUntil: 'networkidle2', timeout: 15000 });
|
|
89
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
90
|
-
|
|
91
|
-
// Check that refreshSessionPrompts and openPromptInEditor functions exist
|
|
92
|
-
const hasRefresh = await mainPage.evaluate(() => typeof refreshSessionPrompts === 'function');
|
|
93
|
-
console.log('9. refreshSessionPrompts exists:', hasRefresh);
|
|
94
|
-
const hasOpenPrompt = await mainPage.evaluate(() => typeof openPromptInEditor === 'function');
|
|
95
|
-
console.log('10. openPromptInEditor exists:', hasOpenPrompt);
|
|
96
|
-
|
|
97
|
-
// Check hash routing function exists
|
|
98
|
-
const hasHashCheck = await mainPage.evaluate(() => typeof checkHashSession === 'function');
|
|
99
|
-
console.log('11. checkHashSession exists:', hasHashCheck);
|
|
100
|
-
|
|
101
|
-
await mainPage.screenshot({ path: 'tests/screenshots/send-06-main-page.png' });
|
|
102
|
-
|
|
103
|
-
await browser.close();
|
|
104
|
-
console.log('\nAll integration tests complete.');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
runTest();
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const puppeteer = require('puppeteer');
|
|
3
|
-
const TOKEN = process.env.CTM_TEST_TOKEN || 'test-token-placeholder';
|
|
4
|
-
const BASE = `http://localhost:3456/prompts.html?token=${TOKEN}`;
|
|
5
|
-
|
|
6
|
-
async function runTest() {
|
|
7
|
-
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
8
|
-
const page = await browser.newPage();
|
|
9
|
-
await page.setViewport({ width: 1920, height: 1080 });
|
|
10
|
-
|
|
11
|
-
// Open prompt 3 directly
|
|
12
|
-
await page.goto(BASE + '#prompt=3', { waitUntil: 'networkidle2', timeout: 15000 });
|
|
13
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
14
|
-
|
|
15
|
-
// Screenshot the meta-bar area showing the Send button and usage badge
|
|
16
|
-
const metaBar = await page.$('#meta-bar');
|
|
17
|
-
if (metaBar) await metaBar.screenshot({ path: 'tests/screenshots/send-07-metabar.png' });
|
|
18
|
-
|
|
19
|
-
// Open the dropdown
|
|
20
|
-
const caret = await page.$('.send-dropdown-wrap .btn-caret');
|
|
21
|
-
if (caret) await caret.click();
|
|
22
|
-
await new Promise(r => setTimeout(r, 300));
|
|
23
|
-
|
|
24
|
-
// Screenshot the top-right area with dropdown open
|
|
25
|
-
await page.screenshot({
|
|
26
|
-
path: 'tests/screenshots/send-08-dropdown-cropped.png',
|
|
27
|
-
clip: { x: 800, y: 0, width: 600, height: 400 }
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
await browser.close();
|
|
31
|
-
console.log('Visual screenshots saved.');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
runTest();
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const puppeteer = require('puppeteer');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const TOKEN = process.env.CTM_TEST_TOKEN || 'test-token-placeholder';
|
|
6
|
-
const BASE_URL = `http://localhost:3456/?token=${TOKEN}`;
|
|
7
|
-
const SCREENSHOT_DIR = path.join(__dirname, 'screenshots');
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
if (!fs.existsSync(SCREENSHOT_DIR)) fs.mkdirSync(SCREENSHOT_DIR, { recursive: true });
|
|
11
|
-
|
|
12
|
-
async function screenshot(page, name) {
|
|
13
|
-
const filePath = path.join(SCREENSHOT_DIR, `${name}.png`);
|
|
14
|
-
await page.screenshot({ path: filePath, fullPage: false });
|
|
15
|
-
console.log(` Screenshot: ${filePath}`);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function runTest() {
|
|
19
|
-
const browser = await puppeteer.launch({
|
|
20
|
-
headless: true,
|
|
21
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const page = await browser.newPage();
|
|
25
|
-
await page.setViewport({ width: 1920, height: 1080 });
|
|
26
|
-
|
|
27
|
-
// Capture console messages
|
|
28
|
-
page.on('console', msg => {
|
|
29
|
-
console.log(` [BROWSER ${msg.type()}] ${msg.text()}`);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
page.on('pageerror', err => {
|
|
33
|
-
console.error(` [PAGE ERROR] ${err.message}`);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
// Step 1: Load the page
|
|
38
|
-
console.log('Step 1: Loading page...');
|
|
39
|
-
await page.goto(BASE_URL, { waitUntil: 'networkidle2', timeout: 15000 });
|
|
40
|
-
await screenshot(page, '01-page-loaded');
|
|
41
|
-
|
|
42
|
-
// Check welcome screen is visible
|
|
43
|
-
const welcomeText = await page.$eval('#welcome h2', el => el.textContent);
|
|
44
|
-
console.log(` Welcome text: "${welcomeText}"`);
|
|
45
|
-
if (!welcomeText.includes('Welcome')) throw new Error('Welcome screen not found');
|
|
46
|
-
console.log(' PASS: Welcome screen visible');
|
|
47
|
-
|
|
48
|
-
// Step 2: Click "+ New Session" button
|
|
49
|
-
console.log('\nStep 2: Clicking "+ New Session" button...');
|
|
50
|
-
await page.click('.btn.primary');
|
|
51
|
-
await new Promise(r => setTimeout(r, 500));
|
|
52
|
-
await screenshot(page, '02-modal-opened');
|
|
53
|
-
|
|
54
|
-
// Step 3: Verify modal appears
|
|
55
|
-
console.log('\nStep 3: Verifying modal...');
|
|
56
|
-
const modalVisible = await page.$eval('#new-session-modal', el => !el.classList.contains('hidden'));
|
|
57
|
-
if (!modalVisible) throw new Error('Modal did not appear');
|
|
58
|
-
console.log(' PASS: Modal is visible');
|
|
59
|
-
|
|
60
|
-
// Step 4: Select "Shell" type
|
|
61
|
-
console.log('\nStep 4: Selecting Shell type...');
|
|
62
|
-
await page.select('#ns-type', 'shell');
|
|
63
|
-
await new Promise(r => setTimeout(r, 300));
|
|
64
|
-
await screenshot(page, '03-shell-selected');
|
|
65
|
-
const selectedType = await page.$eval('#ns-type', el => el.value);
|
|
66
|
-
console.log(` Selected type: ${selectedType}`);
|
|
67
|
-
if (selectedType !== 'shell') throw new Error('Shell type not selected');
|
|
68
|
-
console.log(' PASS: Shell type selected');
|
|
69
|
-
|
|
70
|
-
// Step 5: Click "Create" button
|
|
71
|
-
console.log('\nStep 5: Clicking Create...');
|
|
72
|
-
// Find the Create button in the modal
|
|
73
|
-
const createBtn = await page.evaluateHandle(() => {
|
|
74
|
-
const buttons = Array.from(document.querySelectorAll('.modal .btn.primary'));
|
|
75
|
-
return buttons.find(b => b.textContent.includes('Create'));
|
|
76
|
-
});
|
|
77
|
-
if (!createBtn) throw new Error('Create button not found');
|
|
78
|
-
await createBtn.click();
|
|
79
|
-
|
|
80
|
-
// Wait for WebSocket to create session and terminal to render
|
|
81
|
-
console.log(' Waiting for session creation...');
|
|
82
|
-
await new Promise(r => setTimeout(r, 3000));
|
|
83
|
-
await screenshot(page, '04-after-create');
|
|
84
|
-
|
|
85
|
-
// Step 6: Verify terminal session created
|
|
86
|
-
console.log('\nStep 6: Verifying terminal session...');
|
|
87
|
-
|
|
88
|
-
// Check modal is closed
|
|
89
|
-
const modalHidden = await page.$eval('#new-session-modal', el => el.classList.contains('hidden'));
|
|
90
|
-
console.log(` Modal hidden: ${modalHidden}`);
|
|
91
|
-
|
|
92
|
-
// Check if a tab was created
|
|
93
|
-
const tabs = await page.$$('#tabbar .tab');
|
|
94
|
-
console.log(` Tabs found: ${tabs.length}`);
|
|
95
|
-
|
|
96
|
-
// Check if terminal container exists and is active
|
|
97
|
-
const activeTerms = await page.$$('.term-container.active');
|
|
98
|
-
console.log(` Active terminal containers: ${activeTerms.length}`);
|
|
99
|
-
|
|
100
|
-
// Check if xterm rendered
|
|
101
|
-
const xtermExists = await page.$('.term-container.active .xterm');
|
|
102
|
-
console.log(` xterm element exists: ${!!xtermExists}`);
|
|
103
|
-
|
|
104
|
-
// Check sidebar session list
|
|
105
|
-
const sessionItems = await page.$$('.session-item');
|
|
106
|
-
console.log(` Session items in sidebar: ${sessionItems.length}`);
|
|
107
|
-
|
|
108
|
-
// Check welcome is hidden
|
|
109
|
-
const welcomeDisplay = await page.$eval('#welcome', el => getComputedStyle(el).display);
|
|
110
|
-
console.log(` Welcome display: ${welcomeDisplay}`);
|
|
111
|
-
|
|
112
|
-
await screenshot(page, '05-terminal-session');
|
|
113
|
-
|
|
114
|
-
if (activeTerms.length > 0 && xtermExists) {
|
|
115
|
-
console.log('\n PASS: Terminal session created and visible!');
|
|
116
|
-
} else {
|
|
117
|
-
// Dump more debug info
|
|
118
|
-
const allContainers = await page.$$('.term-container');
|
|
119
|
-
console.log(` Total term containers: ${allContainers.length}`);
|
|
120
|
-
const html = await page.$eval('#terminal-area', el => el.innerHTML.substring(0, 2000));
|
|
121
|
-
console.log(` terminal-area HTML: ${html}`);
|
|
122
|
-
|
|
123
|
-
// Check WebSocket state
|
|
124
|
-
const wsState = await page.evaluate(() => {
|
|
125
|
-
return state.ws ? state.ws.readyState : 'no ws';
|
|
126
|
-
});
|
|
127
|
-
console.log(` WebSocket state: ${wsState}`);
|
|
128
|
-
|
|
129
|
-
const sessionsMap = await page.evaluate(() => {
|
|
130
|
-
return { size: state.sessions.size, tabs: state.tabOrder };
|
|
131
|
-
});
|
|
132
|
-
console.log(` Sessions map size: ${sessionsMap.size}, tabs: ${JSON.stringify(sessionsMap.tabs)}`);
|
|
133
|
-
|
|
134
|
-
throw new Error('Terminal session not visible');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
console.log('\n=== ALL TESTS PASSED ===');
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error(`\n FAIL: ${error.message}`);
|
|
140
|
-
await screenshot(page, 'error-state');
|
|
141
|
-
process.exitCode = 1;
|
|
142
|
-
} finally {
|
|
143
|
-
await browser.close();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
runTest();
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const puppeteer = require('puppeteer');
|
|
3
|
-
const TOKEN = process.env.CTM_TEST_TOKEN || 'test-token-placeholder';
|
|
4
|
-
|
|
5
|
-
async function runTest() {
|
|
6
|
-
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
7
|
-
const page = await browser.newPage();
|
|
8
|
-
page.on('pageerror', err => console.error('[PAGE ERROR]', err.message));
|
|
9
|
-
await page.setViewport({ width: 1920, height: 1080 });
|
|
10
|
-
await page.goto(`http://localhost:3456/prompts.html?token=${TOKEN}`, { waitUntil: 'networkidle2', timeout: 15000 });
|
|
11
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
12
|
-
|
|
13
|
-
// Screenshot 1: Initial sidebar
|
|
14
|
-
const sidebar = await page.$('#prompt-sidebar');
|
|
15
|
-
if (sidebar) await sidebar.screenshot({ path: 'tests/screenshots/sidebar-01-initial.png' });
|
|
16
|
-
|
|
17
|
-
// Click Email Cron v2
|
|
18
|
-
const items = await page.$$('.prompt-item');
|
|
19
|
-
for (const item of items) {
|
|
20
|
-
const title = await item.$eval('.prompt-title', el => el.textContent.trim());
|
|
21
|
-
if (title.includes('Email Cron')) { await item.click(); break; }
|
|
22
|
-
}
|
|
23
|
-
await new Promise(r => setTimeout(r, 500));
|
|
24
|
-
|
|
25
|
-
// Screenshot 2: Sidebar with active prompt
|
|
26
|
-
if (sidebar) await sidebar.screenshot({ path: 'tests/screenshots/sidebar-02-active.png' });
|
|
27
|
-
|
|
28
|
-
// Hover over a prompt to show actions
|
|
29
|
-
const firstItem = await page.$('.prompt-item');
|
|
30
|
-
if (firstItem) {
|
|
31
|
-
await firstItem.hover();
|
|
32
|
-
await new Promise(r => setTimeout(r, 300));
|
|
33
|
-
}
|
|
34
|
-
if (sidebar) await sidebar.screenshot({ path: 'tests/screenshots/sidebar-03-hover.png' });
|
|
35
|
-
|
|
36
|
-
// Test: Edit title in meta bar and check sidebar sync
|
|
37
|
-
await page.evaluate(() => {
|
|
38
|
-
const input = document.getElementById('prompt-title-input');
|
|
39
|
-
input.value = 'Email Cron v2 - Updated';
|
|
40
|
-
input.dispatchEvent(new Event('input'));
|
|
41
|
-
});
|
|
42
|
-
await new Promise(r => setTimeout(r, 300));
|
|
43
|
-
if (sidebar) await sidebar.screenshot({ path: 'tests/screenshots/sidebar-04-title-sync.png' });
|
|
44
|
-
|
|
45
|
-
// Restore title
|
|
46
|
-
await page.evaluate(() => {
|
|
47
|
-
const input = document.getElementById('prompt-title-input');
|
|
48
|
-
input.value = 'Email Cron v2';
|
|
49
|
-
input.dispatchEvent(new Event('input'));
|
|
50
|
-
});
|
|
51
|
-
await new Promise(r => setTimeout(r, 300));
|
|
52
|
-
|
|
53
|
-
// Test: Click pin button
|
|
54
|
-
const pinBtn = await page.$('.prompt-item .act-btn');
|
|
55
|
-
if (pinBtn) {
|
|
56
|
-
await page.evaluate(() => {
|
|
57
|
-
// Hover on first prompt item to show actions
|
|
58
|
-
const item = document.querySelector('.prompt-item');
|
|
59
|
-
item.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
|
|
60
|
-
});
|
|
61
|
-
await new Promise(r => setTimeout(r, 300));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Full page screenshot
|
|
65
|
-
await page.screenshot({ path: 'tests/screenshots/sidebar-05-full.png' });
|
|
66
|
-
|
|
67
|
-
// Check drag handles exist
|
|
68
|
-
const handles = await page.$$('.drag-handle');
|
|
69
|
-
console.log('Drag handles found:', handles.length);
|
|
70
|
-
|
|
71
|
-
// Check action buttons exist
|
|
72
|
-
const actionBtns = await page.$$('.prompt-actions .act-btn');
|
|
73
|
-
console.log('Action buttons found:', actionBtns.length);
|
|
74
|
-
|
|
75
|
-
// Check folder actions
|
|
76
|
-
const folderActions = await page.$$('.folder-actions .act-btn');
|
|
77
|
-
console.log('Folder action buttons found:', folderActions.length);
|
|
78
|
-
|
|
79
|
-
await browser.close();
|
|
80
|
-
console.log('\nScreenshots saved.');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
runTest();
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const puppeteer = require('puppeteer');
|
|
3
|
-
const TOKEN = process.env.CTM_TEST_TOKEN || 'test-token-placeholder';
|
|
4
|
-
const BASE = `http://localhost:3456/prompts.html?token=${TOKEN}`;
|
|
5
|
-
|
|
6
|
-
async function runTest() {
|
|
7
|
-
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
8
|
-
const page = await browser.newPage();
|
|
9
|
-
page.on('pageerror', err => console.error('[PAGE ERROR]', err.message));
|
|
10
|
-
await page.setViewport({ width: 1920, height: 1080 });
|
|
11
|
-
|
|
12
|
-
// Test 1: Open page, click a prompt, check URL updates
|
|
13
|
-
await page.goto(BASE, { waitUntil: 'networkidle2', timeout: 15000 });
|
|
14
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
15
|
-
console.log('1. Initial URL:', page.url());
|
|
16
|
-
|
|
17
|
-
// Click Email Cron v2
|
|
18
|
-
const items = await page.$$('.prompt-item');
|
|
19
|
-
for (const item of items) {
|
|
20
|
-
const title = await item.$eval('.prompt-title', el => el.textContent.trim().replace(/^📌/, '').trim());
|
|
21
|
-
if (title.includes('Email Cron')) { await item.click(); break; }
|
|
22
|
-
}
|
|
23
|
-
await new Promise(r => setTimeout(r, 500));
|
|
24
|
-
console.log('2. After opening prompt:', page.url());
|
|
25
|
-
|
|
26
|
-
// Test 2: Click a view tab
|
|
27
|
-
await page.click('button[onclick="showView(\'chains\')"]');
|
|
28
|
-
await new Promise(r => setTimeout(r, 500));
|
|
29
|
-
console.log('3. After chains view:', page.url());
|
|
30
|
-
|
|
31
|
-
// Test 3: Navigate to a deep link directly
|
|
32
|
-
await page.goto(BASE + '#prompt=3', { waitUntil: 'networkidle2', timeout: 15000 });
|
|
33
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
34
|
-
const title = await page.$eval('#prompt-title-input', el => el.value);
|
|
35
|
-
console.log('4. Deep link to prompt=3:', page.url());
|
|
36
|
-
console.log(' Loaded title:', title);
|
|
37
|
-
|
|
38
|
-
// Take screenshot of deep-linked page
|
|
39
|
-
await page.screenshot({ path: 'tests/screenshots/url-01-deeplink.png' });
|
|
40
|
-
|
|
41
|
-
// Test 4: Navigate to a view deep link
|
|
42
|
-
await page.goto(BASE + '#view=backups', { waitUntil: 'networkidle2', timeout: 15000 });
|
|
43
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
44
|
-
const backupsVisible = await page.$eval('#view-backups', el => el.style.display);
|
|
45
|
-
console.log('5. Deep link to view=backups:', page.url());
|
|
46
|
-
console.log(' Backups view visible:', backupsVisible);
|
|
47
|
-
|
|
48
|
-
// Test 5: Folder deep link
|
|
49
|
-
await page.goto(BASE + '#folder=1', { waitUntil: 'networkidle2', timeout: 15000 });
|
|
50
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
51
|
-
const activeFolder = await page.$eval('.folder-item.active .folder-name', el => el.textContent).catch(() => 'none');
|
|
52
|
-
console.log('6. Deep link to folder=1:', page.url());
|
|
53
|
-
console.log(' Active folder:', activeFolder);
|
|
54
|
-
|
|
55
|
-
// Test 6: Select folder, then open prompt — URL has both
|
|
56
|
-
await page.goto(BASE, { waitUntil: 'networkidle2', timeout: 15000 });
|
|
57
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
58
|
-
// Click first prompt
|
|
59
|
-
const items2 = await page.$$('.prompt-item');
|
|
60
|
-
if (items2.length) await items2[0].click();
|
|
61
|
-
await new Promise(r => setTimeout(r, 500));
|
|
62
|
-
console.log('7. Prompt in folder:', page.url());
|
|
63
|
-
|
|
64
|
-
await browser.close();
|
|
65
|
-
console.log('\nAll URL hash tests passed.');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
runTest();
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const puppeteer = require('puppeteer');
|
|
3
|
-
const TOKEN = process.env.CTM_TEST_TOKEN || 'test-token-placeholder';
|
|
4
|
-
|
|
5
|
-
async function runTest() {
|
|
6
|
-
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
7
|
-
const page = await browser.newPage();
|
|
8
|
-
await page.setViewport({ width: 1920, height: 1080 });
|
|
9
|
-
await page.goto(`http://localhost:3456/prompts.html?token=${TOKEN}`, { waitUntil: 'networkidle2', timeout: 15000 });
|
|
10
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
11
|
-
|
|
12
|
-
const items = await page.$$('.prompt-item');
|
|
13
|
-
for (const item of items) {
|
|
14
|
-
const title = await item.$eval('.prompt-title', el => el.textContent.trim());
|
|
15
|
-
if (title.includes('Email Cron')) { await item.click(); break; }
|
|
16
|
-
}
|
|
17
|
-
await new Promise(r => setTimeout(r, 800));
|
|
18
|
-
|
|
19
|
-
// Crop just the document card area
|
|
20
|
-
const editorDiv = await page.$('#editor');
|
|
21
|
-
if (editorDiv) {
|
|
22
|
-
const box = await editorDiv.boundingBox();
|
|
23
|
-
const clip = {
|
|
24
|
-
x: box.x - 10,
|
|
25
|
-
y: box.y - 10,
|
|
26
|
-
width: box.width + 20,
|
|
27
|
-
height: Math.min(box.height + 20, 750),
|
|
28
|
-
};
|
|
29
|
-
await page.screenshot({ path: 'tests/screenshots/ux-08-document-card.png', clip });
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
await browser.close();
|
|
33
|
-
}
|
|
34
|
-
runTest();
|