Files
intotheeast-com/tests/ui/post/delete-flow.spec.js
T
m038andClaude Opus 4.8 d3c17791b7 test(post-form): add edit/delete/anon coverage and fix stale photo-gate assumptions
New specs: edit-mode (ES1 save round-trip + ES2/ES3 prefill 404/500 states),
delete-flow (DEL1-3 happy/cancel/failed), anon-view (AN1 no owner controls,
AN2 draft hidden from anon), photo-editor (live add/delete/reorder). Existing:
P3-P8 now attach a photo to satisfy the create photo-gate; V3 picker cap 4->6.
Full post suite 38/38, stable across parallel (3-worker) runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn
2026-07-05 23:42:45 +02:00

94 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @ts-check
// Tests: DEL1DEL3 — the owner entry-delete flow (feed-actions.js + the
// entry-actions `deleteEntry` route). Delete is a two-step inline confirm on a
// feed card: Delete → Cancel / Confirm delete → DELETE /api/v1/entry/<slug>.
//
// This flow — a destructive, owner-only action — had zero automated coverage.
// - DEL1: full happy path — the card vanishes AND the folder leaves disk.
// - DEL2: Cancel is a real escape hatch — nothing is deleted.
// - DEL3: a failed DELETE keeps the card and surfaces the inline error.
const { test, expect } = require('@playwright/test');
const path = require('path');
const {
fillEditor, waitForPhotoUpload, cleanupEntry, findEntry, ACTIVE_TRIP_URL,
} = require('../helpers');
const TEST_PHOTO = path.join(__dirname, '../../fixtures/test-photo.jpg');
const created = [];
// cleanupEntry is a no-op when the entry was already deleted by the test.
test.afterAll(() => created.forEach(cleanupEntry));
/** Post a fresh entry through the create form so a feed card exists to delete. */
async function createEntry(page, tag) {
await page.goto('/post');
await page.fill('input[name="data[title]"]', `UI Test ${tag}`);
await fillEditor(page, `Delete-flow fixture ${tag}. Safe to delete.`);
await page.locator('input.filepond--browser').setInputFiles(TEST_PHOTO);
await waitForPhotoUpload(page);
await page.locator('.btn-post').evaluate(el => el.click());
await expect(page.locator('.form-messages, .notices')).toContainText(
'Entry posted successfully!', { timeout: 15_000 });
}
// ── DEL1: happy delete removes the card and the folder ────────────────────────
test('DEL1: owner deletes an entry — the card disappears and the folder is removed', async ({ page }) => {
const tag = `del1-${Date.now()}`;
await createEntry(page, tag);
created.push(tag);
await page.goto(ACTIVE_TRIP_URL);
const card = page.locator('.journal-post', { hasText: tag });
await expect(card).toHaveCount(1);
// The owner-only controls must be present — this also asserts the auth gate.
await card.locator('[data-delete-start]').click();
await card.locator('[data-delete-confirm]').click();
await expect(page.locator('.journal-post', { hasText: tag }))
.toHaveCount(0, { timeout: 15_000 });
await expect.poll(() => findEntry(tag), { timeout: 15_000 }).toBeNull();
});
// ── DEL2: Cancel keeps the entry ──────────────────────────────────────────────
test('DEL2: cancelling the confirm step keeps the entry on the page and on disk', async ({ page }) => {
const tag = `del2-${Date.now()}`;
await createEntry(page, tag);
created.push(tag);
await page.goto(ACTIVE_TRIP_URL);
const card = page.locator('.journal-post', { hasText: tag });
await expect(card).toHaveCount(1);
await card.locator('[data-delete-start]').click();
await expect(card.locator('.entry-delete-confirm')).toBeVisible();
await card.locator('[data-delete-cancel]').click();
await expect(card.locator('.entry-delete-confirm')).toBeHidden();
await expect(card).toHaveCount(1);
expect(findEntry(tag), 'a cancelled delete must not remove the folder').not.toBeNull();
});
// ── DEL3: a failed DELETE keeps the card and shows the inline error ───────────
test('DEL3: a failed delete keeps the card and surfaces the inline error', async ({ page }) => {
const tag = `del3-${Date.now()}`;
await createEntry(page, tag);
created.push(tag);
await page.goto(ACTIVE_TRIP_URL);
// Force the delete request to fail after the confirm.
await page.route('**/api/v1/entry/**', (route) => {
if (route.request().method() === 'DELETE') return route.fulfill({ status: 500, body: '' });
return route.continue();
});
const card = page.locator('.journal-post', { hasText: tag });
await expect(card).toHaveCount(1);
await card.locator('[data-delete-start]').click();
await card.locator('[data-delete-confirm]').click();
await expect(card.locator('.entry-delete-msg'))
.toContainText('Could not delete', { timeout: 15_000 });
await expect(card).toHaveCount(1); // the card survives a failed delete
expect(findEntry(tag), 'a failed delete must not remove the folder').not.toBeNull();
});