Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cypress/e2e/auth.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ describe("Authentication", () => {

it("A-11: Sign out clears state and returns to API key screen", () => {
cy.goToMainApp();
cy.get(".app-signout-btn").click();
// Sign out button is now in the sidebar
cy.contains("Sign out").click();
// With mock user, signOut clears apiKey but user persists (no real Firebase listener).
// App shows API key setup screen since user exists but apiKey is empty.
cy.get(".setup-card").should("exist");
Expand Down
58 changes: 28 additions & 30 deletions cypress/e2e/main-app.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ describe("Main App - Output Box", () => {
cy.goToMainApp();
});

it("O-1: shows placeholder text when empty", () => {
cy.contains("Your formatted text will appear here").should("be.visible");
it("O-1: shows empty state when no output", () => {
cy.get(".chat-empty").should("be.visible");
cy.contains("Tap the mic to start recording").should("be.visible");
});

it("O-2: displays formatted text after recording", () => {
Expand Down Expand Up @@ -76,54 +77,51 @@ describe("Main App - Output Box", () => {
cy.get(".export-modal").should("be.visible");
});

it("O-5: action buttons disabled when no output", () => {
cy.get('.output-icon-btn[title="Copy text"]').should("be.disabled");
cy.get('.output-icon-btn[title="Read aloud"]').should("be.disabled");
cy.get('.output-icon-btn[title="Export"]').should("be.disabled");
it("O-5: action buttons not visible when no output (empty state shown)", () => {
// When there's no output, the empty state is shown instead of OutputBox
cy.get(".chat-empty").should("be.visible");
cy.get(".output-icon-btn").should("not.exist");
});
});

describe("Main App - History", () => {
describe("Main App - Conversations", () => {
beforeEach(() => {
cy.goToMainApp();
});

it("H-1: history section hidden when no items exist", () => {
cy.get(".history-list").should("not.exist");
it("H-1: no conversations in sidebar when none exist", () => {
cy.get(".sidebar-convo-btn").should("not.exist");
cy.contains("No conversations yet").should("exist");
});

it("H-2: history appears after first recording with timestamp", () => {
it("H-2: conversation appears in sidebar after first recording", () => {
cy.get(".mic-btn").click();
cy.get(".mic-btn").click();
cy.get(".output-text", { timeout: 5000 }).should("be.visible");
cy.get(".history-list").should("be.visible");
cy.get(".history-item").should("have.length", 1);
cy.get(".history-item-time").should("be.visible");
cy.get(".sidebar-convo-btn").should("have.length", 1);
});

it("H-3: clicking a history item restores its text to OutputBox", () => {
it("H-3: clicking a conversation restores its text to OutputBox", () => {
// Create a recording first
cy.get(".mic-btn").click();
cy.get(".mic-btn").click();
cy.get(".output-text", { timeout: 5000 }).should("be.visible");

// Get the text content
cy.get(".output-text").invoke("text").then((originalText) => {
// Click the history item
cy.get(".history-item").first().click();
cy.get(".output-text").should("contain.text", originalText.substring(0, 50));
});
// Start a new conversation (clears output)
cy.get(".sidebar-new-btn").click();
cy.get(".chat-empty").should("be.visible");

// Click the sidebar conversation to restore
cy.get(".sidebar-convo-btn").first().click();
cy.get(".output-text").should("be.visible");
});

it("H-4: stores up to 5 items, oldest dropped on overflow", () => {
// Create 6 recordings
for (let i = 0; i < 6; i++) {
cy.get(".mic-btn").click();
cy.get(".mic-btn").click();
cy.get(".output-text", { timeout: 5000 }).should("be.visible");
// Wait for processing to finish before next recording
cy.contains("Tap to speak", { timeout: 5000 }).should("be.visible");
}
cy.get(".history-item").should("have.length", 5);
it("H-4: new chat button clears output", () => {
cy.get(".mic-btn").click();
cy.get(".mic-btn").click();
cy.get(".output-text", { timeout: 5000 }).should("be.visible");

cy.get(".sidebar-new-btn").click();
cy.get(".chat-empty").should("be.visible");
});
});
34 changes: 20 additions & 14 deletions cypress/e2e/settings.cy.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
// Helper to open settings from sidebar
const openSettings = () => {
cy.get(".sidebar-footer-btn").first().click();
};

describe("Settings Modal", () => {
beforeEach(() => {
cy.goToMainApp();
});

it("S-1: gear icon only visible on main app screen", () => {
cy.get('.header-icon-btn[aria-label="Settings"]').should("be.visible");
it("S-1: settings button visible in sidebar on main app screen", () => {
cy.contains("Settings").should("exist");
});

it("S-2: opens when gear icon is clicked", () => {
cy.get('.header-icon-btn[aria-label="Settings"]').click();
it("S-2: opens when settings button is clicked", () => {
openSettings();
cy.get(".settings-modal").should("be.visible");
});

it("S-3: three sidebar tabs render (Account, Formats, About)", () => {
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.get(".settings-tab-btn").should("have.length", 3);
cy.contains("Account").should("be.visible");
cy.contains("Formats").should("be.visible");
cy.contains("About").should("be.visible");
});

it("S-4: closes on overlay click", () => {
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.get(".settings-modal").should("be.visible");
cy.get(".settings-overlay").click({ force: true });
cy.get(".settings-modal").should("not.exist");
});

it("S-5: closes on Escape key", () => {
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.get(".settings-modal").should("be.visible");
cy.get("body").type("{esc}");
cy.get(".settings-modal").should("not.exist");
});

it("S-6: closes on X button", () => {
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.get(".settings-modal").should("be.visible");
cy.get(".settings-close-btn").click();
cy.get(".settings-modal").should("not.exist");
Expand All @@ -45,7 +50,7 @@ describe("Settings Modal", () => {
describe("Settings - Account Tab", () => {
beforeEach(() => {
cy.goToMainApp();
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
});

it("SA-1: displays user name and email", () => {
Expand All @@ -58,7 +63,8 @@ describe("Settings - Account Tab", () => {
});

it("SA-3: password reset button hidden for Google users", () => {
// Re-visit with a Google provider user
// Close current modal and re-visit with a Google provider user
cy.get(".settings-close-btn").click();
cy.visit("/", {
onBeforeLoad(win) {
win.__CYPRESS_MOCK_USER__ = {
Expand All @@ -72,7 +78,7 @@ describe("Settings - Account Tab", () => {
win.localStorage.setItem("sidekick_apikey_google-user-456", "sk-test-key-1234567890");
},
});
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.contains("Reset password").should("not.exist");
});

Expand Down Expand Up @@ -114,7 +120,7 @@ describe("Settings - Account Tab", () => {
describe("Settings - Formats Tab", () => {
beforeEach(() => {
cy.goToMainApp();
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.contains("Formats").click();
});

Expand Down Expand Up @@ -150,7 +156,7 @@ describe("Settings - Formats Tab", () => {

// Close and reopen settings
cy.get(".settings-close-btn").click();
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.contains("Formats").click();

// Should still be toggled off
Expand All @@ -161,7 +167,7 @@ describe("Settings - Formats Tab", () => {
describe("Settings - About Tab", () => {
beforeEach(() => {
cy.goToMainApp();
cy.get('.header-icon-btn[aria-label="Settings"]').click();
openSettings();
cy.contains("About").click();
});

Expand Down
35 changes: 18 additions & 17 deletions cypress/e2e/theme-header.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ describe("Theme", () => {
});

it("T-3: theme persists across page refresh", () => {
// Go to main app and switch to light mode
// Go to main app and switch to light mode via sidebar settings
cy.goToMainApp();
cy.get('.header-icon-btn[aria-label="Settings"]').click();
cy.get(".sidebar-footer-btn").first().click();
cy.get(".settings-toggle").last().click();
cy.get("body").should("not.have.class", "dark");

Expand All @@ -34,9 +34,9 @@ describe("Theme", () => {
});
});

describe("Header", () => {
it("HD-1: logo renders on all screens", () => {
// Welcome screen
describe("Header & Sidebar", () => {
it("HD-1: logo/branding renders on all screens", () => {
// Welcome screen - header logo
cy.visit("/", {
onBeforeLoad(win) {
win.localStorage.removeItem("sidekick_seen_welcome");
Expand All @@ -45,12 +45,12 @@ describe("Header", () => {
cy.get(".header-logo").should("be.visible");
cy.get(".header-logo-name").should("contain.text", "Sidekick");

// Auth screen (skip welcome, no user)
// Auth screen - header logo
cy.skipWelcome();
cy.visit("/");
cy.get(".header-logo").should("be.visible");

// API key screen (has user, no key)
// API key screen - header logo
cy.visit("/", {
onBeforeLoad(win) {
win.localStorage.setItem("sidekick_seen_welcome", "1");
Expand All @@ -65,29 +65,29 @@ describe("Header", () => {
});
cy.get(".header-logo").should("be.visible");

// Main app screen
// Main app screen - sidebar exists
cy.goToMainApp();
cy.get(".header-logo").should("be.visible");
cy.get(".sidebar").should("exist");
});

it("HD-2: gear icon hidden on welcome screen", () => {
it("HD-2: sidebar hidden on welcome screen", () => {
cy.visit("/", {
onBeforeLoad(win) {
win.localStorage.removeItem("sidekick_seen_welcome");
},
});
cy.get(".welcome-card").should("be.visible");
cy.get('.header-icon-btn[aria-label="Settings"]').should("not.exist");
cy.get(".sidebar").should("not.exist");
});

it("HD-3: gear icon hidden on auth screen", () => {
it("HD-3: sidebar hidden on auth screen", () => {
cy.skipWelcome();
cy.visit("/");
cy.get(".auth-card").should("be.visible");
cy.get('.header-icon-btn[aria-label="Settings"]').should("not.exist");
cy.get(".sidebar").should("not.exist");
});

it("HD-4: gear icon hidden on API key screen", () => {
it("HD-4: sidebar hidden on API key screen", () => {
cy.visit("/", {
onBeforeLoad(win) {
win.localStorage.setItem("sidekick_seen_welcome", "1");
Expand All @@ -101,11 +101,12 @@ describe("Header", () => {
},
});
cy.get(".setup-card").should("be.visible");
cy.get('.header-icon-btn[aria-label="Settings"]').should("not.exist");
cy.get(".sidebar").should("not.exist");
});

it("HD-5: gear icon visible on main app screen", () => {
it("HD-5: sidebar with settings visible on main app screen", () => {
cy.goToMainApp();
cy.get('.header-icon-btn[aria-label="Settings"]').should("be.visible");
cy.get(".sidebar").should("exist");
cy.contains("Settings").should("exist");
});
});
Loading