Skip to content

Commit 399d3d1

Browse files
vveerrggclaude
andcommitted
feat: add extension test page, fix CSP and postMessage issues
- Add docs/test.html + test.js — exercises all 7 window.nostr methods (getPublicKey, getRelays, signEvent, nip44 encrypt/decrypt, nip04 encrypt/decrypt) with Fire All button for queue counter testing - Move inline script from sidepanel.html to sidepanel.js (fixes CSP script-src 'self' violation on extension pages) - Fix postMessage target origin in content.js — use '*' instead of window.location.origin (fixes "target origin 'file://' does not match" on local file:// pages) - Hide Experimental button in sidepanel.html Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 10dcf60 commit 399d3d1

5 files changed

Lines changed: 649 additions & 34 deletions

File tree

docs/test.html

Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>NostrKey — Extension Test</title>
7+
<meta name="description" content="Test and verify your NostrKey browser extension. Exercise all window.nostr methods.">
8+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='45' stroke='%23a6e22e' stroke-width='4' fill='%233e3d32'/><path d='M35 50L45 60L65 40' stroke='%23a6e22e' stroke-width='6' stroke-linecap='round' stroke-linejoin='round' fill='none'/></svg>">
9+
<style>
10+
* {
11+
margin: 0;
12+
padding: 0;
13+
box-sizing: border-box;
14+
}
15+
16+
body {
17+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
18+
background: #f5f5f0;
19+
color: #2d2d2d;
20+
line-height: 1.6;
21+
min-height: 100vh;
22+
}
23+
24+
.container {
25+
max-width: 720px;
26+
margin: 0 auto;
27+
padding: 40px 20px 60px;
28+
}
29+
30+
/* Header */
31+
.header {
32+
text-align: center;
33+
margin-bottom: 32px;
34+
}
35+
36+
.header a {
37+
color: #75715e;
38+
text-decoration: none;
39+
font-size: 14px;
40+
}
41+
42+
.header a:hover {
43+
text-decoration: underline;
44+
}
45+
46+
.logo {
47+
display: flex;
48+
align-items: center;
49+
justify-content: center;
50+
gap: 12px;
51+
margin: 16px 0 8px;
52+
}
53+
54+
.logo svg {
55+
width: 40px;
56+
height: 40px;
57+
}
58+
59+
.logo h1 {
60+
font-size: 28px;
61+
font-weight: 700;
62+
color: #2d2d2d;
63+
}
64+
65+
.subtitle {
66+
font-size: 16px;
67+
color: #75715e;
68+
}
69+
70+
/* Status */
71+
.status {
72+
display: flex;
73+
align-items: center;
74+
justify-content: center;
75+
gap: 8px;
76+
margin: 24px 0;
77+
padding: 12px 20px;
78+
border-radius: 8px;
79+
font-size: 15px;
80+
font-weight: 500;
81+
}
82+
83+
.status.detected {
84+
background: #e8f5e9;
85+
color: #2e7d32;
86+
}
87+
88+
.status.not-detected {
89+
background: #fce4ec;
90+
color: #c62828;
91+
}
92+
93+
.status-dot {
94+
width: 10px;
95+
height: 10px;
96+
border-radius: 50%;
97+
flex-shrink: 0;
98+
}
99+
100+
.status.detected .status-dot {
101+
background: #4caf50;
102+
}
103+
104+
.status.not-detected .status-dot {
105+
background: #e53935;
106+
}
107+
108+
/* Sections */
109+
.section {
110+
margin-bottom: 24px;
111+
}
112+
113+
.section-title {
114+
font-size: 13px;
115+
font-weight: 600;
116+
text-transform: uppercase;
117+
letter-spacing: 0.5px;
118+
color: #75715e;
119+
margin-bottom: 10px;
120+
}
121+
122+
.button-row {
123+
display: flex;
124+
gap: 10px;
125+
flex-wrap: wrap;
126+
}
127+
128+
/* Buttons */
129+
.btn {
130+
display: inline-flex;
131+
align-items: center;
132+
gap: 6px;
133+
padding: 10px 18px;
134+
border: none;
135+
border-radius: 6px;
136+
font-family: inherit;
137+
font-size: 14px;
138+
font-weight: 600;
139+
cursor: pointer;
140+
transition: opacity 0.15s, transform 0.1s;
141+
}
142+
143+
.btn:hover {
144+
opacity: 0.9;
145+
}
146+
147+
.btn:active {
148+
transform: scale(0.98);
149+
}
150+
151+
.btn:disabled {
152+
opacity: 0.4;
153+
cursor: not-allowed;
154+
transform: none;
155+
}
156+
157+
.btn-method {
158+
background: #3e3d32;
159+
color: #a6e22e;
160+
}
161+
162+
.btn-fire-all {
163+
background: #a6e22e;
164+
color: #272822;
165+
font-size: 16px;
166+
padding: 14px 28px;
167+
width: 100%;
168+
justify-content: center;
169+
margin-top: 8px;
170+
}
171+
172+
.btn-fire-all:hover {
173+
background: #b8f040;
174+
}
175+
176+
.btn-clear {
177+
background: transparent;
178+
color: #75715e;
179+
border: 1px solid #d5d5d0;
180+
font-weight: 500;
181+
padding: 8px 16px;
182+
}
183+
184+
/* Results */
185+
.results-header {
186+
display: flex;
187+
align-items: center;
188+
justify-content: space-between;
189+
margin-top: 36px;
190+
margin-bottom: 12px;
191+
}
192+
193+
.results-header h2 {
194+
font-size: 13px;
195+
font-weight: 600;
196+
text-transform: uppercase;
197+
letter-spacing: 0.5px;
198+
color: #75715e;
199+
}
200+
201+
#results {
202+
display: flex;
203+
flex-direction: column;
204+
gap: 12px;
205+
}
206+
207+
.result-card {
208+
background: #ffffff;
209+
border-radius: 8px;
210+
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
211+
overflow: hidden;
212+
}
213+
214+
.result-card-header {
215+
display: flex;
216+
align-items: center;
217+
justify-content: space-between;
218+
padding: 12px 16px;
219+
border-bottom: 1px solid #f0f0eb;
220+
}
221+
222+
.result-method {
223+
font-size: 14px;
224+
font-weight: 600;
225+
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
226+
}
227+
228+
.result-badge {
229+
font-size: 12px;
230+
font-weight: 600;
231+
padding: 2px 8px;
232+
border-radius: 4px;
233+
}
234+
235+
.result-badge.success {
236+
background: #e8f5e9;
237+
color: #2e7d32;
238+
}
239+
240+
.result-badge.error {
241+
background: #fce4ec;
242+
color: #c62828;
243+
}
244+
245+
.result-badge.pending {
246+
background: #fff8e1;
247+
color: #f57f17;
248+
}
249+
250+
.result-body {
251+
background: #272822;
252+
color: #f8f8f2;
253+
padding: 14px 16px;
254+
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
255+
font-size: 12px;
256+
line-height: 1.5;
257+
overflow-x: auto;
258+
white-space: pre-wrap;
259+
word-break: break-all;
260+
}
261+
262+
.result-footer {
263+
padding: 6px 16px;
264+
font-size: 11px;
265+
color: #a0a09a;
266+
text-align: right;
267+
}
268+
269+
.empty-state {
270+
text-align: center;
271+
padding: 40px 20px;
272+
color: #a0a09a;
273+
font-size: 14px;
274+
}
275+
276+
/* Responsive */
277+
@media (max-width: 480px) {
278+
.container {
279+
padding: 24px 16px 40px;
280+
}
281+
282+
.logo h1 {
283+
font-size: 24px;
284+
}
285+
286+
.button-row {
287+
flex-direction: column;
288+
}
289+
290+
.btn-method {
291+
width: 100%;
292+
justify-content: center;
293+
}
294+
295+
.btn-fire-all {
296+
font-size: 15px;
297+
padding: 12px 20px;
298+
}
299+
300+
.result-body {
301+
font-size: 11px;
302+
}
303+
}
304+
</style>
305+
</head>
306+
<body>
307+
<div class="container">
308+
<!-- Header -->
309+
<div class="header">
310+
<a href="index.html">&larr; nostrkey.com</a>
311+
<div class="logo">
312+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
313+
<circle cx="50" cy="50" r="45" stroke="#a6e22e" stroke-width="4" fill="#3e3d32"/>
314+
<path d="M35 50L45 60L65 40" stroke="#a6e22e" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
315+
</svg>
316+
<h1>NostrKey</h1>
317+
</div>
318+
<p class="subtitle">Extension Test</p>
319+
</div>
320+
321+
<!-- Status -->
322+
<div id="status" class="status not-detected">
323+
<span class="status-dot"></span>
324+
<span>Checking for extension…</span>
325+
</div>
326+
327+
<!-- Test Buttons -->
328+
<div class="section">
329+
<div class="section-title">Identity</div>
330+
<div class="button-row">
331+
<button class="btn btn-method" id="btn-getPublicKey">Get Public Key</button>
332+
<button class="btn btn-method" id="btn-getRelays">Get Relays</button>
333+
</div>
334+
</div>
335+
336+
<div class="section">
337+
<div class="section-title">Signing</div>
338+
<div class="button-row">
339+
<button class="btn btn-method" id="btn-signEvent">Sign Test Event</button>
340+
</div>
341+
</div>
342+
343+
<div class="section">
344+
<div class="section-title">Encryption — NIP-44</div>
345+
<div class="button-row">
346+
<button class="btn btn-method" id="btn-nip44Encrypt">Encrypt</button>
347+
<button class="btn btn-method" id="btn-nip44Decrypt">Decrypt</button>
348+
</div>
349+
</div>
350+
351+
<div class="section">
352+
<div class="section-title">Encryption — NIP-04 (deprecated)</div>
353+
<div class="button-row">
354+
<button class="btn btn-method" id="btn-nip04Encrypt">Encrypt</button>
355+
<button class="btn btn-method" id="btn-nip04Decrypt">Decrypt</button>
356+
</div>
357+
</div>
358+
359+
<div class="section">
360+
<button class="btn btn-fire-all" id="btn-fire-all">
361+
&#9889; Fire All — Test Every Method
362+
</button>
363+
</div>
364+
365+
<!-- Results -->
366+
<div class="results-header">
367+
<h2>Results</h2>
368+
<button class="btn btn-clear" id="btn-clear">Clear</button>
369+
</div>
370+
371+
<div id="results">
372+
<div class="empty-state">Click a button above to test a window.nostr method</div>
373+
</div>
374+
</div>
375+
376+
<script src="test.js"></script>
377+
</body>
378+
</html>

0 commit comments

Comments
 (0)