@hanzo/browser-extension 1.7.6 → 1.9.31
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 +9 -18
- package/dist/browser-extension/README.md +1 -1
- package/dist/browser-extension/background-firefox.js +4180 -499
- package/dist/browser-extension/background.js +6932 -2419
- package/dist/browser-extension/browser-control.js +652 -18
- package/dist/browser-extension/chrome/background.js +6932 -2419
- package/dist/browser-extension/chrome/content-script.js +1642 -123
- package/dist/browser-extension/chrome/icon128.png +0 -0
- package/dist/browser-extension/chrome/icon16.png +0 -0
- package/dist/browser-extension/chrome/icon48.png +0 -0
- package/dist/browser-extension/chrome/manifest.json +20 -11
- package/dist/browser-extension/chrome/popup.css +352 -110
- package/dist/browser-extension/chrome/popup.html +159 -48
- package/dist/browser-extension/chrome/popup.js +1418 -46
- package/dist/browser-extension/chrome/sidebar.css +772 -156
- package/dist/browser-extension/chrome/sidebar.html +268 -85
- package/dist/browser-extension/chrome/sidebar.js +25846 -84
- package/dist/browser-extension/cli.js +2 -2
- package/dist/browser-extension/content-script.js +1642 -123
- package/dist/browser-extension/firefox/background.js +4180 -499
- package/dist/browser-extension/firefox/content-script.js +1642 -123
- package/dist/browser-extension/firefox/icon128.png +0 -0
- package/dist/browser-extension/firefox/icon16.png +0 -0
- package/dist/browser-extension/firefox/icon48.png +0 -0
- package/dist/browser-extension/firefox/manifest.json +24 -13
- package/dist/browser-extension/firefox/popup.css +352 -110
- package/dist/browser-extension/firefox/popup.html +159 -48
- package/dist/browser-extension/firefox/popup.js +1418 -46
- package/dist/browser-extension/firefox/sidebar.css +772 -156
- package/dist/browser-extension/firefox/sidebar.html +268 -85
- package/dist/browser-extension/firefox/sidebar.js +25846 -84
- package/dist/browser-extension/icon128.png +0 -0
- package/dist/browser-extension/icon16.png +0 -0
- package/dist/browser-extension/icon48.png +0 -0
- package/dist/browser-extension/manifest.json +20 -11
- package/dist/browser-extension/package.json +1 -1
- package/dist/browser-extension/popup.js +1418 -46
- package/dist/browser-extension/safari/Info.plist +3 -3
- package/dist/browser-extension/safari/background.js +6932 -2419
- package/dist/browser-extension/safari/content-script.js +1642 -123
- package/dist/browser-extension/safari/icon128.png +0 -0
- package/dist/browser-extension/safari/icon16.png +0 -0
- package/dist/browser-extension/safari/icon48.png +0 -0
- package/dist/browser-extension/safari/popup.css +352 -110
- package/dist/browser-extension/safari/popup.html +159 -48
- package/dist/browser-extension/safari/popup.js +1418 -46
- package/dist/browser-extension/safari/sidebar.css +772 -156
- package/dist/browser-extension/safari/sidebar.html +268 -85
- package/dist/browser-extension/safari/sidebar.js +25846 -84
- package/dist/browser-extension/sidebar.js +25846 -84
- package/dist/browser-extension/webgpu-ai.js +78 -5
- package/images/icon128.png +0 -0
- package/images/icon16.png +0 -0
- package/images/icon48.png +0 -0
- package/manifest.json +7 -2
- package/package.json +27 -10
- package/src/chat-widget.tsx +96 -0
- package/src/gui-primitives.tsx +116 -0
- package/src/icon128.png +0 -0
- package/src/icon16.png +0 -0
- package/src/icon32.png +0 -0
- package/src/icon48.png +0 -0
- package/src/manifest-firefox.json +23 -12
- package/src/manifest.json +19 -10
- package/src/package.json +1 -1
- package/src/popup.css +352 -110
- package/src/popup.html +159 -48
- package/src/popup.js +196 -0
- package/src/sidebar.css +772 -156
- package/src/sidebar.html +268 -85
- package/src/sidebar.js +885 -0
- package/.github/workflows/docs.yml +0 -44
- package/.github/workflows/release.yml +0 -55
- package/README.md +0 -126
- package/dist/browser-extension/ai-worker.js +0 -30
- package/dist/browser-extension/cdp-bridge-server.js +0 -444
- package/dist/browser-extension/chrome/icon32.png +0 -0
- package/dist/browser-extension/firefox/icon32.png +0 -0
- package/dist/browser-extension/hanzo-browser-extension-firefox-1.1.0.zip +0 -0
- package/dist/browser-extension/icon32.png +0 -0
- package/dist/browser-extension/popup.css +0 -468
- package/dist/browser-extension/popup.html +0 -191
- package/dist/browser-extension/safari/icon32.png +0 -0
- package/dist/browser-extension/sidebar.css +0 -718
- package/dist/browser-extension/sidebar.html +0 -184
- package/dist/cdp-bridge-server.js +0 -411
- package/dist/hanzo-ai-chrome-1.7.2.zip +0 -0
- package/dist/hanzo-ai-chrome-1.7.3.zip +0 -0
- package/dist/hanzo-ai-firefox-1.7.2.zip +0 -0
- package/dist/hanzo-ai-firefox-1.7.3.zip +0 -0
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.pbxproj +0 -1047
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json +0 -11
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -98
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/universal-icon-1024@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/Contents.json +0 -6
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json +0 -21
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/icon128.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Base.lproj/Main.html +0 -20
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Icon.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Script.js +0 -24
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Style.css +0 -61
- package/dist/safari/Hanzo AI/Shared (App)/ViewController.swift +0 -81
- package/dist/safari/Hanzo AI/Shared (Extension)/SafariWebExtensionHandler.swift +0 -42
- package/dist/safari/Hanzo AI/iOS (App)/AppDelegate.swift +0 -24
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/LaunchScreen.storyboard +0 -36
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/Main.storyboard +0 -38
- package/dist/safari/Hanzo AI/iOS (App)/Info.plist +0 -27
- package/dist/safari/Hanzo AI/iOS (App)/SceneDelegate.swift +0 -18
- package/dist/safari/Hanzo AI/iOS (Extension)/Info.plist +0 -13
- package/dist/safari/Hanzo AI/macOS (App)/AppDelegate.swift +0 -21
- package/dist/safari/Hanzo AI/macOS (App)/Base.lproj/Main.storyboard +0 -124
- package/dist/safari/Hanzo AI/macOS (App)/Info.plist +0 -8
- package/dist/safari/Hanzo AI/macOS (Extension)/Info.plist +0 -13
- package/docs/_config.yml +0 -13
- package/docs/_layouts/default.html +0 -435
- package/docs/index.md +0 -54
- package/hanzo-ai-chrome-v1.7.6.zip +0 -0
- package/hanzo-ai-firefox-v1.7.6.zip +0 -0
- package/install-extension.sh +0 -75
- package/test/claude-integration.test.ts +0 -251
- package/test/server-frameworks-edge-cases.test.ts +0 -145
- package/test/server-frameworks.test.ts +0 -395
- package/test-cdp-connection.js +0 -140
- package/tsconfig.json +0 -19
package/install-extension.sh
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Hanzo Browser Extension Installer
|
|
3
|
-
# Provides instructions and opens browser extension pages
|
|
4
|
-
|
|
5
|
-
EXTENSION_DIR="$(dirname "$0")/dist/browser-extension"
|
|
6
|
-
|
|
7
|
-
echo "=== Hanzo Browser Extension Installer ==="
|
|
8
|
-
echo ""
|
|
9
|
-
echo "Extension directories:"
|
|
10
|
-
echo " Chrome: $EXTENSION_DIR/chrome"
|
|
11
|
-
echo " Firefox: $EXTENSION_DIR/firefox"
|
|
12
|
-
echo " Safari: $EXTENSION_DIR/safari (requires Xcode)"
|
|
13
|
-
echo ""
|
|
14
|
-
|
|
15
|
-
# Check if servers are running
|
|
16
|
-
if lsof -i :3001 > /dev/null 2>&1; then
|
|
17
|
-
echo "✓ Browser Extension Server running on port 3001"
|
|
18
|
-
else
|
|
19
|
-
echo "✗ Browser Extension Server NOT running"
|
|
20
|
-
echo " Start with: node dist/browser-extension/cli.js start"
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
if lsof -i :9223 > /dev/null 2>&1; then
|
|
24
|
-
echo "✓ CDP Bridge Server running on port 9223"
|
|
25
|
-
else
|
|
26
|
-
echo "✗ CDP Bridge Server NOT running"
|
|
27
|
-
echo " Start with: node dist/cdp-bridge-server.js"
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
echo ""
|
|
31
|
-
echo "=== Installation Instructions ==="
|
|
32
|
-
echo ""
|
|
33
|
-
|
|
34
|
-
# Chrome
|
|
35
|
-
echo "CHROME:"
|
|
36
|
-
echo " 1. Open chrome://extensions/"
|
|
37
|
-
echo " 2. Enable 'Developer mode'"
|
|
38
|
-
echo " 3. Click 'Load unpacked'"
|
|
39
|
-
echo " 4. Select: $EXTENSION_DIR"
|
|
40
|
-
echo ""
|
|
41
|
-
|
|
42
|
-
# Firefox
|
|
43
|
-
echo "FIREFOX:"
|
|
44
|
-
echo " 1. Open about:debugging#/runtime/this-firefox"
|
|
45
|
-
echo " 2. Click 'Load Temporary Add-on...'"
|
|
46
|
-
echo " 3. Select: $EXTENSION_DIR/firefox/manifest.json"
|
|
47
|
-
echo ""
|
|
48
|
-
|
|
49
|
-
# Safari
|
|
50
|
-
echo "SAFARI:"
|
|
51
|
-
echo " 1. Open Xcode"
|
|
52
|
-
echo " 2. Create new Safari Web Extension project"
|
|
53
|
-
echo " 3. Copy files from: $EXTENSION_DIR/safari/"
|
|
54
|
-
echo " 4. Build and run"
|
|
55
|
-
echo ""
|
|
56
|
-
|
|
57
|
-
# Open browser extension pages based on arguments
|
|
58
|
-
case "$1" in
|
|
59
|
-
chrome)
|
|
60
|
-
open -a "Google Chrome" "chrome://extensions/"
|
|
61
|
-
;;
|
|
62
|
-
firefox)
|
|
63
|
-
open -a "Firefox Developer Edition" "about:debugging#/runtime/this-firefox" 2>/dev/null || \
|
|
64
|
-
open -a "Firefox" "about:debugging#/runtime/this-firefox"
|
|
65
|
-
;;
|
|
66
|
-
all)
|
|
67
|
-
open -a "Google Chrome" "chrome://extensions/"
|
|
68
|
-
open -a "Firefox Developer Edition" "about:debugging#/runtime/this-firefox" 2>/dev/null || \
|
|
69
|
-
open -a "Firefox" "about:debugging#/runtime/this-firefox"
|
|
70
|
-
;;
|
|
71
|
-
*)
|
|
72
|
-
echo "Usage: $0 [chrome|firefox|all]"
|
|
73
|
-
echo " Opens the extension management page in the specified browser"
|
|
74
|
-
;;
|
|
75
|
-
esac
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { BrowserExtensionServer } from '../src/browser-extension-server';
|
|
3
|
-
import WebSocket from 'ws';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import * as fs from 'fs';
|
|
6
|
-
|
|
7
|
-
describe('Claude Code Browser Extension Integration', () => {
|
|
8
|
-
let server: BrowserExtensionServer;
|
|
9
|
-
let ws: WebSocket;
|
|
10
|
-
const TEST_PORT = 3002;
|
|
11
|
-
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
// Start server
|
|
14
|
-
server = new BrowserExtensionServer(TEST_PORT, process.cwd());
|
|
15
|
-
|
|
16
|
-
// Wait for server to be ready
|
|
17
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
18
|
-
|
|
19
|
-
// Connect WebSocket
|
|
20
|
-
ws = new WebSocket(`ws://localhost:${TEST_PORT}/browser-extension`);
|
|
21
|
-
await new Promise(resolve => ws.on('open', resolve));
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
ws.close();
|
|
26
|
-
server.close();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
describe('React source-map integration', () => {
|
|
30
|
-
it('should handle React component with __source', async () => {
|
|
31
|
-
const elementSelected = new Promise((resolve) => {
|
|
32
|
-
server.on('elementSelected', resolve);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Simulate React element click with source-map data
|
|
36
|
-
ws.send(JSON.stringify({
|
|
37
|
-
event: 'elementSelected',
|
|
38
|
-
framework: 'react',
|
|
39
|
-
domPath: 'div#root > div.App > button.submit-btn',
|
|
40
|
-
source: {
|
|
41
|
-
file: 'src/components/SubmitButton.tsx',
|
|
42
|
-
line: 42,
|
|
43
|
-
column: 12
|
|
44
|
-
}
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
|
-
const result = await elementSelected;
|
|
48
|
-
|
|
49
|
-
expect(result).toMatchObject({
|
|
50
|
-
file: expect.stringContaining('SubmitButton.tsx'),
|
|
51
|
-
line: 42,
|
|
52
|
-
column: 12,
|
|
53
|
-
framework: 'react'
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should resolve webpack:// protocol paths', async () => {
|
|
58
|
-
const elementSelected = new Promise((resolve) => {
|
|
59
|
-
server.on('elementSelected', resolve);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
ws.send(JSON.stringify({
|
|
63
|
-
event: 'elementSelected',
|
|
64
|
-
framework: 'react',
|
|
65
|
-
domPath: 'header > nav > a',
|
|
66
|
-
source: {
|
|
67
|
-
file: 'webpack://my-app/./src/components/Navigation.jsx',
|
|
68
|
-
line: 15
|
|
69
|
-
}
|
|
70
|
-
}));
|
|
71
|
-
|
|
72
|
-
const result = await elementSelected;
|
|
73
|
-
|
|
74
|
-
expect(result).toMatchObject({
|
|
75
|
-
file: expect.stringContaining('Navigation.jsx'),
|
|
76
|
-
line: 15,
|
|
77
|
-
framework: 'react'
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe('Vue source-map integration', () => {
|
|
83
|
-
it('should handle Vue component with __file', async () => {
|
|
84
|
-
const elementSelected = new Promise((resolve) => {
|
|
85
|
-
server.on('elementSelected', resolve);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
ws.send(JSON.stringify({
|
|
89
|
-
event: 'elementSelected',
|
|
90
|
-
framework: 'vue',
|
|
91
|
-
domPath: 'div#app > main > custom-button',
|
|
92
|
-
source: {
|
|
93
|
-
file: 'src/components/CustomButton.vue',
|
|
94
|
-
line: 28
|
|
95
|
-
}
|
|
96
|
-
}));
|
|
97
|
-
|
|
98
|
-
const result = await elementSelected;
|
|
99
|
-
|
|
100
|
-
expect(result).toMatchObject({
|
|
101
|
-
file: expect.stringContaining('CustomButton.vue'),
|
|
102
|
-
line: 28,
|
|
103
|
-
framework: 'vue'
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('Fallback to data-hanzo-id', () => {
|
|
109
|
-
it('should handle elements without source-map data', async () => {
|
|
110
|
-
// Create the .hanzo directory and id-map.json for test
|
|
111
|
-
const hanzoDir = path.join(process.cwd(), '.hanzo');
|
|
112
|
-
const mapFile = path.join(hanzoDir, 'id-map.json');
|
|
113
|
-
|
|
114
|
-
if (!fs.existsSync(hanzoDir)) {
|
|
115
|
-
fs.mkdirSync(hanzoDir, { recursive: true });
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Write test id map
|
|
119
|
-
fs.writeFileSync(mapFile, JSON.stringify({
|
|
120
|
-
'hanzo-abc123': {
|
|
121
|
-
file: path.join(process.cwd(), 'src/legacy/OldComponent.js'),
|
|
122
|
-
line: 55
|
|
123
|
-
}
|
|
124
|
-
}));
|
|
125
|
-
|
|
126
|
-
const elementSelected = new Promise((resolve) => {
|
|
127
|
-
server.once('elementSelected', resolve);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
ws.send(JSON.stringify({
|
|
131
|
-
event: 'elementSelected',
|
|
132
|
-
framework: null,
|
|
133
|
-
domPath: 'body > div.legacy > span',
|
|
134
|
-
fallbackId: 'hanzo-abc123'
|
|
135
|
-
}));
|
|
136
|
-
|
|
137
|
-
const result = await elementSelected;
|
|
138
|
-
|
|
139
|
-
expect(result).toMatchObject({
|
|
140
|
-
file: expect.stringContaining('OldComponent.js'),
|
|
141
|
-
line: 55
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Cleanup
|
|
145
|
-
if (fs.existsSync(mapFile)) {
|
|
146
|
-
fs.unlinkSync(mapFile);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe('Claude Code MCP integration', () => {
|
|
152
|
-
it('should format events for Claude Code consumption', async () => {
|
|
153
|
-
const events: any[] = [];
|
|
154
|
-
|
|
155
|
-
server.on('elementSelected', (data) => {
|
|
156
|
-
events.push({
|
|
157
|
-
tool: 'browser_navigate_to_source',
|
|
158
|
-
arguments: {
|
|
159
|
-
file: data.file,
|
|
160
|
-
line: data.line,
|
|
161
|
-
column: data.column,
|
|
162
|
-
reason: `User clicked ${data.framework ? data.framework.charAt(0).toUpperCase() + data.framework.slice(1) : 'HTML'} element at ${data.domPath}`
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// Send multiple events
|
|
168
|
-
const testCases = [
|
|
169
|
-
{
|
|
170
|
-
framework: 'react',
|
|
171
|
-
source: { file: 'App.tsx', line: 10, column: 4 },
|
|
172
|
-
domPath: 'div#root > div.App'
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
framework: 'vue',
|
|
176
|
-
source: { file: 'Home.vue', line: 45 },
|
|
177
|
-
domPath: 'div#app > router-view > div.home'
|
|
178
|
-
}
|
|
179
|
-
];
|
|
180
|
-
|
|
181
|
-
for (const testCase of testCases) {
|
|
182
|
-
ws.send(JSON.stringify({
|
|
183
|
-
event: 'elementSelected',
|
|
184
|
-
...testCase
|
|
185
|
-
}));
|
|
186
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
expect(events).toHaveLength(2);
|
|
190
|
-
expect(events[0].tool).toBe('browser_navigate_to_source');
|
|
191
|
-
expect(events[0].arguments.reason).toContain('React element');
|
|
192
|
-
expect(events[1].arguments.reason).toContain('Vue element');
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
describe('Error handling', () => {
|
|
197
|
-
it('should handle malformed messages gracefully', async () => {
|
|
198
|
-
// Server should not crash on bad input
|
|
199
|
-
ws.send('invalid json');
|
|
200
|
-
ws.send(JSON.stringify({ invalid: 'event' }));
|
|
201
|
-
ws.send(JSON.stringify({ event: 'unknown' }));
|
|
202
|
-
|
|
203
|
-
// Give time for messages to process
|
|
204
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
205
|
-
|
|
206
|
-
// Server should still be responsive
|
|
207
|
-
const elementSelected = new Promise((resolve) => {
|
|
208
|
-
server.on('elementSelected', resolve);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
ws.send(JSON.stringify({
|
|
212
|
-
event: 'elementSelected',
|
|
213
|
-
framework: 'react',
|
|
214
|
-
source: { file: 'test.tsx', line: 1 }
|
|
215
|
-
}));
|
|
216
|
-
|
|
217
|
-
const result = await elementSelected;
|
|
218
|
-
expect(result).toBeDefined();
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe('Performance', () => {
|
|
223
|
-
it('should handle rapid clicks efficiently', async () => {
|
|
224
|
-
const events: any[] = [];
|
|
225
|
-
server.on('elementSelected', (data) => events.push(data));
|
|
226
|
-
|
|
227
|
-
// Send 100 events rapidly
|
|
228
|
-
const startTime = Date.now();
|
|
229
|
-
|
|
230
|
-
for (let i = 0; i < 100; i++) {
|
|
231
|
-
ws.send(JSON.stringify({
|
|
232
|
-
event: 'elementSelected',
|
|
233
|
-
framework: 'react',
|
|
234
|
-
domPath: `div#item-${i}`,
|
|
235
|
-
source: {
|
|
236
|
-
file: `Component${i}.tsx`,
|
|
237
|
-
line: i + 1
|
|
238
|
-
}
|
|
239
|
-
}));
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Wait for all events to process
|
|
243
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
244
|
-
|
|
245
|
-
const duration = Date.now() - startTime;
|
|
246
|
-
|
|
247
|
-
expect(events).toHaveLength(100);
|
|
248
|
-
expect(duration).toBeLessThan(1000); // Should process 100 events in under 1 second
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
});
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { ServerFrameworkDetector } from '../src/server-frameworks';
|
|
3
|
-
import { JSDOM } from 'jsdom';
|
|
4
|
-
|
|
5
|
-
describe('Server Framework Detection - Edge Cases', () => {
|
|
6
|
-
let detector: ServerFrameworkDetector;
|
|
7
|
-
let dom: JSDOM;
|
|
8
|
-
let document: Document;
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
detector = new ServerFrameworkDetector();
|
|
12
|
-
dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
13
|
-
document = dom.window.document;
|
|
14
|
-
// @ts-ignore
|
|
15
|
-
global.document = document;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('Django Debug Toolbar extraction', () => {
|
|
19
|
-
it('should handle Django with toolbar but no view info', () => {
|
|
20
|
-
// Create Django debug toolbar
|
|
21
|
-
const toolbar = document.createElement('div');
|
|
22
|
-
toolbar.id = 'djDebug';
|
|
23
|
-
document.body.appendChild(toolbar);
|
|
24
|
-
|
|
25
|
-
const element = document.createElement('div');
|
|
26
|
-
const result = detector.detectDjango(element);
|
|
27
|
-
|
|
28
|
-
expect(result).toMatchObject({
|
|
29
|
-
framework: 'django'
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('Code coverage for edge cases', () => {
|
|
35
|
-
it('should cover Django toolbar with specific panel', () => {
|
|
36
|
-
// Cover the Django Debug Toolbar code path
|
|
37
|
-
const toolbar = document.createElement('div');
|
|
38
|
-
toolbar.id = 'djDebugToolbar';
|
|
39
|
-
const panel = document.createElement('div');
|
|
40
|
-
panel.className = 'djDebugPanelContent';
|
|
41
|
-
const viewInfo = document.createElement('div');
|
|
42
|
-
viewInfo.className = 'view-info';
|
|
43
|
-
viewInfo.textContent = 'View: myapp.views.index';
|
|
44
|
-
panel.appendChild(viewInfo);
|
|
45
|
-
toolbar.appendChild(panel);
|
|
46
|
-
document.body.appendChild(toolbar);
|
|
47
|
-
|
|
48
|
-
const element = document.createElement('div');
|
|
49
|
-
const info = detector['extractDjangoViewInfo']();
|
|
50
|
-
|
|
51
|
-
// Just verify it doesn't crash and returns an object
|
|
52
|
-
expect(info).toBeDefined();
|
|
53
|
-
expect(typeof info).toBe('object');
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('Error handling', () => {
|
|
58
|
-
it('should handle missing Werkzeug traceback gracefully', () => {
|
|
59
|
-
const debuggerEl = document.createElement('div');
|
|
60
|
-
debuggerEl.id = 'werkzeug-debugger';
|
|
61
|
-
document.body.appendChild(debuggerEl);
|
|
62
|
-
|
|
63
|
-
const element = document.createElement('div');
|
|
64
|
-
const result = detector.detectFlask(element);
|
|
65
|
-
|
|
66
|
-
expect(result).toMatchObject({
|
|
67
|
-
framework: 'flask'
|
|
68
|
-
});
|
|
69
|
-
expect(result?.file).toBeUndefined();
|
|
70
|
-
expect(result?.line).toBeUndefined();
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('Rails edge cases', () => {
|
|
75
|
-
it('should handle Rails template without controller extraction', () => {
|
|
76
|
-
document.head.innerHTML = '<meta name="csrf-token" content="abc123">';
|
|
77
|
-
const element = document.createElement('div');
|
|
78
|
-
element.innerHTML = '<!-- BEGIN app/views/show.html.erb --><p>Content</p>';
|
|
79
|
-
|
|
80
|
-
const result = detector.detectRails(element);
|
|
81
|
-
|
|
82
|
-
expect(result).toMatchObject({
|
|
83
|
-
framework: 'rails',
|
|
84
|
-
template: 'app/views/show.html.erb',
|
|
85
|
-
controller: '',
|
|
86
|
-
action: 'show'
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should handle Rails without any comments', () => {
|
|
91
|
-
document.head.innerHTML = '<meta name="csrf-token" content="abc123">';
|
|
92
|
-
const element = document.createElement('div');
|
|
93
|
-
|
|
94
|
-
const result = detector.detectRails(element);
|
|
95
|
-
|
|
96
|
-
expect(result).toMatchObject({
|
|
97
|
-
framework: 'rails'
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
describe('Real browser environment', () => {
|
|
103
|
-
it('should handle createTreeWalker in real browser', () => {
|
|
104
|
-
// Mock NodeFilter
|
|
105
|
-
global.NodeFilter = { SHOW_COMMENT: 8 };
|
|
106
|
-
|
|
107
|
-
// Create a more realistic DOM structure
|
|
108
|
-
const element = document.createElement('div');
|
|
109
|
-
const parent = document.createElement('div');
|
|
110
|
-
parent.appendChild(element);
|
|
111
|
-
document.body.appendChild(parent);
|
|
112
|
-
|
|
113
|
-
// Simulate TreeWalker behavior
|
|
114
|
-
const originalCreateTreeWalker = document.createTreeWalker;
|
|
115
|
-
let walkerCalled = false;
|
|
116
|
-
document.createTreeWalker = function() {
|
|
117
|
-
walkerCalled = true;
|
|
118
|
-
return {
|
|
119
|
-
nextNode: () => null
|
|
120
|
-
};
|
|
121
|
-
} as any;
|
|
122
|
-
|
|
123
|
-
const comments = detector['extractHTMLComments'](element);
|
|
124
|
-
|
|
125
|
-
expect(walkerCalled).toBe(true);
|
|
126
|
-
expect(comments).toEqual([]);
|
|
127
|
-
|
|
128
|
-
// Restore
|
|
129
|
-
document.createTreeWalker = originalCreateTreeWalker;
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('All framework detection - null cases', () => {
|
|
134
|
-
it('should handle all detectors returning null', () => {
|
|
135
|
-
const element = document.createElement('div');
|
|
136
|
-
|
|
137
|
-
// Ensure no framework markers are present
|
|
138
|
-
document.head.innerHTML = '';
|
|
139
|
-
document.body.innerHTML = '<div></div>';
|
|
140
|
-
|
|
141
|
-
const result = detector.detect(element);
|
|
142
|
-
expect(result).toBeNull();
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|