wix / Detox

Gray box end-to-end testing and automation framework for mobile apps
https://wix.github.io/Detox/
MIT License
11.14k stars 1.92k forks source link

Platform parity and uniform documentation #563

Closed DanielMSchmidt closed 3 years ago

DanielMSchmidt commented 6 years ago

These two topics normally don't mix that good, but please bare with me for a moment. I would like to introduce a change to the way we call native code from the tests, that IMHO enables us to clearer communicate where we are not on par with either platform, to document this and also to ensure new features are built consistently.

Current State: Currently we have a rough architecture like this:

Test Runner -> classes in expect.js / matchers.js -> invoke.call(Directly) -> WS

The part I would like to change is mainly the second one, the wrapper classes around the generated code. They had a reason to exist before we mostly ran on generated code, but nowadays there is no real reason behind having these, expect for that they expose our public API.

Vision:

I would like us to write wrapper code like this:

element.cmd("replaceText", {
  "documentation": "Replaces the text of the element",
  "example": "await element(by.id('textField')).replaceText('passcode again');",
  "implementations": {
    "android": ViewActionsApi.replaceText,
    "ios": GreyActions.actionForReplaceText,
  }
});

Benefits:

LeoNatan commented 6 years ago

Should solve a lot of boilerplate code. 👌 What I am missing is argument information. An example is fine, but sometimes it's hard to convey all inputs possible in one or two examples.

LeoNatan commented 6 years ago

Couldn't the third part ("invoke.call(Directly)") also be auto-generated?

DanielMSchmidt commented 6 years ago

Third part is invoke.call or invoke.callDirectly directly is for the generated code, the other one is what we assemble ourselves.

DanielMSchmidt commented 6 years ago

@rotemmiz You mentioned in person that you have also some input on this, would you mind sharing? 😇

DanielMSchmidt commented 6 years ago

I put some more thoughts and research into this I think the approach I proposed goes a bit far away from the current code base (especially the classes). With that in mind I would suggest a more class based approach like this:

expect.js

/**
 *  Some comments on how Element is supposed to work
 */
class Element {
  constructor(matcher) {
    this._originalMatcher = matcher;
  }

  /**
   * Tap the element
   * @example
   * element(by.id('foo')).tap();
   */
  async tap() {
    throw new Error("Not supported");
  }

  /**
   * Taps at a certain point
   * @param {value} Point to tap at.
   */
  async tapAtPoint(value) {
    throw new Error("Not supported");
  }
}

ios/expect.js

import Element from '../expect';

class iOSElement extends Element{
  constructor(matcher) {
    super(matcher)
  }

  async tap() {
    return GreyActions.actionForTap();
  }

  async tapAtPoint(value) {
    return GreyActions.actionForTapAtPoint(value);
  }
}

android/expect.js

import Element from '../expect';

class AndroidElement extends Element{
  constructor(matcher) {
    super(matcher)
  }

  async tap() {
    return ViewActionsApi.actionForTap();
  }

  async tapAtPoint(value) {
    return DetoxActionApi.tapAtLocation(value.x, value.y);
  }
}

I think this refactoring could be a good time smaller and we would achieve a central place for documentation. What do you think?

DanielMSchmidt commented 6 years ago

I did a Proof of Concept on this branch: danielmschmidt/platform-parity-object-oriented.

My goals were to write as little boilerplate as possible when hooking up a new API and to generate documentation from the code by adding more structure to it.

My findings derived from the PoC are:

If that's okay to y'all I would try to extract the documentation extraction part into a separate package that we can then use to deliver a documentation updating from the code.