mirror of
https://github.com/garrytan/gstack.git
synced 2026-07-07 16:48:01 +02:00
fix: sidebar arrow hint stays visible until sidebar actually opens
Previously the welcome page arrow hid immediately when the extension's content script loaded — but extension loaded ≠ sidebar open. Now the signal flow is: sidepanel connects → tells background.js → relays to content script → dispatches gstack-extension-ready → arrow hides. Adds welcome-page.test.ts: 14 tests verifying arrow, branding, feature cards, dark theme, and auto-hide behavior via real HTTP server. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4
-15
@@ -213,25 +213,14 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Hide sidebar prompt when the gstack extension content script fires
|
||||
// Hide sidebar prompt ONLY when the sidebar is actually opened.
|
||||
// The content script dispatches 'gstack-extension-ready' when it receives
|
||||
// a 'sidebarOpened' message from the side panel (via background.js).
|
||||
// This means the arrow stays visible until the user actually opens the sidebar.
|
||||
document.addEventListener('gstack-extension-ready', () => {
|
||||
const prompt = document.getElementById('sidebar-prompt');
|
||||
if (prompt) prompt.classList.add('hidden');
|
||||
});
|
||||
// Fallback: also check for the status pill DOM element
|
||||
function checkPill() {
|
||||
if (document.getElementById('gstack-status-pill')) {
|
||||
const prompt = document.getElementById('sidebar-prompt');
|
||||
if (prompt) prompt.classList.add('hidden');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
let checks = 0;
|
||||
const interval = setInterval(() => {
|
||||
if (checkPill() || ++checks > 15) clearInterval(interval);
|
||||
}, 2000);
|
||||
checkPill();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Welcome page E2E test — verifies the sidebar arrow hint and key elements
|
||||
* render correctly when the welcome page is served via HTTP.
|
||||
*
|
||||
* Spins up a real Bun.serve, fetches the HTML, and parses it to verify
|
||||
* the sidebar prompt arrow, feature cards, and branding are present.
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
const WELCOME_PATH = path.join(import.meta.dir, '../src/welcome.html');
|
||||
const welcomeHtml = fs.readFileSync(WELCOME_PATH, 'utf-8');
|
||||
|
||||
let server: ReturnType<typeof Bun.serve>;
|
||||
let baseUrl: string;
|
||||
|
||||
beforeAll(() => {
|
||||
// Serve the welcome page exactly as the browse server does
|
||||
server = Bun.serve({
|
||||
port: 0,
|
||||
hostname: '127.0.0.1',
|
||||
fetch() {
|
||||
return new Response(welcomeHtml, {
|
||||
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
||||
});
|
||||
},
|
||||
});
|
||||
baseUrl = `http://127.0.0.1:${server.port}`;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
server?.stop();
|
||||
});
|
||||
|
||||
describe('welcome page served via HTTP', () => {
|
||||
let html: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
const resp = await fetch(baseUrl);
|
||||
expect(resp.ok).toBe(true);
|
||||
expect(resp.headers.get('content-type')).toContain('text/html');
|
||||
html = await resp.text();
|
||||
});
|
||||
|
||||
// ─── Sidebar arrow hint (the bug that triggered this test) ────────
|
||||
|
||||
test('sidebar prompt arrow is present and visible', () => {
|
||||
// The arrow element with class "arrow-right" must exist
|
||||
expect(html).toContain('class="arrow-right"');
|
||||
// It should contain the right-arrow character (→ = →)
|
||||
expect(html).toContain('→');
|
||||
});
|
||||
|
||||
test('sidebar prompt container is visible by default (no hidden class)', () => {
|
||||
// The prompt div should NOT have the "hidden" class on initial load
|
||||
expect(html).toContain('id="sidebar-prompt"');
|
||||
// Check it doesn't start hidden
|
||||
expect(html).not.toMatch(/class="sidebar-prompt[^"]*hidden/);
|
||||
});
|
||||
|
||||
test('sidebar prompt has instruction text', () => {
|
||||
expect(html).toContain('Open the sidebar to get started');
|
||||
expect(html).toContain('puzzle piece');
|
||||
});
|
||||
|
||||
test('sidebar prompt is positioned on the right side', () => {
|
||||
// CSS should position it on the right
|
||||
expect(html).toMatch(/\.sidebar-prompt\s*\{[^}]*right:\s*\d+px/);
|
||||
});
|
||||
|
||||
test('arrow has nudge animation', () => {
|
||||
expect(html).toContain('@keyframes nudge');
|
||||
expect(html).toMatch(/\.arrow-right\s*\{[^}]*animation:\s*nudge/);
|
||||
});
|
||||
|
||||
// ─── Branding ─────────────────────────────────────────────────────
|
||||
|
||||
test('has GStack Browser title and branding', () => {
|
||||
expect(html).toContain('<title>GStack Browser</title>');
|
||||
expect(html).toContain('GStack Browser');
|
||||
});
|
||||
|
||||
test('has amber dot logo', () => {
|
||||
expect(html).toContain('class="logo-dot"');
|
||||
expect(html).toContain('class="logo-text"');
|
||||
});
|
||||
|
||||
// ─── Feature cards ────────────────────────────────────────────────
|
||||
|
||||
test('has all four feature cards', () => {
|
||||
expect(html).toContain('Talk to the sidebar');
|
||||
expect(html).toContain('Clean up any page');
|
||||
expect(html).toContain('Smart screenshots');
|
||||
expect(html).toContain('Modify any page');
|
||||
});
|
||||
|
||||
// ─── Try it section ───────────────────────────────────────────────
|
||||
|
||||
test('has try-it section with example prompts', () => {
|
||||
expect(html).toContain('Try it now');
|
||||
expect(html).toContain('news.ycombinator.com');
|
||||
});
|
||||
|
||||
// ─── Extension auto-hide ──────────────────────────────────────────
|
||||
|
||||
test('hides sidebar prompt when extension is detected', () => {
|
||||
// Should listen for the extension-ready event
|
||||
expect(html).toContain("'gstack-extension-ready'");
|
||||
// Should add 'hidden' class to sidebar-prompt
|
||||
expect(html).toContain("classList.add('hidden')");
|
||||
});
|
||||
|
||||
test('does NOT auto-hide based on extension detection alone', () => {
|
||||
// The arrow should only hide when the sidebar actually opens,
|
||||
// not when the content script loads (which happens on every page)
|
||||
expect(html).not.toContain('gstack-status-pill');
|
||||
expect(html).not.toContain('checkPill');
|
||||
});
|
||||
|
||||
// ─── Dark theme ───────────────────────────────────────────────────
|
||||
|
||||
test('uses dark theme colors', () => {
|
||||
expect(html).toContain('--base: #0C0C0C');
|
||||
expect(html).toContain('--surface: #141414');
|
||||
});
|
||||
|
||||
// ─── Left-aligned text ────────────────────────────────────────────
|
||||
|
||||
test('text is left-aligned, not centered', () => {
|
||||
expect(html).not.toMatch(/text-align:\s*center/);
|
||||
});
|
||||
|
||||
// ─── Footer ───────────────────────────────────────────────────────
|
||||
|
||||
test('has footer with attribution', () => {
|
||||
expect(html).toContain('Garry Tan');
|
||||
expect(html).toContain('github.com/garrytan/gstack');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user