Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: New Matcher "toBeFilteredElementsArrayOfSize" for WebdriverIO.ElementArrays #1717

Open
htho opened this issue Dec 2, 2024 · 3 comments
Labels
Bug 🐛 help wanted Extra attention is needed

Comments

@htho
Copy link

htho commented Dec 2, 2024

I'd like to add the missing "Missing support for attributes, properties and visible in waitForElement" for Chrome Recorder.

But it seems, that the underlying problem is, that toBeElementsArrayOfSize is the only matcher that properly handles WebdriverIO.ElementArrays (or ChainablePromiseArrays - what's the difference anyway?)

So we need a new matcher that works like this:

await expect($$(`input`)).toBeFilteredElementsArrayOfSize(async (el) => {
   return await el.isDisplayed() && await el.getProperty("value") === "foo";
}, 1);

The signature could look like this:

export async function toBeFilteredElementsArrayOfSize(
    received: WdioElementsMaybePromise,
    filterFunction: (element: WebdriverIO.Element) => boolean | Promise<boolean>,
    expectedValue: number | ExpectWebdriverIO.NumberOptions,
    options: ExpectWebdriverIO.StringOptions
)

A simpler alternative could be, to add the filter function to the options for toBeElementsArrayOfSize.

export async function toBeElementsArrayOfSize(
    received: WdioElementsMaybePromise,
    expectedValue: number | ExpectWebdriverIO.NumberOptions,
    options: (ExpectWebdriverIO.StringOptions & {filter?: (element: WebdriverIO.Element) => boolean | Promise<boolean>})
);

To keep things DRY I think this should be the implementation anyway.
toBeFilteredElementsArrayOfSize can be added as an alias/facade with the signature from above.

@christian-bromann
Copy link
Member

So we need a new matcher that works like this:

I don't think this looks much different to just doing this:

await expect($$(`input`).filter(async (el) => {
   return await el.isDisplayed() && await el.getProperty("value") === "foo";
})).toBeElementsArrayOfSize(1);

..at least not different enough that it would justify its own matcher. You can create your custom matcher for this:

expect.extend({
    async toBeFilteredElementsArrayOfSize(elements: WebdriverIO.Element[], amount: number) {
        const elements = await $$(`input`).filter(async (el) => {
          return await el.isDisplayed() && await el.getProperty("value") === "foo";
        })

        return {
            message: () => `expected element size to be "${amount}"`,
            pass: elements.length === amount,
        }
    }
})

Let me know if you have further questions.

@htho
Copy link
Author

htho commented Dec 2, 2024

Hi,

it was my first guess, to expect the result of filter. But this does not work.
I created a self-contained example. The button#show inserts a span#toShow 3s after it was clicked.

only("selfContained", () => {
	it("non-existing", async () => {
		await browser.executeScript(`
			document.body.insertAdjacentHTML("afterbegin", \`
				<button id="show" onclick="setTimeout(() => window['show'].insertAdjacentHTML('afterend', '<span id=toShow>toShow</span>'), 3000)">show</button>
			\`);
		`, []);

		await expect(browser.$(`#toShow`)).not.toBeDisplayed();
		await expect(browser.$$(`#toShow`)).toBeElementsArrayOfSize(0)
		// NEXT LINE FAILS
		await expect(browser.$$(`#toShow`).filter((el) => el.isDisplayed())).toBeElementsArrayOfSize(0);

		await browser.$(`#show`).click();

		await expect(browser.$(`#toShow`)).toBeDisplayed();
		await expect(browser.$$(`#toShow`)).toBeElementsArrayOfSize(1)
		// NEXT LINE FAILS
		await expect(browser.$$(`#toShow`).filter((el) => el.isDisplayed())).toBeElementsArrayOfSize(1);
	});
});

But it fails in line 12:

[0-0] TypeError in "selfContained.non-existing"
TypeError: Cannot read properties of undefined (reading 'length')
    at getSelector (file:///C:/dev/UtsWdio/node_modules/expect-webdriverio/lib/util/formatMessage.js:9:39)
    at getSelectors (file:///C:/dev/UtsWdio/node_modules/expect-webdriverio/lib/util/formatMessage.js:18:45)
    at enhanceError (file:///C:/dev/UtsWdio/node_modules/expect-webdriverio/lib/util/formatMessage.js:37:55)
    at Object.toBeElementsArrayOfSize (file:///C:/devUtsWdio/node_modules/expect-webdriverio/lib/matchers/elements/toBeElementsArrayOfSize.js:38:21)
    at Context.<anonymous> (C:\dev\test\records\SimpleCounter.ts:12:3)
    at Context.executeAsync (C:\dev\node_modules\@wdio\utils\build\index.js:1004:20)
    at Context.testFrameworkFnWrapper (C:\dev\node_modules\@wdio\utils\build\index.js:1075:14)

If we remove/comment line 12 it fails in line 19:

[0-0] TypeError in "selfContained.non-existing"
TypeError: Cannot read properties of undefined (reading 'length')
    at getSelector (file:///C:/dev/UtsWdio/node_modules/expect-webdriverio/lib/util/formatMessage.js:9:39)
    at getSelectors (file:///C:/dev/UtsWdio/node_modules/expect-webdriverio/lib/util/formatMessage.js:18:45)
    at enhanceError (file:///C:/dev/UtsWdio/node_modules/expect-webdriverio/lib/util/formatMessage.js:37:55)
    at Object.toBeElementsArrayOfSize (file:///C:/dev/UtsWdio/node_modules/expect-webdriverio/lib/matchers/elements/toBeElementsArrayOfSize.js:38:21)
    at Context.<anonymous> (C:\dev\UtsWdio\test\records\SimpleCounter.ts:19:3)
    at Context.executeAsync (C:\dev\\UtsWdio\node_modules\@wdio\utils\build\index.js:1004:20)
    at Context.testFrameworkFnWrapper (C:\dev\UtsWdio\node_modules\@wdio\utils\build\index.js:1075:14)
[0-0] FAILED in chrome - file:///C:/dev/UtsWdio/test/records/SimpleCounter.ts

Maybe its a bug? It could also be a Problem in formatMessage.js

About the proposed custom matcher. Is it re-exectued the same way toBeElementsArrayOfSize retries until the amount of elements is met? I mean: will the test succeed if the assertion is inserted right after the click?

@christian-bromann
Copy link
Member

Maybe its a bug? It could also be a Problem in formatMessage.js

mhm .. not sure but worth investigating.

@christian-bromann christian-bromann added help wanted Extra attention is needed Bug 🐛 labels Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🐛 help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants