Files
intotheeast-com/tests/ui/post/post-form-ux.spec.js
T
m038 421c21345e test(post-form): retarget suite to redesigned /post + add UX coverage (U7)
- test-form-config.sh: assert parent is NOT hardcoded (injected server-side),
  active_trip set in site.yaml, and the new fields incl. custom 'photos' type.
- helpers.js: resolve active trip from site.active_trip (parent coupling gone);
  fillEditor() drives EasyMDE via window.postFormEditor; waitForPhotoUpload()
  waits on the new picker.
- post.spec / validation.spec: content via the editor, filepond selectors ->
  the photo picker, P8 checks editor value, V3/V4 exercise the picker cap +
  fail-closed non-image.
- post-form-ux.spec.js (new): AE3 disclosure, AE1 HEIC->JPEG, AE4 corrupt-HEIC
  fail-closed, R18 weather gating, R20 draft restore.
- fixtures: real + corrupt .heic.
- test-post.sh: resolve dailies dir from active_trip.

Refs AE1-AE4, R18, R20, U7.
2026-07-04 16:56:07 +02:00

103 lines
5.1 KiB
JavaScript

// @ts-check
// Tests: post-form redesign UX — disclosure, HEIC conversion + failure,
// weather-button gating, draft restore. Covers AE1, AE3, AE4 and R18/R20.
const { test, expect } = require('@playwright/test');
const path = require('path');
const fs = require('fs');
const { fillEditor, waitForPhotoUpload, cleanupEntry, findEntry } = require('../helpers');
const TEST_HEIC = path.join(__dirname, '../../fixtures/test-photo.heic');
const TEST_CORRUPT_HEIC = path.join(__dirname, '../../fixtures/test-corrupt.heic');
const DRAFT_KEY = 'intotheeast:new-entry-draft';
const created = [];
test.afterAll(() => { created.forEach(cleanupEntry); });
// ── AE3: advanced fields sit behind "More options" ────────────────────────────
test('AE3: advanced fields are hidden until "More options" is expanded', async ({ page }) => {
await page.goto('/post');
const hero = page.locator('input[name="data[hero_image]"]');
await expect(page.locator('details.more-options')).toBeAttached();
await expect(hero).toBeHidden(); // collapsed <details> hides content
await page.locator('.more-options__summary').click();
await expect(hero).toBeVisible();
});
// ── AE1: HEIC → JPEG conversion, posted with a working thumbnail ───────────────
test('AE1: a HEIC photo is converted to JPEG client-side and posted', async ({ page }) => {
const tag = `heic-${Date.now()}`;
await page.goto('/post');
await page.fill('input[name="data[title]"]', `UI Test ${tag}`);
await fillEditor(page, 'HEIC conversion test. Safe to delete.');
await page.locator('input.photo-picker__input').setInputFiles(TEST_HEIC);
await waitForPhotoUpload(page, 1); // is-done == converted + uploaded
await expect(page.locator('.photo-card.is-done .photo-card__thumb')).toBeVisible();
await page.locator('.btn-post').evaluate(el => el.click());
await expect(page.locator('.notices')).toContainText('Entry posted successfully!', { timeout: 15_000 });
created.push(tag);
const dir = findEntry(tag);
expect(dir, 'Entry folder should exist on disk').toBeTruthy();
const files = fs.readdirSync(dir);
expect(files.some(f => /\.jpe?g$/i.test(f)), 'a JPEG should be posted').toBe(true);
expect(files.some(f => /\.heic$/i.test(f)), 'the original HEIC must NOT be posted').toBe(false);
});
// ── AE4: corrupt HEIC fails closed; Submit + other photos stay usable ─────────
test('AE4: a corrupt HEIC is blocked (fail-closed) and never posted', async ({ page }) => {
const tag = `heicfail-${Date.now()}`;
await page.goto('/post');
await page.fill('input[name="data[title]"]', `UI Test ${tag}`);
await fillEditor(page, 'HEIC failure test. Safe to delete.');
await page.locator('input.photo-picker__input').setInputFiles(TEST_CORRUPT_HEIC);
await expect(page.locator('.photo-card.is-error')).toBeVisible({ timeout: 15_000 });
await expect(page.locator('.btn-post')).toBeEnabled(); // Submit still usable
await page.locator('.btn-post').evaluate(el => el.click());
await expect(page.locator('.notices')).toContainText('Entry posted successfully!', { timeout: 15_000 });
created.push(tag);
const dir = findEntry(tag);
expect(dir).toBeTruthy();
const files = fs.readdirSync(dir);
expect(files.some(f => /\.heic$/i.test(f)), 'the corrupt HEIC must never be posted').toBe(false);
});
// ── R18: Get Weather is gated on coordinates ──────────────────────────────────
test('R18: Get Weather is disabled until Get Location provides coordinates', async ({ page, context }) => {
await context.grantPermissions(['geolocation']);
await context.setGeolocation({ latitude: 35.6812, longitude: 139.7671 });
await page.goto('/post');
await expect(page.locator('#get-weather')).toBeDisabled();
await page.click('#get-location');
await expect(page.locator('input[name="data[lat]"]')).toHaveValue(/35\.68/, { timeout: 5_000 });
await expect(page.locator('#get-weather')).toBeEnabled();
});
// ── R20: text draft survives a reload; photos need re-selecting ────────────────
test('R20: in-progress text is restored after a reload, with a photos hint', async ({ page }) => {
await page.goto('/post');
await page.evaluate((k) => localStorage.removeItem(k), DRAFT_KEY);
const marker = `draft-${Date.now()}`;
await page.fill('input[name="data[title]"]', marker);
await fillEditor(page, `Draft body ${marker}`);
// Nudge an input event so the draft is written, then let it flush.
await page.locator('input[name="data[title]"]').press('End');
await page.waitForTimeout(300);
await page.reload();
await expect(page.locator('input[name="data[title]"]')).toHaveValue(marker);
expect(await page.evaluate(() => window.postFormEditor.value())).toContain(marker);
await expect(page.locator('.photo-picker__reauth-hint')).toBeVisible();
await page.evaluate((k) => localStorage.removeItem(k), DRAFT_KEY);
});