Tasks 2 to 4 of the plan. Nine `collection.find(...)` dereferences become findOrFail, so a missing row fails as a named assertion naming what was wanted and how many rows were searched, rather than "Cannot read properties of undefined" pointing at test plumbing. Where the old code followed the lookup with expect(x).toBeTruthy(), that assertion is dropped: findOrFail already guarantees it, and with a better message. The expect timeout goes from Playwright's default 5s to 10s. It costs nothing on a green run — it bounds how long a failing assertion waits, not how long a passing one takes — and #239 died reporting exactly Timeout: 5000ms on a runner that also builds, migrates and runs three other suites. The admin-save happy path comes back from the #245 skip. It is the only end-to-end check that adding an item reaches the database rather than merely firing a toast, and it passed both full parallel runs and in isolation. filters.spec.ts:216 is deliberately untouched: its .find() searches CSS class names on a string array, not test data, and has no missing-row failure mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
64 lines
2.5 KiB
TypeScript
64 lines
2.5 KiB
TypeScript
import { test, expect, uniqueSuffix, createCategory, findOrFail } from './fixtures';
|
|
|
|
test.describe('Inline category creation from the item form', () => {
|
|
test('creates a category without leaving the item form and assigns it', async ({
|
|
page,
|
|
admin,
|
|
adminInventory
|
|
}) => {
|
|
const RUN = `i${uniqueSuffix()}`;
|
|
const categoryName = `Inline ${RUN}`;
|
|
const itemName = `Item ${RUN}`;
|
|
|
|
await admin.goto();
|
|
await adminInventory.openItemFormLoaded();
|
|
await adminInventory.name.fill(itemName);
|
|
await adminInventory.price.fill('99');
|
|
|
|
await adminInventory.categoryField.click();
|
|
await expect(adminInventory.newCategoryName).toBeVisible();
|
|
await expect(adminInventory.createCategoryButton).toBeVisible();
|
|
await adminInventory.newCategoryName.fill(categoryName);
|
|
// Submitted with Enter rather than a click: the popup sits over a
|
|
// virtualized tree that keeps re-measuring, so a click target inside it is
|
|
// never geometrically stable. Enter runs the same handler as the button.
|
|
await adminInventory.newCategoryName.press('Enter');
|
|
|
|
// The new category should be selected straight away — having to hunt for it
|
|
// in the tree afterwards defeats the point of creating it inline.
|
|
await expect(adminInventory.formDialog.getByText(categoryName)).toBeVisible();
|
|
|
|
await adminInventory.confirmButton.click();
|
|
await expect(page.getByText('Item added')).toBeVisible();
|
|
|
|
const items = await (await page.request.get('/api/admin/items')).json();
|
|
// findOrFail already fails with the row count when the item is missing,
|
|
// which the toBeTruthy assertion this replaces could not report.
|
|
const saved = findOrFail(
|
|
items,
|
|
(item: { name: string }) => item.name === itemName,
|
|
`the item named ${itemName}`
|
|
);
|
|
expect(saved.category_name).toBe(categoryName);
|
|
});
|
|
|
|
test('reports a duplicate category name instead of silently doing nothing', async ({
|
|
page,
|
|
admin,
|
|
adminInventory
|
|
}) => {
|
|
const categoryName = `Dupe i${uniqueSuffix()}`;
|
|
await createCategory(page.request, categoryName);
|
|
|
|
await admin.goto();
|
|
await adminInventory.openItemFormLoaded();
|
|
await adminInventory.categoryField.click();
|
|
await expect(adminInventory.newCategoryName).toBeVisible();
|
|
await expect(adminInventory.createCategoryButton).toBeVisible();
|
|
await adminInventory.newCategoryName.fill(categoryName);
|
|
await adminInventory.newCategoryName.press('Enter');
|
|
|
|
await expect(page.getByText(/already exists/i)).toBeVisible();
|
|
});
|
|
});
|