const { test, expect } = require('playwright/test'); const BASE_URL = process.env.SX_TEST_URL || 'http://localhost:8013'; test.describe('Handler responses render correctly', () => { test('bulk-update: deactivate renders proper HTML attributes', async ({ page }) => { await page.goto(BASE_URL + '/sx/(geography.(hypermedia.(example.bulk-update)))', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); // Check first row, note its status const firstCheckbox = page.locator('input[type="checkbox"]').first(); await firstCheckbox.check(); // Click Deactivate await page.locator('button:has-text("Deactivate")').first().click(); await page.waitForTimeout(3000); // The table should still have proper HTML — no raw "class" text visible const tableText = await page.locator('table').first().textContent(); expect(tableText).not.toContain('classpx'); expect(tableText).not.toContain('classborder'); // Rows should have proper class attributes, not class as text content const firstTd = page.locator('table tbody tr td').first(); const tdClass = await firstTd.getAttribute('class'); expect(tdClass).toBeTruthy(); expect(tdClass).toContain('px-'); }); test('delete-row: deleted row disappears with proper rendering', async ({ page }) => { await page.goto(BASE_URL + '/sx/(geography.(hypermedia.(example.delete-row)))', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const rows = page.locator('#delete-rows tr, table tbody tr'); const countBefore = await rows.count(); await page.locator('button:has-text("Delete")').first().click(); await page.waitForTimeout(3000); // Row should be removed const countAfter = await rows.count(); expect(countAfter).toBeLessThan(countBefore); // Remaining rows should have proper class attrs, no raw text if (countAfter > 0) { const td = page.locator('table tbody tr td').first(); const text = await td.textContent(); expect(text).not.toContain('classpx'); } }); test('click-to-load: loaded rows have proper HTML', async ({ page }) => { await page.goto(BASE_URL + '/sx/(geography.(hypermedia.(example.click-to-load)))', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const rowsBefore = await page.locator('table tbody tr').count(); const loadBtn = page.locator('button:has-text("Load More")').first(); if (await loadBtn.count() > 0) { await loadBtn.click(); await page.waitForTimeout(3000); const rowsAfter = await page.locator('table tbody tr').count(); expect(rowsAfter).toBeGreaterThan(rowsBefore); // New rows should have proper class attrs const lastTd = page.locator('table tbody tr:last-child td').first(); const text = await lastTd.textContent(); expect(text).not.toContain('classpx'); } }); test('active-search: results render as proper HTML', async ({ page }) => { await page.goto(BASE_URL + '/sx/(geography.(hypermedia.(example.active-search)))', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const input = page.locator('input[placeholder*="earch"], input[name="q"]').first(); await input.fill('python'); await page.waitForTimeout(2000); const results = page.locator('#search-results'); const text = await results.textContent(); // Should contain search results, not raw SX class text expect(text).not.toContain('classpx'); expect(text).not.toContain('classbg'); expect(text.toLowerCase()).toContain('python'); }); test('form-submission: response renders as HTML not SX text', async ({ page }) => { await page.goto(BASE_URL + '/sx/(geography.(hypermedia.(example.form-submission)))', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const form = page.locator('form').first(); const inputs = form.locator('input[type="text"], input:not([type])'); if (await inputs.count() > 0) { await inputs.first().fill('test-value'); } const submit = form.locator('button[type="submit"], button').first(); await submit.click(); await page.waitForTimeout(3000); // Response should not have raw SX class text const root = page.locator('#sx-root'); const text = await root.textContent(); expect(text).not.toContain('classpx'); expect(text).not.toContain('classborder'); }); test('edit-row: edited row renders with proper classes', async ({ page }) => { await page.goto(BASE_URL + '/sx/(geography.(hypermedia.(example.edit-row)))', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const editBtn = page.locator('button:has-text("Edit")').first(); if (await editBtn.count() > 0) { await editBtn.click(); await page.waitForTimeout(2000); // Edit form or inline edit should not show raw class text const root = page.locator('#sx-root'); const text = await root.textContent(); expect(text).not.toContain('classpx'); } }); test('tabs: tab content renders as proper HTML', async ({ page }) => { await page.goto(BASE_URL + '/sx/(geography.(hypermedia.(example.tabs)))', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); // Click a tab const tabs = page.locator('[sx-get*="tab"]'); if (await tabs.count() >= 2) { await tabs.nth(1).click(); await page.waitForTimeout(2000); const root = page.locator('#sx-root'); const text = await root.textContent(); expect(text).not.toContain('classpx'); } }); });