import { test, expect } from '@playwright/test' import { imhotep } from 'imhotep-playwright' import { loadFixtureInPage, waitForFixtureReady } from './harness.js' async function fixtureUrl(category: string): Promise { const path = await loadFixtureInPage({ goto: async () => {} }, category) const { resolveFixturePage } = await import('./harness.js') return 'file://' + resolveFixturePage(category) } test.describe('E2E: Public API — leftOf vertical slice', () => { test('leftOf with minGap:8 passes when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults.length).toBe(1) expect(result.clauseResults[0].status).toBe('pass') }) test('leftOf with minGap:20 fails when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.leftOf('[data-testid="box-right"]', { minGap: 20 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults.length).toBe(1) expect(result.clauseResults[0].status).toBe('fail') }) test('failing leftOf includes diagnostic with measured gap value', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.leftOf('[data-testid="box-right"]', { minGap: 20 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.diagnostics.length).toBeGreaterThan(0) const diag = result.diagnostics.find((d: { code: string; message: string }) => d.code === 'IMH_RELATION_LEFT_OF_FAILED') expect(diag).toBeDefined() expect(diag!.message).toContain('measured gap is') expect(diag!.message).toContain('10') }) test('assertion on non-existent selector fails with extraction error', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('.does-not-exist').to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('error') expect(result.diagnostics.some((d: { code: string }) => d.code === 'IMH_SELECTOR_ZERO_MATCHES')).toBe(true) }) test('valid assertions pass when other assertions have missing selectors', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) ui.expect('.does-not-exist').to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults.length).toBe(2) // The valid assertion should pass const validCr = result.clauseResults.find((cr: any) => cr.clauseLabel?.includes('box-left')) expect(validCr?.status).toBe('pass') // The invalid assertion should fail with selector error const invalidCr = result.clauseResults.find((cr: any) => cr.clauseLabel?.includes('does-not-exist')) expect(invalidCr?.status).toBe('error') expect(invalidCr?.diagnostics).toContain('IMH_SELECTOR_ZERO_MATCHES') }) test('separatedFrom passes when elements do not overlap', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) // box-left and box-right are in a flex container with 10px gap — they do not overlap ui.expect('[data-testid="box-left"]').to.be.separatedFrom('[data-testid="box-right"]') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('above with minGap:8 passes when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-above"]').to.be.above('[data-testid="box-below"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('above with minGap:20 fails when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-above"]').to.be.above('[data-testid="box-below"]', { minGap: 20 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('below with minGap:8 passes when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-below"]').to.be.below('[data-testid="box-above"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('below with minGap:20 fails when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-below"]').to.be.below('[data-testid="box-above"]', { minGap: 20 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('inside passes when subject is fully within reference', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-inside"]').to.be.inside('[data-testid="container-inside"]') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('inside fails when subject overflows reference', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="container-inside"]').to.be.inside('[data-testid="box-inside"]') const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('alignedWith centerY passes when centers align', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="align-subject"]').to.be.alignedWith('[data-testid="align-ref"]', { axis: 'centerY', tolerance: 0 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('alignedWith centerY fails when misaligned beyond tolerance', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-above"]').to.be.alignedWith('[data-testid="box-below"]', { axis: 'centerY', tolerance: 0 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test("atLeast('44px').wide passes when width >= 44", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atLeast('44px').wide const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test("atLeast('100px').wide fails when width < 100", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atLeast('100px').wide const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test("atLeast('44px').tall passes when height >= 44", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atLeast('44px').tall const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test("atLeast('100px').tall fails when height < 100", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atLeast('100px').tall const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('rightOf with minGap:8 passes when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-right"]').to.be.rightOf('[data-testid="box-left"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults.length).toBe(1) expect(result.clauseResults[0].status).toBe('pass') }) test('rightOf with minGap:20 fails when actual gap is 10px', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-right"]').to.be.rightOf('[data-testid="box-left"]', { minGap: 20 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults.length).toBe(1) expect(result.clauseResults[0].status).toBe('fail') }) test('centeredWithin passes when element is centered', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-centered"]').to.be.centeredWithin('[data-testid="center-container"]', { tolerance: 0 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('centeredWithin fails when element is offset', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-offset"]').to.be.centeredWithin('[data-testid="center-container-offset"]', { tolerance: 0 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test("atMost('100px').wide passes when width <= max", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atMost('100px').wide const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test("atMost('50px').wide fails when width > max", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atMost('50px').wide const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test("atMost('100px').tall passes when height <= max", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atMost('100px').tall const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test("atMost('50px').tall fails when height > max", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atMost('50px').tall const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test("between('50px','100px').wide passes when width in range", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.between('50px', '100px').wide const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test("between('90px','100px').wide fails when width < min", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.between('90px', '100px').wide const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test("between('50px','100px').tall passes when height in range", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.between('50px', '100px').tall const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test("between('90px','100px').tall fails when height < min", async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.between('90px', '100px').tall const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) // ---- New Size Assertion Overloads ---- test('atLeast(44, "width") passes when width >= 44', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atLeast(44, 'width') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('atLeast(100, "width") fails when width < 100', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atLeast(100, 'width') const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('atLeast({ width: 44 }) passes when width >= 44', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atLeast({ width: 44 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('atMost(100, "height") passes when height <= 100', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atMost(100, 'height') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('atMost(50, "height") fails when height > 50', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.atMost(50, 'height') const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('between(50, 100, "width") passes when width in range', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.between(50, 100, 'width') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('between(90, 100, "width") fails when width < min', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.between(90, 100, 'width') const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('between({ width: [50, 100] }) passes when width in range', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.between({ width: [50, 100] }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('contains passes when subject encloses reference', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="contains-container"]').to.be.contains('[data-testid="box-contained"]') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('contains fails when subject does not enclose reference', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-contained"]').to.be.contains('[data-testid="contains-container"]') const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('overlaps passes when elements intersect', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="overlap-a"]').to.be.overlaps('[data-testid="overlap-b"]') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('overlaps fails when elements are separated', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="overlap-a"]').to.be.overlaps('[data-testid="overlap-separate"]') const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('failing page relation prints stable trace metadata (sourceRef + clauseLabel)', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.leftOf('[data-testid="box-right"]', { minGap: 20 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults.length).toBe(1) const cr = result.clauseResults[0] expect(cr.status).toBe('fail') // sourceRef must be present with fluentIndex for fluent assertions expect(cr.sourceRef).toBeDefined() expect(cr.sourceRef!.fluentIndex).toBe(0) expect(cr.sourceRef!.specLine).toBeUndefined() // clauseLabel must be a human-readable string describing the contract expect(cr.clauseLabel).toBeDefined() expect(typeof cr.clauseLabel).toBe('string') expect(cr.clauseLabel).toContain('leftOf') expect(cr.clauseLabel).toContain('[data-testid="box-left"]') expect(cr.clauseLabel).toContain('[data-testid="box-right"]') // Diagnostics should also carry traceability const diag = result.diagnostics.find((d: { code: string }) => d.code === 'IMH_RELATION_LEFT_OF_FAILED') expect(diag).toBeDefined() expect(diag!.clauseLabel).toBeDefined() expect(diag!.sourceRef).toBeDefined() expect((diag!.sourceRef as { fluentIndex?: number }).fluentIndex).toBe(0) }) test('dense spec assertion carries specLine/specColumn in E2E', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.spec("'[data-testid=\"box-left\"]' leftOf '[data-testid=\"box-right\"]' gap 20px") const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults.length).toBe(1) const cr = result.clauseResults[0] expect(cr.sourceRef).toBeDefined() expect(cr.sourceRef!.fluentIndex).toBeUndefined() expect(typeof cr.sourceRef!.specLine).toBe('number') expect(typeof cr.sourceRef!.specColumn).toBe('number') expect(cr.sourceRef!.specLine).toBeGreaterThanOrEqual(1) expect(cr.sourceRef!.specColumn).toBeGreaterThanOrEqual(1) expect(cr.clauseLabel).toBeDefined() expect(cr.clauseLabel).toContain('leftOf') }) test('mixed fluent + dense batch maps each failure to exact authored assertion in E2E', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.leftOf('[data-testid="box-right"]', { minGap: 20 }) ui.spec("'[data-testid=\"box-above\"]' above '[data-testid=\"box-below\"]' gap 20px") const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults.length).toBe(2) // Fluent assertion comes first in processing order const fluentCr = result.clauseResults[0] expect(fluentCr.sourceRef!.fluentIndex).toBe(0) expect(fluentCr.sourceRef!.specLine).toBeUndefined() expect(fluentCr.clauseLabel).toContain('leftOf') // Dense spec comes second const denseCr = result.clauseResults[1] expect(denseCr.sourceRef!.fluentIndex).toBeUndefined() expect(typeof denseCr.sourceRef!.specLine).toBe('number') expect(denseCr.clauseLabel).toContain('above') }) }) test.describe('E2E: Selector Cardinality Contracts (P2.1)', () => { test('exactlyOne passes when selector matches exactly 1 element', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="size-box"]').to.be.exactlyOne() const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults.length).toBe(1) expect(result.clauseResults[0].status).toBe('pass') expect(result.clauseResults[0].metrics.observedCount).toBe(1) expect(result.clauseResults[0].metrics.expectedCount).toBe(1) expect(result.clauseResults[0].metrics.selector).toBe('[data-testid="size-box"]') }) test('exactlyOne fails when selector matches 0 elements', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('.does-not-exist').to.be.exactlyOne() const result = await ui.checkAll() expect(result.passed).toBe(false) const cardClause = result.clauseResults.find((cr: any) => cr.metrics?.expectedCount === 1) expect(cardClause).toBeDefined() expect(cardClause!.status).toBe('fail') expect(cardClause!.metrics.observedCount).toBe(0) expect(cardClause!.metrics.selector).toBe('.does-not-exist') }) test('exactlyOne fails when selector matches multiple elements', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('multi-button')) await waitForFixtureReady(page) ui.expect('.button').to.be.exactlyOne() const result = await ui.checkAll() expect(result.passed).toBe(false) const cardClause = result.clauseResults.find((cr: any) => cr.metrics?.expectedCount === 1) expect(cardClause).toBeDefined() expect(cardClause!.status).toBe('fail') expect(cardClause!.metrics.observedCount).toBe(3) expect(cardClause!.metrics.selector).toBe('.button') }) test('atLeastN passes when count >= n', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('multi-button')) await waitForFixtureReady(page) ui.expect('.button').to.be.atLeastN(2) const result = await ui.checkAll() expect(result.passed).toBe(true) const cardClause = result.clauseResults.find((cr: any) => cr.metrics?.expectedCount === 2) expect(cardClause).toBeDefined() expect(cardClause!.status).toBe('pass') expect(cardClause!.metrics.observedCount).toBe(3) }) test('atLeastN fails when count < n', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('multi-button')) await waitForFixtureReady(page) ui.expect('.button').to.be.atLeastN(5) const result = await ui.checkAll() expect(result.passed).toBe(false) const cardClause = result.clauseResults.find((cr: any) => cr.metrics?.expectedCount === 5) expect(cardClause).toBeDefined() expect(cardClause!.status).toBe('fail') expect(cardClause!.metrics.observedCount).toBe(3) }) test('atMostN passes when count <= n', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('multi-button')) await waitForFixtureReady(page) ui.expect('.button').to.be.atMostN(5) const result = await ui.checkAll() expect(result.passed).toBe(true) const cardClause = result.clauseResults.find((cr: any) => cr.metrics?.expectedCount === 5) expect(cardClause).toBeDefined() expect(cardClause!.status).toBe('pass') expect(cardClause!.metrics.observedCount).toBe(3) }) test('atMostN fails when count > n', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('multi-button')) await waitForFixtureReady(page) ui.expect('.button').to.be.atMostN(2) const result = await ui.checkAll() expect(result.passed).toBe(false) const cardClause = result.clauseResults.find((cr: any) => cr.metrics?.expectedCount === 2) expect(cardClause).toBeDefined() expect(cardClause!.status).toBe('fail') expect(cardClause!.metrics.observedCount).toBe(3) }) test('cardinality failure includes diagnostic with selector and counts', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('multi-button')) await waitForFixtureReady(page) ui.expect('.button').to.be.exactlyOne() const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.diagnostics.length).toBeGreaterThan(0) const diag = result.diagnostics.find((d: any) => d.code === 'IMH_CARDINALITY_EXACTLYONE_FAILED') expect(diag).toBeDefined() expect(diag!.message).toContain('.button') expect(diag!.message).toContain('3') expect(diag!.message).toContain('exactly 1') }) test('mixed batch: cardinality + spatial assertions evaluate together', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.exactlyOne() ui.expect('[data-testid="box-left"]').to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults.length).toBe(2) const cardClause = result.clauseResults.find((cr: any) => cr.metrics?.expectedCount === 1) const spatialClause = result.clauseResults.find((cr: any) => cr.status === 'pass' && !cr.metrics?.expectedCount) expect(cardClause).toBeDefined() expect(spatialClause).toBeDefined() }) }) test.describe('E2E: Spatial Alias Relations', () => { test('beside passes when subject is leftOf reference', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.beside('[data-testid="box-right"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('beside passes when subject is rightOf reference', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-right"]').to.be.beside('[data-testid="box-left"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('beside fails when subject is not horizontally adjacent', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-above"]').to.be.beside('[data-testid="box-below"]', { minGap: 0, maxGap: 5 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') expect(result.diagnostics.some((d: any) => d.code === 'IMH_RELATION_BESIDE_FAILED')).toBe(true) }) test('nextTo is alias for beside and passes', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.nextTo('[data-testid="box-right"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('under is alias for below and passes', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-below"]').to.be.under('[data-testid="box-above"]', { minGap: 8 }) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('within is alias for inside and passes', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-inside"]').to.be.within('[data-testid="container-inside"]') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('within fails when subject overflows reference', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="container-inside"]').to.be.within('[data-testid="box-inside"]') const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') }) test('near passes for overlapping elements', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="overlap-a"]').to.be.near('[data-testid="overlap-b"]') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('near passes for proximate non-overlapping elements', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) // box-left and box-right have a 10px gap, within default radius 100 ui.expect('[data-testid="box-left"]').to.be.near('[data-testid="box-right"]') const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults[0].status).toBe('pass') }) test('near fails when elements are far apart', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) ui.expect('[data-testid="box-left"]').to.be.near('[data-testid="overlap-separate"]', { maxGap: 5 }) const result = await ui.checkAll() expect(result.passed).toBe(false) expect(result.clauseResults[0].status).toBe('fail') expect(result.diagnostics.some((d: any) => d.code === 'IMH_RELATION_NEAR_FAILED')).toBe(true) }) }) test.describe('E2E: Fluent FOL Quantifiers', () => { test('forAll with leftOf predicate passes when all items satisfy relation', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) const { FluentAssertion } = await import('imhotep-dsl') const quantifier = FluentAssertion.forAll('[data-testid="box-left"]', (el) => el.expect().to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) ) ui.quantifier(quantifier) const result = await ui.checkAll() expect(result.passed).toBe(true) }) test('exists with above predicate passes when at least one item satisfies', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) const { FluentAssertion } = await import('imhotep-dsl') const quantifier = FluentAssertion.exists('[data-testid="box-above"]', (el) => el.expect().to.be.above('[data-testid="box-below"]', { minGap: 8 }) ) ui.quantifier(quantifier) const result = await ui.checkAll() expect(result.passed).toBe(true) }) test('nested quantifiers compile and evaluate through adapter', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) const { FluentAssertion } = await import('imhotep-dsl') const quantifier = FluentAssertion.forAll('[data-testid="box-left"]', (el) => el.expect().to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) ) ui.quantifier(quantifier) const result = await ui.checkAll() expect(result.passed).toBe(true) }) test('fluent FOL quantifier carries sourceRef and clauseLabel', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) const { FluentAssertion } = await import('imhotep-dsl') const quantifier = FluentAssertion.forAll('[data-testid="box-left"]', (el) => el.expect().to.be.leftOf('[data-testid="box-right"]', { minGap: 8 }) ) ui.quantifier(quantifier) const result = await ui.checkAll() expect(result.passed).toBe(true) expect(result.clauseResults.length).toBe(1) expect(result.clauseResults[0].sourceRef).toBeDefined() expect(result.clauseResults[0].clauseLabel).toBeDefined() }) }) test.describe('E2E: applyEnvironment — full environment axes', () => { test('applyEnvironment exposes colorScheme axis (dark mode)', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) // Apply dark color scheme via the public API await ui.applyEnvironment({ viewport: { width: 800, height: 600 }, colorScheme: 'dark', }) // Verify the page reflects the dark mode emulation const isDark = await page.evaluate(() => window.matchMedia('(prefers-color-scheme: dark)').matches ) expect(isDark).toBe(true) }) test('applyEnvironment exposes reducedMotion axis', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) // Apply reduced motion preference await ui.applyEnvironment({ viewport: { width: 800, height: 600 }, reducedMotion: 'reduce', }) // Verify reduced motion is active via media query or injected style const motionReduced = await page.evaluate(() => { // Check if the injected style element exists (CSS injection fallback path) const style = document.getElementById('__imhotep-reduced-motion__') if (style) return true // Otherwise rely on native emulation return window.matchMedia('(prefers-reduced-motion: reduce)').matches }) expect(motionReduced).toBe(true) }) test('applyEnvironment exposes pointerType axis (coarse)', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) // Apply coarse pointer type await ui.applyEnvironment({ viewport: { width: 800, height: 600 }, pointerType: 'coarse', }) // Verify the CSS custom property was set by the implementation const pointerType = await page.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--imhotep-pointer-type').trim() ) expect(pointerType).toBe('coarse') }) test('applyEnvironment keeps backward compat with viewport-only calls', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) // Viewport-only call must still compile and execute without error await ui.applyEnvironment({ viewport: { width: 1024, height: 768 }, }) const size = await page.viewportSize() expect(size).toEqual({ width: 1024, height: 768 }) }) test('applyEnvironment applies multiple axes in one call', async ({ page }) => { const ui = await imhotep(page) await page.goto(await fixtureUrl('public-api')) await waitForFixtureReady(page) // Apply multiple axes simultaneously await ui.applyEnvironment({ viewport: { width: 1280, height: 720 }, colorScheme: 'dark', reducedMotion: 'reduce', pointerType: 'fine', deviceScaleFactor: 2, locale: 'en-US', }) // Verify viewport const size = await page.viewportSize() expect(size).toEqual({ width: 1280, height: 720 }) // Verify color scheme const isDark = await page.evaluate(() => window.matchMedia('(prefers-color-scheme: dark)').matches ) expect(isDark).toBe(true) // Verify locale const lang = await page.evaluate(() => document.documentElement.lang) expect(lang).toBe('en-US') // Verify pointer type const pointerType = await page.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--imhotep-pointer-type').trim() ) expect(pointerType).toBe('fine') }) })