chore: harden reliability checks #2

Merged
nalalangan merged 67 commits from main-new into main 2026-05-25 14:28:32 -09:00
2 changed files with 113 additions and 13 deletions
Showing only changes of commit 12b514262e - Show all commits

View File

@ -2,6 +2,8 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea
import { createHousehold as createHouseholdApi, getUserHouseholds } from '../api/households'; import { createHousehold as createHouseholdApi, getUserHouseholds } from '../api/households';
import { AuthContext } from './AuthContext'; import { AuthContext } from './AuthContext';
const ACTIVE_HOUSEHOLD_STORAGE_KEY = 'activeHouseholdId';
export const HouseholdContext = createContext({ export const HouseholdContext = createContext({
households: [], households: [],
activeHousehold: null, activeHousehold: null,
@ -23,7 +25,7 @@ export const HouseholdProvider = ({ children }) => {
const clearActiveHousehold = useCallback(() => { const clearActiveHousehold = useCallback(() => {
setActiveHouseholdState(null); setActiveHouseholdState(null);
localStorage.removeItem('activeHouseholdId'); localStorage.removeItem(ACTIVE_HOUSEHOLD_STORAGE_KEY);
}, []); }, []);
const loadHouseholds = useCallback(async () => { const loadHouseholds = useCallback(async () => {
@ -32,10 +34,8 @@ export const HouseholdProvider = ({ children }) => {
setLoading(true); setLoading(true);
setError(null); setError(null);
try { try {
console.log('[HouseholdContext] Loading households...');
const response = await getUserHouseholds(); const response = await getUserHouseholds();
const nextHouseholds = Array.isArray(response.data) ? response.data : []; const nextHouseholds = Array.isArray(response.data) ? response.data : [];
console.log('[HouseholdContext] Loaded households:', nextHouseholds);
setHouseholds(nextHouseholds); setHouseholds(nextHouseholds);
if (nextHouseholds.length === 0) { if (nextHouseholds.length === 0) {
@ -69,33 +69,30 @@ export const HouseholdProvider = ({ children }) => {
// Load active household from localStorage on mount // Load active household from localStorage on mount
useEffect(() => { useEffect(() => {
if (households.length === 0) { if (households.length === 0) {
clearActiveHousehold(); setActiveHouseholdState(null);
return; return;
} }
console.log('[HouseholdContext] Setting active household from:', households); const savedHouseholdId = localStorage.getItem(ACTIVE_HOUSEHOLD_STORAGE_KEY);
const savedHouseholdId = localStorage.getItem('activeHouseholdId');
if (savedHouseholdId) { if (savedHouseholdId) {
const household = households.find(h => h.id === parseInt(savedHouseholdId, 10)); const household = households.find((candidate) => String(candidate.id) === savedHouseholdId);
if (household) { if (household) {
console.log('[HouseholdContext] Found saved household:', household);
setActiveHouseholdState(household); setActiveHouseholdState(household);
return; return;
} }
} }
// No saved household or not found, use first one // No saved household or not found, use first one
console.log('[HouseholdContext] Using first household:', households[0]);
setActiveHouseholdState(households[0]); setActiveHouseholdState(households[0]);
localStorage.setItem('activeHouseholdId', households[0].id); localStorage.setItem(ACTIVE_HOUSEHOLD_STORAGE_KEY, String(households[0].id));
}, [clearActiveHousehold, households]); }, [households]);
const setActiveHousehold = (household) => { const setActiveHousehold = (household) => {
setActiveHouseholdState(household); setActiveHouseholdState(household);
if (household) { if (household) {
localStorage.setItem('activeHouseholdId', household.id); localStorage.setItem(ACTIVE_HOUSEHOLD_STORAGE_KEY, String(household.id));
} else { } else {
localStorage.removeItem('activeHouseholdId'); localStorage.removeItem(ACTIVE_HOUSEHOLD_STORAGE_KEY);
} }
}; };

View File

@ -0,0 +1,103 @@
import { expect, test } from "@playwright/test";
function seedAuthStorage(page: import("@playwright/test").Page) {
return page.addInitScript(() => {
localStorage.setItem("token", "test-token");
localStorage.setItem("userId", "1");
localStorage.setItem("role", "admin");
localStorage.setItem("username", "persistent-user");
});
}
async function mockConfig(page: import("@playwright/test").Page) {
await page.route("**/config", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
maxFileSizeMB: 20,
maxImageDimension: 800,
imageQuality: 85,
}),
});
});
}
test("selected household stays active after refreshing on settings and home pages", async ({ page }) => {
await seedAuthStorage(page);
await mockConfig(page);
const households = [
{ id: 1, name: "Alpha Home", role: "owner" },
{ id: 2, name: "Bravo Home", role: "admin" },
];
const storesByHousehold = {
1: [{ id: 101, name: "Costco", is_default: true }],
2: [{ id: 201, name: "Trader Joe's", is_default: true }],
};
await page.route("**/households", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(households),
});
});
await page.route("**/stores/household/*", async (route) => {
const householdId = Number(route.request().url().split("/").pop());
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(
storesByHousehold[householdId as keyof typeof storesByHousehold] ?? []
),
});
});
await page.route("**/households/*/stores/*/list/recent", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([]),
});
});
await page.route("**/households/*/stores/*/list", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ items: [] }),
});
});
await page.route("**/households/*/members", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([{ id: 1, username: "persistent-user", role: "owner" }]),
});
});
await page.goto("/");
await expect(page.getByRole("button", { name: "Alpha Home" })).toBeVisible();
await page.getByRole("button", { name: "Alpha Home" }).click();
await page.getByRole("button", { name: "Bravo Home" }).click();
await expect(page.getByRole("button", { name: "Bravo Home" })).toBeVisible();
await expect.poll(() => page.evaluate(() => localStorage.getItem("activeHouseholdId"))).toBe("2");
await page.goto("/settings");
await expect(page.getByRole("button", { name: "Bravo Home" })).toBeVisible();
await page.reload();
await expect(page.getByRole("button", { name: "Bravo Home" })).toBeVisible();
await expect.poll(() => page.evaluate(() => localStorage.getItem("activeHouseholdId"))).toBe("2");
await page.goto("/");
await expect(page.getByRole("button", { name: "Bravo Home" })).toBeVisible();
await expect(page.getByRole("button", { name: "Trader Joe's" })).toBeVisible();
});