Best JavaScript code snippet using appium-base-driver
protocol-e2e-specs.js
Source:protocol-e2e-specs.js
1// transpile:mocha2import {3 server, routeConfiguringFunction, errors, JWProxy, BaseDriver4} from '../..';5import { FakeDriver } from './fake-driver';6import _ from 'lodash';7import axios from 'axios';8import chai from 'chai';9import chaiAsPromised from 'chai-as-promised';10import sinon from 'sinon';11import { StatusCodes as HTTPStatusCodes } from 'http-status-codes';12import { createProxyServer } from './helpers';13import {14 MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY15} from '../../lib/constants';16import qs from 'querystring';17let should = chai.should();18chai.use(chaiAsPromised);19const serverPort = 8181;20const baseUrl = `http://localhost:${serverPort}/wd/hub`;21describe('Protocol', function () {22 //TODO: more tests!:23 // Unknown commands should return 40424 describe('direct to driver', function () {25 let d = new FakeDriver();26 it('should return response values directly from the driver', async function () {27 (await d.setUrl('http://google.com')).should.contain('google');28 });29 });30 describe('via express router', function () {31 let mjsonwpServer;32 let driver;33 before(async function () {34 driver = new FakeDriver();35 driver.sessionId = 'foo';36 mjsonwpServer = await server({37 routeConfiguringFunction: routeConfiguringFunction(driver),38 port: serverPort,39 });40 });41 after(async function () {42 await mjsonwpServer.close();43 });44 it('should proxy to driver and return valid jsonwp response', async function () {45 const {data} = await axios({46 url: `${baseUrl}/session/foo/url`,47 method: 'POST',48 data: {url: 'http://google.com'}49 });50 data.should.eql({51 status: 0,52 value: 'Navigated to: http://google.com',53 sessionId: 'foo'54 });55 });56 it('should assume requests without a Content-Type are json requests', async function () {57 const {data} = await axios({58 url: `${baseUrl}/session/foo/url`,59 method: 'POST',60 data: {url: 'http://google.com'},61 });62 data.should.eql({63 status: 0,64 value: 'Navigated to: http://google.com',65 sessionId: 'foo'66 });67 });68 it('should respond to x-www-form-urlencoded as well as json requests', async function () {69 const {data} = await axios({70 url: `${baseUrl}/session/foo/url`,71 headers: {72 'Content-Type': 'application/x-www-form-urlencoded',73 },74 method: 'POST',75 data: qs.stringify({76 url: 'http://google.com',77 }),78 });79 data.should.eql({80 status: 0,81 value: 'Navigated to: http://google.com',82 sessionId: 'foo'83 });84 });85 it('should include url request parameters for methods to use - sessionid', async function () {86 const {data} = await axios({87 url: `${baseUrl}/session/foo/back`,88 method: 'POST',89 data: {},90 });91 data.should.eql({92 status: 0,93 value: 'foo',94 sessionId: 'foo'95 });96 });97 it('should include url request parameters for methods to use - elementid', async function () {98 const {data} = await axios({99 url: `${baseUrl}/session/foo/element/bar/click`,100 method: 'POST',101 data: {},102 });103 data.status.should.equal(0);104 data.value.should.eql(['bar', 'foo']);105 });106 it('should include url req params in the order: custom, element, session', async function () {107 const {data} = await axios({108 url: `${baseUrl}/session/foo/element/bar/attribute/baz`,109 });110 data.status.should.equal(0);111 data.value.should.eql(['baz', 'bar', 'foo']);112 });113 it('should respond with 400 Bad Request if parameters missing', async function () {114 const {data, status} = await axios({115 url: `${baseUrl}/session/foo/url`,116 method: 'POST',117 data: {},118 validateStatus: null,119 });120 status.should.equal(400);121 JSON.stringify(data).should.contain('url');122 });123 it('should reject requests with a badly formatted body and not crash', async function () {124 await axios({125 url: `${baseUrl}/session/foo/url`,126 method: 'POST',127 data: 'oh hello'128 }).should.eventually.be.rejected;129 const {data} = await axios({130 url: `${baseUrl}/session/foo/url`,131 method: 'POST',132 data: {url: 'http://google.com'}133 });134 data.should.eql({135 status: 0,136 value: 'Navigated to: http://google.com',137 sessionId: 'foo'138 });139 });140 it('should get 404 for bad routes', async function () {141 await axios({142 url: `${baseUrl}/blargimarg`,143 }).should.eventually.be.rejectedWith(/404/);144 });145 it('4xx responses should have content-type of application/json', async function () {146 const {headers} = await axios({147 url: `${baseUrl}/blargimargarita`,148 validateStatus: null,149 });150 headers['content-type'].should.include('application/json');151 });152 it('should throw not yet implemented for unfilledout commands', async function () {153 const {status, data} = await axios({154 url: `${baseUrl}/session/foo/element/bar/location`,155 validateStatus: null,156 });157 status.should.equal(501);158 data.should.eql({159 status: 405,160 value: {161 message: 'Method has not yet been implemented'162 },163 sessionId: 'foo'164 });165 });166 it('should throw not implemented for ignored commands', async function () {167 const {status, data} = await axios({168 url: `${baseUrl}/session/foo/buttonup`,169 method: 'POST',170 validateStatus: null,171 data: {},172 });173 status.should.equal(501);174 data.should.eql({175 status: 405,176 value: {177 message: 'Method has not yet been implemented'178 },179 sessionId: 'foo'180 });181 });182 it('should get 400 for bad parameters', async function () {183 await axios({184 url: `${baseUrl}/session/foo/url`,185 method: 'POST',186 data: {}187 }).should.eventually.be.rejectedWith(/400/);188 });189 it('should ignore special extra payload params in the right contexts', async function () {190 await axios({191 url: `${baseUrl}/session/foo/element/bar/value`,192 method: 'POST',193 data: {id: 'baz', sessionId: 'lol', value: ['a']}194 });195 await axios({196 url: `${baseUrl}/session/foo/element/bar/value`,197 method: 'POST',198 data: {id: 'baz'}199 }).should.eventually.be.rejectedWith(/400/);200 // make sure adding the optional 'id' doesn't clobber a route where we201 // have an actual required 'id'202 await axios({203 url: `${baseUrl}/session/foo/frame`,204 method: 'POST',205 data: {id: 'baz'}206 });207 });208 it('should return the correct error even if driver does not throw', async function () {209 const {status, data} = await axios({210 url: `${baseUrl}/session/foo/appium/receive_async_response`,211 method: 'POST',212 data: {response: 'baz'},213 validateStatus: null,214 });215 status.should.equal(500);216 data.should.eql({217 status: 13,218 value: {219 message: 'An unknown server-side error occurred while processing ' +220 'the command. Original error: Mishandled Driver Error'221 },222 sessionId: 'foo'223 });224 });225 describe('w3c sendkeys migration', function () {226 it('should accept value for sendkeys', async function () {227 const {data} = await axios({228 url: `${baseUrl}/session/foo/element/bar/value`,229 method: 'POST',230 data: {value: 'text to type'}231 });232 data.status.should.equal(0);233 data.value.should.eql(['text to type', 'bar']);234 });235 it('should accept text for sendkeys', async function () {236 const {data} = await axios({237 url: `${baseUrl}/session/foo/element/bar/value`,238 method: 'POST',239 data: {text: 'text to type'}240 });241 data.status.should.equal(0);242 data.value.should.eql(['text to type', 'bar']);243 });244 it('should accept value and text for sendkeys, and use value', async function () {245 const {data} = await axios({246 url: `${baseUrl}/session/foo/element/bar/value`,247 method: 'POST',248 data: {value: 'text to type', text: 'text to ignore'}249 });250 data.status.should.equal(0);251 data.value.should.eql(['text to type', 'bar']);252 });253 });254 describe('multiple sets of arguments', function () {255 describe('optional', function () {256 it('should allow moveto with element', async function () {257 const {data} = await axios({258 url: `${baseUrl}/session/foo/moveto`,259 method: 'POST',260 data: {element: '3'}261 });262 data.status.should.equal(0);263 data.value.should.eql(['3', null, null]);264 });265 it('should allow moveto with xoffset/yoffset', async function () {266 const {data} = await axios({267 url: `${baseUrl}/session/foo/moveto`,268 method: 'POST',269 data: {xoffset: 42, yoffset: 17}270 });271 data.status.should.equal(0);272 data.value.should.eql([null, 42, 17]);273 });274 });275 describe('required', function () {276 it('should allow removeApp with appId', async function () {277 const {data} = await axios({278 url: `${baseUrl}/session/foo/appium/device/remove_app`,279 method: 'POST',280 data: {appId: 42}281 });282 data.status.should.equal(0);283 data.value.should.eql(42);284 });285 it('should allow removeApp with bundleId', async function () {286 const {data} = await axios({287 url: `${baseUrl}/session/foo/appium/device/remove_app`,288 method: 'POST',289 data: {bundleId: 42}290 });291 data.status.should.equal(0);292 data.value.should.eql(42);293 });294 });295 });296 describe('default param wrap', function () {297 it('should wrap', async function () {298 const {data} = await axios({299 url: `${baseUrl}/session/foo/touch/perform`,300 method: 'POST',301 data: [{'action': 'tap', 'options': {'element': '3'}}]302 });303 data.value.should.deep.equal([[{'action': 'tap', 'options': {'element': '3'}}], 'foo']);304 });305 it('should not wrap twice', async function () {306 const {data} = await axios({307 url: `${baseUrl}/session/foo/touch/perform`,308 method: 'POST',309 data: {actions: [{'action': 'tap', 'options': {'element': '3'}}]}310 });311 data.value.should.deep.equal([[{'action': 'tap', 'options': {'element': '3'}}], 'foo']);312 });313 });314 describe('create sessions via HTTP endpoint', function () {315 let desiredCapabilities = {a: 'b'};316 let requiredCapabilities = {c: 'd'};317 let capabilities = {e: 'f'};318 let sessionId;319 beforeEach(function () {320 sessionId = null;321 });322 afterEach(async function () {323 if (sessionId) {324 await axios.delete(`${baseUrl}/session/${sessionId}`);325 }326 });327 it('should allow create session with desired caps (MJSONWP)', async function () {328 const {data} = await axios({329 url: `${baseUrl}/session`,330 method: 'POST',331 data: {desiredCapabilities}332 });333 sessionId = data.sessionId;334 data.status.should.equal(0);335 data.value.should.eql(desiredCapabilities);336 });337 it('should allow create session with desired and required caps', async function () {338 const {data} = await axios({339 url: `${baseUrl}/session`,340 method: 'POST',341 data: {342 desiredCapabilities,343 requiredCapabilities344 }345 });346 sessionId = data.sessionId;347 data.status.should.equal(0);348 data.value.should.eql(_.extend({}, desiredCapabilities, requiredCapabilities));349 });350 it('should fail to create session without capabilities or desiredCapabilities', async function () {351 await axios({352 url: `${baseUrl}/session`,353 method: 'POST',354 data: {},355 }).should.eventually.be.rejectedWith(/400/);356 });357 it('should allow create session with capabilities (W3C)', async function () {358 const {data} = await axios({359 url: `${baseUrl}/session`,360 method: 'POST',361 data: {362 capabilities,363 }364 });365 sessionId = data.sessionId;366 should.not.exist(data.status);367 should.not.exist(data.sessionId);368 data.value.capabilities.should.eql(capabilities);369 data.value.sessionId.should.exist;370 });371 it('should fall back to MJSONWP if driver does not support W3C yet', async function () {372 const createSessionStub = sinon.stub(driver, 'createSession').callsFake(function (capabilities) {373 driver.sessionId = null;374 return BaseDriver.prototype.createSession.call(driver, capabilities);375 });376 try {377 let caps = {378 ...desiredCapabilities,379 platformName: 'Fake',380 deviceName: 'Fake',381 };382 // let {status, value, sessionId} = await request({383 const {data} = await axios({384 url: `${baseUrl}/session`,385 method: 'POST',386 data: {387 desiredCapabilities: caps,388 capabilities: {389 alwaysMatch: caps,390 firstMatch: [{}],391 },392 }393 });394 sessionId = data.sessionId;395 should.exist(data.status);396 should.exist(data.sessionId);397 data.value.should.eql(caps);398 } finally {399 createSessionStub.restore();400 }401 });402 describe('w3c endpoints', function () {403 let w3cCaps = {404 alwaysMatch: {405 platformName: 'Fake',406 deviceName: 'Commodore 64',407 },408 firstMatch: [{}],409 };410 let sessionUrl;411 beforeEach(async function () {412 // Start a W3C session413 const {value} = (await axios({414 url: `${baseUrl}/session`,415 method: 'POST',416 data: {417 capabilities: w3cCaps,418 },419 })).data;420 sessionId = value.sessionId;421 sessionUrl = `${baseUrl}/session/${sessionId}`;422 });423 it(`should throw 400 Bad Parameters exception if the parameters are bad`, async function () {424 const {status, data} = await axios({425 url: `${sessionUrl}/actions`,426 method: 'POST',427 validateStatus: null,428 data: {429 bad: 'params',430 }431 });432 status.should.equal(400);433 const {error: w3cError, message, stacktrace} = data.value;434 message.should.match(/Parameters were incorrect/);435 stacktrace.should.match(/protocol.js/);436 w3cError.should.be.a.string;437 w3cError.should.equal(errors.InvalidArgumentError.error());438 });439 it(`should throw 405 exception if the command hasn't been implemented yet`, async function () {440 const {status, data} = await axios({441 url: `${sessionUrl}/actions`,442 method: 'POST',443 validateStatus: null,444 data: {445 actions: [],446 },447 });448 status.should.equal(405);449 const {error: w3cError, message, stacktrace} = data.value;450 message.should.match(/Method has not yet been implemented/);451 stacktrace.should.match(/protocol.js/);452 w3cError.should.be.a.string;453 w3cError.should.equal(errors.NotYetImplementedError.error());454 message.should.match(/Method has not yet been implemented/);455 });456 it(`should throw 500 Unknown Error if the command throws an unexpected exception`, async function () {457 driver.performActions = () => { throw new Error(`Didn't work`); };458 const {status, data} = await axios({459 url: `${sessionUrl}/actions`,460 method: 'POST',461 validateStatus: null,462 data: {463 actions: [],464 }465 });466 status.should.equal(500);467 const {error: w3cError, message, stacktrace} = data.value;468 stacktrace.should.match(/protocol.js/);469 w3cError.should.be.a.string;470 w3cError.should.equal(errors.UnknownError.error());471 message.should.match(/Didn't work/);472 delete driver.performActions;473 });474 it(`should translate element format from MJSONWP to W3C`, async function () {475 const retValue = [476 {477 something: {478 [MJSONWP_ELEMENT_KEY]: 'fooo',479 other: 'bar'480 }481 }, {482 [MJSONWP_ELEMENT_KEY]: 'bar'483 },484 'ignore',485 ];486 const expectedValue = [487 {488 something: {489 [MJSONWP_ELEMENT_KEY]: 'fooo',490 [W3C_ELEMENT_KEY]: 'fooo',491 other: 'bar'492 }493 }, {494 [MJSONWP_ELEMENT_KEY]: 'bar',495 [W3C_ELEMENT_KEY]: 'bar'496 },497 'ignore',498 ];499 const findElementsBackup = driver.findElements;500 driver.findElements = () => retValue;501 const {data} = await axios.post(`${sessionUrl}/elements`, {502 using: 'whatever',503 value: 'whatever',504 });505 data.value.should.eql(expectedValue);506 driver.findElements = findElementsBackup;507 });508 it(`should fail with a 408 error if it throws a TimeoutError exception`, async function () {509 let setUrlStub = sinon.stub(driver, 'setUrl').callsFake(function () {510 throw new errors.TimeoutError;511 });512 const {status, data} = await axios({513 url: `${sessionUrl}/url`,514 method: 'POST',515 validateStatus: null,516 data: {517 url: 'https://example.com/',518 }519 });520 status.should.equal(408);521 const {error: w3cError, message, stacktrace} = data.value;522 stacktrace.should.match(/protocol.js/);523 w3cError.should.be.a.string;524 w3cError.should.equal(errors.TimeoutError.error());525 message.should.match(/An operation did not complete before its timeout expired/);526 setUrlStub.restore();527 });528 it(`should pass with 200 HTTP status code if the command returns a value`, async function () {529 driver.performActions = (actions) => 'It works ' + actions.join('');530 const {status, value, sessionId} = (await axios.post(`${sessionUrl}/actions`, {531 actions: ['a', 'b', 'c'],532 })).data;533 should.not.exist(sessionId);534 should.not.exist(status);535 value.should.equal('It works abc');536 delete driver.performActions;537 });538 describe('jwproxy', function () {539 const port = 56562;540 let server, jwproxy, app;541 beforeEach(function () {542 const res = createProxyServer(sessionId, port);543 server = res.server;544 app = res.app;545 jwproxy = new JWProxy({host: 'localhost', port});546 jwproxy.sessionId = sessionId;547 driver.performActions = async (actions) => await jwproxy.command('/perform-actions', 'POST', actions);548 });549 afterEach(async function () {550 delete driver.performActions;551 await server.close();552 });553 it('should work if a proxied request returns a response with status 200', async function () {554 app.post('/wd/hub/session/:sessionId/perform-actions', (req, res) => {555 res.json({556 sessionId: req.params.sessionId,557 value: req.body,558 status: 0,559 });560 });561 const {status, value, sessionId} = (await axios.post(`${sessionUrl}/actions`, {562 actions: [1, 2, 3],563 })).data;564 value.should.eql([1, 2, 3]);565 should.not.exist(status);566 should.not.exist(sessionId);567 });568 it('should return error if a proxied request returns a MJSONWP error response', async function () {569 app.post('/wd/hub/session/:sessionId/perform-actions', (req, res) => {570 res.status(500).json({571 sessionId,572 status: 6,573 value: 'A problem occurred',574 });575 });576 const {status, data} = await axios({577 url: `${sessionUrl}/actions`,578 method: 'POST',579 validateStatus: null,580 data: {581 actions: [1, 2, 3],582 }583 });584 status.should.equal(HTTPStatusCodes.NOT_FOUND);585 JSON.stringify(data).should.match(/A problem occurred/);586 });587 it('should return W3C error if a proxied request returns a W3C error response', async function () {588 const error = new Error(`Some error occurred`);589 error.w3cStatus = 414;590 const executeCommandStub = sinon.stub(driver, 'executeCommand').returns({591 protocol: 'W3C',592 error,593 });594 const {status, data} = await axios({595 url: `${sessionUrl}/actions`,596 method: 'POST',597 validateStatus: null,598 data: {actions: [1, 2, 3]},599 });600 status.should.equal(414);601 const {error: w3cError, message: errMessage, stacktrace} = data.value;602 w3cError.should.equal('unknown error');603 stacktrace.should.match(/Some error occurred/);604 errMessage.should.equal('Some error occurred');605 executeCommandStub.restore();606 });607 it('should return error if a proxied request returns a MJSONWP error response but HTTP status code is 200', async function () {608 app.post('/wd/hub/session/:sessionId/perform-actions', (req, res) => {609 res.status(200).json({610 sessionId: 'Fake Session Id',611 status: 7,612 value: 'A problem occurred',613 });614 });615 const {status, data} = await axios({616 url: `${sessionUrl}/actions`,617 method: 'POST',618 validateStatus: null,619 data: {620 actions: [1, 2, 3],621 }622 });623 status.should.equal(HTTPStatusCodes.NOT_FOUND);624 const {error: w3cError, message: errMessage, stacktrace} = data.value;625 w3cError.should.equal('no such element');626 errMessage.should.match(/A problem occurred/);627 stacktrace.should.exist;628 });629 it('should return error if a proxied request returns a W3C error response', async function () {630 app.post('/wd/hub/session/:sessionId/perform-actions', (req, res) => {631 res.status(404).json({632 value: {633 error: 'no such element',634 message: 'does not make a difference',635 stacktrace: 'arbitrary stacktrace',636 },637 });638 });639 const {status, data} = await axios({640 url: `${sessionUrl}/actions`,641 method: 'POST',642 validateStatus: null,643 data: {644 actions: [1, 2, 3],645 }646 });647 status.should.equal(HTTPStatusCodes.NOT_FOUND);648 const {error: w3cError, stacktrace} = data.value;649 w3cError.should.equal('no such element');650 stacktrace.should.match(/arbitrary stacktrace/);651 });652 it('should return an error if a proxied request returns a W3C error response', async function () {653 app.post('/wd/hub/session/:sessionId/perform-actions', (req, res) => {654 res.set('Connection', 'close');655 res.status(444).json({656 value: {657 error: 'bogus error code',658 message: 'does not make a difference',659 stacktrace: 'arbitrary stacktrace',660 },661 });662 });663 const {status, data} = await axios({664 url: `${sessionUrl}/actions`,665 method: 'POST',666 validateStatus: null,667 data: {668 actions: [1, 2, 3],669 }670 });671 status.should.equal(HTTPStatusCodes.INTERNAL_SERVER_ERROR);672 const {error: w3cError, stacktrace} = data.value;673 w3cError.should.equal('unknown error');674 stacktrace.should.match(/arbitrary stacktrace/);675 });676 });677 });678 });679 it('should handle commands with no response values', async function () {680 const {data} = await axios({681 url: `${baseUrl}/session/foo/forward`,682 method: 'POST',683 });684 data.should.eql({685 status: 0,686 value: null,687 sessionId: 'foo'688 });689 });690 it('should allow empty string response values', async function () {691 const {data} = await axios({692 url: `${baseUrl}/session/foo/element/bar/text`,693 });694 data.should.eql({695 status: 0,696 value: '',697 sessionId: 'foo'698 });699 });700 it('should send 500 response and an Unknown object for rejected commands', async function () {701 const {status, data} = await axios({702 url: `${baseUrl}/session/foo/refresh`,703 method: 'POST',704 validateStatus: null,705 });706 status.should.equal(500);707 data.should.eql({708 status: 13,709 value: {710 message: 'An unknown server-side error occurred while processing ' +711 'the command. Original error: Too Fresh!'712 },713 sessionId: 'foo'714 });715 });716 it('should not throw UnknownError when known', async function () {717 const {status, data} = await axios({718 url: `${baseUrl}/session/foo`,719 validateStatus: null,720 });721 status.should.equal(404);722 data.should.eql({723 status: 6,724 value: {725 message: 'A session is either terminated or not started'726 },727 sessionId: 'foo'728 });729 });730 });731 describe('session Ids', function () {732 let driver = new FakeDriver();733 let mjsonwpServer;734 before(async function () {735 mjsonwpServer = await server({736 routeConfiguringFunction: routeConfiguringFunction(driver),737 port: serverPort,738 });739 });740 after(async function () {741 await mjsonwpServer.close();742 });743 afterEach(function () {744 driver.sessionId = null;745 });746 it('should return null SessionId for commands without sessionIds', async function () {747 const {data} = await axios({748 url: `${baseUrl}/status`,749 });750 should.equal(data.sessionId, null);751 });752 it('responds with the same session ID in the request', async function () {753 let sessionId = 'Vader Sessions';754 driver.sessionId = sessionId;755 const {data} = await axios({756 url: `${baseUrl}/session/${sessionId}/url`,757 method: 'POST',758 data: {url: 'http://google.com'}759 });760 should.exist(data.sessionId);761 data.sessionId.should.eql(sessionId);762 });763 it('yells if no session exists', async function () {764 let sessionId = 'Vader Sessions';765 const {data, status} = await axios({766 url: `${baseUrl}/session/${sessionId}/url`,767 method: 'POST',768 validateStatus: null,769 data: {url: 'http://google.com'},770 });771 status.should.equal(404);772 data.status.should.equal(6);773 data.value.message.should.contain('session');774 });775 it('yells if invalid session is sent', async function () {776 let sessionId = 'Vader Sessions';777 driver.sessionId = 'recession';778 const {data, status} = await axios({779 url: `${baseUrl}/session/${sessionId}/url`,780 method: 'POST',781 validateStatus: null,782 data: {url: 'http://google.com'},783 });784 status.should.equal(404);785 data.status.should.equal(6);786 data.value.message.should.contain('session');787 });788 it('should have session IDs in error responses', async function () {789 let sessionId = 'Vader Sessions';790 driver.sessionId = sessionId;791 const {data, status} = await axios({792 url: `${baseUrl}/session/${sessionId}/refresh`,793 method: 'POST',794 validateStatus: null,795 });796 status.should.equal(500);797 data.should.eql({798 status: 13,799 value: {800 message: 'An unknown server-side error occurred while processing ' +801 'the command. Original error: Too Fresh!'802 },803 sessionId804 });805 });806 it('should return a new session ID on create', async function () {807 const {data} = await axios({808 url: `${baseUrl}/session`,809 method: 'POST',810 data: {desiredCapabilities: {greeting: 'hello'}, requiredCapabilities: {valediction: 'bye'}}811 });812 should.exist(data.sessionId);813 data.sessionId.indexOf('fakeSession_').should.equal(0);814 data.value.should.eql({greeting: 'hello', valediction: 'bye'});815 });816 });817 describe('via drivers jsonwp proxy', function () {818 let driver;819 let sessionId = 'foo';820 let mjsonwpServer;821 beforeEach(async function () {822 driver = new FakeDriver();823 driver.sessionId = sessionId;824 driver.proxyActive = () => true;825 driver.canProxy = () => true;826 mjsonwpServer = await server({827 routeConfiguringFunction: routeConfiguringFunction(driver),828 port: serverPort,829 });830 });831 afterEach(async function () {832 await mjsonwpServer.close();833 });834 it('should give a nice error if proxying is set but no proxy function exists', async function () {835 driver.canProxy = () => false;836 const {status, data} = await axios({837 url: `${baseUrl}/session/${sessionId}/url`,838 method: 'POST',839 validateStatus: null,840 data: {url: 'http://google.com'},841 });842 status.should.equal(500);843 data.should.eql({844 status: 13,845 value: {846 message: 'An unknown server-side error occurred while processing ' +847 'the command. Original error: Trying to proxy to a JSONWP ' +848 'server but driver is unable to proxy'849 },850 sessionId851 });852 });853 it('should pass on any errors in proxying', async function () {854 driver.proxyReqRes = async function () { // eslint-disable-line require-await855 throw new Error('foo');856 };857 const {status, data} = await axios({858 url: `${baseUrl}/session/${sessionId}/url`,859 method: 'POST',860 validateStatus: null,861 data: {url: 'http://google.com'},862 });863 status.should.equal(500);864 data.should.eql({865 status: 13,866 value: {867 message: 'An unknown server-side error occurred while processing ' +868 'the command. Original error: Could not proxy. Proxy ' +869 'error: foo'870 },871 sessionId872 });873 });874 it('should able to throw ProxyRequestError in proxying', async function () {875 driver.proxyReqRes = async function () { // eslint-disable-line require-await876 let jsonwp = {status: 35, value: 'No such context found.', sessionId: 'foo'};877 throw new errors.ProxyRequestError(`Could not proxy command to remote server. `, jsonwp);878 };879 const {status, data} = await axios({880 url: `${baseUrl}/session/${sessionId}/url`,881 method: 'POST',882 validateStatus: null,883 data: {url: 'http://google.com'},884 });885 status.should.equal(500);886 data.should.eql({887 status: 35,888 value: {889 message: 'No such context found.'890 },891 sessionId: 'foo'892 });893 });894 it('should let the proxy handle req/res', async function () {895 driver.proxyReqRes = async function (req, res) { // eslint-disable-line require-await896 res.status(200).json({custom: 'data'});897 };898 const {status, data} = await axios({899 url: `${baseUrl}/session/${sessionId}/url`,900 method: 'POST',901 data: {url: 'http://google.com'}902 });903 status.should.equal(200);904 data.should.eql({custom: 'data'});905 });906 it('should avoid jsonwp proxying when path matches avoidance list', async function () {907 driver.getProxyAvoidList = () => [['POST', new RegExp('^/session/[^/]+/url$')]];908 const {status, data} = await axios({909 url: `${baseUrl}/session/${sessionId}/url`,910 method: 'POST',911 data: {url: 'http://google.com'},912 });913 status.should.equal(200);914 data.should.eql({915 status: 0,916 value: 'Navigated to: http://google.com',917 sessionId918 });919 });920 it('should fail if avoid proxy list is malformed in some way', async function () {921 async function badProxyAvoidanceList (list) {922 driver.getProxyAvoidList = () => list;923 const {status, data} = await axios({924 url: `${baseUrl}/session/${sessionId}/url`,925 method: 'POST',926 validateStatus: null,927 data: {url: 'http://google.com'},928 });929 status.should.equal(500);930 data.status.should.equal(13);931 data.value.message.should.contain('roxy');932 }933 const lists = [934 'foo',935 [['foo']],936 [['BAR', /lol/]],937 [['GET', 'foo']]938 ];939 for (let list of lists) {940 await badProxyAvoidanceList(list);941 }942 });943 it('should avoid proxying non-session commands even if not in the list', async function () {944 driver.getProxyAvoidList = () => [['POST', new RegExp('')]];945 const {status, data} = await axios({946 url: `${baseUrl}/status`,947 });948 status.should.equal(200);949 data.should.eql({950 status: 0,951 value: "I'm fine",952 sessionId: null953 });954 });955 it('should avoid proxying deleteSession commands', async function () {956 driver.getProxyAvoidList = () => [['POST', new RegExp('')]];957 driver.sessionId.should.equal(sessionId);958 const {status} = await axios.delete(`${baseUrl}/session/${sessionId}`);959 status.should.equal(200);960 should.not.exist(driver.sessionId);961 driver.jwpProxyActive.should.be.false;962 });963 });...
protocol.js
Source:protocol.js
...257 SESSIONS_CACHE.getLogger(req.params.sessionId, currentProtocol).debug(`Calling ` +258 `${driver.constructor.name}.${spec.command}() with args: ` +259 _.truncate(JSON.stringify(args), {length: MAX_LOG_BODY_LENGTH}));260 if (driver.executeCommand) {261 driverRes = await driver.executeCommand(spec.command, ...args);262 } else {263 driverRes = await driver.execute(spec.command, ...args);264 }265 // Get the protocol after executeCommand266 currentProtocol = extractProtocol(driver, req.params.sessionId) || currentProtocol;267 // If `executeCommand` was overridden and the method returns an object268 // with a protocol and value/error property, re-assign the protocol269 if (_.isPlainObject(driverRes) && _.has(driverRes, 'protocol')) {270 currentProtocol = driverRes.protocol || currentProtocol;271 if (driverRes.error) {272 throw driverRes.error;273 }274 driverRes = driverRes.value;275 }276 // unpack createSession response277 if (spec.command === CREATE_SESSION_COMMAND) {278 newSessionId = driverRes[0];279 SESSIONS_CACHE.putSession(newSessionId, currentProtocol);280 SESSIONS_CACHE.getLogger(newSessionId, currentProtocol)281 .debug(`Cached the protocol value '${currentProtocol}' for the new session ${newSessionId}`);282 if (currentProtocol === PROTOCOLS.MJSONWP) {283 driverRes = driverRes[1];284 } else if (currentProtocol === PROTOCOLS.W3C) {285 driverRes = {286 capabilities: driverRes[1],287 };288 }289 }290 driverRes = formatResponseValue(driverRes);291 // delete should not return anything even if successful292 if (spec.command === DELETE_SESSION_COMMAND) {293 SESSIONS_CACHE.getLogger(req.params.sessionId, currentProtocol)294 .debug(`Received response: ${_.truncate(JSON.stringify(driverRes), {length: MAX_LOG_BODY_LENGTH})}`);295 SESSIONS_CACHE.getLogger(req.params.sessionId, currentProtocol).debug('But deleting session, so not returning');296 driverRes = null;297 }298 // if the status is not 0, throw the appropriate error for status code.299 if (util.hasValue(driverRes)) {300 if (util.hasValue(driverRes.status) && !isNaN(driverRes.status) && parseInt(driverRes.status, 10) !== 0) {301 throw errorFromMJSONWPStatusCode(driverRes.status, driverRes.value);302 } else if (_.isPlainObject(driverRes.value) && driverRes.value.error) {303 throw errorFromW3CJsonCode(driverRes.value.error, driverRes.value.message, driverRes.value.stacktrace);304 }305 }306 httpResBody.value = driverRes;307 SESSIONS_CACHE.getLogger(req.params.sessionId || newSessionId, currentProtocol).debug(`Responding ` +308 `to client with driver.${spec.command}() result: ${_.truncate(JSON.stringify(driverRes), {length: MAX_LOG_BODY_LENGTH})}`);309 if (spec.command === DELETE_SESSION_COMMAND) {310 // We don't want to keep the logger instance in the cache311 // after the session is deleted, because it contains the logging history312 // and consumes the memory313 SESSIONS_CACHE.resetLogger(req.params.sessionId);314 }315 } catch (err) {316 // if anything goes wrong, figure out what our response should be317 // based on the type of error that we encountered318 let actualErr = err;319 currentProtocol = currentProtocol || extractProtocol(driver, req.params.sessionId || newSessionId);320 let errMsg = err.stacktrace || err.stack;321 if (!_.includes(errMsg, err.message)) {322 // if the message has more information, add it. but often the message323 // is the first part of the stack trace324 errMsg = `${err.message}${errMsg ? ('\n' + errMsg) : ''}`;325 }326 if (isErrorType(err, errors.ProxyRequestError)) {327 actualErr = err.getActualError();328 } else {329 SESSIONS_CACHE.getLogger(req.params.sessionId || newSessionId, currentProtocol)330 .debug(`Encountered internal error running command: ${errMsg}`);331 }332 if (currentProtocol === PROTOCOLS.W3C) {333 [httpStatus, httpResBody] = getResponseForW3CError(actualErr);334 } else if (currentProtocol === PROTOCOLS.MJSONWP) {335 [httpStatus, httpResBody] = getResponseForJsonwpError(actualErr);336 } else {337 // If it's unknown what the protocol is (like if it's `getStatus` prior to `createSession`), merge the responses338 // together to be protocol-agnostic339 let jsonwpRes = getResponseForJsonwpError(actualErr);340 let w3cRes = getResponseForW3CError(actualErr);341 httpResBody = {342 ...jsonwpRes[1],343 ...w3cRes[1],344 };345 // Use the JSONWP status code (which is usually 500)346 httpStatus = jsonwpRes[0];347 }348 }349 // decode the response, which is either a string or json350 if (_.isString(httpResBody)) {351 res.status(httpStatus).send(httpResBody);352 } else {353 if (newSessionId) {354 if (currentProtocol === PROTOCOLS.W3C) {355 httpResBody.value.sessionId = newSessionId;356 } else {357 httpResBody.sessionId = newSessionId;358 }359 } else {360 httpResBody.sessionId = req.params.sessionId || null;361 }362 // Don't include sessionId in W3C responses363 if (currentProtocol === PROTOCOLS.W3C) {364 delete httpResBody.sessionId;365 }366 httpResBody = formatStatus(httpResBody, httpStatus, currentProtocol);367 res.status(httpStatus).json(httpResBody);368 }369 };370 // add the method to the app371 app[method.toLowerCase()](path, (req, res) => {372 B.resolve(asyncHandler(req, res)).done();373 });374}375function driverShouldDoJwpProxy (driver, req, command) {376 // drivers need to explicitly say when the proxy is active377 if (!driver.proxyActive(req.params.sessionId)) {378 return false;379 }380 // we should never proxy deleteSession because we need to give the containing381 // driver an opportunity to clean itself up382 if (command === 'deleteSession') {383 return false;384 }385 // validate avoidance schema, and say we shouldn't proxy if anything in the386 // avoid list matches our req387 if (driver.proxyRouteIsAvoided(req.params.sessionId, req.method, req.originalUrl)) {388 return false;389 }390 // if it looks like we have an image element in the url (as a route391 // parameter), never proxy. Just look for our image element prefix in allowed392 // positions (either after an 'element' or 'screenshot' path segment), and393 // ensure the prefix is followed by something394 if (IMG_EL_URL_RE.test(req.originalUrl)) {395 return false;396 }397 // also if it looks like we have an image element in the request body (as398 // a JSON parameter), never proxy. Basically check against a regexp of the399 // json string of the body, where we know what the form of an image element400 // must be401 const stringBody = JSON.stringify(req.body);402 if (stringBody && IMG_EL_BODY_RE.test(stringBody)) {403 return false;404 }405 return true;406}407async function doJwpProxy (driver, req, res) {408 SESSIONS_CACHE.getLogger(req.params.sessionId, extractProtocol(driver, req.params.sessionId))409 .info('Driver proxy active, passing request on via HTTP proxy');410 // check that the inner driver has a proxy function411 if (!driver.canProxy(req.params.sessionId)) {412 throw new Error('Trying to proxy to a JSONWP server but driver is unable to proxy');413 }414 try {415 const proxiedRes = await driver.executeCommand('proxyReqRes', req, res, req.params.sessionId);416 if (proxiedRes && proxiedRes.error) throw proxiedRes.error; // eslint-disable-line curly417 } catch (err) {418 if (isErrorType(err, errors.ProxyRequestError)) {419 throw err;420 } else {421 throw new Error(`Could not proxy. Proxy error: ${err.message}`);422 }423 }424}425export {426 Protocol, routeConfiguringFunction, isSessionCommand,427 driverShouldDoJwpProxy, determineProtocol...
mjsonwp.js
Source:mjsonwp.js
...190 // run the driver command wrapped inside the argument validators191 log.info(`Calling ${driver.constructor.name}.${spec.command}() with args: ` +192 _.trunc(JSON.stringify(args), LOG_OBJ_LENGTH));193 if (driver.executeCommand) {194 driverRes = await driver.executeCommand(spec.command, ...args);195 } else {196 driverRes = await driver.execute(spec.command, ...args);197 }198 // unpack createSession response199 if (spec.command === 'createSession') {200 newSessionId = driverRes[0];201 driverRes = driverRes[1];202 }203 // convert undefined to null, but leave all other values the same204 if (_.isUndefined(driverRes)) {205 driverRes = null;206 }207 // delete should not return anything even if successful208 if (spec.command === 'deleteSession') {209 log.debug(`Received response: ${_.trunc(JSON.stringify(driverRes), LOG_OBJ_LENGTH)}`);210 log.debug('But deleting session, so not returning');211 driverRes = null;212 }213 // assuming everything worked, set up the json response object214 httpResBody.status = JSONWP_SUCCESS_STATUS_CODE;215 httpResBody.value = driverRes;216 log.info(`Responding to client with driver.${spec.command}() ` +217 `result: ${_.trunc(JSON.stringify(driverRes), LOG_OBJ_LENGTH)}`);218 } catch (err) {219 let actualErr = err;220 if (!(isErrorType(err, MJSONWPError) ||221 isErrorType(err, errors.BadParametersError))) {222 log.error(`Encountered internal error running command: ${err.stack}`);223 actualErr = new errors.UnknownError(err);224 }225 // if anything goes wrong, figure out what our response should be226 // based on the type of error that we encountered227 [httpStatus, httpResBody] = getResponseForJsonwpError(actualErr);228 }229 // decode the response, which is either a string or json230 if (_.isString(httpResBody)) {231 res.status(httpStatus).send(httpResBody);232 } else {233 if (newSessionId) {234 httpResBody.sessionId = newSessionId;235 } else {236 httpResBody.sessionId = req.params.sessionId || null;237 }238 res.status(httpStatus).json(httpResBody);239 }240 };241 // add the method to the app242 app[method.toLowerCase()](path, (req, res) => {243 B.resolve(asyncHandler(req, res)).done();244 });245}246function driverShouldDoJwpProxy (driver, req, command) {247 // drivers need to explicitly say when the proxy is active248 if (!driver.proxyActive(req.params.sessionId)) {249 return false;250 }251 // we should never proxy deleteSession because we need to give the containing252 // driver an opportunity to clean itself up253 if (command === 'deleteSession') {254 return false;255 }256 // validate avoidance schema, and say we shouldn't proxy if anything in the257 // avoid list matches our req258 let proxyAvoidList = driver.getProxyAvoidList(req.params.sessionId);259 for (let avoidSchema of proxyAvoidList) {260 if (!_.isArray(avoidSchema) || avoidSchema.length !== 2) {261 throw new Error('Proxy avoidance must be a list of pairs');262 }263 let [avoidMethod, avoidPathRegex] = avoidSchema;264 if (!_.contains(['GET', 'POST', 'DELETE'], avoidMethod)) {265 throw new Error(`Unrecognized proxy avoidance method '${avoidMethod}'`);266 }267 if (!(avoidPathRegex instanceof RegExp)) {268 throw new Error('Proxy avoidance path must be a regular expression');269 }270 let normalizedUrl = req.originalUrl.replace(/^\/wd\/hub/, '');271 if (avoidMethod === req.method && avoidPathRegex.test(normalizedUrl)) {272 return false;273 }274 }275 return true;276}277async function doJwpProxy (driver, req, res) {278 log.info('Driver proxy active, passing request on via HTTP proxy');279 // check that the inner driver has a proxy function280 if (!driver.canProxy(req.params.sessionId)) {281 throw new Error('Trying to proxy to a JSONWP server but driver is unable to proxy');282 }283 try {284 await driver.executeCommand('proxyReqRes', req, res, req.params.sessionId);285 } catch (err) {286 throw new Error(`Could not proxy. Proxy error: ${err.message}`);287 }288}...
driver-e2e-specs.js
Source:driver-e2e-specs.js
1// transpile:mocha2import _ from 'lodash';3import B from 'bluebird';4import chai from 'chai';5import chaiAsPromised from 'chai-as-promised';6import wd from 'wd';7import request from 'request-promise';8import { main as appiumServer } from '../lib/main';9import { TEST_FAKE_APP, TEST_HOST, TEST_PORT } from './helpers';10chai.use(chaiAsPromised);11const should = chai.should();12const shouldStartServer = process.env.USE_RUNNING_SERVER !== "0";13const caps = {platformName: "Fake", deviceName: "Fake", app: TEST_FAKE_APP};14describe('FakeDriver - via HTTP', function () {15 let server = null;16 const baseUrl = `http://${TEST_HOST}:${TEST_PORT}/wd/hub/session`;17 before(async function () {18 if (shouldStartServer) {19 let args = {port: TEST_PORT, host: TEST_HOST};20 server = await appiumServer(args);21 }22 });23 after(async function () {24 if (server) {25 await server.close();26 }27 });28 describe('session handling', function () {29 it('should start and stop a session', async function () {30 let driver = wd.promiseChainRemote(TEST_HOST, TEST_PORT);31 let [sessionId] = await driver.init(caps);32 should.exist(sessionId);33 sessionId.should.be.a('string');34 await driver.quit();35 await driver.title().should.eventually.be.rejectedWith(/terminated/);36 });37 it('should be able to run two FakeDriver sessions simultaneously', async function () {38 let driver1 = wd.promiseChainRemote(TEST_HOST, TEST_PORT);39 let [sessionId1] = await driver1.init(caps);40 should.exist(sessionId1);41 sessionId1.should.be.a('string');42 let driver2 = wd.promiseChainRemote(TEST_HOST, TEST_PORT);43 let [sessionId2] = await driver2.init(caps);44 should.exist(sessionId2);45 sessionId2.should.be.a('string');46 sessionId1.should.not.equal(sessionId2);47 await driver1.quit();48 await driver2.quit();49 });50 it('should not be able to run two FakeDriver sessions simultaneously when one is unique', async function () {51 let uniqueCaps = _.clone(caps);52 uniqueCaps.uniqueApp = true;53 let driver1 = wd.promiseChainRemote(TEST_HOST, TEST_PORT);54 let [sessionId1] = await driver1.init(uniqueCaps);55 should.exist(sessionId1);56 sessionId1.should.be.a('string');57 let driver2 = wd.promiseChainRemote(TEST_HOST, TEST_PORT);58 await driver2.init(caps).should.eventually.be.rejected;59 await driver1.quit();60 });61 it('should use the newCommandTimeout of the inner Driver on session creation', async function () {62 let driver = wd.promiseChainRemote(TEST_HOST, TEST_PORT);63 caps.newCommandTimeout = 0.25;64 let [sessionId] = await driver.init(caps);65 should.exist(sessionId);66 await B.delay(250);67 await driver.source().should.eventually.be.rejectedWith(/terminated/);68 });69 it('should accept valid W3C capabilities and start a W3C session', async function () {70 // Try with valid capabilities and check that it returns a session ID71 const w3cCaps = {72 capabilities: {73 alwaysMatch: {platformName: 'Fake'},74 firstMatch: [{'appium:deviceName': 'Fake', 'appium:app': TEST_FAKE_APP}],75 }76 };77 // Create the session78 const {status, value, sessionId} = await request.post({url: baseUrl, json: w3cCaps});79 should.not.exist(status); // Test that it's a W3C session by checking that 'status' is not in the response80 should.not.exist(sessionId);81 value.sessionId.should.be.a.string;82 value.should.exist;83 value.capabilities.should.deep.equal({84 platformName: 'Fake',85 deviceName: 'Fake',86 app: TEST_FAKE_APP,87 });88 // Now use that sessionId to call /screenshot89 const {status:screenshotStatus, value:screenshotValue} = await request({url: `${baseUrl}/${value.sessionId}/screenshot`, json: true});90 should.not.exist(screenshotStatus);91 screenshotValue.should.equal('hahahanotreallyascreenshot');92 // Now use that sessionID to call an arbitrary W3C-only endpoint that isn't implemented to see if it responds with correct error93 const {statusCode, error} = await request.post({url: `${baseUrl}/${value.sessionId}/execute/async`, json: {script: '', args: ['a']}}).should.eventually.be.rejected;94 statusCode.should.equal(404);95 const {error:errorMessage, message, stacktrace} = error.value;96 errorMessage.should.match(/unknown method/);97 message.should.match(/Method has not yet been implemented/);98 stacktrace.should.match(/FakeDriver.executeCommand/);99 // End session100 await request.delete({url: `${baseUrl}/${value.sessionId}`}).should.eventually.be.resolved;101 });102 it('should reject invalid W3C capabilities and respond with a 400 Bad Parameters error', async function () {103 const badW3Ccaps = {104 capabilities: {105 alwaysMatch: {},106 firstMatch: [{'appium:deviceName': 'Fake', 'appium:app': TEST_FAKE_APP}],107 }108 };109 const {statusCode, error} = await request.post({url: baseUrl, json: badW3Ccaps}).should.eventually.be.rejected;110 statusCode.should.equal(400);111 error.value.message.should.match(/can't be blank/);112 });113 it('should accept a combo of W3C and JSONWP capabilities but default to W3C', async function () {114 const combinedCaps = {115 "desiredCapabilities": {116 ...caps,117 },118 "capabilities": {119 "alwaysMatch": {...caps},120 "firstMatch": [{121 w3cParam: 'w3cParam',122 }],123 }124 };125 const {status, value, sessionId} = await request.post({url: baseUrl, json: combinedCaps});126 should.not.exist(status); // If it's a W3C session, should not respond with 'status'127 should.not.exist(sessionId);128 value.sessionId.should.exist;129 value.capabilities.should.deep.equal({130 ...caps,131 w3cParam: 'w3cParam',132 });133 // End session134 await request.delete({ url: `${baseUrl}/${value.sessionId}` }).should.eventually.be.resolved;135 });136 it('should accept a combo of W3C and JSONWP but use JSONWP if desiredCapabilities contains extraneous keys', async function () {137 const combinedCaps = {138 "desiredCapabilities": {139 ...caps,140 automationName: 'Fake',141 anotherParam: 'Hello',142 },143 "capabilities": {144 "alwaysMatch": {...caps},145 "firstMatch": [{146 w3cParam: 'w3cParam',147 }],148 }149 };150 const {status, sessionId, value} = await request.post({url: baseUrl, json: combinedCaps});151 status.should.exist;152 sessionId.should.exist;153 should.not.exist(value.sessionId);154 value.should.deep.equal({155 ...caps,156 automationName: 'Fake',157 anotherParam: 'Hello',158 });159 // End session160 await request.delete({ url: `${baseUrl}/${value.sessionId}` }).should.eventually.be.resolved;161 });162 it('should reject bad W3C capabilities with a BadParametersError (400)', async function () {163 const w3cCaps = {164 "capabilities": {165 "alwaysMatch": {166 ...caps,167 "automationName": "BadAutomationName",168 }169 },170 };171 const {error, statusCode, response} = await request.post({url: baseUrl, json: w3cCaps}).should.eventually.be.rejected;172 response.headers['content-type'].should.match(/application\/json/);173 const {message} = error.value;174 message.should.match(/BadAutomationName not part of/);175 statusCode.should.equal(400);176 });177 it('should accept capabilities that are provided in the firstMatch array', async function () {178 const w3cCaps = {179 "capabilities": {180 "alwaysMatch": {},181 "firstMatch": [{}, {182 ...caps183 }],184 },185 };186 const {value, sessionId, status} = await request.post({url: baseUrl, json: w3cCaps});187 should.not.exist(status);188 should.not.exist(sessionId);189 value.capabilities.should.deep.equal(caps);190 // End session191 await request.delete({ url: `${baseUrl}/${value.sessionId}` }).should.eventually.be.resolved;192 });193 it('should fall back to MJSONWP if w3c caps are invalid', async function () {194 const combinedCaps = {195 "desiredCapabilities": {196 ...caps,197 },198 "capabilities": {199 "alwaysMatch": {},200 "firstMatch": [{}, {201 ...caps,202 deviceName: null,203 }],204 },205 };206 const {value, sessionId, status} = await request.post({url: baseUrl, json: combinedCaps});207 status.should.exist;208 sessionId.should.exist;209 value.should.deep.equal(caps);210 // End session211 await request.delete({ url: `${baseUrl}/${value.sessionId}` }).should.eventually.be.resolved;212 });213 });214});215describe('Logsink', function () {216 let server = null;217 let logs = [];218 let logHandler = (level, message) => {219 logs.push([level, message]);220 };221 let args = {port: TEST_PORT, host: TEST_HOST, logHandler};222 before(async function () {223 server = await appiumServer(args);224 });225 after(async function () {226 await server.close();227 });228 it('should send logs to a logHandler passed in by a parent package', async function () {229 logs.length.should.be.above(1);230 let welcomeIndex = logs[0][1].includes('versions of node') ? 1 : 0;231 logs[welcomeIndex].length.should.equal(2);232 logs[welcomeIndex][1].should.include("Welcome to Appium");233 });...
WebElement.js
Source:WebElement.js
...106 }107 let cmd = new command.Command(command.Name.ELEMENT_EQUALS);108 cmd.setParameter('id', elementA);109 cmd.setParameter('other', elementB);110 return a._driver.executeCommand(cmd).then(r => {111 const {value} = r;112 return value;113 }).catch(e => {114 throw e;115 });116 }117 /**118 * @param keysToSend119 * @returns {Promise}120 */121 sendKeys(keysToSend) {122 const that = this;123 return that._driver.remoteWebDriver.elementIdClick(this._element.ELEMENT).then(() => {124 return that._driver.remoteWebDriver.keys(keysToSend);...
session.js
Source:session.js
1'use strict';2import env from './env';3import B from 'bluebird';4import _ from 'lodash';5import { IosDriver } from '../../..';6import { ALL_COMMANDS } from 'appium-base-driver';7import log from '../../../lib/logger';8const MOCHA_TIMEOUT = 60 * 1000 * (process.env.TRAVIS ? 20 : 4);9const MOCHA_SAFARI_TIMEOUT = MOCHA_TIMEOUT * 2;10class Session {11 constructor (desired = {}, opts = {}) {12 this.desired = desired;13 this.opts = opts;14 this.initialized = false;15 this.rawDriver = new IosDriver(this.opts);16 // wrapping the driver so that the call goes17 // through executeCommand18 this.driver = {};19 for (let c of ALL_COMMANDS) {20 this.driver[c] = function (...args) {21 return this.rawDriver.executeCommand(c, ...args);22 }.bind(this);23 }24 for (let c of ['createSession', 'deleteSession']) {25 this.driver[c] = this.rawDriver[c].bind(this.rawDriver);26 }27 }28 async setUp () {29 let caps = _.clone(this.desired);30 _.defaults(caps, env.CAPS);31 log.debug('caps -->', caps);32 log.debug('opts -->', this.opts);33 let init = async (remainingAttempts) => {34 log.debug(`remainingAttempts --> ${remainingAttempts}`);35 try {36 await this.driver.createSession(caps);37 } catch (err) {38 log.debug(`Init failed with error --> ${err}`);39 remainingAttempts--;40 if (remainingAttempts === 0) {41 throw err;42 } else {43 await this.driver.deleteSession();44 await B.delay(10000);45 await init(remainingAttempts);46 }47 }48 };49 let attempts = this.opts['no-retry'] ? 1 : 3;50 if (env.MAX_RETRY) {51 attempts = Math.min(env.MAX_RETRY, attempts);52 }53 await init(attempts);54 this.initialized = true;55 await this.driver.implicitWait(env.IMPLICIT_WAIT_TIMEOUT);56 }57 async tearDown () {58 if (this.initialized) {59 try {60 await this.driver.deleteSession();61 } catch (err) {62 log.warn("didn't quit cleanly.");63 }64 }65 }66}...
Using AI Code Generation
1var webdriverio = require('webdriverio');2var options = { desiredCapabilities: { browserName: 'chrome' } };3var client = webdriverio.remote(options);4 .init()5 .execute('mobile: shell', {command: 'ls', args: ['-l']})6 .end();7var webdriverio = require('webdriverio');8var options = { desiredCapabilities: { browserName: 'chrome' } };9var client = webdriverio.remote(options);10 .init()11 .execute('mobile: shell', {command: 'ls', args: ['-l']})12 .end();13caps.setCapability("app", "/Users/xxxx/Documents/xxxx/xxxx.ipa");14caps.setCapability("platformName", "iOS");15caps.setCapability("platformVersion", "10.3.1");16caps.setCapability("deviceName", "iPhone 6");17caps.setCapability("browserName", "Safari");18caps.setCapability("autoAcceptAlerts", "true");19caps.setCapability("autoDismissAlerts", "true");20caps.setCapability("fullReset", "true");21caps.setCapability("noReset", "false");22caps.setCapability("newCommandTimeout", "600");23caps.setCapability("automationName", "XCUITest");24driver.context("WEBVIEW");
Using AI Code Generation
1var webdriverio = require('webdriverio');2var options = {3 desiredCapabilities: {4 }5};6var client = webdriverio.remote(options);7 .init()8 .execute('mobile: shell', {command: 'am', args: ['broadcast', '-a', 'io.appium.settings.wifi', '--es', 'setstatus', 'enable']})9 .end();
Using AI Code Generation
1var webdriverio = require('webdriverio'),2 assert = require('assert');3var opts = {4 desiredCapabilities: {5 }6};7 .remote(opts)8 .init()9 .getTitle().then(function(title) {10 console.log('Title was: ' + title);11 })12 .end();13var webdriverio = require('webdriverio'),14 assert = require('assert');15var opts = {16 desiredCapabilities: {17 }18};19 .remote(opts)20 .init()21 .getTitle().then(function(title) {22 console.log('Title was: ' + title);23 })24 .end();25var webdriverio = require('webdriverio'),26 assert = require('assert');27var opts = {28 desiredCapabilities: {29 }30};31 .remote(opts)32 .init()33 .getTitle().then(function(title) {34 console.log('Title was: ' + title);35 })36 .end();37var webdriverio = require('webdriverio'),38 assert = require('assert');39var opts = {40 desiredCapabilities: {41 }42};43 .remote(opts)44 .init()45 .getTitle().then(function(title) {46 console.log('Title was: ' + title);47 })48 .end();49var webdriverio = require('webdriverio'),50 assert = require('
Using AI Code Generation
1var wd = require('wd');2var assert = require('assert');3var desired = {4};5driver.init(desired).then(function () {6 return driver.execute('mobile: shell', {7 });8}).then(function () {9 return driver.execute('mobile: shell', {10 });11}).then(function () {12 return driver.execute('mobile: shell', {13 });14}).then(function () {15 return driver.execute('mobile: shell', {16 });17}).then(function () {18 return driver.execute('mobile: shell', {19 });20}).then(function () {21 return driver.execute('mobile: shell', {22 });23}).then(function () {24 return driver.execute('mobile: shell', {
Using AI Code Generation
1var webdriver = require('selenium-webdriver');2driver.executeCommand('mobile: clearDevice', {'deviceName': 'Samsung Galaxy S4 - 4.2.2 - API 17 - 1080x1920'});3var webdriver = require('selenium-webdriver');4driver.executeScript('mobile: clearDevice', {'deviceName': 'Samsung Galaxy S4 - 4.2.2 - API 17 - 1080x1920'});5var webdriver = require('selenium-webdriver');6driver.execute('mobile: clearDevice', {'deviceName': 'Samsung Galaxy S4 - 4.2.2 - API 17 - 1080x1920'});7var webdriver = require('selenium-webdriver');8driver.executeAsync('mobile: clearDevice', {'deviceName': 'Samsung Galaxy S4 - 4.2.2 - API 17 - 1080x1920'});9var webdriver = require('selenium-webdriver');10driver.executeAsyncScript('mobile: clearDevice', {'deviceName': 'Samsung Galaxy S4 - 4.2.2 -
Using AI Code Generation
1var webdriver = require('selenium-webdriver');2var driver = new webdriver.Builder()3 .withCapabilities({'browserName': 'Chrome'})4 .build();5driver.getSession().then(function(session) {6 console.log(session.getId());7 driver.execute('mobile: shell', {command: 'ls', args: ['-l']}).then(function(stdout) {8 console.log(stdout);9 driver.quit();10 });11});
Using AI Code Generation
1driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mCurrentFocus', args: []})2.then(function (activity) {3console.log(activity);4});5driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})6.then(function (package) {7console.log(package);8});9driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})10.then(function (package) {11console.log(package);12});13driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})14.then(function (package) {15console.log(package);16});17driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})18.then(function (package) {19console.log(package);20});21driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})22.then(function (package) {23console.log(package);24});25driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})26.then(function (package) {27console.log(package);28});29driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})30.then(function (package) {31console.log(package);32});33driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})34.then(function (package) {35console.log(package);36});37driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})38.then(function (package) {39console.log(package);40});41driver.executeCommand('mobile: shell', {command: 'dumpsys window windows | grep -E mFocusedApp', args: []})42.then(function (package
Using AI Code Generation
1var webdriverio = require('webdriverio');2var options = {3 desiredCapabilities: {4 }5};6 .remote(options)7 .init()8 .timeoutsImplicitWait(10000)9 .click('~App')10 .click('~Alert Dialogs')11 .click('~Date Widgets')12 .click('~Inline')13 .click('~2. Dark Theme')14 .timeoutsImplicitWait(10000)15 .click('~App')16 .click('~Alert Dialogs')17 .click('~Date Widgets')18 .click('~Inline')19 .click('~2. Dark Theme')20 .timeoutsImplicitWait(10000)21 .click('~App')22 .click('~Alert Dialogs')23 .click('~Date Widgets')24 .click('~Inline')25 .click('~2. Dark Theme')26 .timeoutsImplicitWait(10000)27 .click('~App')28 .click('~Alert Dialogs')29 .click('~Date Widgets')30 .click('~Inline')31 .click('~2. Dark Theme')32 .timeoutsImplicitWait(10000)33 .click('~App')34 .click('~Alert Dialogs')35 .click('~Date Widgets')36 .click('~Inline')37 .click('~2. Dark Theme')38 .timeoutsImplicitWait(10000)39 .click('~App')40 .click('~Alert Dialogs')41 .click('~Date Widgets')42 .click('~Inline')43 .click('~2. Dark Theme')44 .timeoutsImplicitWait(10000)45 .click('~App')46 .click('~Alert Dialogs')47 .click('~Date Widgets')48 .click('~Inline')49 .click('~2. Dark Theme')50 .timeoutsImplicitWait(10000)51 .click('~App')52 .click('~Alert Dialogs')53 .click('~Date Widgets')54 .click('~Inline')55 .click('~2. Dark Theme')56 .timeoutsImplicitWait(10000)
Using AI Code Generation
1var wd = require('wd'),2 assert = require('assert'),3 _ = require('underscore'),4 path = require('path'),5 request = require('request'),6 serverConfigs = require('./helpers/appium-servers');7var serverConfig = serverConfigs.local;8var desired = {9 app: path.resolve(__dirname, '../../apps/ApiDemos-debug.apk'),10};11var driver = wd.promiseChainRemote(serverConfig);12 .init(desired)13 .setImplicitWaitTimeout(5000)14 .then(function () {15 return driver.execute("mobile: shell", {command: "dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'"});16 })17 .then(function (res) {18 console.log(res);19 })20 .fin(function () { return driver.quit(); })21 .done();
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.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!