From 5e23a374dbf7d44592649c787317921ae3abfcf2 Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Fri, 19 Sep 2025 23:38:52 -0700 Subject: [PATCH] fix: wait for CSS assertions in playwright base --- playwright-e2e/common/base.ts | 65 ++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/playwright-e2e/common/base.ts b/playwright-e2e/common/base.ts index 81d419725c..b69f72a26a 100644 --- a/playwright-e2e/common/base.ts +++ b/playwright-e2e/common/base.ts @@ -302,29 +302,54 @@ export class BaseMethods { isMultiple = false, isInclude = true, }: ElementPropertyOptions): Promise { - let locator = this.resolveLocator(selector, { parentSelector, text, index }); - - if (isMultiple) { - const handles = await locator.elementHandles(); - for (const handle of handles) { - const actual = await this.getProperty(handle, prop, attr); - if (isInclude) { - expect(actual).toContain(value); - } else { - expect(actual).not.toContain(value); + const locator = this.resolveLocator(selector, { parentSelector, text, index }); + const expectationMessage = + attr === 'attribute' + ? `Expected attribute "${prop}" on selector "${selector}" to ${isInclude ? '' : 'not '}include "${value}".` + : `Expected CSS property "${prop}" on selector "${selector}" to ${isInclude ? '' : 'not '}include "${value}".`; + + const doesMatch = (actual: string): boolean => (isInclude ? actual.includes(value) : !actual.includes(value)); + + await expect + .poll(async () => { + if (isMultiple) { + const handles = await locator.elementHandles(); + if (handles.length === 0) { + return false; + } + + try { + const results = await Promise.all( + handles.map(async handle => { + try { + return await this.getProperty(handle, prop, attr); + } finally { + await handle.dispose(); + } + }), + ); + + return results.every(doesMatch); + } catch { + return false; + } } - } - return; - } - - const actual = await this.getProperty(await locator.elementHandle(), prop, attr); + const handle = await locator.elementHandle({ timeout: 0 }); + if (!handle) { + return false; + } - if (isInclude) { - expect(actual).toContain(value); - } else { - expect(actual).not.toContain(value); - } + try { + const actual = await this.getProperty(handle, prop, attr); + return doesMatch(actual); + } catch { + return false; + } finally { + await handle.dispose(); + } + }, { message: expectationMessage }) + .toBeTruthy(); } async checkUrlText(urlPart: string, isInclude: boolean = false): Promise {