expect-webdriverio icon indicating copy to clipboard operation
expect-webdriverio copied to clipboard

Support for array matchers, similar like the `some` method, but for use with `expect`

Open dietergeerts opened this issue 4 years ago • 9 comments

This would be awesome, so that we don't need to apply multiple transformations and calculations, and having no good error feedback when the expect fails.

dietergeerts avatar Aug 06 '21 17:08 dietergeerts

@dietergeerts can you provide an example?

christian-bromann avatar Aug 07 '21 09:08 christian-bromann

I have written the following helper:

export function areNotSelected(elements: ReturnType<typeof browser['$$']>) {
  return elements.every(async (element) => !(await element.isSelected()))
}

And used it like:

Then(/^I see that neither region options are selected$/, async () => {
  const noneSelected = await areNotSelected(authLoginRegionSelect.options)
  await expect(noneSelected).toBe(true)
})

So basically, the methods that work on a single element, like isSelected, I would like to see a version that works on an array of elements, so I don't need to write my own helper methods, because with my own helpers, I don't have nice error messages from the test framework. (I could extend expect myself, but I think they can fit really nicely in this package as others can use this too, so first going for this route)

dietergeerts avatar Aug 16 '21 07:08 dietergeerts

How do you think these matchers should be named?

christian-bromann avatar Aug 16 '21 12:08 christian-bromann

🤔 , when we compare with isSelected for a single element, which is toBeSelected, we could have:

  • toBeAllSelected (toBeEverySelected doesn't sound that natural to me, though in util libraries like Lodash, this is what's used a lot for checking if a list of items all match)
  • toBeSomeSelected

Because webdriver-io itself doesn't have functions for getting the selection state of all elements, we could also expose such helpers like areAllSelected/areEverySelected and areSomeSelected, because sometimes you want to check that without actually asserting it.

I don't have too much experience with webdriver-io, so I might be missing things, not seeing existing ways of doing this easily, but also not knowing all (edge-)cases that can happen with element arrays. I've used protractor for years, and it's the first project I'm really using webdriver-io for end-2-end testing now.

dietergeerts avatar Aug 18 '21 06:08 dietergeerts

How about we modify matchers such as toBeSelected to detect if the provided object is an array of elements and if so the matcher checks if all elements are selected. That would be easily applicable to toBeExisting, toBeDisplayed etc., for example:

// checks if a single element is selected
expect($('#singleElem')).toBeSelected()
// checks if all elements are selected
expect($$('.manyElems')).toBeSelected()

To enable the other use case of checking if just some are selected we could use a partial parameter to the matcher. This parameter would be ignored if used on a single element, e.g.:

// this would be the same as "expect($('#singleElem')).toBeSelected()"
expect($('#singleElem')).toBeSelected({partial: true})
// checks if at least one element is selected
expect($$('.manyElems')).toBeSelected({partial: true})

This way we can re-use existing matchers and don't bloat the API. What do you think?

christian-bromann avatar Aug 18 '21 07:08 christian-bromann

This way we can re-use existing matchers and don't bloat the API

It wouldn't be bloat, unless you think of bloat as just more matchers. Imho, it's better to have more matchers if they make things more clear than having less matchers where there can be confusion in what they can handle. toBeSelected could work for an array of elements, but does that mean all/every or some of them? Of course, some things can be re-used internally, but to the outside, it will be better to have more matchers to be clearer. Your IDE will also help you better, because when writing a test, you know if you have an array of elements or just one element, and thus you can start typing toBeAll and you'll see what's possible for an array of elements where all needs to be whatever the check is.

I understand that this can be seen as "taste", but from experience, I have seen that having clear functions(/matchers in this case), where there is only 1 thing they do with 1 way to call them, is the best way to go for everyone, as they are very clear. This is also what Functional Programming is partly about. So whatever you want to choose, this is my preference and experience.

dietergeerts avatar Aug 23 '21 07:08 dietergeerts

@webdriverio/project-committers and @mgrybyk any suggestions?

christian-bromann avatar Aug 23 '21 09:08 christian-bromann

I agree with Christian that having a single and easy to understand API makes more sense here.

Imagine that we would allow you to retrieve the text of all elements. You wouldn't want getAllText but rather getText. This way the user knows the API and does not have to think about it.

erwinheitzman avatar Sep 20 '23 13:09 erwinheitzman

I agree with Christian that having a single and easy to understand API makes more sense here.

Imagine that we would allow you to retrieve the text of all elements. You wouldn't want getAllText but rather getText. This way the user knows the API and does not have to think about it.

I agree too, but sometimes you have something in-between. toBeSelected could work with single element and an array of elements, as that is the same as "all" of these elemetens are selected. But when you want to check that only some are selected, so at least 1, then you need something else, and that's what I'm asking for here, as then the error message will be specific too, which makes it easier to understand etc....

dietergeerts avatar Sep 20 '23 16:09 dietergeerts