chore(autonomous-session): consolidate uncommitted work from prior session

Bundles the prior autonomous-session output that was sitting unstaged:

- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
  never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
  after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
  redirects (ocr to ai, reports to dashboard, invitations to users),
  docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
  flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
  let-reassign), set-state-in-effect disables in CountryFlag and
  UploadForSigning preview-bytes effect, unused 'confirm' destructures in
  interest contract + reservation tabs, unescaped apostrophe in test-template
  card copy
This commit is contained in:
2026-05-23 00:52:59 +02:00
parent 43719b49e9
commit 221ae5784e
749 changed files with 7440 additions and 3118 deletions

View File

@@ -73,7 +73,7 @@ test.describe('Error Recovery', () => {
await emailInput.fill('recovery@test.com');
}
// Re-submit should succeed or at minimum no longer show fullName error
// Re-submit - should succeed or at minimum no longer show fullName error
if (await submitBtn.isVisible({ timeout: 3_000 }).catch(() => false)) {
await submitBtn.click();
await page.waitForTimeout(2_000);
@@ -94,7 +94,7 @@ test.describe('Error Recovery', () => {
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2_000);
// Find the search input try multiple selectors
// Find the search input - try multiple selectors
const searchInput = page
.locator('input[type="search"]')
.first()
@@ -105,10 +105,12 @@ test.describe('Error Recovery', () => {
const searchVisible = await searchInput.isVisible({ timeout: 5_000 }).catch(() => false);
if (!searchVisible) {
console.log(' Search input not found checking global search');
console.log(' Search input not found - checking global search');
// Try the global search bar (usually a keyboard shortcut or top-bar icon)
const globalSearch = page.locator('[class*="global-search"], [data-testid*="global"]').first()
const globalSearch = page
.locator('[class*="global-search"], [data-testid*="global"]')
.first()
.or(page.getByRole('button', { name: /search/i }).first());
if (await globalSearch.isVisible({ timeout: 3_000 }).catch(() => false)) {
@@ -117,35 +119,47 @@ test.describe('Error Recovery', () => {
}
}
const activeSearch = page.locator('input[type="search"], input[placeholder*="search" i], [role="searchbox"]').first();
const activeSearch = page
.locator('input[type="search"], input[placeholder*="search" i], [role="searchbox"]')
.first();
const isActive = await activeSearch.isVisible({ timeout: 3_000 }).catch(() => false);
if (!isActive) {
console.log(' No accessible search input found skipping search validation');
console.log(' No accessible search input found - skipping search validation');
expect(true).toBeTruthy();
return;
}
// Single character should not crash
// Single character - should not crash
await activeSearch.fill('a');
await page.waitForTimeout(1_500);
const noError1 = await page.getByText(/uncaught error|cannot read|undefined/i).isVisible({ timeout: 1_000 }).catch(() => false);
const noError1 = await page
.getByText(/uncaught error|cannot read|undefined/i)
.isVisible({ timeout: 1_000 })
.catch(() => false);
expect(noError1).toBeFalsy();
// SQL injection payload should not crash or expose DB errors
// SQL injection payload - should not crash or expose DB errors
await activeSearch.fill("'; DROP TABLE clients; --");
await page.waitForTimeout(1_500);
const noSqlError = await page.getByText(/sql|syntax error|pg error|database/i).isVisible({ timeout: 1_000 }).catch(() => false);
const noSqlError = await page
.getByText(/sql|syntax error|pg error|database/i)
.isVisible({ timeout: 1_000 })
.catch(() => false);
expect(noSqlError).toBeFalsy();
// XSS payload should be escaped, not executed
// XSS payload - should be escaped, not executed
await activeSearch.fill('<script>alert("xss")</script>');
await page.waitForTimeout(1_000);
// The payload text itself (escaped) may appear, but no alert dialog
const hasAlertDialog = await page.locator('[role="alertdialog"]').filter({ hasText: 'xss' }).isVisible({ timeout: 1_000 }).catch(() => false);
const hasAlertDialog = await page
.locator('[role="alertdialog"]')
.filter({ hasText: 'xss' })
.isVisible({ timeout: 1_000 })
.catch(() => false);
expect(hasAlertDialog).toBeFalsy();
// Clear the search
@@ -153,7 +167,10 @@ test.describe('Error Recovery', () => {
await page.waitForTimeout(500);
// Page should still be functional
const body = await page.locator('body').textContent().catch(() => '');
const body = await page
.locator('body')
.textContent()
.catch(() => '');
expect(body && body.length > 10).toBeTruthy();
});
@@ -164,12 +181,24 @@ test.describe('Error Recovery', () => {
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2_000);
const is404 = await page.getByText('404').isVisible({ timeout: 3_000 }).catch(() => false);
const isNotFound = await page.getByText(/not found/i).isVisible({ timeout: 3_000 }).catch(() => false);
const isError = await page.getByText(/error/i).isVisible({ timeout: 3_000 }).catch(() => false);
const is404 = await page
.getByText('404')
.isVisible({ timeout: 3_000 })
.catch(() => false);
const isNotFound = await page
.getByText(/not found/i)
.isVisible({ timeout: 3_000 })
.catch(() => false);
const isError = await page
.getByText(/error/i)
.isVisible({ timeout: 3_000 })
.catch(() => false);
// Page should not be a blank crash must render something meaningful
const body = await page.locator('body').textContent().catch(() => '');
// Page should not be a blank crash - must render something meaningful
const body = await page
.locator('body')
.textContent()
.catch(() => '');
const hasContent = body !== null && body.length > 20;
expect(is404 || isNotFound || isError || hasContent).toBeTruthy();
@@ -182,13 +211,19 @@ test.describe('Error Recovery', () => {
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2_000);
const body = await page.locator('body').textContent().catch(() => '');
const body = await page
.locator('body')
.textContent()
.catch(() => '');
// Should render something not crash with empty body
// Should render something - not crash with empty body
expect(body && body.length > 10).toBeTruthy();
// Should not show a raw Next.js error page with stack traces
const hasStackTrace = await page.getByText(/at Object|at Module|stack trace/i).isVisible({ timeout: 1_000 }).catch(() => false);
const hasStackTrace = await page
.getByText(/at Object|at Module|stack trace/i)
.isVisible({ timeout: 1_000 })
.catch(() => false);
expect(hasStackTrace).toBeFalsy();
});
@@ -209,9 +244,15 @@ test.describe('Error Recovery', () => {
// Should be back on a functional page
const returnUrl = page.url();
const body = await page.locator('body').textContent().catch(() => '');
const body = await page
.locator('body')
.textContent()
.catch(() => '');
expect(body && body.length > 10).toBeTruthy();
// URL should differ from the 404 page (we went back)
expect(returnUrl !== `${page.url().split('//')[0]}//${page.url().split('//')[1]?.split('/')[0]}/${PORT_SLUG}/this-does-not-exist`).toBeTruthy();
expect(
returnUrl !==
`${page.url().split('//')[0]}//${page.url().split('//')[1]?.split('/')[0]}/${PORT_SLUG}/this-does-not-exist`,
).toBeTruthy();
});
});