grafana / xk6-browser

k6 module that adds support for browser automation and end-to-end web testing via the Chrome Devtools Protocol
https://grafana.com/docs/k6/latest/javascript-api/k6-experimental/browser/
GNU Affero General Public License v3.0
341 stars 42 forks source link

Locator API #100

Open robingustafsson opened 2 years ago

robingustafsson commented 2 years ago

We started the development of the first version on May 16th.

Strict forwarders (milestone: v0.7.0)

Strict forwarders (milestone: v0.4.0)

The locator methods that forward calls to the Frame methods with the strict mode on (meaning: only select a single element).

Just-in-time element finder methods

The locator methods wrap around existing ElementHandle methods. For example:

// locator method
async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn, arg?: Arg, options?: TimeoutOptions): Promise<R> {
  return this._withElement(h => h.evaluate(pageFunction, arg), options?.timeout);
}

// wrapped method (jsHandle.ts)
async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn, arg?: Arg): Promise < R > { ... }

_withElement queries the element using waitForSelector in the current frame and returns a disposed element handle:

  private async _withElement<R>(task: (handle: ElementHandle<SVGElement | HTMLElement>, timeout?: number) => Promise<R>, timeout?: number): Promise<R> {
    timeout = this._frame.page()._timeoutSettings.timeout({ timeout });
    const deadline = timeout ? monotonicTime() + timeout : 0;

    return this._frame._wrapApiCall<R>(async () => {
      const result = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: 'attached', timeout });
      const handle = ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
      if (!handle)
        throw new Error(`Could not resolve ${this._selector} to DOM Element`);
      try {
        return await task(handle, deadline ? deadline - monotonicTime() : 0);
      } finally {
        await handle.dispose();
      }
    });
  }

Locator-specific methods

Allows users to move between the selected element.

Blocked (milestone: unknown)


Explanation

Add support for Locator, a way to capture a pointer to DOM element(s) without attaching to an actual DOM node as ElementHandle does. Sort of a just-in-time version of querying the DOM and getting an ElementHandle back to do actions on.

Relevant links:


Below are the May 11th, 2022, findings for this feature.

Summary

The whole idea of locators is that you can carry on selectors with you—without dealing with actual elements. Locators enable the Page Object Model for reducing code duplication and improving test maintenance.

A locator is created with a selector for a specific frame. When you invoke one of the Locator methods, they query the frame and run the action. Since they query the frame, each call can resolve to a different DOM element.

Benefits:

Good explanation of the strict mode and locators API:

Key knowledge

inancgumus commented 2 years ago

I checked out the Playwright Locator API and its underlying code. It seems like we need to do this for every ElementHandle action, etc. It looks involved.

Maybe we should separate this issue into sub-issues and handle the main issue together?

Could you also check out the PW docs and tell me WDYT, @imiric?

imiric commented 2 years ago

Yeah, this seems more involved than I originally thought. We should definitely split it up somehow. Let's discuss it tomorrow.

We don't have to finish everything for v0.3.0, and we likely won't have time to, but we can release it gradually. It should be easier once we lay the groundwork and start ticking off each method.

inancgumus commented 2 years ago

My findings on this feature are in the description. I'll start working on this next week as there is some stuff that I'm trying to figure out.

inancgumus commented 2 years ago

TODO

I'm noticing a lot of repetitions.