Giter Club home page Giter Club logo

selenium-appium's Introduction

selenium-appium

selenium-appium is selenium-webdriver extension to make selenium-webdriver to drive Appium to run automation for native, hybrid and mobile web and desktop apps.

NPM version Monthly Downloads Build Status

Break changes

  1. [email protected] The default URL is changed from http://localhost:4723/wd/hub to http://localhost:4723. The goal is to remove appium from the project and use winappdriver to launch and stop WinAppDriver

Features

  1. A bridge to make selenium-webdriver to drive appium for native app automation. Implement Mobile JSON Wire Protocol Specification locator which selenium-webdriver doesn't support.
  2. Supports PageObject Pattern for selenium-webdriver
  3. Supports iOS, Andriod, Windows which Appium supports.
  4. Supports Typescript
  5. Avoid webdriver.wait(until.elementLocated()).click() pattern and replaced it with By2.click()

Installation

npm i selenium-appium --save-dev
  • By2 is a subclass of selenium-webdriver.By. So you can use it anywhere which selenium-webdriver provides.
  • Also By2 implements the WebElement interface, so you can call click and other element operation directly.
  • No webdriver.wait needed. Instead of findElement, By2 itself loop until element is located before actual click happens.
    test("By2 used in selenium-webdriver.WebDriver", async () => {
        const webdriver = await new Builder()
            .usingServer(url)
            .withCapabilities(capabilities)
            .build();
        const element = await webdriver.wait(until.elementLocated(By2.nativeName('One')));
        await element.click();
        await webdriver.quit();
    });

    test("By2 Supports multiple webdriver2", async () => {
        const webdriver = new WebDriver2();
        await webdriver.startWithCapabilities(capabilities)
        await By2.nativeName('One', webdriver).click();
        await webdriver.quit();
    });


    test("By2 deduced WebDriver2 for single WebDriver2", async () => {
        await driver.startWithCapabilities(capabilities)
        await By2.nativeName('One').click();
        await driver.quit();
    });
  • driver is a wrapper of WebDriver which selenium-webdriver provides.
  • Implements all functions of WebDriver of selenium-webdriver.
  • It provides the default driver for By2.
  • It delayed the creation of WebDriver, and implement the interface of WebDriver.

There are two ways to initialize the driver: startWithWebDriver or startWithWebDriver.

startWithWebDriver allows you to attach the driver to existing WebDriver if capability is not enough for your testing.

    test("simple webdriver2, and driver create from WebDriver", async () => {
        const webdriver = await new Builder()
            .usingServer(url)
            .withCapabilities(capabilities)
            .build();
        await driver.startWithWebDriver(webdriver);
        await By2.nativeName('One').click();
        await webdriver.quit();
    });

    test("Multiple webdriver2", async () => {
        const webdriver = new WebDriver2();
        await webdriver.startWithCapabilities(capabilities)
        await By2.nativeName('One', webdriver).click();
        await webdriver.quit();
    });


    test("Simple Webdriver2, and Driver create from capabilities", async () => {
        await driver.startWithCapabilities(capabilities)
        await By2.nativeName('One').click();
        await driver.quit();
    });

PageObject reduces the amount of duplicated code and easy to maintain.

  1. get in typescript would make you always get new instance of By2. For example, get resultTextBox()
  2. waitForPageLoaded pairs with isPageLoaded. You only need to overload isPageLoaded. waitForPageLoaded would poll until isPageLoaded is true or timeout.
  3. In practice, we only need simple instance of PageObject since it doesn't save state, and all state could be query by By2 from remote app.

PageObject

import { PageObject, By2 } from "selenium-appium";

class CalculatorPage extends PageObject {
  isPageLoaded() {
    return this.minusButton.isDisplayed();
  }

  get resultTextBox() { return By2.nativeAccessibilityId('CalculatorResults');}
  get equalButton() { return By2.nativeAccessibilityId('equalButton'); }
  get clearButton() { return By2.nativeName('Clear'); }
  get plusButton() { return By2.nativeName('Plus'); }
  get divideButton() { return By2.nativeAccessibilityId('divideButton'); }
  get multiplyButton() { return By2.nativeXpath("//Button[@Name='Multiply by']") }
  get minusButton() { return By2.nativeXpath("//Button[@AutomationId=\"minusButton\"]"); }
 

  private async pressKeys(keys: string) {
    for (var key of keys) {
      await By2.nativeAccessibilityId('num' + key + 'Button').click();
    }
  }
  async divid(a: string, b: string): Promise<string> {
    await this.pressKeys(a);
    await this.divideButton.click();
    await this.pressKeys(b);
    await this.equalButton.click();
    return await this.getCalculatorResultText();
  }

  async multiply(a: string, b: string): Promise<string> {
    await this.pressKeys(a);
    await this.multiplyButton.click();
    await this.pressKeys(b);
    await this.equalButton.click();
    return await this.getCalculatorResultText();
  }

  async plus(a: string, b: string): Promise<string> {
    await this.pressKeys(a);
    await this.plusButton.click();
    await this.pressKeys(b);
    await this.equalButton.click();
    return await this.getCalculatorResultText();
  }

  async minus(a: string, b: string): Promise<string> {
    await this.pressKeys(a);
    await this.minusButton.click();
    await this.pressKeys(b);
    await this.equalButton.click();
    return await this.getCalculatorResultText();
  }

  private async getCalculatorResultText(): Promise<string> {
    return (await this.resultTextBox.getText()).replace('Display is', '').trim();
  } 
}

export default new CalculatorPage();

use the PageObject

  beforeEach(async () => {
    await CalculatorPage.waitForPageLoaded();
    await CalculatorPage.clearButton.clear();
  });
  test('Addition', async () => {
    // Find the buttons by their names and click them in sequence to perform 1 + 7 = 8
    expect(await CalculatorPage.plus('1', '7')).toBe('8');
  });

  test('Division', async () => {
    // Find the buttons by their accessibility ids and click them in sequence to perform 88 / 11 = 8
    expect(await CalculatorPage.divid('88', '11')).toBe('8');
  });

  test('Multiplication', async () => {
    // Find the buttons by their names using XPath and click them in sequence to perform 9 x 9 = 81
    expect(await CalculatorPage.multiply('9', '9')).toBe('81');
  });

  test('Subtraction', async () => {
    // Find the buttons by their accessibility ids using XPath and click them in sequence to perform 9 - 1 = 8
    expect(await CalculatorPage.minus('9', '1')).toBe('8');
  });

There are two global timers: waitforPageTimeout and waitforTimeout.

waitforTimeout is used by By2. When call any WebElement function of By2, it loops until the element is located

waitforPageTimeout is used by PageObject. It defined the default timeout for waitForPageLoaded.

    Config.setWaitForPageTimeout(100);
    expect(Config.getWaitForPageTimeout()).toBe(100);
Config.setWaitForTimeout(100);
expect(Config.getWaitForTimeout()).toBe(100);

driver.seleniumDriver

driver.seleniumDriver returns the instance of actual WebDriver.

Note

Because of typescript error, unlike By.name, By2 only replaced it with By2.name2.

Why selenium-appium

selenium-appium projected is created when I prototype automation for react-native Windows testing. Appium is an open source, cross-platform test automation tool for native, hybrid and mobile web and desktop apps. We support simulators (iOS), emulators (Android), and real devices (iOS, Android, Windows, Mac).

Selenium is a browser automation library. Most often used for testing web-applications.

selenium-webdriver is the offical WebDriver Javascript binding from selenium project.

Although WebDriverIO provides webdriver for Appium, it has some restrictions:

  1. Selenium has very large user base in browser automation, but WebDriverIO API is different from selenium javascript API
  2. [Already fixed] WebDriverIO has problem to support WinAppDriver, for example, webdriverio/webdriverio#4369

Unfortunately selenium-webdriver doesn't support Mobile JSON Wire Protocol Specification. And that's why selenium-appium project is created.

Note

To know more about how to integrate JavaScript test runner and WinAppDriver for UI automation, please refer to:

  1. Jest + selenium-webdriver + WinAppDriver
  2. Jasmine + WebDriverIO + WinAppDriver. This part is obselete, please refer to WinAppDriver + WebDriverIO example for newer change.

License

The selenium-appium and all newly contributed code is provided under the MIT License.

selenium-appium's People

Contributors

dependabot-preview[bot] avatar licanhua avatar thdk avatar ulquiomaru avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

selenium-appium's Issues

Double click not working

Can someone give me an example of double click using selenium-appium / selenium-webdriver using nodejs?

I am doing the following to double click but looks like it's not working.

import {By2, driver, WebDriver2, PageObject} from 'selenium-appium';
import { Builder} from 'selenium-webdriver';

  var currentCapabilities=
            {
                "browserName": '',
                "appTopLevelWindow": hex.trim(),
                "platformName": "Windows",
                "deviceName": "WindowsPC",
                "newCommandTimeout": "120000"
            };
        let appDriver = await new Builder()
            .usingServer("http://localhost:4723/wd/hub")
            .withCapabilities(currentEwarpCapabilities)
            .build();
        const doubleClickDriver = await driver.startWithWebDriver(currentCapabilities);

doubleClickDriver.actions().doubleClick(By2.nativeAccessibilityId("gridAppInfoCedingPolicies")).perform();

The error message i received says

cannot find actions of undefined.

I also tried the below with the same result.

appDriver.actions().doubleClick(By2.nativeAccessibilityId("gridAppInfoCedingPolicies")).perform();

What am I doing wrong? Why is it not working?

By2 not compatible with Locator

By2 from selenium-appium is not compatible with Locator from selenium-webdriver. The error displayed is:

Argument of type 'By2' is not assignable to parameter of type 'Locator'.
  Property 'toObject' is missing in type 'By2' but required in type 'By'.ts(2345)

Using the following versions:

  "devDependencies": {
    "@tsconfig/node18": "^18.2.2",
    "@types/jest": "^29.5.5",
    "@types/selenium-webdriver": "^4.1.17",
    "eslint": "^8.51.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-import": "^2.28.1",
    "eslint-plugin-jest": "^27.4.2",
    "jest": "^29.7.0",
    "selenium": "^2.20.0",
    "selenium-appium": "^1.0.2",
    "selenium-webdriver": "^4.14.0",
    "ts-jest": "^29.1.1",
    "ts-node": "^10.9.1",
    "winappdriver": "^0.0.7"
  }

UnknownMethodError: Method is not implemented for getAttribute() and isDisplayed()

Hello, I am running tests written in typescript for native Android and iOS applications ("node" -v 14.15.4, "typescript" -v 4.5.2, "selenium-appium" -v 1.0.2, "selenium-webdriver" - v 4.1.1, "appium" -v 1.22.2)

But for methods getAttribute() and isDisplayed() I got an error "Method is not implemented":

await driver.startWithCapabilities(config.capabilities);
const element = By2.nativeAccessibilityId('Scroll Up');
const attribute = await element.getAttribute('class');

I've tried different elements and attributes, but got the same error for Android and iOS devices.
(Also, similar error is thrown for isDisplayed() method)

From Appium logs: "Encountered internal error running command: NotImplementedError: Method is not implemented"

Some other methods, like: await element.click(); or await element.getText(); give correct results.

It looks like this is "Selenium 4" related error, since getAttribute() and isDisplayed() give correct results with "selenium-webdriver" v 3.0.6, but not with version 4.
Is there support of Selenium 4?

"status":105,"value":"error":"element not interactable"

The element is visible on the screen, but when I try to enter values in the field I am getting this error,

Accept: application/json; charset=utf-8
Connection: close
Content-Length: 2
Content-Type: application/json;charset=UTF-8
Host: localhost:4723
User-Agent: selenium/4.0.0-alpha.7 (js windows)
{}
HTTP/1.1 400 Bad Request
Content-Length: 175
Content-Type: application/json
{"status":105,"value":{"error":"element not interactable","message":"An element command could not be completed because the element is not pointer- or keyboard interactable."}}

also tried force clicking the element based on the previous issue I raised - #30
still the same result.

let cmd = new Command('legacyAction:click');
        cmd.setParameter('id', this.quotesNotesTab_ActionCode);
        await driver.execute(cmd);
        ```
Would appreciate some guidance on this on how to proceed further for troubleshooting this.

Incompatible with latest releases of appium-windows-driver

The latest release of appium-windows-driver (1.15.0) seems to be incompatible with selenium-appium.

Trying to start the driver causes appium to go into a loop with this error:

[BaseDriver] The following capabilities were provided, but are not recognized by Appium:
[BaseDriver]   browserName

I've submitted a fix for this in: appium/appium-windows-driver#88

However, after the fix, there's still at least another error to be fixed, where appium goes into a loop with the error:

[WinAppDriver] No old WinAppDrivers seemed to exist
[WinAppDriver] Spawning WinAppDriver with: 4723/wd/hub
[WinAppDriver] [STDOUT] Windows Application Driver listening for requests at: http://127.0.0.1:4723/wd/hub
[debug] [WD Proxy] Proxying [GET /status] to [GET http://127.0.0.1:4723/wd/hub/status] with no body
[WinAppDriver] [STDOUT]
[WinAppDriver] [STDOUT] Address 'http://127.0.0.1:4723/wd/hub' is already in use
[WinAppDriver] [STDOUT]
[WinAppDriver] [STDOUT] Failed to initialize: 0x80004005
[WinAppDriver] WinAppDriver exited unexpectedly with code 2147500037, signal null

I suspect this may be due to some port scanning logic changes in appium-windows-driver, but I haven't looked into it in detail.

In the meanwhile, the current workaround I'm using is forcing appium to use the latest version of appium-windows-driver I know is compatible (1.13.0). This can be achieved by adding the following to package.json and running yarn:

  "resolutions": {
    "appium/appium-windows-driver": "1.13.0"
  }

How to interact with child window screen along with Parent?

@licanhua
I have seen the solution using Java winappdriver but not using selenium-appium.

My requirement is -

When I interact with the parent application screen new child screen gets popup and I have to interact with that too and then parent app.

`describe("RHAM Test Suite", function () {
before(() => {
const appId = "ActualAppId";
const capabilites = windowsAppDriverCapabilities(appId);
return driver.startWithCapabilities(capabilites);
});

// Parent app windows
it("Get RHAM App Title", async function () {
let rhamTitle = await RHAMPage.getTitleBar();
expect(rhamTitle).equal('Remote Hardware Access');
});

it("Restart WebSocket Client", async function () {
await RHAMPage.clickRestarWebSocket();
});

// New Child app windows
it("Clear SignalRClient", async function () {
await RHAMPage.clearClientScreen();
});
});`

如何发送“鼠标右键单击”命令?

作者您好,

该如何实现发送“鼠标右键单击”命令呢?
contextClick

我找不到如何实现鼠标右键单击的命令。
我尝试了driver.actions().contextClick(元素)命令,但是软件界面毫无反应。

selenium中关于driver.actions().contextClick(元素)命令的说明超链接:
https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html

我注意到“微软”的“WinAppDriver”似乎是支持“鼠标右键单击”的:
https://github.com/microsoft/WinAppDriver/blob/master/Tests/WebDriverAPI/Mouse.cs,代码的第77行。

似乎selenium、WinAppDriver都在理论上是支持的“鼠标右键单击”,但我不知道该如何通过你的工具实现这一功能,您能给出一点示例,或者给出一些帮助提示么?

提前致谢,
s0521

Unrecognized command: actions

Hello, I am running on windows with the following package versions:

"@types/selenium-webdriver": "^4.0.11",
"selenium-appium": "^1.0.2",
"selenium-webdriver": "^4.0.0-alpha.8",
"winappdriver": "0.0.7"

I have the following code:

await driver.actions().move({x:400,y:540}).perform();
await driver.actions().click();

I get the following error:

UnknownCommandError: Unrecognized command: actions

"Target browser must be a string" Builder still expects browser.

Hello, I am running on MacOS with the following package versions

"selenium-appium": "0.0.16",
"selenium-webdriver": "^4.0.0-alpha.7"

I have the following code:

const Webdriver = require('selenium-appium').WebDriver2;
const By = require('selenium-appium').By2;

const caps = {
    platformName: 'Mac',
    deviceName: "Mac",
    app: "Mail"
};

async function main() {
  const webdriver = new Webdriver();
  await webdriver.startWithCapabilities(caps)
  const element = await By.xpath("/AXApplication[@AXTitle='Mail']/AXWindow[@AXTitle='Inbox (1 message)' and @AXIdentifier='_NS:15' and @AXSubrole='AXStandardWindow']/AXGroup[@AXIdentifier='_NS:10']/AXCheckBox[@AXTitle='Sent' and @AXSubrole='AXToggle']");

  await element.click();
  await webdriver.quit()
}

main();

This causes

(node:18125) UnhandledPromiseRejectionWarning: TypeError: Target browser must be a string, but is <undefined>; did you forget to call forBrowser()?
    at Builder.build (/Users/User/Desktop/dev/appium-instagram/node_modules/selenium-webdriver/index.js:589:13)
    at Promise (/Users/User/Desktop/dev/appium-instagram/node_modules/selenium-appium/dist/driver.js:187:22)
    at new Promise (<anonymous>)
    at WebDriver2.startWithCapabilities (/Users/User/Desktop/dev/appium-instagram/node_modules/selenium-appium/dist/driver.js:182:16)
    at main (/Users/User/Desktop/dev/appium-instagram/macOsCalculator.js:13:19)
    at Object.<anonymous> (/Users/User/Desktop/dev/appium-instagram/macOsCalculator.js:20:1)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
(node:18125) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:18125) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Initially I tried using the official selenium-webdriver as found in one of your examples but this didn't work either.

const Builder = require('selenium-webdriver').Builder;
const By = require('selenium-appium').By2;

const url = 'http://localhost:4723/wd/hub'

const caps = {
    platformName: 'Mac',
    deviceName: "Mac",
    app: "Mail"
};

async function main() {
  const webdriver = await new Builder()
    .usingServer(url)
    .withCapabilities(caps)
    .build()

  const element = await webdriver.wait(until.elementLocated(By.xpath("/AXApplication[@AXTitle='Mail']/AXWindow[@AXTitle='Inbox (1 message)' and @AXIdentifier='_NS:15' and @AXSubrole='AXStandardWindow']/AXGroup[@AXIdentifier='_NS:10']/AXCheckBox[@AXTitle='Sent' and @AXSubrole='AXToggle']")));
    await element.click();
    await webdriver.quit()
}

main();

I understand you are from Microsoft and that your examples show this working on windows. Does this mean this wouldn't work at all with Appium For Mac? The error seems agnostic of the platform.

Imports broken on case sensitive file system

As mentioned already in #9 some imports are using wrong casing when importing modules.

Filenames for Driver, Config and WinAppDriver are in PascalCasing while pageobject and by2 are in lowercase.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.