bendemboski / fractal-page-object

A lightweight page object implementation with a focus on simplicity and extensibility
MIT License
30 stars 9 forks source link

Is there an easy way to swap our the query / document interaction interface for use with other environments? (such as WebDriver.io) #1

Open NullVoxPopuli opened 3 years ago

NullVoxPopuli commented 3 years ago

looks like this is the only thing that needs swapped out for webdriver usage: https://github.com/bendemboski/fractal-page-object/blob/main/packages/fractal-page-object/src/-private/dom-query.ts

this is very exciting!

I guess one thing that may put a dampener on webdriver support is that accessing anything is mostly async. :thinking:

bendemboski commented 3 years ago

looks like this is the only thing that needs swapped out for webdriver usage: https://github.com/bendemboski/fractal-page-object/blob/main/packages/fractal-page-object/src/-private/dom-query.ts

Yeah, I think that and probably something about how getRoot works, but in principle not operating on actual DOM objects seems like it should work. As far as async, yeah, that's an interesting challenge. Ignoring indexing, it could potentially be achievable by subclassing PageObject to make element and elements return a promise, so the API would be

let el = await page.thing.otherThing.element;
// do stuff with el

assert.dom(await page.thing.otherThing.element).hasClass('active');

Indexing is a bit trickier because the [1] in page.listItems[1].button currently executes a query. I'm pretty sure it would be doable to make the DOMQuery a little more complex so it contains the index information and we don't have to issue any queries until somebody actually accesses element or elements.

bendemboski commented 3 years ago

I just merged #7 that encapsulates all of the querying in the DOMQuery class, which moves us closer to this. I thought a bit about how it might look to have async queries, and ran into a couple of questions/issues that I'll document here.

Configuration/API

I'm kinda unsure what the ideal API would look like. How would you want your page object syntax to look such that they could have either async or synchronous API methods (the functionality exposed by the element and elements properties right now -- let's ignore the array API for the moment which is its own can of worms), and so they could return different types (e.g. HTMLElement vs. whatever proxy thingy you get from webdriver queries)?

And how would the types look? I'm having trouble picturing how an async/sync-agnostic and HTMLElement/webdriver-proxy-thingy-agnostic interface would look.

Array API

The array API is its own set of challenges. #7 covers the indexing operations, and implementing .length to be async seems doable. It also seems doable to implement a number of the iteration methods in async form, e.g. await page.items.filter(async (item) => await item.isSelected). This would diverge significantly from the current implementation in the proxy where we detect when an array method is being accessed and synchronously populated the underlying array with the element result set so that we can then just call the native array method and let it do its thing.

Conclusion?

I'm starting to suspect more and more that this would be a significant refactor, rather than "just" making the DOMQuery pluggable. Probably we'd want to separate out the functionality into three buckets:

  1. Core page object tree and factory support (with pluggable DOM Queries)
  2. Array functionality (which would likely be two parallel implementations)
  3. Proxy scaffolding to glue it all together

I'm not likely to make much more progress on this in the near future, as it's going to be fairly involved and there's other lower hanging fruit/more common use cases I want to target. An interesting experiment might be to fork this, change DOMQuery's query() and queryAll() methods to be async, and then try to figure out how to make the whole library work with only async queries. That would probably help a lot in informing how we might go about pluggably/configurably supporting both at the same time.