Skip to content

Commit 2065725

Browse files
authored
feat: Implement initial TestPilot browser extension with session management, issue detection, and popup UI. (#1)
* feat: Implement initial TestPilot browser extension with session management, issue detection, and popup UI. * feat: establish core browser extension with session management, issue monitoring, and reporting features via a new popup UI.
1 parent e6ec949 commit 2065725

10 files changed

Lines changed: 733 additions & 191 deletions

File tree

README.md

Lines changed: 168 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,197 @@
11
# 🧪 TestPilot
22

3-
**Intelligent Bug Detection & Telemetry Extension for Modern Web Apps**
4-
5-
TestPilot is a powerful, production-grade Chrome extension designed to catch bugs before your users do. It acts as a black-box flight recorder for your web application, capturing console errors, network failures, slow APIs, performance metrics, and security risks in real-time.
6-
7-
![TestPilot Banner](https://via.placeholder.com/800x200?text=TestPilot+Extension)
8-
9-
## ✨ Key Features
10-
11-
- **🚀 Real-time Telemetry**: Captures `console.error`, `console.warn`, unhandled exceptions, and promise rejections.
12-
- **🌐 Network Intelligence**:
13-
- Detects **Slow APIs** (>1000ms by default)
14-
- Identifies **Retry Storms** (rapid repeated failures)
15-
- Captures **CORS Errors** and HTTP 4xx/5xx failures
16-
- **⚡ Performance Monitoring**:
17-
- **Long Task Detection**: Flags UI freezes (>200ms)
18-
- **White Screen Detection**: Alerts on potential rendering crashes
19-
- **🛡️ Security Scanner**:
20-
- Detects sensitive data leaks (JWTs, API keys, PII) in console/storage
21-
- Monitors unsafe storage access
22-
- **📱 Smart Context**: Captures environment details (User Agent, Viewport, Route Changes) for actionable bug reports.
23-
- **🧠 Adaptive Severity**: Automatically escalates issue severity based on frequency (e.g., repeating errors become Critical).
24-
25-
## 🛠️ Usage
26-
27-
1. **Install the Extension** (Developer Mode):
28-
- Clone this repo
29-
- Run `npm install` and `npm run build`
30-
- Open `chrome://extensions`
31-
- Enable "Developer mode"
32-
- Click "Load unpacked" and select the `dist` folder
33-
34-
2. **Start a Session**:
35-
- Click the extension icon
36-
- Hit **Start Session**
37-
- Interact with your web application
38-
- TestPilot records all hidden issues in the background
39-
40-
3. **Analyze & Export**:
41-
- Open the popup to see a categorized list of issues
42-
- Use **Filters** to focus on Critical/High severity bugs
43-
- Click **Export JSON** or **Export Markdown** to generate a bug report
3+
**Intelligent Bug Detection & Telemetry Chrome Extension for Modern Web Apps**
4+
5+
TestPilot is a production-grade Chrome extension that acts as a silent flight recorder for your web application. It captures bugs, network failures, security risks, and performance issues in real-time — before your users report them.
6+
7+
---
8+
9+
## ✨ Features
10+
11+
### 🖥️ Console Monitoring
12+
- Intercepts `console.error` and `console.warn` calls from deep inside the page (via a **Main World bridge script** that runs before any other JS)
13+
- Captures the **caller file, line, and column** from the stack trace automatically
14+
- Passes every console message through the **Security Scanner** before logging
15+
16+
### 🌐 Network Intelligence
17+
- Wraps both **`window.fetch`** and **`XMLHttpRequest`** to intercept all outgoing requests
18+
- Captures **HTTP 4xx / 5xx** failures as `network_failure` issues
19+
- Detects **Slow APIs** — requests exceeding the configurable threshold (default: 1000ms)
20+
- Detects **API Retry Storms** — automatically escalates to `retry_storm` (Critical) when the same endpoint fails 3+ times within 5 seconds
21+
- Records **request payload**, **response status**, and **response body** (up to 2000 chars) for every failing or slow request
22+
23+
### ⚡ Runtime Crash Detection
24+
- Listens for `window.onerror` to catch **unhandled JavaScript exceptions** with full file/line/column/stack metadata
25+
- Listens for `window.onunhandledrejection` to catch **unhandled Promise rejections**
26+
27+
### 📦 Broken Resource Detection
28+
- Listens on the capture phase for failed loads of `<img>`, `<script>`, and `<link>` tags
29+
- Records the tag type, source URL, and outer HTML of the broken element
30+
31+
### 🛡️ Security Scanner
32+
Automatically scans console output and `localStorage` writes for sensitive data leaks:
33+
| Pattern | Detected As |
34+
|---|---|
35+
| JWT tokens (`eyJ...`) | `JWT Token` |
36+
| Email addresses | `Email Address` |
37+
| Phone numbers | `Phone Number` |
38+
| API keys / secrets | `API Key/Secret` |
39+
| Sensitive keys written to `localStorage` (`token`, `password`, `api_key`, `jwt`, `auth`) | `storage_leak` |
40+
41+
### 🧠 Severity Engine
42+
All issues are automatically classified into four severity levels:
43+
44+
| Level | Issue Types |
45+
|---|---|
46+
| 🔴 **Critical** | Runtime crashes, white screens, retry storms, CORS failures, security risks, HTTP 5xx |
47+
| 🟠 **High** | Console errors, resource failures, HTTP 4xx network failures |
48+
| 🟡 **Medium** | Slow APIs, console warnings |
49+
|**Low** | Console logs |
50+
51+
**Adaptive Escalation:** If the same issue repeats beyond a configurable threshold (default: 10×), its severity automatically upgrades one level (e.g. Medium → High → Critical).
52+
53+
### 🗂️ Issue Deduplication
54+
Issues are fingerprinted by type + message hash. Repeated occurrences of the same bug increment an `occurrences` counter and update `lastSeen` instead of creating duplicates — keeping reports clean and actionable.
55+
56+
### 📊 Session Management
57+
- Every recording is isolated as a **Session** with a unique UUID
58+
- Sessions store full metadata: `userAgent`, `viewport`, `URL`, `platform`
59+
- Sessions persist across page navigations via `chrome.storage.local`
60+
- Session history is browsable from the popup
61+
62+
### 📤 Report Export
63+
Completed sessions can be exported in two formats:
64+
65+
- **JSON** — Raw structured data, ideal for programmatic processing or filing in issue trackers
66+
- **Markdown** — Human-readable report with:
67+
- Executive summary with issue counts per severity
68+
- ⚠️ Critical issue call-out banner
69+
- **Release Blockers** section (Critical issues only)
70+
- **High Priority** section
71+
- **Medium Priority** section
72+
- Full **Timeline** of all issues sorted by time of occurrence
73+
74+
### 🎨 UI
75+
- Modern, white-and-blue minimal popup (400px wide)
76+
- Live issue list with **severity filter buttons** (All / Critical / High / Medium / Low)
77+
- Issue cards showing type, message, occurrence count, and URL
78+
- Expandable **request/response body detail** for network issues
79+
- Session history view with per-session issue count pills
80+
- Extension badge shows the live total issue count during recording
81+
82+
---
4483

4584
## ⚙️ Configuration
4685

47-
TestPilot includes a **Settings Panel** (click the ⚙ icon) to customize detection thresholds:
86+
Click the **** icon in the popup to open the Settings panel:
4887

4988
| Setting | Default | Description |
50-
|Observed Metric| Threshold | Impact |
5189
|---|---|---|
52-
| **Slow API** | 1000ms | Requests taking longer than this are flagged as "Medium" severity |
53-
| **Long Task** | 200ms | UI freezes longer than this are flagged as "Medium" severity |
54-
| **Escalation** | 10x | Issues repeating this many times automatically upgrade severity |
90+
| **Slow API Threshold** | `1000` ms | Requests taking longer than this are flagged as Slow API (Medium) |
91+
| **Escalation Threshold** | `10` × | Issues repeating this many times auto-escalate one severity level |
92+
| **Monitor Types** | All enabled | Toggle individual monitors on/off (runtime crash, console error, network failure, slow API, retry storm, resource failure, CORS, security risk, white screen) |
5593

56-
## 🏗️ Architecture
57-
58-
- **Core**: TypeScript, Vite, Preact
59-
- **State Management**: Chronicled session storage in `chrome.storage.local`
60-
- **Bridge**: Injected script (`bridge.ts`) for deep network/console interception
61-
- **Analysis**: Independent `SeverityEngine` for classifying and prioritizing issues
94+
---
6295

63-
## 📦 Build & Develop
96+
## 🛠️ Installation (Developer Mode)
6497

6598
```bash
66-
# Install dependencies
99+
# 1. Clone the repo
100+
git clone https://github.com/your-username/TestPilot.git
101+
cd TestPilot
102+
103+
# 2. Install dependencies
67104
npm install
68105

69-
# Run development build (watch mode)
106+
# 3. Build for production
107+
npm run build
108+
```
109+
110+
Then in Chrome:
111+
1. Go to `chrome://extensions`
112+
2. Enable **Developer mode** (top-right toggle)
113+
3. Click **Load unpacked**
114+
4. Select the `dist/` folder
115+
116+
---
117+
118+
## 💻 Development
119+
120+
```bash
121+
# Type-check only (no emit)
122+
npm run type-check
123+
124+
# Start Vite dev server (for popup UI iteration)
70125
npm run dev
71126

72127
# Build for production
73128
npm run build
74129
```
75130

131+
---
132+
133+
## 🏗️ Architecture
134+
135+
```
136+
src/
137+
├── background/ # Service Worker (persistent background process)
138+
│ ├── main.ts # Message router — receives TELEMETRY_EVENT messages
139+
│ ├── eventProcessor.ts # Deduplication, retry-storm detection, badge updates
140+
│ ├── sessionManager.ts # Start/end sessions, persist to chrome.storage.local
141+
│ ├── severityEngine.ts # Classifies and escalates issue severity levels
142+
│ └── storage.ts # Typed wrapper around chrome.storage.local
143+
144+
├── content/ # Content Script (runs on every page)
145+
│ ├── main.ts # Orchestrator — injects bridge, enables all monitors
146+
│ ├── bridge.ts # Injected into Main World to intercept fetch/XHR/console
147+
│ ├── consoleMonitor.ts # console.error / console.warn override + security scan
148+
│ ├── networkMonitor.ts # fetch + XHR override for slow/failed requests
149+
│ ├── runtimeMonitor.ts # window.onerror + unhandledrejection listeners
150+
│ └── securityScanner.ts# Regex-based PII / secret detection engine
151+
152+
├── core/ # Shared types and utilities
153+
│ ├── types.ts # IssueType, IssueLevel, Issue, Session, StorageSchema
154+
│ ├── issueFactory.ts # Constructs Issue objects with fingerprinting
155+
│ └── fingerprint.ts # Deterministic hash for issue deduplication
156+
157+
├── popup/ # Preact UI
158+
│ ├── index.html
159+
│ ├── popup.tsx # Full popup app — all views and state logic
160+
│ └── popup.css # Design system — white/blue theme, scrollbars, animations
161+
162+
└── reporting/
163+
└── reportGenerator.ts # Generates Markdown and JSON session reports
164+
```
165+
166+
**Key design decisions:**
167+
- The **bridge script** (`bridge.ts`) is injected into the **Main World** (not isolated content script context) so it can intercept the page's own `fetch`, `XMLHttpRequest`, and `console` before any frameworks or libraries wrap them.
168+
- The **background service worker** is the single source of truth for session state, keeping the popup and content scripts stateless and easily restartable.
169+
- **Fingerprinting** is done at issue creation time, making deduplication a simple array lookup.
170+
171+
---
172+
173+
## 📦 Tech Stack
174+
175+
| Layer | Technology |
176+
|---|---|
177+
| UI | [Preact](https://preactjs.com/) + TypeScript |
178+
| Build | [Vite](https://vitejs.dev/) v7 |
179+
| Styles | Vanilla CSS |
180+
| Storage | `chrome.storage.local` |
181+
| Types | `@types/chrome` |
182+
183+
---
184+
76185
## 🤝 Contributing
77186

78187
1. Fork the repository
79188
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
80-
3. Commit your changes (`git commit -m 'Add amazing feature'`)
189+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
81190
4. Push to the branch (`git push origin feature/amazing-feature`)
82191
5. Open a Pull Request
83192

193+
---
194+
84195
## 📄 License
85196

86-
MIT License - free to use for personal and commercial projects.
197+
MIT License free to use for personal and commercial projects.

src/background/main.ts

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,65 @@
22

33
import { SessionManager } from './sessionManager.ts';
44
import { EventProcessor } from './eventProcessor.ts';
5+
import { StorageService } from './storage.ts';
56

67
// Listen for messages from Content Script or Popup
7-
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
8+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
89
if (message.action === 'TELEMETRY_EVENT') {
9-
EventProcessor.processEvent(message.payload);
10+
// Only accept telemetry from the tab that owns the session
11+
StorageService.getCurrentTabId().then((ownedTabId) => {
12+
if (sender.tab?.id === ownedTabId) {
13+
EventProcessor.processEvent(message.payload);
14+
}
15+
});
16+
1017
} else if (message.action === 'GET_SESSION_STATUS') {
11-
SessionManager.isActive().then((active) => {
12-
sendResponse({ active });
18+
// Only return active=true to the tab that owns the session
19+
Promise.all([
20+
SessionManager.getCurrentSession(),
21+
StorageService.getCurrentTabId()
22+
]).then(([session, ownedTabId]) => {
23+
const isOwnerTab = sender.tab?.id === ownedTabId;
24+
sendResponse({
25+
active: !!session && isOwnerTab,
26+
config: (!!session && isOwnerTab) ? session.config : undefined
27+
});
1328
});
1429
return true; // async
30+
1531
} else if (message.action === 'START_SESSION') {
16-
const { envData } = message.payload || {};
17-
SessionManager.startSession(envData).then((session) => {
18-
notifyTabs('SESSION_STARTED');
32+
// tabId is sent by the popup, which queries the active tab before sending
33+
const tabId: number | undefined = message.payload?.tabId;
34+
SessionManager.startSession(message.payload).then(async (session) => {
35+
if (tabId) {
36+
await StorageService.setCurrentTabId(tabId);
37+
// Only notify the single target tab
38+
chrome.tabs.sendMessage(tabId, {
39+
action: 'SESSION_STARTED',
40+
config: session.config
41+
}).catch(() => { });
42+
}
1943
sendResponse({ session });
2044
});
2145
return true;
46+
2247
} else if (message.action === 'STOP_SESSION') {
23-
SessionManager.endSession().then((session) => {
24-
notifyTabs('SESSION_STOPPED', { session });
25-
sendResponse({ session });
48+
StorageService.getCurrentTabId().then((tabId) => {
49+
SessionManager.endSession().then(async (session) => {
50+
await StorageService.setCurrentTabId(null);
51+
// Only notify the tab that owned the session
52+
if (tabId) {
53+
chrome.tabs.sendMessage(tabId, {
54+
action: 'SESSION_STOPPED'
55+
}).catch(() => { });
56+
}
57+
sendResponse({ session });
58+
});
2659
});
2760
return true;
2861
}
2962
});
3063

31-
async function notifyTabs(action: string, payload?: any) {
32-
const tabs = await chrome.tabs.query({});
33-
tabs.forEach((tab) => {
34-
if (tab.id) {
35-
chrome.tabs.sendMessage(tab.id, { action, ...payload }).catch(() => { });
36-
}
37-
});
38-
}
39-
4064
// Initialize
4165
chrome.runtime.onInstalled.addListener(() => {
4266
// Extension installed

src/background/sessionManager.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { StorageService } from './storage.ts';
22
import type { Session } from '../core/types.ts';
33

44
export class SessionManager {
5-
static async startSession(envData?: any): Promise<Session> {
5+
static async startSession(payload?: { envData?: any, config?: any }): Promise<Session> {
66
const sessionId = crypto.randomUUID();
7+
const { envData, config } = payload || {};
78
const session: Session = {
89
sessionId,
910
startTime: Date.now(),
@@ -14,10 +15,21 @@ export class SessionManager {
1415
url: location.href,
1516
platform: (navigator as any).platform
1617
},
17-
config: {
18+
config: config || {
1819
slowApiThreshold: 1000,
19-
longTaskThreshold: 200,
20-
escalationThreshold: 10
20+
escalationThreshold: 10,
21+
enabledTypes: {
22+
runtime_crash: true,
23+
console_error: true,
24+
console_log: false,
25+
network_failure: true,
26+
slow_api: true,
27+
retry_storm: true,
28+
resource_failure: true,
29+
cors_failure: true,
30+
security_risk: true,
31+
white_screen: true
32+
}
2133
}
2234
};
2335
await StorageService.saveSession(session);

src/background/severityEngine.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@ export class SeverityEngine {
1919
return 'high';
2020

2121
case 'slow_api':
22-
case 'long_task':
2322
return 'medium';
2423

2524
case 'console_log':
26-
case 'route_change':
2725
return 'low';
2826

2927
default:

0 commit comments

Comments
 (0)