bendemboski / fractal-page-object

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

Ember helper utilities? #41

Open NullVoxPopuli opened 2 years ago

NullVoxPopuli commented 2 years ago

I find that I need to create some utilities to help make things a bit easier:

For example:

export function text(pageObject) {
  return pageObject.element?.textContent?.trim();
}

// and most recently: 
interface WithElement {
  element: Element;
}

export function assertExists(
  msg: string,
  pageObject: PageObject,
): asserts pageObject is PageObject & WithElement {
  // Petition to make public: https://github.com/bendemboski/fractal-page-object/issues/41
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-expect-error
  let { selector } = pageObject;

  assert(`${msg} >> Tried selector: \`${selector}\``, pageObject.element instanceof Element);
}

This allows usage like:

  async toggle() {
    assertExists('Cannot open or close a menu with no trigger element', this._trigger);

    return await click(this._trigger.element);
    // ^ normally this would have a type error because element could be null
  }

The motivation here is to reduce the amount of boilerplate needed while also providing additional messaging to help with debugging.

bendemboski commented 2 years ago

Sorry, I'm not quite clear on what you want to be public. Is it WithElement? Or perhaps it would be better if it were WithElement<T = PageObject>?

NullVoxPopuli commented 2 years ago

the selector property is @private atm. but, it can remain private if fractal-page-object adds assertExists :upside_down_face:

NullVoxPopuli commented 2 years ago

and I suppose it doesn't actually need to know anything about ember. we can throw real errors instead of use assert from ember/debug

bendemboski commented 2 years ago

If selector were public, it would only be the "leaf" page object's selector fragment, though. e.g.

class Page extends PageObject {
  pane = selector('.pane', class extends PageObject {
    list = selector('.list', class extends PageObject {
      items = selector('.list-item');
    });
  });
});
const page = new Page();
// all of these would be `.list-item`:
page.pane.list.items.selector;
page.pane.list.items[0].selector;
page.pane.list.items[3].selector;

To get a full selector-like thingy (not always a proper CSS selector because it could involve intermediate array accesses), we could export a getDescription(obj: PageObject): string that would return this, which I think is more what you're looking for?

I'm still wanting to keep fractal-page-object focused on representing dom querying and page object representation, not actually making assertions or interacting with the DOM. And of course, that will all be covered with https://github.com/emberjs/rfcs/pull/726, which I remain unable to get moving again ☹️

NullVoxPopuli commented 2 years ago

which I think is more what you're looking for?

yes!

o keep fractal-page-object focused on representing dom querying and page object representation,

it still would -- what I'm suggesting is only additional exported utility functions, not any sort of change the the page objects themselves.

Otherwise every serious project using fractal page object will probably need an additional library focused on making common utilities a bit more ergonomic

bendemboski commented 2 years ago

Got it. Wanna PR an exported getDescription() (or something like that) utility function? I'm pretty pinched for OSS time these days.

My last paragraph was in response to your but, it can remain private if fractal-page-object adds assertExists 🙃 which I guess wasn't a serious proposal 😄