Add Equipment page: owned brewers and grinders filter the Brews UI
Test and deploy / test-and-deploy (push) Successful in 59s
Test and deploy / test-and-deploy (push) Successful in 59s
- user_gear table (migration 008) + GET/PUT /api/gear (brewer keys validated against the taxonomy, grinders deduped/trimmed) - /gear page: silhouette multi-select for owned brewers (autosaves) and a grinder list; nav gains Brewing → Equipment - Brews page: with gear configured, the picker shows only owned brewers (plus the edited brew's method) with a show-all toggle; grinder field suggests your grinders; empty-gear state links to /gear - Included in full backups, per-user export, and the OpenAPI spec Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3bceb44ae3
commit
e62b9601a2
@@ -377,6 +377,52 @@ test("roaster profile aggregates uploaded roasts; plan chat is grounded in it",
|
||||
assert.equal(chatCalls.length, 1);
|
||||
});
|
||||
|
||||
test("gear: owned brewers/grinders round-trip, validate, and stay private", async () => {
|
||||
const { app, agent } = await setup();
|
||||
const { csrf } = await signup(agent, "[email protected]");
|
||||
|
||||
// Defaults to empty
|
||||
const empty = await agent.get("/api/gear");
|
||||
assert.equal(empty.status, 200);
|
||||
assert.deepEqual(empty.body.gear, { brewers: [], grinders: [] });
|
||||
|
||||
// Save + read back (upsert twice to cover the ON CONFLICT path)
|
||||
const first = await agent
|
||||
.put("/api/gear")
|
||||
.set("x-csrf-token", csrf)
|
||||
.send({ brewers: ["v60", "aeropress"], grinders: ["Comandante C40"] });
|
||||
assert.equal(first.status, 200);
|
||||
const second = await agent
|
||||
.put("/api/gear")
|
||||
.set("x-csrf-token", csrf)
|
||||
.send({ brewers: ["v60", "hario-switch", "v60"], grinders: ["Comandante C40", "DF64", " "] });
|
||||
assert.equal(second.status, 200);
|
||||
assert.deepEqual(second.body.gear.brewers, ["v60", "hario-switch"]); // deduped
|
||||
assert.deepEqual(second.body.gear.grinders, ["Comandante C40", "DF64"]); // blank dropped
|
||||
assert.deepEqual((await agent.get("/api/gear")).body.gear.brewers, ["v60", "hario-switch"]);
|
||||
|
||||
// Unknown brewer keys and non-array bodies are rejected
|
||||
assert.equal(
|
||||
(
|
||||
await agent
|
||||
.put("/api/gear")
|
||||
.set("x-csrf-token", csrf)
|
||||
.send({ brewers: ["teapot"], grinders: [] })
|
||||
).status,
|
||||
400,
|
||||
);
|
||||
assert.equal(
|
||||
(await agent.put("/api/gear").set("x-csrf-token", csrf).send({ brewers: "v60", grinders: [] })).status,
|
||||
400,
|
||||
);
|
||||
|
||||
// Private per user
|
||||
const request = (await import("supertest")).default;
|
||||
const stranger = request.agent(app);
|
||||
await signup(stranger, "[email protected]");
|
||||
assert.deepEqual((await stranger.get("/api/gear")).body.gear.brewers, []);
|
||||
});
|
||||
|
||||
test("per-user export and openapi spec", async () => {
|
||||
const { agent } = await setup();
|
||||
const { csrf } = await signup(agent, "[email protected]");
|
||||
|
||||
Reference in New Issue
Block a user