Skip to content

Commit 0af9511

Browse files
committed
fix: resets store state on logout, fixes E2E test assertions
1 parent 6e8cd3a commit 0af9511

File tree

5 files changed

+35
-15
lines changed

5 files changed

+35
-15
lines changed

e2e/settings.spec.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ test("changing theme to dark adds dark class to html element", async ({
9797
await page.goto("/settings");
9898

9999
// Locate the Theme setting row by its label text, then find its <select> child.
100-
const themeRow = page.locator("div").filter({ hasText: /^Theme$/ }).first();
101-
const themeSelect = themeRow.locator("select");
100+
const themeSelect = page.getByRole("combobox").filter({ has: page.locator('option[value="dark"]') });
102101
await themeSelect.selectOption("dark");
103102

104103
const htmlElement = page.locator("html");
@@ -125,9 +124,15 @@ test("sign out clears auth and redirects to login", async ({ page }) => {
125124
// clearAuth() clears in-memory token and navigates to /login
126125
await expect(page).toHaveURL(/\/login/);
127126

128-
// Verify config was cleared from localStorage (SDR-016 data leakage prevention)
127+
// Verify config was reset (SDR-016 data leakage prevention).
128+
// The persistence effect may re-write defaults, so check that user-specific
129+
// data (selectedOrgs, onboardingComplete) was cleared rather than checking null.
129130
const configEntry = await page.evaluate(() =>
130131
localStorage.getItem("github-tracker:config")
131132
);
132-
expect(configEntry).toBeNull();
133+
if (configEntry !== null) {
134+
const parsed = JSON.parse(configEntry);
135+
expect(parsed.selectedOrgs).toEqual([]);
136+
expect(parsed.onboardingComplete).toBe(false);
137+
}
133138
});

e2e/smoke.spec.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ test("OAuth callback flow completes and redirects", async ({ page }) => {
123123

124124
await page.goto(`/oauth/callback?code=fakecode&state=${fakeState}`);
125125

126-
// After successful auth the callback navigates to '/' then to /dashboard
127-
await expect(page).toHaveURL(/\/dashboard/);
126+
// After successful auth the callback navigates to '/' which redirects
127+
// to /dashboard (if onboardingComplete) or /onboarding (first login)
128+
await expect(page).toHaveURL(/\/(dashboard|onboarding)/);
128129
});
129130

130131
// ── Dashboard ────────────────────────────────────────────────────────────────
@@ -136,20 +137,20 @@ test("dashboard loads with tab bar visible", async ({ page }) => {
136137
const nav = page.getByRole("navigation", { name: /dashboard tabs/i });
137138
await expect(nav).toBeVisible();
138139

139-
await expect(page.getByRole("button", { name: /^issues$/i })).toBeVisible();
140+
await expect(page.getByRole("button", { name: /^issues/i })).toBeVisible();
140141
await expect(
141-
page.getByRole("button", { name: /^pull requests$/i })
142+
page.getByRole("button", { name: /^pull requests/i })
142143
).toBeVisible();
143-
await expect(page.getByRole("button", { name: /^actions$/i })).toBeVisible();
144+
await expect(page.getByRole("button", { name: /^actions/i })).toBeVisible();
144145
});
145146

146147
test("switching tabs changes active tab indicator", async ({ page }) => {
147148
await setupAuth(page);
148149
await page.goto("/dashboard");
149150

150-
const issuesBtn = page.getByRole("button", { name: /^issues$/i });
151-
const prBtn = page.getByRole("button", { name: /^pull requests$/i });
152-
const actionsBtn = page.getByRole("button", { name: /^actions$/i });
151+
const issuesBtn = page.getByRole("button", { name: /^issues/i });
152+
const prBtn = page.getByRole("button", { name: /^pull requests/i });
153+
const actionsBtn = page.getByRole("button", { name: /^actions/i });
153154

154155
// Default tab should be issues (or whatever config says; we didn't set defaultTab)
155156
await expect(issuesBtn).toBeVisible();

src/app/stores/auth.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createSignal } from "solid-js";
22
import { clearCache } from "./cache";
3-
import { CONFIG_STORAGE_KEY } from "./config";
4-
import { VIEW_STORAGE_KEY } from "./view";
3+
import { CONFIG_STORAGE_KEY, resetConfig } from "./config";
4+
import { VIEW_STORAGE_KEY, resetViewState } from "./view";
55

66
export interface GitHubUser {
77
login: string;
@@ -47,7 +47,11 @@ export function onAuthCleared(cb: () => void): void {
4747
}
4848

4949
export function clearAuth(): void {
50-
// Clear config and view state to prevent data leakage between users (SDR-016)
50+
// Reset in-memory stores to defaults BEFORE clearing localStorage,
51+
// so the persistence effects re-write defaults (not stale user data).
52+
resetConfig();
53+
resetViewState();
54+
// Clear localStorage entries (persistence effects will write back defaults)
5155
localStorage.removeItem(CONFIG_STORAGE_KEY);
5256
localStorage.removeItem(VIEW_STORAGE_KEY);
5357
_setToken(null);

src/app/stores/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ export function updateConfig(partial: Partial<Config>): void {
5959
);
6060
}
6161

62+
export function resetConfig(): void {
63+
const defaults = ConfigSchema.parse({});
64+
setConfig(defaults);
65+
}
66+
6267
export function initConfigPersistence(): void {
6368
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
6469
createEffect(() => {

src/app/stores/view.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ export const [viewState, setViewState] = createStore<ViewState>(
9292
loadViewState()
9393
);
9494

95+
export function resetViewState(): void {
96+
const defaults = ViewStateSchema.parse({});
97+
setViewState(defaults);
98+
}
99+
95100
export function updateViewState(partial: Partial<ViewState>): void {
96101
setViewState(
97102
produce((draft) => {

0 commit comments

Comments
 (0)