How to use setupNewChromedriver method in Appium Android Driver

Best JavaScript code snippet using appium-android-driver

driver-specs.js

Source:driver-specs.js Github

copy

Full Screen

1import chai from 'chai';2import chaiAsPromised from 'chai-as-promised';3import log from '../../lib/logger';4import sinon from 'sinon';5import { helpers, SETTINGS_HELPER_PKG_ID } from '../../lib/android-helpers';6import { withMocks } from 'appium-test-support';7import AndroidDriver from '../..';8import ADB from 'appium-adb';9import { errors } from 'appium-base-driver';10import { fs } from 'appium-support';11import { SharedPrefsBuilder } from 'shared-preferences-builder';12import _ from 'lodash';13let driver;14let sandbox = sinon.createSandbox();15let expect = chai.expect;16chai.should();17chai.use(chaiAsPromised);18describe('driver', function () {19 describe('constructor', function () {20 it('should call BaseDriver constructor with opts', function () {21 let driver = new AndroidDriver({foo: 'bar'});22 driver.should.exist;23 driver.opts.foo.should.equal('bar');24 });25 it('should have this.findElOrEls', function () {26 let driver = new AndroidDriver({foo: 'bar'});27 driver.findElOrEls.should.exist;28 driver.findElOrEls.should.be.a('function');29 });30 });31 describe('emulator methods', function () {32 describe('fingerprint', function () {33 it('should be rejected if isEmulator is false', function () {34 let driver = new AndroidDriver();35 sandbox.stub(driver, 'isEmulator').returns(false);36 driver.fingerprint(1111).should.eventually.be.rejectedWith('fingerprint method is only available for emulators');37 driver.isEmulator.calledOnce.should.be.true;38 });39 });40 describe('sendSMS', function () {41 it('sendSMS should be rejected if isEmulator is false', function () {42 let driver = new AndroidDriver();43 sandbox.stub(driver, 'isEmulator').returns(false);44 driver.sendSMS(4509, 'Hello Appium').should.eventually.be.rejectedWith('sendSMS method is only available for emulators');45 driver.isEmulator.calledOnce.should.be.true;46 });47 });48 describe('sensorSet', function () {49 it('sensorSet should be rejected if isEmulator is false', function () {50 let driver = new AndroidDriver();51 sandbox.stub(driver, 'isEmulator').returns(false);52 driver.sensorSet({sensorType: 'light', value: 0}).should.eventually.be.rejectedWith('sensorSet method is only available for emulators');53 driver.isEmulator.calledOnce.should.be.true;54 });55 });56 });57 describe('sharedPreferences', function () {58 driver = new AndroidDriver();59 let adb = new ADB();60 driver.adb = adb;61 let builder = new SharedPrefsBuilder();62 describe('should skip setting sharedPreferences', withMocks({driver}, (mocks) => {63 it('on undefined name', async function () {64 driver.opts.sharedPreferences = {};65 (await driver.setSharedPreferences()).should.be.false;66 mocks.driver.verify();67 });68 }));69 describe('should set sharedPreferences', withMocks({driver, adb, builder, fs}, (mocks) => {70 it('on defined sharedPreferences object', async function () {71 driver.opts.appPackage = 'io.appium.test';72 driver.opts.sharedPreferences = {73 name: 'com.appium.prefs',74 prefs: [{type: 'string', name: 'mystr', value: 'appium rocks!'}]75 };76 mocks.driver.expects('getPrefsBuilder').once().returns(builder);77 mocks.builder.expects('build').once();78 mocks.builder.expects('toFile').once();79 mocks.adb.expects('shell').once()80 .withExactArgs(['mkdir', '-p', '/data/data/io.appium.test/shared_prefs']);81 mocks.adb.expects('push').once()82 .withExactArgs('/tmp/com.appium.prefs.xml', '/data/data/io.appium.test/shared_prefs/com.appium.prefs.xml');83 mocks.fs.expects('exists').once()84 .withExactArgs('/tmp/com.appium.prefs.xml')85 .returns(true);86 mocks.fs.expects('unlink').once()87 .withExactArgs('/tmp/com.appium.prefs.xml');88 await driver.setSharedPreferences();89 mocks.driver.verify();90 mocks.adb.verify();91 mocks.builder.verify();92 mocks.fs.verify();93 });94 }));95 });96 describe('createSession', function () {97 beforeEach(function () {98 driver = new AndroidDriver();99 sandbox.stub(driver, 'checkAppPresent');100 sandbox.stub(driver, 'checkPackagePresent');101 sandbox.stub(driver, 'startAndroidSession');102 sandbox.stub(ADB, 'createADB').callsFake(function (opts) {103 return {104 getDevicesWithRetry () {105 return [106 {udid: 'emulator-1234'},107 {udid: 'rotalume-1337'}108 ];109 },110 getPortFromEmulatorString () {111 return 1234;112 },113 setDeviceId: () => {},114 setEmulatorPort: () => {},115 adbPort: opts.adbPort,116 networkSpeed: () => {},117 getApiLevel: () => 22,118 };119 });120 sandbox.stub(driver.helpers, 'configureApp')121 .withArgs('/path/to/some', '.apk')122 .returns('/path/to/some.apk');123 });124 afterEach(function () {125 sandbox.restore();126 });127 it('should verify device is an emulator', function () {128 driver.opts.avd = 'Nexus_5X_Api_23';129 driver.isEmulator().should.equal(true);130 driver.opts.avd = undefined;131 driver.opts.udid = 'emulator-5554';132 driver.isEmulator().should.equal(true);133 driver.opts.udid = '01234567889';134 driver.isEmulator().should.equal(false);135 });136 it('should get browser package details if browserName is provided', async function () {137 sandbox.spy(helpers, 'getChromePkg');138 await driver.createSession({platformName: 'Android', deviceName: 'device', browserName: 'Chrome'});139 helpers.getChromePkg.calledOnce.should.be.true;140 });141 it('should check an app is present', async function () {142 await driver.createSession({platformName: 'Android', deviceName: 'device', app: '/path/to/some.apk'});143 driver.checkAppPresent.calledOnce.should.be.true;144 });145 it('should check a package is present', async function () {146 await driver.createSession({platformName: 'Android', deviceName: 'device', appPackage: 'some.app.package'});147 driver.checkPackagePresent.calledOnce.should.be.true;148 });149 it('should accept a package via the app capability', async function () {150 await driver.createSession({platformName: 'Android', deviceName: 'device', app: 'some.app.package'});151 driver.checkPackagePresent.calledOnce.should.be.true;152 });153 it('should add server details to caps', async function () {154 await driver.createSession({platformName: 'Android', deviceName: 'device', appPackage: 'some.app.package'});155 driver.caps.webStorageEnabled.should.exist;156 });157 it('should pass along adbPort capability to ADB', async function () {158 await driver.createSession({platformName: 'Android', deviceName: 'device', appPackage: 'some.app.package', adbPort: 1111});159 driver.adb.adbPort.should.equal(1111);160 });161 it('should proxy screenshot if nativeWebScreenshot is off', async function () {162 await driver.createSession({platformName: 'Android', deviceName: 'device', browserName: 'chrome', nativeWebScreenshot: false});163 driver.getProxyAvoidList()164 .some((x) => x[1].toString().includes('/screenshot'))165 .should.be.false;166 });167 it('should not proxy screenshot if nativeWebScreenshot is on', async function () {168 await driver.createSession({platformName: 'Android', deviceName: 'device', browserName: 'chrome', nativeWebScreenshot: true});169 driver.getProxyAvoidList()170 .some((x) => x[1].toString().includes('/screenshot'))171 .should.be.true;172 });173 });174 describe('deleteSession', function () {175 beforeEach(function () {176 driver = new AndroidDriver();177 driver.caps = {};178 driver.adb = new ADB();179 driver.bootstrap = new helpers.bootstrap(driver.adb);180 sandbox.stub(driver, 'stopChromedriverProxies');181 sandbox.stub(driver.adb, 'setIME');182 sandbox.stub(driver.adb, 'forceStop');183 sandbox.stub(driver.adb, 'goToHome');184 sandbox.stub(driver.adb, 'uninstallApk');185 sandbox.stub(driver.adb, 'stopLogcat');186 sandbox.stub(driver.adb, 'setAnimationState');187 sandbox.stub(driver.adb, 'setDefaultHiddenApiPolicy');188 sandbox.stub(driver.adb, 'getApiLevel').returns(27);189 sandbox.stub(driver.bootstrap, 'shutdown');190 sandbox.spy(log, 'debug');191 });192 afterEach(function () {193 sandbox.restore();194 });195 it('should not do anything if Android Driver has already shut down', async function () {196 driver.bootstrap = null;197 await driver.deleteSession();198 driver.stopChromedriverProxies.called.should.be.false;199 driver.adb.stopLogcat.called.should.be.true;200 });201 it('should call stopLogcat even if skipLogcatCapture is true', async function () {202 driver.opts.skipLogcatCapture = true;203 await driver.deleteSession();204 driver.adb.stopLogcat.called.should.be.true;205 });206 it('should reset keyboard to default IME', async function () {207 driver.opts.unicodeKeyboard = true;208 driver.opts.resetKeyboard = true;209 driver.defaultIME = 'someDefaultIME';210 await driver.deleteSession();211 driver.adb.setIME.calledOnce.should.be.true;212 });213 it('should force stop non-Chrome sessions', async function () {214 await driver.deleteSession();215 driver.adb.forceStop.calledOnce.should.be.true;216 });217 it('should uninstall APK if required', async function () {218 driver.opts.fullReset = true;219 await driver.deleteSession();220 driver.adb.uninstallApk.calledOnce.should.be.true;221 });222 it('should call setAnimationState to enable it with API Level 27', async function () {223 driver._wasWindowAnimationDisabled = true;224 await driver.deleteSession();225 driver.adb.setAnimationState.calledOnce.should.be.true;226 driver.adb.setDefaultHiddenApiPolicy.calledOnce.should.be.false;227 });228 it('should call setAnimationState to enable it with API Level 28', async function () {229 driver._wasWindowAnimationDisabled = true;230 driver.adb.getApiLevel.restore();231 sandbox.stub(driver.adb, 'getApiLevel').returns(28);232 await driver.deleteSession();233 driver.adb.setAnimationState.calledOnce.should.be.true;234 driver.adb.setDefaultHiddenApiPolicy.calledOnce.should.be.true;235 });236 it('should not call setAnimationState', async function () {237 driver._wasWindowAnimationDisabled = false;238 await driver.deleteSession();239 driver.adb.setAnimationState.calledOnce.should.be.false;240 driver.adb.setDefaultHiddenApiPolicy.calledOnce.should.be.false;241 });242 });243 describe('dismissChromeWelcome', function () {244 before(function () {245 driver = new AndroidDriver();246 });247 it('should verify chromeOptions args', function () {248 driver.opts = {};249 driver.shouldDismissChromeWelcome().should.be.false;250 driver.opts = {chromeOptions: {}};251 driver.shouldDismissChromeWelcome().should.be.false;252 driver.opts = {chromeOptions: {args: []}};253 driver.shouldDismissChromeWelcome().should.be.false;254 driver.opts = {chromeOptions: {args: '--no-first-run'}};255 driver.shouldDismissChromeWelcome().should.be.false;256 driver.opts = {chromeOptions: {args: ['--disable-dinosaur-easter-egg']}};257 driver.shouldDismissChromeWelcome().should.be.false;258 driver.opts = {chromeOptions: {args: ['--no-first-run']}};259 driver.shouldDismissChromeWelcome().should.be.true;260 });261 });262 describe('initAUT', withMocks({helpers}, (mocks) => {263 beforeEach(function () {264 driver = new AndroidDriver();265 driver.caps = {};266 });267 it('should throw error if run with full reset', async function () {268 driver.opts = {appPackage: 'app.package', appActivity: 'act', fullReset: true};269 await driver.initAUT().should.be.rejectedWith(/Full reset requires an app capability/);270 });271 it('should reset if run with fast reset', async function () {272 driver.opts = {appPackage: 'app.package', appActivity: 'act', fullReset: false, fastReset: true};273 driver.adb = 'mock_adb';274 mocks.helpers.expects('resetApp').withArgs('mock_adb');275 await driver.initAUT();276 mocks.helpers.verify();277 });278 it('should keep data if run without reset', async function () {279 driver.opts = {appPackage: 'app.package', appActivity: 'act', fullReset: false, fastReset: false};280 mocks.helpers.expects('resetApp').never();281 await driver.initAUT();282 mocks.helpers.verify();283 });284 it('should install "otherApps" if set in capabilities', async function () {285 const otherApps = ['http://URL_FOR/fake/app.apk'];286 const tempApps = ['/path/to/fake/app.apk'];287 driver.opts = {288 appPackage: 'app.package',289 appActivity: 'act',290 fullReset: false,291 fastReset: false,292 otherApps: `["${otherApps[0]}"]`,293 };294 sandbox.stub(driver.helpers, 'configureApp')295 .withArgs(otherApps[0], '.apk')296 .returns(tempApps[0]);297 mocks.helpers.expects('installOtherApks').once().withArgs(tempApps, driver.adb, driver.opts);298 await driver.initAUT();299 mocks.helpers.verify();300 });301 it('should uninstall a package "uninstallOtherPackages" if set in capabilities', async function () {302 const uninstallOtherPackages = 'app.bundle.id1';303 driver.opts = {304 appPackage: 'app.package',305 appActivity: 'act',306 fullReset: false,307 fastReset: false,308 uninstallOtherPackages,309 };310 driver.adb = new ADB();311 sandbox.stub(driver.adb, 'uninstallApk')312 .withArgs('app.bundle.id1')313 .returns(true);314 mocks.helpers.expects('uninstallOtherPackages').once().withArgs(driver.adb, [uninstallOtherPackages], [SETTINGS_HELPER_PKG_ID]);315 await driver.initAUT();316 mocks.helpers.verify();317 });318 it('should uninstall multiple packages "uninstallOtherPackages" if set in capabilities', async function () {319 const uninstallOtherPackages = ['app.bundle.id1', 'app.bundle.id2'];320 driver.opts = {321 appPackage: 'app.package',322 appActivity: 'act',323 fullReset: false,324 fastReset: false,325 uninstallOtherPackages: `["${uninstallOtherPackages[0]}", "${uninstallOtherPackages[1]}"]`,326 };327 driver.adb = new ADB();328 sandbox.stub(driver.adb, 'uninstallApk')329 .returns(true);330 mocks.helpers.expects('uninstallOtherPackages').once().withArgs(driver.adb, uninstallOtherPackages, [SETTINGS_HELPER_PKG_ID]);331 await driver.initAUT();332 mocks.helpers.verify();333 });334 it('get all 3rd party packages', async function () {335 driver.adb = new ADB();336 sandbox.stub(driver.adb, 'shell')337 .returns('package:app.bundle.id1\npackage:io.appium.settings\npackage:io.appium.uiautomator2.server\npackage:io.appium.uiautomator2.server.test\n');338 (await helpers.getThirdPartyPackages(driver.adb, [SETTINGS_HELPER_PKG_ID]))339 .should.eql(['app.bundle.id1', 'io.appium.uiautomator2.server', 'io.appium.uiautomator2.server.test']);340 });341 it('get all 3rd party packages with multiple package filter', async function () {342 driver.adb = new ADB();343 sandbox.stub(driver.adb, 'shell')344 .returns('package:app.bundle.id1\npackage:io.appium.settings\npackage:io.appium.uiautomator2.server\npackage:io.appium.uiautomator2.server.test\n');345 (await helpers.getThirdPartyPackages(driver.adb, [SETTINGS_HELPER_PKG_ID, 'io.appium.uiautomator2.server']))346 .should.eql(['app.bundle.id1', 'io.appium.uiautomator2.server.test']);347 });348 it('get no 3rd party packages', async function () {349 driver.adb = new ADB();350 sandbox.stub(driver.adb, 'shell').throws('');351 (await helpers.getThirdPartyPackages(driver.adb, [SETTINGS_HELPER_PKG_ID]))352 .should.eql([]);353 });354 }));355 describe('startAndroidSession', function () {356 beforeEach(function () {357 driver = new AndroidDriver();358 driver.adb = new ADB();359 driver.bootstrap = new helpers.bootstrap(driver.adb);360 driver.settings = { update () {} };361 driver.caps = {};362 // create a fake bootstrap because we can't mock363 // driver.bootstrap.<whatever> in advance364 let fakeBootstrap = {365 start () {},366 onUnexpectedShutdown: {catch () {}}367 };368 sandbox.stub(helpers, 'initDevice');369 sandbox.stub(helpers, 'unlock');370 sandbox.stub(helpers, 'bootstrap').returns(fakeBootstrap);371 sandbox.stub(driver, 'initAUT');372 sandbox.stub(driver, 'startAUT');373 sandbox.stub(driver, 'defaultWebviewName');374 sandbox.stub(driver, 'setContext');375 sandbox.stub(driver, 'startChromeSession');376 sandbox.stub(driver, 'dismissChromeWelcome');377 sandbox.stub(driver.settings, 'update');378 sandbox.stub(driver.adb, 'getPlatformVersion');379 sandbox.stub(driver.adb, 'getScreenSize');380 sandbox.stub(driver.adb, 'getModel');381 sandbox.stub(driver.adb, 'getManufacturer');382 sandbox.stub(driver.adb, 'getApiLevel').returns(27);383 sandbox.stub(driver.adb, 'setHiddenApiPolicy');384 sandbox.stub(driver.adb, 'setAnimationState');385 });386 afterEach(function () {387 sandbox.restore();388 });389 it('should set actual platform version', async function () {390 await driver.startAndroidSession();391 driver.adb.getPlatformVersion.calledOnce.should.be.true;392 });393 it('should auto launch app if it is on the device', async function () {394 driver.opts.autoLaunch = true;395 await driver.startAndroidSession();396 driver.initAUT.calledOnce.should.be.true;397 });398 it('should handle chrome sessions', async function () {399 driver.opts.browserName = 'Chrome';400 await driver.startAndroidSession();401 driver.startChromeSession.calledOnce.should.be.true;402 });403 it('should unlock the device', async function () {404 await driver.startAndroidSession();405 helpers.unlock.calledOnce.should.be.true;406 });407 it('should start AUT if auto lauching', async function () {408 driver.opts.autoLaunch = true;409 await driver.startAndroidSession();410 driver.initAUT.calledOnce.should.be.true;411 });412 it('should not start AUT if not auto lauching', async function () {413 driver.opts.autoLaunch = false;414 await driver.startAndroidSession();415 driver.initAUT.calledOnce.should.be.false;416 });417 it('should set the context if autoWebview is requested', async function () {418 driver.opts.autoWebview = true;419 await driver.startAndroidSession();420 driver.defaultWebviewName.calledOnce.should.be.true;421 driver.setContext.calledOnce.should.be.true;422 });423 it('should set the context if autoWebview is requested using timeout', async function () {424 driver.setContext.onCall(0).throws(errors.NoSuchContextError);425 driver.setContext.onCall(1).returns();426 driver.opts.autoWebview = true;427 driver.opts.autoWebviewTimeout = 5000;428 await driver.startAndroidSession();429 driver.defaultWebviewName.calledOnce.should.be.true;430 driver.setContext.calledTwice.should.be.true;431 });432 it('should respect timeout if autoWebview is requested', async function () {433 this.timeout(10000);434 driver.setContext.throws(new errors.NoSuchContextError());435 let begin = Date.now();436 driver.opts.autoWebview = true;437 driver.opts.autoWebviewTimeout = 5000;438 await driver.startAndroidSession().should.eventually.be.rejected;439 driver.defaultWebviewName.calledOnce.should.be.true;440 // we have a timeout of 5000ms, retrying on 500ms, so expect 10 times441 driver.setContext.callCount.should.equal(10);442 let end = Date.now();443 (end - begin).should.be.above(4500);444 });445 it('should not set the context if autoWebview is not requested', async function () {446 await driver.startAndroidSession();447 driver.defaultWebviewName.calledOnce.should.be.false;448 driver.setContext.calledOnce.should.be.false;449 });450 it('should set ignoreUnimportantViews cap', async function () {451 driver.opts.ignoreUnimportantViews = true;452 await driver.startAndroidSession();453 driver.settings.update.calledOnce.should.be.true;454 driver.settings.update.firstCall.args[0].ignoreUnimportantViews.should.be.true;455 });456 it('should not call dismissChromeWelcome on missing chromeOptions', async function () {457 driver.opts.browserName = 'Chrome';458 await driver.startAndroidSession();459 driver.dismissChromeWelcome.calledOnce.should.be.false;460 });461 it('should call setAnimationState with API level 27', async function () {462 driver.opts.disableWindowAnimation = true;463 sandbox.stub(driver.adb, 'isAnimationOn').returns(true);464 await driver.startAndroidSession();465 driver.adb.isAnimationOn.calledOnce.should.be.true;466 driver.adb.setHiddenApiPolicy.calledOnce.should.be.false;467 driver.adb.setAnimationState.calledOnce.should.be.true;468 });469 it('should call setAnimationState with API level 28', async function () {470 driver.opts.disableWindowAnimation = true;471 sandbox.stub(driver.adb, 'isAnimationOn').returns(true);472 driver.adb.getApiLevel.restore();473 sandbox.stub(driver.adb, 'getApiLevel').returns(28);474 await driver.startAndroidSession();475 driver.adb.isAnimationOn.calledOnce.should.be.true;476 driver.adb.setHiddenApiPolicy.calledOnce.should.be.true;477 driver.adb.setAnimationState.calledOnce.should.be.true;478 });479 it('should not call setAnimationState', async function () {480 driver.opts.disableWindowAnimation = true;481 sandbox.stub(driver.adb, 'isAnimationOn').returns(false);482 await driver.startAndroidSession();483 driver.adb.isAnimationOn.calledOnce.should.be.true;484 driver.adb.setHiddenApiPolicy.calledOnce.should.be.false;485 driver.adb.setAnimationState.calledOnce.should.be.false;486 });487 });488 describe('startChromeSession', function () {489 beforeEach(function () {490 driver = new AndroidDriver();491 driver.adb = new ADB();492 driver.bootstrap = new helpers.bootstrap(driver.adb);493 driver.settings = { update () { } };494 driver.caps = {};495 sandbox.stub(driver, 'setupNewChromedriver').returns({496 on: _.noop,497 proxyReq: _.noop,498 });499 sandbox.stub(driver, 'dismissChromeWelcome');500 });501 afterEach(function () {502 sandbox.restore();503 });504 it('should call dismissChromeWelcome', async function () {505 driver.opts.browserName = 'Chrome';506 driver.opts.chromeOptions = {507 'args': ['--no-first-run']508 };509 await driver.startChromeSession();510 driver.dismissChromeWelcome.calledOnce.should.be.true;511 });512 });513 describe('validateDesiredCaps', function () {514 before(function () {515 driver = new AndroidDriver();516 });517 it('should throw an error if caps do not contain an app, package or valid browser', function () {518 expect(() => {519 driver.validateDesiredCaps({platformName: 'Android', deviceName: 'device'});520 }).to.throw(/must include/);521 expect(() => {522 driver.validateDesiredCaps({platformName: 'Android', deviceName: 'device', browserName: 'Netscape Navigator'});523 }).to.throw(/must include/);524 });525 it('should not throw an error if caps contain an app, package or valid browser', function () {526 expect(() => {527 driver.validateDesiredCaps({platformName: 'Android', deviceName: 'device', app: '/path/to/some.apk'});528 }).to.not.throw(Error);529 expect(() => {530 driver.validateDesiredCaps({platformName: 'Android', deviceName: 'device', browserName: 'Chrome'});531 }).to.not.throw(Error);532 expect(() => {533 driver.validateDesiredCaps({platformName: 'Android', deviceName: 'device', appPackage: 'some.app.package'});534 }).to.not.throw(/must include/);535 });536 it('should not be sensitive to platform name casing', function () {537 expect(() => {538 driver.validateDesiredCaps({platformName: 'AnDrOiD', deviceName: 'device', app: '/path/to/some.apk'});539 }).to.not.throw(Error);540 });541 it('should not throw an error if caps contain both an app and browser, for grid compatibility', function () {542 expect(() => {543 driver.validateDesiredCaps({platformName: 'Android', deviceName: 'device', app: '/path/to/some.apk', browserName: 'iPhone'});544 }).to.not.throw(Error);545 });546 it('should not throw an error if caps contain androidScreenshotPath capability', function () {547 expect(() => {548 driver.validateDesiredCaps({platformName: 'Android', deviceName: 'device', app: '/path/to/some.apk', androidScreenshotPath: '/path/to/screenshotdir'});549 }).to.not.throw(Error);550 });551 });552 describe('proxying', function () {553 before(function () {554 driver = new AndroidDriver();555 driver.sessionId = 'abc';556 });557 describe('#proxyActive', function () {558 it('should exist', function () {559 driver.proxyActive.should.be.an.instanceof(Function);560 });561 it('should return false', function () {562 driver.proxyActive('abc').should.be.false;563 });564 it('should throw an error if session id is wrong', function () {565 (() => { driver.proxyActive('aaa'); }).should.throw;566 });567 });568 describe('#getProxyAvoidList', function () {569 it('should exist', function () {570 driver.getProxyAvoidList.should.be.an.instanceof(Function);571 });572 it('should return jwpProxyAvoid array', function () {573 let avoidList = driver.getProxyAvoidList('abc');574 avoidList.should.be.an.instanceof(Array);575 avoidList.should.eql(driver.jwpProxyAvoid);576 });577 it('should throw an error if session id is wrong', function () {578 (() => { driver.getProxyAvoidList('aaa'); }).should.throw;579 });580 });581 describe('#canProxy', function () {582 it('should exist', function () {583 driver.canProxy.should.be.an.instanceof(Function);584 });585 it('should return false', function () {586 driver.canProxy('abc').should.be.false;587 });588 it('should throw an error if session id is wrong', function () {589 (() => { driver.canProxy('aaa'); }).should.throw;590 });591 });592 });...

Full Screen

Full Screen

driver.js

Source:driver.js Github

copy

Full Screen

...331 "org.chromium.webview_shell"];332 if (!_.includes(knownPackages, this.opts.appPackage)) {333 opts.chromeAndroidActivity = this.opts.appActivity;334 }335 this.chromedriver = await setupNewChromedriver(opts, this.adb.curDeviceId,336 this.adb);337 this.chromedriver.on(Chromedriver.EVENT_CHANGED, (msg) => {338 if (msg.state === Chromedriver.STATE_STOPPED) {339 this.onChromedriverStop(CHROMIUM_WIN);340 }341 });342 // Now that we have a Chrome session, we ensure that the context is343 // appropriately set and that this chromedriver is added to the list344 // of session chromedrivers so we can switch back and forth345 this.curContext = CHROMIUM_WIN;346 this.sessionChromedrivers[CHROMIUM_WIN] = this.chromedriver;347 this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);348 this.jwpProxyActive = true;349 }...

Full Screen

Full Screen

context.js

Source:context.js Github

copy

Full Screen

...111 if (androidPackage && androidPackage.length > 0) {112 opts.chromeAndroidPackage = androidPackage[1];113 }114 }115 cd = await this.setupNewChromedriver(opts, this.adb.curDeviceId, this.adb);116 // bind our stop/exit handler, passing in context so we know which117 // one stopped unexpectedly118 cd.on(Chromedriver.EVENT_CHANGED, (msg) => {119 if (msg.state === Chromedriver.STATE_STOPPED) {120 this.onChromedriverStop(context);121 }122 });123 // save the chromedriver object under the context124 this.sessionChromedrivers[context] = cd;125 }126 // hook up the local variables so we can proxy this biz127 this.chromedriver = cd;128 this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);129 this.jwpProxyActive = true;130};131// Stop proxying to any Chromedriver132helpers.suspendChromedriverProxy = function suspendChromedriverProxy () {133 this.chromedriver = null;134 this.proxyReqRes = null;135 this.jwpProxyActive = false;136};137// Handle an out-of-band Chromedriver stop event138helpers.onChromedriverStop = async function onChromedriverStop (context) {139 log.warn(`Chromedriver for context ${context} stopped unexpectedly`);140 if (context === this.curContext) {141 // we exited unexpectedly while automating the current context and so want142 // to shut down the session and respond with an error143 let err = new Error('Chromedriver quit unexpectedly during session');144 await this.startUnexpectedShutdown(err);145 } else {146 // if a Chromedriver in the non-active context barfs, we don't really147 // care, we'll just make a new one next time we need the context.148 log.warn("Chromedriver quit unexpectedly, but it wasn't the active " +149 'context, ignoring');150 delete this.sessionChromedrivers[context];151 }152};153// Intentionally stop all the chromedrivers currently active, and ignore154// their exit events155helpers.stopChromedriverProxies = async function stopChromedriverProxies () {156 this.suspendChromedriverProxy(); // make sure we turn off the proxy flag157 for (let context of _.keys(this.sessionChromedrivers)) {158 let cd = this.sessionChromedrivers[context];159 log.debug(`Stopping chromedriver for context ${context}`);160 // stop listening for the stopped state event161 cd.removeAllListeners(Chromedriver.EVENT_CHANGED);162 try {163 await cd.stop();164 } catch (err) {165 log.warn(`Error stopping Chromedriver: ${err.message}`);166 }167 delete this.sessionChromedrivers[context];168 }169};170helpers.isChromedriverContext = function isChromedriverContext (viewName) {171 return _.includes(viewName, WEBVIEW_WIN) || viewName === CHROMIUM_WIN;172};173helpers.shouldDismissChromeWelcome = function shouldDismissChromeWelcome () {174 return !!this.opts.chromeOptions &&175 _.isArray(this.opts.chromeOptions.args) &&176 this.opts.chromeOptions.args.includes('--no-first-run');177};178helpers.dismissChromeWelcome = async function dismissChromeWelcome () {179 log.info('Trying to dismiss Chrome welcome');180 let activity = await this.getCurrentActivity();181 if (activity !== 'org.chromium.chrome.browser.firstrun.FirstRunActivity') {182 log.info('Chrome welcome dialog never showed up! Continuing');183 return;184 }185 let el = await this.findElOrEls('id', 'com.android.chrome:id/terms_accept', false);186 await this.click(el.ELEMENT);187 try {188 let el = await this.findElOrEls('id', 'com.android.chrome:id/negative_button', false);189 await this.click(el.ELEMENT);190 } catch (e) {191 // DO NOTHING, THIS DEVICE DIDNT LAUNCH THE SIGNIN DIALOG192 // IT MUST BE A NON GMS DEVICE193 log.warn(`This device did not show Chrome SignIn dialog, ${e.message}`);194 }195};196// extract so sub-classes can decide197helpers.shouldUseChromeRunningApp = function shouldUseChromeRunningApp () {198 return false;199};200helpers.startChromeSession = async function startChromeSession () {201 log.info('Starting a chrome-based browser session');202 let opts = _.cloneDeep(this.opts);203 opts.chromeUseRunningApp = this.shouldUseChromeRunningApp();204 const knownPackages = [205 'org.chromium.chrome.shell',206 'com.android.chrome',207 'com.chrome.beta',208 'org.chromium.chrome',209 'org.chromium.webview_shell',210 ];211 if (_.includes(knownPackages, this.opts.appPackage)) {212 opts.chromeBundleId = this.opts.appPackage;213 } else {214 opts.chromeAndroidActivity = this.opts.appActivity;215 }216 this.chromedriver = await this.setupNewChromedriver(opts, this.adb.curDeviceId, this.adb);217 this.chromedriver.on(Chromedriver.EVENT_CHANGED, (msg) => {218 if (msg.state === Chromedriver.STATE_STOPPED) {219 this.onChromedriverStop(CHROMIUM_WIN);220 }221 });222 // Now that we have a Chrome session, we ensure that the context is223 // appropriately set and that this chromedriver is added to the list224 // of session chromedrivers so we can switch back and forth225 this.curContext = CHROMIUM_WIN;226 this.sessionChromedrivers[CHROMIUM_WIN] = this.chromedriver;227 this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);228 this.jwpProxyActive = true;229 if (this.shouldDismissChromeWelcome()) {230 // dismiss Chrome welcome dialog...

Full Screen

Full Screen

context-specs.js

Source:context-specs.js Github

copy

Full Screen

...233 });234 });235 describe('setupNewChromedriver', function () {236 it('should be able to set app package from chrome options', async function () {237 let chromedriver = await setupNewChromedriver({chromeOptions: {androidPackage: 'apkg'}});238 chromedriver.start.getCall(0).args[0].chromeOptions.androidPackage239 .should.be.equal('apkg');240 });241 it('should use prefixed chromeOptions', async function () {242 let chromedriver = await setupNewChromedriver({243 'goog:chromeOptions': {244 androidPackage: 'apkg',245 },246 });247 chromedriver.start.getCall(0).args[0].chromeOptions.androidPackage248 .should.be.equal('apkg');249 });250 it('should merge chromeOptions', async function () {251 let chromedriver = await setupNewChromedriver({252 chromeOptions: {253 androidPackage: 'apkg',254 },255 'goog:chromeOptions': {256 androidWaitPackage: 'bpkg',257 },258 'appium:chromeOptions': {259 androidActivity: 'aact',260 },261 });262 chromedriver.start.getCall(0).args[0].chromeOptions.androidPackage263 .should.be.equal('apkg');264 chromedriver.start.getCall(0).args[0].chromeOptions.androidActivity265 .should.be.equal('aact');266 chromedriver.start.getCall(0).args[0].chromeOptions.androidWaitPackage267 .should.be.equal('bpkg');268 });269 it('should be able to set androidActivity chrome option', async function () {270 let chromedriver = await setupNewChromedriver({chromeAndroidActivity: 'act'});271 chromedriver.start.getCall(0).args[0].chromeOptions.androidActivity272 .should.be.equal('act');273 });274 it('should be able to set androidProcess chrome option', async function () {275 let chromedriver = await setupNewChromedriver({chromeAndroidProcess: 'proc'});276 chromedriver.start.getCall(0).args[0].chromeOptions.androidProcess277 .should.be.equal('proc');278 });279 it('should be able to set loggingPrefs capability', async function () {280 let chromedriver = await setupNewChromedriver({enablePerformanceLogging: true});281 chromedriver.start.getCall(0).args[0].loggingPrefs282 .should.deep.equal({performance: 'ALL'});283 });284 it('should set androidActivity to appActivity if browser name is chromium-webview', async function () {285 let chromedriver = await setupNewChromedriver({browserName: 'chromium-webview',286 appActivity: 'app_act'});287 chromedriver.start.getCall(0).args[0].chromeOptions.androidActivity288 .should.be.equal('app_act');289 });290 it('should be able to set loggingPrefs capability', async function () {291 let chromedriver = await setupNewChromedriver({pageLoadStrategy: 'strategy'});292 chromedriver.start.getCall(0).args[0].pageLoadStrategy293 .should.be.equal('strategy');294 });295 });...

Full Screen

Full Screen

android-hybrid.js

Source:android-hybrid.js Github

copy

Full Screen

1"use strict";2var logger = require('../../server/logger.js').get('appium')3 , _ = require('underscore')4 , errors = require('../../server/errors.js')5 , UnknownError = errors.UnknownError6 , async = require('async')7 , Chromedriver = require('./chromedriver.js')8 , status = require("../../server/status.js");9var androidHybrid = {};10androidHybrid.chromedriver = null;11androidHybrid.sessionChromedrivers = {};12androidHybrid.listWebviews = function (cb) {13 logger.debug("Getting a list of available webviews");14 var webviews = [];15 var definedDeviceSocket = this.args.androidDeviceSocket;16 this.adb.shell("cat /proc/net/unix", function (err, out) {17 if (err) return cb(err);18 _.each(out.split("\n"), function (line) {19 line = line.trim();20 var webviewPid = line.match(/@?webview_devtools_remote_(\d+)/);21 if (definedDeviceSocket) {22 if (line.indexOf("@" + definedDeviceSocket) ===23 line.length - definedDeviceSocket.length - 1) {24 if (webviewPid) {25 webviews.push(this.WEBVIEW_BASE + webviewPid[1]);26 } else {27 webviews.push(this.CHROMIUM_WIN);28 }29 }30 } else if (webviewPid) {31 // for multiple webviews a list of 'WEBVIEW_<index>' will be returned32 // where <index> is zero based (same is in selendroid)33 webviews.push(this.WEBVIEW_BASE + webviewPid[1]);34 }35 }.bind(this));36 webviews = _.uniq(webviews);37 if (definedDeviceSocket) {38 return cb(null, webviews);39 }40 var webviewsTmp = webviews;41 webviews = [];42 var getProcessNameFromWebview = function (view, cb) {43 this.getProcessNameFromWebview(view, function (err, pkg) {44 if (err) return cb(err);45 webviews.push(this.WEBVIEW_BASE + pkg);46 cb();47 }.bind(this));48 }.bind(this);49 async.each(webviewsTmp, getProcessNameFromWebview, function (err) {50 if (err) return cb(err);51 logger.debug("Available contexts: " + this.contexts);52 logger.debug(JSON.stringify(webviews));53 cb(null, webviews);54 }.bind(this));55 }.bind(this));56};57var previousState = {};58androidHybrid.rememberProxyState = function () {59 previousState.proxyHost = this.proxyHost;60 previousState.proxyPort = this.proxyPort;61 previousState.proxySessionId = this.proxySessionId;62 previousState.isProxy = this.isProxy;63};64androidHybrid.restoreProxyState = function () {65 this.proxyHost = previousState.proxyHost;66 this.proxyPort = previousState.proxyPort;67 this.proxySessionId = previousState.proxySessionId;68 this.isProxy = previousState.isProxy;69};70androidHybrid.getProcessNameFromWebview = function (webview, cb) {71 // webview_devtools_remote_4296 => 429672 var pid = webview.match(/\d+$/);73 if (!pid) return cb("No pid for webview " + webview);74 pid = pid[0];75 logger.debug(webview + " mapped to pid " + pid);76 logger.debug("Getting process name for webview");77 this.adb.shell("ps", function (err, out) {78 if (err) return cb(err);79 var pkg = "unknown";80 var lines = out.split(/\r?\n/);81 /*82 USER PID PPID VSIZE RSS WCHAN PC NAME83 u0_a136 6248 179 946000 48144 ffffffff 4005903e R com.example.test84 */85 var header = lines[0].trim().split(/\s+/);86 // the column order may not be identical on all androids87 // dynamically locate the pid and name column.88 var pidColumn = header.indexOf("PID");89 var pkgColumn = header.indexOf("NAME") + 1;90 _.find(lines, function (line) {91 line = line.trim().split(/\s+/);92 if (line[pidColumn].indexOf(pid) !== -1) {93 logger.debug("Parsed pid: " + line[pidColumn] + " pkg: " + line[pkgColumn]);94 logger.debug("from: " + line);95 pkg = line[pkgColumn];96 return pkg; // exit from _.find97 }98 });99 logger.debug("returning process name: " + pkg);100 cb(null, pkg);101 });102};103androidHybrid.startChromedriverProxy = function (context, cb) {104 logger.debug("Connecting to chrome-backed webview");105 if (this.chromedriver !== null) {106 return cb(new Error("We already have a chromedriver instance running"));107 }108 var setupNewChromeDriver = function (ocb) {109 var chromeArgs = {110 port: this.args.chromeDriverPort111 , executable: this.args.chromedriverExecutable112 , deviceId: this.adb.curDeviceId113 , enablePerformanceLogging: this.args.enablePerformanceLogging114 };115 this.chromedriver = new Chromedriver(chromeArgs, this.onChromedriverExit.bind(this));116 this.rememberProxyState();117 this.proxyHost = this.chromedriver.proxyHost;118 this.proxyPort = this.chromedriver.proxyPort;119 this.isProxy = true;120 var caps = {121 chromeOptions: {122 androidPackage: this.args.appPackage,123 androidUseRunningApp: true124 }125 };126 // For now the only known arg passed this way is androidDeviceSocked used by Operadriver (deriving from Chromedriver)127 // We don't know how other Chromium embedders will call this argument so for now it's name needs to be configurable128 // When Google adds the androidDeviceSocket argument to the original Chromedriver then we will be sure about it's name129 // for all Chromium embedders (as their Webdrivers will derive from Chromedriver)130 if (this.args.specialChromedriverSessionArgs) {131 _.each(this.args.specialChromedriverSessionArgs, function (val, option) {132 logger.debug("This method is being deprecated. Apply chromeOptions normally to pass along options," +133 "see sites.google.com/a/chromium.org/chromedriver/capabilities for more info");134 caps.chromeOptions[option] = val;135 });136 }137 caps = this.decorateChromeOptions(caps);138 this.chromedriver.createSession(caps, function (err, sessId) {139 if (err) return cb(err);140 logger.debug("Setting proxy session id to " + sessId);141 this.proxySessionId = sessId;142 this.sessionChromedrivers[context] = this.chromedriver;143 ocb();144 }.bind(this));145 }.bind(this);146 if (this.sessionChromedrivers[context]) {147 logger.debug("Found existing Chromedriver for context '" + context + "'." +148 " Using it.");149 this.rememberProxyState();150 this.chromedriver = this.sessionChromedrivers[context];151 this.proxyHost = this.chromedriver.proxyHost;152 this.proxyPort = this.chromedriver.proxyPort;153 this.proxySessionId = this.chromedriver.chromeSessionId;154 this.isProxy = true;155 // check the status by sending a simple window-based command to ChromeDriver156 // if there is an error, we want to recreate the ChromeDriver session157 var url = '/wd/hub/session/' + this.proxySessionId + '/url';158 this.chromedriver.proxyTo(url, 'GET', {}, function (err, res, body) {159 body = JSON.parse(body);160 if ((body.status === status.codes.NoSuchWindow.code && body.value.message.indexOf('no such window: window was already closed') !== -1) ||161 (body.status === 100 && body.value.message.indexOf('chrome not reachable') !== -1)) {162 // the window is closed and we need to reinitialize163 logger.debug("ChromeDriver is not associated with a window. Re-initializing the session.");164 this.cleanupChromedriver(this.chromedriver, function () {165 setupNewChromeDriver(cb);166 });167 } else {168 cb();169 }170 }.bind(this));171 } else {172 setupNewChromeDriver(cb);173 }174};175androidHybrid.onChromedriverExit = function () {176 logger.debug("Chromedriver exited unexpectedly");177 if (typeof this.cbForCurrentCmd === "function") {178 var error = new UnknownError("Chromedriver quit unexpectedly during session");179 this.shutdown(function () {180 this.cbForCurrentCmd(error, null);181 }.bind(this));182 }183};184androidHybrid.cleanupChromedriver = function (chromedriver, cb) {185 if (chromedriver) {186 logger.debug("Cleaning up Chromedriver");187 chromedriver.stop(function (err) {188 if (err) logger.warn("Error stopping chromedriver: " + err.message);189 this.restoreProxyState();190 cb();191 }.bind(this));192 } else {193 cb();194 }195};196androidHybrid.suspendChromedriverProxy = function (cb) {197 this.chromedriver = null;198 this.restoreProxyState();199 cb();200};201androidHybrid.stopChromedriverProxies = function (ocb) {202 async.eachSeries(Object.keys(this.sessionChromedrivers), function (context, cb) {203 var chromedriver = this.sessionChromedrivers[context];204 chromedriver.deleteSession(function (err) {205 if (err) return cb(err);206 this.cleanupChromedriver(chromedriver, cb);207 delete this.sessionChromedrivers[context];208 }.bind(this));209 }.bind(this), ocb);210};211androidHybrid.defaultWebviewName = function () {212 return this.WEBVIEW_BASE + this.appProcess;213};214androidHybrid.initAutoWebview = function (cb) {215 if (this.args.autoWebview) {216 logger.debug('Setting auto webview');217 var viewName = this.defaultWebviewName();218 var timeout = (this.args.autoWebviewTimeout) || 2000;219 this.setContext(viewName, function (err, res) {220 if (err && res.status !== status.codes.NoSuchContext.code) return cb(err);221 if (res.status === status.codes.Success.code) return cb();222 setTimeout(function () {223 logger.debug("Retrying context switch with timeout '" + timeout + "'");224 this.setContext(viewName, cb);225 }.bind(this), timeout);226 }.bind(this));227 } else {228 cb();229 }230};...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const AppiumAndroidDriver = require('appium-android-driver');2const driver = new AppiumAndroidDriver();3driver.setupNewChromedriver();4const AppiumAndroidDriver = require('appium-android-driver');5const driver = new AppiumAndroidDriver();6driver.setupNewChromedriver();7const AppiumAndroidDriver = require('appium-android-driver');8const driver = new AppiumAndroidDriver();9driver.setupNewChromedriver();10const AppiumAndroidDriver = require('appium-android-driver');11const driver = new AppiumAndroidDriver();12driver.setupNewChromedriver();13const AppiumAndroidDriver = require('appium-android-driver');14const driver = new AppiumAndroidDriver();15driver.setupNewChromedriver();16const AppiumAndroidDriver = require('appium-android-driver');17const driver = new AppiumAndroidDriver();18driver.setupNewChromedriver();19const AppiumAndroidDriver = require('appium-android-driver');20const driver = new AppiumAndroidDriver();21driver.setupNewChromedriver();22const AppiumAndroidDriver = require('appium-android-driver');23const driver = new AppiumAndroidDriver();24driver.setupNewChromedriver();25const AppiumAndroidDriver = require('appium-android-driver');26const driver = new AppiumAndroidDriver();27driver.setupNewChromedriver();28const AppiumAndroidDriver = require('appium-android-driver');29const driver = new AppiumAndroidDriver();30driver.setupNewChromedriver();31const AppiumAndroidDriver = require('appium-android-driver');32const driver = new AppiumAndroidDriver();33driver.setupNewChromedriver();

Full Screen

Using AI Code Generation

copy

Full Screen

1var AppiumAndroidDriver = require('appium-android-driver');2var driver = new AppiumAndroidDriver();3driver.setupNewChromedriver();4driver.startChromedriverSession();5var AppiumIOSDriver = require('appium-ios-driver');6var driver = new AppiumIOSDriver();7driver.setupNewChromedriver();8driver.startChromedriverSession();9var AppiumWindowsDriver = require('appium-windows-driver');10var driver = new AppiumWindowsDriver();11driver.setupNewChromedriver();12driver.startChromedriverSession();13var AppiumMacDriver = require('appium-mac-driver');14var driver = new AppiumMacDriver();15driver.setupNewChromedriver();16driver.startChromedriverSession();17var AppiumYouiEngineDriver = require('appium-youiengine-driver');18var driver = new AppiumYouiEngineDriver();19driver.setupNewChromedriver();20driver.startChromedriverSession();21var AppiumTizenDriver = require('appium-tizen-driver');22var driver = new AppiumTizenDriver();23driver.setupNewChromedriver();24driver.startChromedriverSession();25var AppiumWebDriver = require('appium-webdriver');26var driver = new AppiumWebDriver();27driver.setupNewChromedriver();28driver.startChromedriverSession();29var AppiumFakeDriver = require('appium-fake-driver');30var driver = new AppiumFakeDriver();31driver.setupNewChromedriver();32driver.startChromedriverSession();33var AppiumFireOSDriver = require('appium-fireos-driver');34var driver = new AppiumFireOSDriver();35driver.setupNewChromedriver();36driver.startChromedriverSession();37var AppiumAndroidDriver = require('appium-android-driver');

Full Screen

Using AI Code Generation

copy

Full Screen

1var android = require('appium-android-driver');2var appium = require('appium');3var path = require('path');4var adb = require('appium-adb');5var androidDriver = new android.AndroidDriver();6var appiumServer = new appium.AppiumServer();7var adb = new adb.ADB();8var chromedriver = new android.Chromedriver();9var opts = {};10opts.app = path.resolve(__dirname, 'app-debug.apk');11opts.appPackage = 'com.example.android.testing.uiautomator.BasicSample';12opts.appActivity = '.MainActivity';13opts.deviceName = 'Android Emulator';14opts.platformName = 'Android';15opts.platformVersion = '6.0';16opts.chromedriverExecutable = path.resolve(__dirname, 'chromedriver');17opts.autoWebview = true;18opts.fullReset = true;19opts.noReset = false;20opts.avd = 'Nexus_5X_API_23';21opts.udid = 'emulator-5554';22opts.androidInstallTimeout = 90000;23opts.androidDeviceReadyTimeout = 90000;24opts.adbPort = 5037;25opts.systemPort = 8200;26opts.chromedriverPort = 9515;27opts.chromedriverExecutableDir = path.resolve(__dirname, 'chromedriver');28opts.chromedriverChromeMappingFile = path.resolve(__dirname, 'mapping.json');29opts.chromedriverUseSystemExecutable = true;30opts.chromedriverExecutable = path.resolve(__dirname, 'chromedriver');31opts.chromedriverArgs = ['--verbose'];

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Appium Android Driver automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful