diff --git a/packages/imhotep-playwright/src/public.test.ts b/packages/imhotep-playwright/src/public.test.ts index 988b707..967520f 100644 --- a/packages/imhotep-playwright/src/public.test.ts +++ b/packages/imhotep-playwright/src/public.test.ts @@ -163,6 +163,56 @@ describe('Playwright public API', () => { assert.strictEqual(chain.relation, 'leftOf') }) + it('compound .and chaining creates compound assertion with both parts', async () => { + const page = createMockPage() + const ui = await imhotep(page) + + const chain = ui.expect('.a') + .to.be.leftOf('.b', { minGap: 8 }).and.above('.c', { maxGap: 16 }) + + assert.strictEqual(chain.relation, 'above') + const parts = (chain as any)._compoundParts as Array<{ relation: string; referenceSelector: string; options: Record }> | undefined + assert.ok(parts, 'compound parts should be defined') + assert.strictEqual(parts!.length, 2) + assert.strictEqual(parts![0].relation, 'leftOf') + assert.strictEqual(parts![0].referenceSelector, '.b') + assert.strictEqual(parts![1].relation, 'above') + assert.strictEqual(parts![1].referenceSelector, '.c') + assert.strictEqual(parts![1].options.maxGap, 16) + }) + + it('compound .or chaining creates compound assertion with both parts', async () => { + const page = createMockPage() + const ui = await imhotep(page) + + const chain = ui.expect('.x') + .to.be.inside('.y').or.centeredWithin('.z') + + assert.strictEqual(chain.relation, 'centeredWithin') + const parts = (chain as any)._compoundParts as Array<{ relation: string; referenceSelector: string }> | undefined + assert.ok(parts, 'compound parts should be defined') + assert.strictEqual(parts!.length, 2) + assert.strictEqual(parts![0].relation, 'inside') + assert.strictEqual(parts![1].relation, 'centeredWithin') + }) + + it('triple .and chaining accumulates all parts', async () => { + const page = createMockPage() + const ui = await imhotep(page) + + const chain = ui.expect('.a') + .to.be.leftOf('.b').and.above('.c').and.inside('.d') + + assert.strictEqual(chain.relation, 'inside') + const parts = (chain as any)._compoundParts as Array<{ relation: string; referenceSelector: string }> | undefined + assert.ok(parts, 'compound parts should be defined for triple chain') + assert.strictEqual(parts!.length, 3) + assert.strictEqual(parts![0].relation, 'leftOf') + assert.strictEqual(parts![1].relation, 'above') + assert.strictEqual(parts![2].relation, 'inside') + assert.strictEqual(parts![2].referenceSelector, '.d') + }) + it('supports quantifier entry via ui.expect.all(subject)', async () => { const page = createMockPage() const ui = await imhotep(page) diff --git a/packages/imhotep-playwright/src/public.ts b/packages/imhotep-playwright/src/public.ts index e1c1f2c..2b878b6 100644 --- a/packages/imhotep-playwright/src/public.ts +++ b/packages/imhotep-playwright/src/public.ts @@ -285,6 +285,29 @@ export async function imhotep( }) } + // Ensure .and / .or on a FluentRelation result return proxy-wrapped + // builders so compound assertions flow through the assertion store. + function ensureAndOrProxied(result: any): void { + if (!result || typeof result !== 'object') return + const proto = Object.getPrototypeOf(result) + const andDesc = Object.getOwnPropertyDescriptor(proto, 'and') + const orDesc = Object.getOwnPropertyDescriptor(proto, 'or') + if (andDesc?.get) { + Object.defineProperty(result, 'and', { + get() { return wrapBeProxy(andDesc.get!.call(result)) }, + configurable: true, + enumerable: true, + }) + } + if (orDesc?.get) { + Object.defineProperty(result, 'or', { + get() { return wrapBeProxy(orDesc.get!.call(result)) }, + configurable: true, + enumerable: true, + }) + } + } + const wrapBeProxy = (obj: any): any => new Proxy(obj, { get(target, prop) { @@ -305,6 +328,7 @@ export async function imhotep( currentInStore = result } assertionStore.set(ui, currentList) + ensureAndOrProxied(result) if (result && typeof result === 'object' && typeof (result as any).relation !== 'string') { return wrapBeProxy(result) }