feat: add CSV import/export for gear collection
Adds export (GET /api/items/export) and import (POST /api/items/import) routes backed by a pure csv.service with no external deps, plus useExportItems/useImportItems hooks and an Import/Export section in the Settings page. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -144,4 +144,64 @@ describe("Item Routes", () => {
|
||||
});
|
||||
expect(res.status).toBe(404);
|
||||
});
|
||||
|
||||
it("GET /api/items/export returns CSV with correct content-type", async () => {
|
||||
// Create an item first
|
||||
await app.request("/api/items", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: "Tent",
|
||||
weightGrams: 1200,
|
||||
priceCents: 35000,
|
||||
categoryId: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
const res = await app.request("/api/items/export");
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.headers.get("Content-Type")).toContain("text/csv");
|
||||
|
||||
const text = await res.text();
|
||||
const lines = text.split("\n");
|
||||
expect(lines[0]).toBe(
|
||||
"name,quantity,weightGrams,priceCents,category,notes,productUrl",
|
||||
);
|
||||
expect(lines.length).toBeGreaterThanOrEqual(2);
|
||||
expect(lines[1]).toContain("Tent");
|
||||
});
|
||||
|
||||
it("POST /api/items/import with CSV file creates items", async () => {
|
||||
const csvContent = [
|
||||
"name,quantity,weightGrams,priceCents,category,notes,productUrl",
|
||||
"Sleeping Bag,1,800,25000,Camping,,",
|
||||
].join("\n");
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append(
|
||||
"file",
|
||||
new Blob([csvContent], { type: "text/csv" }),
|
||||
"import.csv",
|
||||
);
|
||||
|
||||
const res = await app.request("/api/items/import", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.imported).toBe(1);
|
||||
expect(body.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("POST /api/items/import with no file returns 400", async () => {
|
||||
const res = await app.request("/api/items/import", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user