Skip to content
This repository has been archived by the owner on Apr 19, 2022. It is now read-only.

feat: port image element plugin from main appium server #1

Merged
merged 5 commits into from
Jan 18, 2021

Conversation

jlipps
Copy link
Contributor

@jlipps jlipps commented Dec 18, 2020

all of this functionality is going to be a plugin. there will be another PR that will remove the related code from basedriver.

@mykola-mokhnach
Copy link

CI seems to be unhappy

@mykola-mokhnach
Copy link

this module needs a good README

@mykola-mokhnach
Copy link

@jlipps What it your strategy about moving these parts out of the base driver, so we don't break anything in future Appium versions (both 1 and 2)?

@jlipps
Copy link
Contributor Author

jlipps commented Jan 14, 2021

@mykola-mokhnach

CI seems to be unhappy

am working on it, this isn't ready to merge yet

this module needs a good README

Yes, will add one before merge

What it your strategy about moving these parts out of the base driver, so we don't break anything in future Appium versions (both 1 and 2)?

I am not going to move anything out of base driver for Appium 1.x. There is a branch in basedriver (2.0) that contains changes for the basedriver that will be included in Appium 2.x. That is where I will remove support for this functionality. So if you're using Appium 2.x, this will be a breaking change from Appium 1.x in that you'll have to install the plugin. But I take it we want to make these kinds of breaking changes in the move to 2.x so it should be fine.

@jlipps
Copy link
Contributor Author

jlipps commented Jan 15, 2021

Ok, CI is working, now all that remains is a good readme for the plug-in itself

@jlipps
Copy link
Contributor Author

jlipps commented Jan 15, 2021

OK, all READMEs and appropriate docs have been added. This is ready to go IMO.

@@ -21,12 +25,14 @@
},
"homepage": "https://github.com/appium/appium-plugins#readme",
"dependencies": {
"@appium/base-plugin": "^1.5.0"
"@appium/base-plugin": "^1.5.0",
Copy link
Contributor

@KazuCocoa KazuCocoa Jan 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but just I thought it was probably good to print author, homepage and license with --details option etc as part of the below. We'll also can guide reporters to check --details option to ensure where they can report issues. (or only homepage??)

e.g.

kazu$ appium driver list --details
✔ Listing available drivers
- xcuitest@3.31.5 [installed (NPM)], Appium <maintainers@appium.io>,  https://github.com/appium/appium-plugins#readme, Apache-2.0
kazu$ appium plugin list
✔ Listing available plugins

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that's a nice feature idea!

packages/images/docs/find-by-image.md Outdated Show resolved Hide resolved
@mykola-mokhnach
Copy link

I am not going to move anything out of base driver for Appium 1.x. There is a branch in basedriver (2.0) that contains changes for the basedriver that will be included in Appium 2.x. That is where I will remove support for this functionality. So if you're using Appium 2.x, this will be a breaking change from Appium 1.x in that you'll have to install the plugin. But I take it we want to make these kinds of breaking changes in the move to 2.x so it should be fine.

How about npm package versioning? If we bump the major version of base-driver package then this would mean we cannot do any major updates to it in Appium 1 anymore

Copy link

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a couple of comments and suggestions. Great job and exciting work here 🤩.


The same goes for updating and removing the plugin using the CLI.

The `mainClass` value is the name that Appium should import from your project's entrypoint. So for example if somewhere in your project you have defined your plugin as follows:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect plugin authors to export multiple classes? If not we can just expect an ESM default export and can avoid having to define this.

WebdriverIO does this for reporters, e.g. if you define:

// wdio.conf.js
export.config = {
  // ...
  reporters: ['foobar']
  // ...
}

it will check first if there is a @wdio/foobar-reporter and if not imports wdio-foobar-reporter (so it prioritises project maintained plugins before community maintained ones).

It is slightly different with services as there are services that are loaded in the main process and others loaded in the worker process (and some in both). In the main process we try to import a named export launcher and check at the same time if there is also a default export we need to load in the worker. If not we ignore the service and save some memory as we don't require it in the worker process anymore.

I am not sure if we have for Appium plugins multiple types of version it would want to import. I can imagine different types of plugins here. One that enhances the driver with new commands, a different type for extending selector strategies etc. Plugins could be used as middlewares, e.g. before a command is send (to modify the payload) or after response has been received (to modify the response). Appium can define all these types and then expect plugin authors to have respective named exports for them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect plugin authors to export multiple classes? If not we can just expect an ESM default export and can avoid having to define this.

Plugins might export other things, or one package might export both a driver and a plugin, for example. I thought it best to require an explicit export, but we could also have a convention where if mainClass is not present, then the default export is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plugins could be used as middlewares, e.g. before a command is send (to modify the payload) or after response has been received (to modify the response). Appium can define all these types and then expect plugin authors to have respective named exports for them.

Yes, this is a good idea. However, in that case, the various exported plugin behaviors would not cohere as easily with one another. I thought a class-based approach was interesting because one instance of a plugin could have state shared between all its expressions (server updater, command handler, etc...)

packages/base/README.md Outdated Show resolved Hide resolved
Comment on lines 74 to 75
1. Set the class variable `updatesServer` to `true`, so that Appium knows your plugin needs to update the server before launch.
2. Implement the `async updateServer` method in your class. This method takes two parameters, `expressApp` and `httpServer`, which are the Express app object and the Node HTTP server object respectively. You can do whatever you want with them inside the `updateServer` method.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be enough to check for an updateServer in the plugin prototype and if so assume the user likes to update the server?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appium could offer an automatism for this, e.g.:

export default class AppiumPlugin {
  routes = [
    ['/some/command/route', this.commandA]
  ]
  
  commandA (req, res) {
    // ...
  }
}

Having the flexibility to work with the express objects is great no question but if Appium can simplify the most common use case it could make things easier!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question to be answered here is what happens if a command/endpoint is already defined? I assume right now it would just execute both command implementations but I can see scenarios where this is not a desired behavior or can cause confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be enough to check for an updateServer in the plugin prototype and if so assume the user likes to update the server?

Not currently because the base plugin has updateServer defined, though granted this is primarily for the purpose of hosting docstrings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the flexibility to work with the express objects is great no question but if Appium can simplify the most common use case it could make things easier!

Implementing new commands doesn't require working with express objects, it involves handling it as part of handle. But I think you're right we could come up with a convention where we say a plugin handles a command if a method with that command name exists on the plugin instance. I think I like that. I'll probably implement it as a separate set of changes between this repo and basedriver.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question to be answered here is what happens if a command/endpoint is already defined? I assume right now it would just execute both command implementations but I can see scenarios where this is not a desired behavior or can cause confusion.

This is why we have next provided as part of handle. It allows a plugin to wrap the default behavior, or execute it after its own functionality. Here next encodes a whole chain of plugins that might act on a command. But it's up to each plugin to play nice, because we can't assume that plugins are designed to work with one another. If the goal of plugin A is to completely replace the functionality of a command, and the goal of plugin B is the same, then whichever plugin is loaded first is the one which will handle it, and the other won't.

This does mean that plugins are not necessarily compatible with one another and users might need to be aware of this. But I can't think of any meaningful way for us to force plugins to play nice with one another since the point is for them to have a lot of power.

packages/base/README.md Show resolved Hide resolved
@jlipps
Copy link
Contributor Author

jlipps commented Jan 15, 2021

How about npm package versioning? If we bump the major version of base-driver package then this would mean we cannot do any major updates to it in Appium 1 anymore

The Appium 2.0 branch of basedriver won't be merged until Appium 2.0 is ready to go. I don't think we're planning on supporting Appium 1.x with major changes to basedriver alongside Appium 2.x work, right?

@jlipps jlipps merged commit fcce05a into master Jan 18, 2021
@jlipps jlipps deleted the images-plugin branch January 18, 2021 17:12
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants