How to use proxy.command method in Appium Base Driver

Best JavaScript code snippet using appium-base-driver

proficonf.spec.js

Source:proficonf.spec.js Github

copy

Full Screen

1import { DependencyContainer } from '../src/dependency-container';2import { factoryMockHelper } from './setup';3import { Proficonf } from '../src/proficonf';4describe('Proficonf', () => {5 let proficonf;6 let eventEmitterFactory;7 let eventEmitter;8 let iframeLoaderFactory;9 let iframeLoader;10 let iframeMessengerFactory;11 let iframeMessenger;12 let window;13 let nanoid;14 let eventForwarderFactory;15 let eventForwarder;16 let document;17 let iframeElement;18 let rootElement;19 let interfaceConfigSerializer;20 const messageHandlers = new Map();21 function emitMessage(command, payload) {22 return messageHandlers.get(command)(payload);23 }24 beforeEach(() => {25 eventEmitter = jasmine.createSpyObj('eventEmitter', ['on', 'removeListener', 'once']);26 eventEmitterFactory = factoryMockHelper.create(eventEmitter);27 iframeLoader = jasmine.createSpyObj('iframeLoader', ['loadUrl']);28 iframeLoaderFactory = factoryMockHelper.create(iframeLoader);29 iframeMessenger = jasmine.createSpyObj('iframeMessenger', [30 'initialize',31 'addMessageHandlerOnce',32 'sendMessage',33 'sendRequest',34 ]);35 iframeMessenger.addMessageHandlerOnce.and.callFake((command, handler) => {36 messageHandlers.set(command, handler);37 });38 iframeMessengerFactory = factoryMockHelper.create(iframeMessenger);39 window = { stub: true };40 nanoid = jasmine.createSpy('nanoid').and.returnValue('Fake-Id');41 eventForwarder = jasmine.createSpyObj('eventForwarder', [42 'initialize'43 ]);44 eventForwarderFactory = factoryMockHelper.create(eventForwarder);45 iframeElement = {46 setAttribute: jasmine.createSpy('setAttribute'),47 contentWindow: { contentWindowStub: true },48 style: {}49 };50 document = jasmine.createSpyObj('document', {51 createElement: iframeElement52 });53 rootElement = jasmine.createSpyObj('root', ['appendChild']);54 interfaceConfigSerializer = jasmine.createSpyObj('interfaceConfigSerializer', ['serializeToString', 'serializeToObject']);55 interfaceConfigSerializer.serializeToString.and.returnValue('fake-serialized-ui-config');56 interfaceConfigSerializer.serializeToObject.withArgs({57 removeElements: ['fake-element'],58 customPrimaryColor: 'fake-color',59 customLogoSrc: 'fake-logo-src',60 displayMode: 'fake-display-mode'61 }).and.returnValue('fake-serialized-object');62 DependencyContainer63 .set('eventEmitterFactory', eventEmitterFactory)64 .set('iframeLoaderFactory', iframeLoaderFactory)65 .set('iframeMessengerFactory', iframeMessengerFactory)66 .set('nanoid', nanoid)67 .set('window', window)68 .set('document', document)69 .set('interfaceConfigSerializer', interfaceConfigSerializer)70 .set('eventForwarderFactory', eventForwarderFactory)71 .set('location', {72 protocol: 'http:',73 stub: true74 });75 proficonf = new Proficonf({76 rootElement,77 meetingUrl: 'https://fake.com/j/meeting-alias',78 user: {79 name: 'fake-user-name',80 locale: 'fake-locale'81 },82 iframe: {83 width: 'fake-width',84 height: 'fake-height',85 style: {86 'fake-style-prop': 'fake-style-prop-value',87 }88 },89 ui: {90 disableElements: ['element-1']91 },92 });93 });94 describe('constructor()', () => {95 it('should create new EventEmitter', () => {96 expect(eventEmitterFactory.create).toHaveBeenCalledOnceWith();97 });98 describe('[iframe initialization]', () => {99 it('should create iframe element', () => {100 expect(document.createElement).toHaveBeenCalledOnceWith('iframe');101 });102 it('should initialize default iframe properties', () => {103 expect(iframeElement.allow).toBe('camera; microphone; display-capture; autoplay; clipboard-write; clipboard-read; fullscreen');104 expect(iframeElement.name).toBe('ProficonfEmbeddedRoom-meeting-alias');105 expect(iframeElement.id).toBe('ProficonfEmbeddedRoom-meeting-alias');106 });107 it('should initialize iframe styles', () => {108 expect(iframeElement.style['border']).toBe('0');109 expect(iframeElement.style['fake-style-prop']).toBe('fake-style-prop-value');110 expect(iframeElement.style['width']).toBe('fake-width');111 expect(iframeElement.style['height']).toBe('fake-height');112 });113 it('should set default values for height and width', () => {114 new Proficonf({115 rootElement,116 meetingUrl: 'https://fake.com/j/meeting-alias',117 user: {118 name: 'fake-user-name',119 locale: 'fake-locale'120 },121 iframe: {122 style: {123 'fake-style-prop': 'fake-style-prop-value',124 }125 },126 ui: {127 disableLeftBar: 'fake-disable-left-bar'128 },129 });130 expect(iframeElement.style['width']).toBe('640px');131 expect(iframeElement.style['height']).toBe('450px');132 });133 });134 });135 it('has iframeElement getter', () => {136 expect(proficonf.getIframeElement()).toBe(iframeElement);137 });138 it('has rootElement getter', () => {139 expect(proficonf.getRootElement()).toBe(rootElement);140 });141 it('should allow to subscribe for events', () => {142 proficonf.on('x', 'y');143 expect(eventEmitter.on).toHaveBeenCalledOnceWith('x', 'y');144 });145 it('should allow to subscribe for events once', () => {146 proficonf.once('x', 'y');147 expect(eventEmitter.once).toHaveBeenCalledOnceWith('x', 'y');148 });149 it('should allow to unsubscribe from events', () => {150 proficonf.removeListener('x', 'y');151 expect(eventEmitter.removeListener).toHaveBeenCalledOnceWith('x', 'y');152 });153 describe('join()', () => {154 beforeAll(() => {155 jasmine.clock().install().mockDate(new Date());156 });157 afterAll(() => {158 jasmine.clock().uninstall();159 });160 beforeEach(() => {161 iframeMessenger.sendMessage162 .withArgs('initialize', {})163 .and.callFake(() => emitMessage('app:ready', {}));164 });165 it('should make iframe visible', async () => {166 await proficonf.join();167 expect(rootElement.appendChild).toHaveBeenCalledOnceWith(iframeElement);168 });169 it('should initialize iframeLoader', async () => {170 await proficonf.join();171 expect(iframeLoaderFactory.create).toHaveBeenCalledOnceWith(iframeElement);172 });173 it('should load iframe url', async () => {174 await proficonf.join();175 expect(iframeLoader.loadUrl).toHaveBeenCalledOnceWith(176 'https://fake.com/j/meeting-alias?embedded=1&locale=fake-locale&un=fake-user-name&ui=fake-serialized-ui-config'177 );178 });179 it('should use user token when provided for url', async () => {180 proficonf = new Proficonf({181 rootElement,182 meetingUrl: 'https://fake.com/j/meeting-alias',183 user: {184 token: 'fake-token'185 },186 iframe: {187 width: 'fake-width',188 height: 'fake-height',189 style: {190 'fake-style-prop': 'fake-style-prop-value',191 }192 },193 ui: {194 disableLeftBar: 'fake-disable-left-bar'195 },196 });197 await proficonf.join();198 expect(iframeLoader.loadUrl).toHaveBeenCalledOnceWith(199 'https://fake.com/j/meeting-alias?embedded=1&t=fake-token&ui=fake-serialized-ui-config'200 );201 });202 it('Should createe iframe messenger', async () => {203 await proficonf.join();204 expect(iframeMessengerFactory.create).toHaveBeenCalledOnceWith({205 targetOrigin: 'https://fake.com',206 targetWindow: iframeElement.contentWindow,207 window,208 nanoid,209 correlationId: 'meeting-alias'210 });211 });212 it('Should create eventForwarder', async () => {213 await proficonf.join();214 expect(eventForwarderFactory.create).toHaveBeenCalledOnceWith({215 iframeMessenger,216 eventEmitter217 });218 });219 it('Should initialize eventForwarder', async () => {220 await proficonf.join();221 expect(eventForwarder.initialize).toHaveBeenCalledOnceWith();222 });223 it('Should initialize iframeMessenger', async () => {224 await proficonf.join();225 expect(iframeMessenger.initialize).toHaveBeenCalledOnceWith();226 });227 it('Should send initialize command', async () => {228 await proficonf.join();229 expect(iframeMessenger.sendMessage).toHaveBeenCalledOnceWith('initialize', {});230 });231 it('Should throw on initialization timeout', async () => {232 iframeMessenger.sendMessage233 .withArgs('initialize', {})234 .and.callFake(() => {235 jasmine.clock().tick(300001);236 });237 await expectAsync(238 proficonf.join()239 ).toBeRejectedWith(new Error('App initialization timeout'));240 });241 });242 describe('[joined]', () => {243 beforeEach(() => {244 iframeMessenger.sendMessage245 .withArgs('initialize', {})246 .and.callFake(() => emitMessage('app:ready', {}));247 return proficonf.join();248 });249 async function testCommandProxy({250 command,251 functionName,252 expectedRequestPayload,253 functionArguments = []254 }) {255 it(`should proxy command: ${command}`, async () => {256 const commandArguments = expectedRequestPayload257 ? [command, expectedRequestPayload]258 : [command];259 iframeMessenger.sendRequest.withArgs(...commandArguments).and.resolveTo('fake-result');260 await expectAsync(proficonf[functionName || command](...functionArguments)).toBeResolvedTo('fake-result');261 });262 }263 describe('getParticipants()', () => {264 testCommandProxy({ command: 'getParticipants' });265 });266 describe('getParticipantById()', () => {267 testCommandProxy({268 command: 'getParticipantById',269 functionArguments: ['fake-id'],270 expectedRequestPayload: { id: 'fake-id' }271 });272 });273 describe('blockParticipant()', () => {274 testCommandProxy({275 command: 'blockParticipant',276 functionArguments: ['fake-id'],277 expectedRequestPayload: { id: 'fake-id' }278 });279 });280 describe('unblockParticipant()', () => {281 testCommandProxy({282 command: 'unblockParticipant',283 functionArguments: ['fake-id'],284 expectedRequestPayload: { id: 'fake-id' }285 });286 });287 describe('banParticipant()', () => {288 testCommandProxy({289 command: 'banParticipant',290 functionArguments: ['fake-id'],291 expectedRequestPayload: { id: 'fake-id' }292 });293 });294 describe('setUserName()', () => {295 testCommandProxy({296 command: 'setUserName',297 functionArguments: ['fake-name'],298 expectedRequestPayload: { name: 'fake-name' }299 });300 });301 describe('setUserLocale()', () => {302 testCommandProxy({303 command: 'setUserLocale',304 functionArguments: ['fake-locale'],305 expectedRequestPayload: { locale: 'fake-locale' }306 });307 });308 describe('enableChatForParticipant()', () => {309 testCommandProxy({310 command: 'setChatState',311 functionName: 'enableChatForParticipant',312 functionArguments: ['fake-id'],313 expectedRequestPayload: { participantId: 'fake-id', isChatAllowed: true }314 });315 });316 describe('disableChatForParticipant()', () => {317 testCommandProxy({318 command: 'setChatState',319 functionName: 'disableChatForParticipant',320 functionArguments: ['fake-id'],321 expectedRequestPayload: { participantId: 'fake-id', isChatAllowed: false }322 });323 });324 describe('disableParticipantMicrophone()', () => {325 testCommandProxy({326 command: 'disableParticipantMicrophone',327 functionArguments: ['fake-id'],328 expectedRequestPayload: { id: 'fake-id' }329 });330 });331 describe('askToEnableParticipantMicrophone()', () => {332 testCommandProxy({333 command: 'askToEnableParticipantMicrophone',334 functionArguments: ['fake-id'],335 expectedRequestPayload: { id: 'fake-id' }336 });337 });338 describe('blockParticipantMicrophone()', () => {339 testCommandProxy({340 command: 'blockParticipantMicrophone',341 functionArguments: ['fake-id'],342 expectedRequestPayload: { id: 'fake-id' }343 });344 });345 describe('unblockParticipantMicrophone()', () => {346 testCommandProxy({347 command: 'unblockParticipantMicrophone',348 functionArguments: ['fake-id'],349 expectedRequestPayload: { id: 'fake-id' }350 });351 });352 describe('askToEnableParticipantCamera()', () => {353 testCommandProxy({354 command: 'askToEnableParticipantCamera',355 functionArguments: ['fake-id'],356 expectedRequestPayload: { id: 'fake-id' }357 });358 });359 describe('blockParticipantCamera()', () => {360 testCommandProxy({361 command: 'blockParticipantCamera',362 functionArguments: ['fake-id'],363 expectedRequestPayload: { id: 'fake-id' }364 });365 });366 describe('unblockParticipantCamera()', () => {367 testCommandProxy({368 command: 'unblockParticipantCamera',369 functionArguments: ['fake-id'],370 expectedRequestPayload: { id: 'fake-id' }371 });372 });373 describe('enableCamera()', () => {374 testCommandProxy({375 command: 'enableCamera',376 functionArguments: [{ stub: true }],377 expectedRequestPayload: { constraints: { stub: true } }378 });379 });380 describe('disableCamera()', () => {381 testCommandProxy({382 command: 'disableCamera'383 });384 });385 describe('setCameraDevice()', () => {386 testCommandProxy({387 command: 'setCameraDevice',388 functionArguments: ['fake-device-id'],389 expectedRequestPayload: { deviceId: 'fake-device-id' }390 });391 });392 describe('getCameraState()', () => {393 testCommandProxy({394 command: 'getCameraState',395 });396 });397 describe('enableMicrophone()', () => {398 testCommandProxy({399 command: 'enableMicrophone',400 functionArguments: [{ stub: true }],401 expectedRequestPayload: { constraints: { stub: true } }402 });403 });404 describe('setMicrophoneDevice()', () => {405 testCommandProxy({406 command: 'setMicrophoneDevice',407 functionArguments: ['fake-device'],408 expectedRequestPayload: { deviceId: 'fake-device' }409 });410 });411 describe('disableMicrophone()', () => {412 testCommandProxy({413 command: 'disableMicrophone'414 });415 });416 describe('getDeviceList()', () => {417 testCommandProxy({418 command: 'getDeviceList'419 });420 });421 describe('switchCamera()', () => {422 testCommandProxy({423 command: 'switchCamera'424 });425 });426 describe('switchMicrophone()', () => {427 testCommandProxy({428 command: 'switchMicrophone'429 });430 });431 describe('getMicrophoneState()', () => {432 testCommandProxy({433 command: 'getMicrophoneState'434 });435 });436 describe('startScreenSharing()', () => {437 testCommandProxy({438 command: 'startScreenSharing',439 functionArguments: [{ stub: true }],440 expectedRequestPayload: { constraints: { stub: true } }441 });442 });443 describe('getScreenSharingState()', () => {444 testCommandProxy({445 command: 'getScreenSharingState',446 });447 });448 describe('stopScreenSharing()', () => {449 testCommandProxy({450 command: 'stopScreenSharing',451 });452 });453 describe('disableParticipantMicrophone()', () => {454 testCommandProxy({455 command: 'disableParticipantMicrophone',456 functionArguments: ['fake-id'],457 expectedRequestPayload: { id: 'fake-id' }458 });459 });460 describe('disableParticipantCamera()', () => {461 testCommandProxy({462 command: 'disableParticipantCamera',463 functionArguments: ['fake-id'],464 expectedRequestPayload: { id: 'fake-id' }465 });466 });467 describe('blockParticipantMicrophone()', () => {468 testCommandProxy({469 command: 'blockParticipantMicrophone',470 functionArguments: ['fake-id'],471 expectedRequestPayload: { id: 'fake-id' }472 });473 });474 describe('blockParticipantCamera()', () => {475 testCommandProxy({476 command: 'blockParticipantCamera',477 functionArguments: ['fake-id'],478 expectedRequestPayload: { id: 'fake-id' }479 });480 });481 describe('unblockParticipantCamera()', () => {482 testCommandProxy({483 command: 'unblockParticipantCamera',484 functionArguments: ['fake-id'],485 expectedRequestPayload: { id: 'fake-id' }486 });487 });488 describe('unblockParticipantMicrophone()', () => {489 testCommandProxy({490 command: 'unblockParticipantMicrophone',491 functionArguments: ['fake-id'],492 expectedRequestPayload: { id: 'fake-id' }493 });494 });495 describe('setParticipantRole()', () => {496 testCommandProxy({497 command: 'setParticipantRole',498 functionArguments: ['fake-id', 'fake-role' ],499 expectedRequestPayload: { id: 'fake-id', role: 'fake-role' }500 });501 });502 describe('setScreenLayout()', () => {503 testCommandProxy({504 command: 'setScreenLayout',505 functionArguments: ['fake-mode'],506 expectedRequestPayload: { layout: 'fake-mode' }507 });508 });509 describe('startMeeting()', () => {510 testCommandProxy({511 command: 'startMeeting',512 });513 });514 describe('endMeeting()', () => {515 testCommandProxy({516 command: 'endMeeting',517 });518 });519 describe('startRecording()', () => {520 testCommandProxy({521 command: 'startRecording',522 functionArguments: ['fake-ui-state'],523 expectedRequestPayload: { uiState: 'fake-ui-state' },524 });525 });526 describe('setRecordingConfig()', () => {527 testCommandProxy({528 command: 'setRecordingConfig',529 functionArguments: ['fake-ui-state'],530 expectedRequestPayload: { uiState: 'fake-ui-state' },531 });532 });533 describe('stopRecording()', () => {534 testCommandProxy({535 command: 'stopRecording',536 });537 });538 describe('getRecordingState()', () => {539 testCommandProxy({540 command: 'getRecordingState',541 });542 });543 describe('sendChatMessage()', () => {544 testCommandProxy({545 command: 'sendChatMessage',546 functionArguments: ['fake-message'],547 expectedRequestPayload: { message: 'fake-message' },548 });549 });550 describe('updateUIConfig()', () => {551 testCommandProxy({552 command: 'updateUIConfig',553 functionArguments: [{554 removeElements: ['fake-element'],555 customPrimaryColor: 'fake-color',556 customLogoSrc: 'fake-logo-src',557 displayMode: 'fake-display-mode'558 }],559 expectedRequestPayload: 'fake-serialized-object'560 });561 });562 describe('startStream()', () => {563 testCommandProxy({564 command: 'startStream',565 functionArguments: [{ serverUrl: 'fake-server-url', streamKey: 'fake-stream-key' }],566 expectedRequestPayload: { serverUrl: 'fake-server-url', streamKey: 'fake-stream-key' },567 });568 });569 describe('stopStream()', () => {570 testCommandProxy({571 command: 'stopStream',572 functionArguments: [{ serverUrl: 'fake-server-url', streamKey: 'fake-stream-key' }],573 expectedRequestPayload: { serverUrl: 'fake-server-url', streamKey: 'fake-stream-key' },574 });575 });576 describe('stopAllStreams()', () => {577 testCommandProxy({578 command: 'stopAllStreams',579 });580 });581 });...

Full Screen

Full Screen

gesture.js

Source:gesture.js Github

copy

Full Screen

1import { errors } from 'appium-base-driver';2import { util } from 'appium-support';3import { iosCommands } from 'appium-ios-driver';4import _ from 'lodash';5import log from '../logger';6let helpers = {}, extensions = {}, commands = {};7commands.moveTo = iosCommands.gesture.moveTo;8commands.mobileShake = async function mobileShake () {9 if (!this.isSimulator()) {10 throw new errors.UnknownError('Shake is not supported on real devices');11 }12 await this.opts.device.shake();13};14commands.click = async function click (el) {15 if (!this.isWebContext()) {16 // there are multiple commands that map here, so manually proxy17 return await this.nativeClick(el);18 }19 el = util.unwrapElement(el);20 if ((await this.settings.getSettings()).nativeWebTap) {21 // atoms-based clicks don't always work in safari 722 log.debug('Using native web tap');23 await this.nativeWebTap(el);24 } else {25 let atomsElement = this.useAtomsElement(el);26 return await this.executeAtom('click', [atomsElement]);27 }28};29function gesturesChainToString (gestures, keysToInclude = ['options']) {30 return gestures.map((item) => {31 let otherKeys = _.difference(_.keys(item), ['action']);32 otherKeys = _.isArray(keysToInclude) ? _.intersection(otherKeys, keysToInclude) : otherKeys;33 if (otherKeys.length) {34 return `${item.action}` +35 `(${_.map(otherKeys, (x) => x + '=' + (_.isPlainObject(item[x]) ? JSON.stringify(item[x]) : item[x])).join(', ')})`;36 }37 return item.action;38 }).join('-');39}40commands.performActions = async function performActions (actions) {41 log.debug(`Received the following W3C actions: ${JSON.stringify(actions, null, ' ')}`);42 // This is mandatory, since WDA only supports TOUCH pointer type43 // and Selenium API uses MOUSE as the default one44 const preprocessedActions = actions45 .map((action) => Object.assign({}, action, action.type === 'pointer' ? {46 parameters: {47 pointerType: 'touch'48 }49 } : {}))50 .map((action) => {51 const modifiedAction = _.clone(action) || {};52 // Selenium API unexpectedly inserts zero pauses, which are not supported by WDA53 modifiedAction.actions = (action.actions || [])54 .filter((innerAction) => !(innerAction.type === 'pause' && innerAction.duration === 0));55 return modifiedAction;56 });57 log.debug(`Preprocessed actions: ${JSON.stringify(preprocessedActions, null, ' ')}`);58 return await this.proxyCommand('/actions', 'POST', {actions: preprocessedActions});59};60commands.performTouch = async function performTouch (gestures) {61 log.debug(`Received the following touch action: ${gesturesChainToString(gestures)}`);62 try {63 return await this.proxyCommand('/wda/touch/perform', 'POST', {actions: gestures});64 } catch (e) {65 if (!this.isWebContext()) {66 throw e;67 }68 log.errorAndThrow('The Touch API is aimed for usage in NATIVE context. ' +69 'Consider using "execute" API with custom events trigger script ' +70 `to emulate touch events being in WEBVIEW context. Original error: ${e.message}`);71 }72};73commands.performMultiAction = async function performMultiAction (actions) {74 log.debug(`Received the following multi touch action:`);75 for (let i in actions) {76 log.debug(` ${parseInt(i, 10) + 1}: ${_.map(actions[i], 'action').join('-')}`);77 }78 try {79 return await this.proxyCommand('/wda/touch/multi/perform', 'POST', {actions});80 } catch (e) {81 if (!this.isWebContext()) {82 throw e;83 }84 log.errorAndThrow('The MultiTouch API is aimed for usage in NATIVE context. ' +85 'Consider using "execute" API with custom events trigger script ' +86 `to emulate multitouch events being in WEBVIEW context. Original error: ${e.message}`);87 }88};89commands.nativeClick = async function nativeClick (el) {90 el = util.unwrapElement(el);91 let endpoint = `/element/${el}/click`;92 return await this.proxyCommand(endpoint, 'POST', {});93};94/*95 * See https://github.com/facebook/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBElementCommands.m96 * to get the info about available WDA gestures API97 *98 * See https://developer.apple.com/reference/xctest/xcuielement and99 * https://developer.apple.com/reference/xctest/xcuicoordinate to get the detailed description of100 * all XCTest gestures101*/102helpers.mobileScroll = async function mobileScroll (opts = {}, swipe = false) {103 if (!opts.element) {104 opts.element = await this.findNativeElementOrElements(`class name`, `XCUIElementTypeApplication`, false);105 }106 // WDA supports four scrolling strategies: predication based on name, direction,107 // predicateString, and toVisible, in that order. Swiping requires direction.108 let params = {};109 if (opts.name && !swipe) {110 params.name = opts.name;111 } else if (opts.direction) {112 if (!['up', 'down', 'left', 'right'].includes(opts.direction.toLowerCase())) {113 let msg = 'Direction must be up, down, left or right';114 log.errorAndThrow(msg);115 }116 params.direction = opts.direction;117 } else if (opts.predicateString && !swipe) {118 params.predicateString = opts.predicateString;119 } else if (opts.toVisible && !swipe) {120 params.toVisible = opts.toVisible;121 } else {122 let msg = swipe123 ? 'Mobile swipe requires direction'124 : 'Mobile scroll supports the following strategies: name, direction, predicateString, and toVisible. Specify one of these';125 log.errorAndThrow(msg);126 }127 // we can also optionally pass a distance which appears to be a ratio of128 // screen height, so 1.0 means a full screen's worth of scrolling129 if (!swipe && opts.distance) {130 params.distance = opts.distance;131 }132 let element = util.unwrapElement(opts.element);133 let endpoint = `/wda/element/${element}/${swipe ? 'swipe' : 'scroll'}`;134 return await this.proxyCommand(endpoint, 'POST', params);135};136helpers.mobileSwipe = async function mobileSwipe (opts = {}) {137 return await this.mobileScroll(opts, true);138};139function parseFloatParameter (paramName, paramValue, methodName) {140 if (_.isUndefined(paramValue)) {141 log.errorAndThrow(`"${paramName}" parameter is mandatory for "${methodName}" call`);142 }143 const result = parseFloat(paramValue);144 if (isNaN(result)) {145 log.errorAndThrow(`"${paramName}" parameter should be a valid number. "${paramValue}" is given instead`);146 }147 return result;148}149helpers.mobilePinch = async function mobilePinch (opts = {}) {150 if (!opts.element) {151 opts.element = await this.findNativeElementOrElements(`class name`, `XCUIElementTypeApplication`, false);152 }153 const params = {154 scale: parseFloatParameter('scale', opts.scale, 'pinch'),155 velocity: parseFloatParameter('velocity', opts.velocity, 'pinch')156 };157 const el = util.unwrapElement(opts.element);158 return await this.proxyCommand(`/wda/element/${el}/pinch`, 'POST', params);159};160helpers.mobileDoubleTap = async function mobileDoubleTap (opts = {}) {161 if (opts.element) {162 // Double tap element163 const el = util.unwrapElement(opts.element);164 return await this.proxyCommand(`/wda/element/${el}/doubleTap`, 'POST');165 }166 // Double tap coordinates167 const params = {168 x: parseFloatParameter('x', opts.x, 'doubleTap'),169 y: parseFloatParameter('y', opts.y, 'doubleTap')170 };171 return await this.proxyCommand('/wda/doubleTap', 'POST', params);172};173helpers.mobileTwoFingerTap = async function mobileTwoFingerTap (opts = {}) {174 if (!opts.element) {175 opts.element = await this.findNativeElementOrElements(`class name`, `XCUIElementTypeApplication`, false);176 }177 const el = util.unwrapElement(opts.element);178 return await this.proxyCommand(`/wda/element/${el}/twoFingerTap`, 'POST');179};180helpers.mobileTouchAndHold = async function mobileTouchAndHold (opts = {}) {181 let params = {182 duration: parseFloatParameter('duration', opts.duration, 'touchAndHold')183 };184 if (opts.element) {185 // Long tap element186 const el = util.unwrapElement(opts.element);187 return await this.proxyCommand(`/wda/element/${el}/touchAndHold`, 'POST', params);188 }189 // Long tap coordinates190 params.x = parseFloatParameter('x', opts.x, 'touchAndHold');191 params.y = parseFloatParameter('y', opts.y, 'touchAndHold');192 return await this.proxyCommand('/wda/touchAndHold', 'POST', params);193};194helpers.mobileTap = async function mobileTap (opts = {}) {195 const params = {196 x: parseFloatParameter('x', opts.x, 'tap'),197 y: parseFloatParameter('y', opts.y, 'tap')198 };199 const el = opts.element ? (util.unwrapElement(opts.element)) : '0';200 return await this.proxyCommand(`/wda/tap/${el}`, 'POST', params);201};202helpers.mobileDragFromToForDuration = async function mobileDragFromToForDuration (opts = {}) {203 const params = {204 duration: parseFloatParameter('duration', opts.duration, 'dragFromToForDuration'),205 fromX: parseFloatParameter('fromX', opts.fromX, 'dragFromToForDuration'),206 fromY: parseFloatParameter('fromY', opts.fromY, 'dragFromToForDuration'),207 toX: parseFloatParameter('toX', opts.toX, 'dragFromToForDuration'),208 toY: parseFloatParameter('toY', opts.toY, 'dragFromToForDuration')209 };210 if (opts.element) {211 // Drag element212 const el = util.unwrapElement(opts.element);213 return await this.proxyCommand(`/wda/element/${el}/dragfromtoforduration`, 'POST', params);214 }215 // Drag coordinates216 return await this.proxyCommand('/wda/dragfromtoforduration', 'POST', params);217};218helpers.mobileSelectPickerWheelValue = async function mobileSelectPickerWheelValue (opts = {}) {219 if (!opts.element) {220 log.errorAndThrow('Element id is expected to be set for selectPickerWheelValue method');221 }222 if (!_.isString(opts.order) || !['next', 'previous'].includes(opts.order.toLowerCase())) {223 log.errorAndThrow(`The mandatory 'order' parameter is expected to be equal either to 'next' or 'previous'. ` +224 `'${opts.order}' is given instead`);225 }226 const el = util.unwrapElement(opts.element);227 const params = {order: opts.order};228 if (opts.offset) {229 params.offset = parseFloatParameter('offset', opts.offset, 'selectPickerWheelValue');230 }231 return await this.proxyCommand(`/wda/pickerwheel/${el}/select`, 'POST', params);232};233helpers.getCoordinates = async function getCoordinates (gesture) {234 let el = gesture.options.element;235 // defaults236 let coordinates = {x: 0, y: 0, areOffsets: false};237 let optionX = null;238 if (gesture.options.x) {239 optionX = parseFloatParameter('x', gesture.options.x, 'getCoordinates');240 }241 let optionY = null;242 if (gesture.options.y) {243 optionY = parseFloatParameter('y', gesture.options.y, 'getCoordinates');244 }245 // figure out the element coordinates.246 if (el) {247 let rect = await this.getElementRect(el);248 let pos = {x: rect.x, y: rect.y};249 let size = {w: rect.width, h: rect.height};250 // defaults251 let offsetX = 0;252 let offsetY = 0;253 // get the real offsets254 if (optionX || optionY) {255 offsetX = (optionX || 0);256 offsetY = (optionY || 0);257 } else {258 offsetX = (size.w / 2);259 offsetY = (size.h / 2);260 }261 // apply the offsets262 coordinates.x = pos.x + offsetX;263 coordinates.y = pos.y + offsetY;264 } else {265 // moveTo coordinates are passed in as offsets266 coordinates.areOffsets = (gesture.action === 'moveTo');267 coordinates.x = (optionX || 0);268 coordinates.y = (optionY || 0);269 }270 return coordinates;271};272helpers.applyMoveToOffset = function applyMoveToOffset (firstCoordinates, secondCoordinates) {273 if (secondCoordinates.areOffsets) {274 return {275 x: firstCoordinates.x + secondCoordinates.x,276 y: firstCoordinates.y + secondCoordinates.y,277 };278 } else {279 return secondCoordinates;280 }281};282Object.assign(extensions, helpers, commands);283export { extensions, helpers, commands, gesturesChainToString };...

Full Screen

Full Screen

alert.js

Source:alert.js Github

copy

Full Screen

1import { errors, isErrorType } from 'appium-base-driver';2let commands = {}, helpers = {}, extensions = {};3commands.getAlertText = async function () {4 if (this.isWebContext()) {5 let alert = await this.getAlert();6 let text = await alert.getText();7 return text;8 }9 let method = 'GET';10 let endpoint = `/alert/text`;11 return await this.proxyCommand(endpoint, method);12};13// TODO: WDA does not currently support this natively14commands.setAlertText = async function (text) {15 if (!Array.isArray(text)) {16 text = text.split('');17 }18 if (this.isWebContext()) {19 let alert = await this.getAlert();20 await alert.setText(text);21 return;22 }23 let method = 'POST';24 let endpoint = `/alert/text`;25 return await this.proxyCommand(endpoint, method, text);26};27commands.postAcceptAlert = async function () {28 if (this.isWebContext()) {29 let alert = await this.getAlert();30 if (alert.close) {31 await alert.close();32 } else {33 await alert.ok();34 }35 return;36 }37 let method = 'POST';38 let endpoint = `/alert/accept`;39 return await this.proxyCommand(endpoint, method);40};41commands.postDismissAlert = async function () {42 if (this.isWebContext()) {43 let alert = await this.getAlert();44 if (alert.close) {45 await alert.close();46 } else {47 await alert.cancel();48 }49 return;50 }51 let method = 'POST';52 let endpoint = `/alert/dismiss`;53 return await this.proxyCommand(endpoint, method);54};55helpers.getAlert = async function () {56 let possibleAlert = await this.findNativeElementOrElements('class name', 'XCUIElementTypeScrollView', true);57 if (possibleAlert.length !== 1) throw new errors.NoAlertOpenError();58 let possibleAlertButtons = await this.findNativeElementOrElements('class name', 'XCUIElementTypeButton', true, possibleAlert[0].ELEMENT);59 if (possibleAlertButtons.length < 1 || possibleAlertButtons.length > 2) throw new errors.NoAlertOpenError();60 let assertButtonName = async (button, expectedName) => {61 button = button.ELEMENT ? button.ELEMENT : button;62 let name = await this.proxyCommand(`/element/${button}/attribute/name`, 'GET');63 if (name.toLowerCase() !== expectedName) throw new errors.NoAlertOpenError();64 };65 let alert = possibleAlert[0];66 if (possibleAlertButtons.length === 1) {67 // make sure the button is 'Close'68 let closeButton = possibleAlertButtons[0];69 await assertButtonName(closeButton, 'close');70 alert.close = async () => {71 await this.proxyCommand(`/element/${closeButton.ELEMENT}/click`, 'POST');72 };73 } else {74 // ensure the buttons are 'Cancel' and 'OK'75 let cancelButton = possibleAlertButtons[0];76 await assertButtonName(cancelButton, 'cancel');77 let okButton = possibleAlertButtons[1];78 await assertButtonName(okButton, 'ok');79 alert.cancel = async () => {80 await this.proxyCommand(`/element/${cancelButton.ELEMENT}/click`, 'POST');81 };82 alert.ok = async () => {83 await this.proxyCommand(`/element/${okButton.ELEMENT}/click`, 'POST');84 };85 }86 alert.getText = async () => {87 let textView = await this.findNativeElementOrElements('class name', 'XCUIElementTypeTextView', false, alert.ELEMENT);88 return await this.proxyCommand(`/element/${textView.ELEMENT}/attribute/value`, 'GET');89 };90 alert.setText = async (value) => {91 try {92 let textView = await this.findNativeElementOrElements('class name', 'XCUIElementTypeTextField', false, alert.ELEMENT);93 await this.proxyCommand(`/element/${textView.ELEMENT}/value `, 'POST', {value});94 } catch (err) {95 if (isErrorType(err, errors.NoSuchElementError)) {96 throw new Error('Tried to set text of an alert that was not a prompt');97 }98 throw err;99 }100 };101 return alert;102};103Object.assign(extensions, commands, helpers);104export { commands, helpers };...

Full Screen

Full Screen

proxy-test.js

Source:proxy-test.js Github

copy

Full Screen

1const td = require('testdouble');2const Promise = require('rsvp').Promise;3const path = require('path');4const expect = require('../../helpers/expect');5const contains = td.matchers.contains;6const cordovaPath = path.join('appPath', 'corber', 'cordova');7describe('Proxy Command', () => {8 let proxyCommand;9 let logger;10 let spawn;11 let VerifyInstall;12 beforeEach(() => {13 let project = {14 root: 'appPath',15 isEmberCLIProject: td.function()16 };17 logger = td.object(['warn', 'error', 'success']);18 td.replace('../../../lib/utils/logger', logger);19 let getCordovaPath = td.replace('../../../lib/targets/cordova/utils/get-path');20 td.when(getCordovaPath(project)).thenReturn(cordovaPath);21 VerifyInstall = td.replace('../../../lib/targets/cordova/validators/is-installed');22 td.when(VerifyInstall.prototype.run()).thenReturn(Promise.resolve());23 spawn = td.replace('../../../lib/utils/spawn');24 td.when(spawn('cordova build', [], { shell: true }, contains({ cwd: cordovaPath })))25 .thenReturn(Promise.resolve());26 let ProxyCommand = require('../../../lib/commands/proxy');27 proxyCommand = new ProxyCommand({28 project,29 analytics: td.object(['track', 'trackTiming', 'trackError'])30 });31 });32 afterEach(() => {33 td.reset();34 });35 it('resolves when proxy spawn exits successfully', () => {36 let promise = proxyCommand.validateAndRun(['build']);37 return expect(promise).to.eventually.be.fulfilled;38 });39 it('rejects if install not verified', () => {40 td.when(VerifyInstall.prototype.run())41 .thenReturn(Promise.reject('install not verified'));42 return proxyCommand.validateAndRun(['build']).then(() => {43 td.verify(logger.error(contains('install not verified')));44 });45 });46 it('rejects with error code msg when proxy spawn exits in failure', () => {47 td.when(spawn('cordova build', [], { shell: true }, contains({ cwd: cordovaPath })))48 .thenReturn(Promise.resolve({ code: -1 }));49 return proxyCommand.validateAndRun(['build']).then(() => {50 td.verify(logger.error(contains('\'cordova build\' failed with error code -1')));51 });52 });53 it('warns if a supported corber command is used', () => {54 return proxyCommand.validateAndRun(['build']).then(() => {55 td.verify(logger.warn(contains('bypassed corber command')));56 });57 });58 it('warns if cordova command is unknown', () => {59 td.when(spawn('cordova foo', [], { shell: true }, contains({ cwd: cordovaPath })))60 .thenReturn(Promise.resolve());61 return proxyCommand.validateAndRun(['foo']).then(() => {62 td.verify(logger.warn(contains('unknown Cordova command')));63 });64 });65 it('does not warn if known non-supported corber command is used', () => {66 td.when(spawn('cordova emulate', [], { shell: true }, contains({ cwd: cordovaPath })))67 .thenReturn(Promise.resolve())68 return proxyCommand.validateAndRun(['emulate']).then(() => {69 td.verify(logger.warn(contains('bypassed corber command')), { times: 0 });70 td.verify(logger.warn(contains('unknown Cordova command')), { times: 0 });71 });72 });...

Full Screen

Full Screen

20211005131430-config-without-proxy-command-permission-for-renaming.js

Source:20211005131430-config-without-proxy-command-permission-for-renaming.js Github

copy

Full Screen

1import mongoose from 'mongoose';2import { getModelSafely, getMongoUri, mongoOptions } from '@growi/core';3import loggerFactory from '~/utils/logger';4import Config from '~/server/models/config';5const logger = loggerFactory('growi:migrate:slack-app-integration-rename-keys');6module.exports = {7 async up(db) {8 mongoose.connect(getMongoUri(), mongoOptions);9 const isExist = (await Config.count({ key: 'slackbot:withoutProxy:commandPermission' })) > 0;10 if (!isExist) return;11 const commandPermissionValue = await Config.findOne({ key: 'slackbot:withoutProxy:commandPermission' });12 // do nothing if data is 'null' or null13 if (commandPermissionValue._doc.value === 'null' || commandPermissionValue._doc.value == null) return;14 const commandPermission = JSON.parse(commandPermissionValue._doc.value);15 const newCommandPermission = {16 note: false,17 keep: false,18 };19 Object.entries(commandPermission).forEach((entry) => {20 const [key, value] = entry;21 switch (key) {22 case 'create':23 newCommandPermission.note = value;24 break;25 case 'togetter':26 newCommandPermission.keep = value;27 break;28 default:29 newCommandPermission[key] = value;30 break;31 }32 });33 await Config.findOneAndUpdate(34 { key: 'slackbot:withoutProxy:commandPermission' },35 {36 $set: {37 value: JSON.stringify(newCommandPermission),38 },39 },40 );41 logger.info('Migration has successfully applied');42 },43 async down(db, next) {44 mongoose.connect(getMongoUri(), mongoOptions);45 const isExist = (await Config.count({ key: 'slackbot:withoutProxy:commandPermission' })) > 0;46 if (!isExist) return next();47 const commandPermissionValue = await Config.findOne({ key: 'slackbot:withoutProxy:commandPermission' });48 // do nothing if data is 'null' or null49 if (commandPermissionValue._doc.value === 'null' || commandPermissionValue._doc.value == null) return next();50 const commandPermission = JSON.parse(commandPermissionValue._doc.value);51 const newCommandPermission = {52 create: false,53 togetter: false,54 };55 Object.entries(commandPermission).forEach((entry) => {56 const [key, value] = entry;57 switch (key) {58 case 'note':59 newCommandPermission.create = value;60 break;61 case 'keep':62 newCommandPermission.togetter = value;63 break;64 default:65 newCommandPermission[key] = value;66 break;67 }68 });69 await Config.findOneAndUpdate(70 { key: 'slackbot:withoutProxy:commandPermission' },71 {72 $set: {73 value: JSON.stringify(newCommandPermission),74 },75 },76 );77 next();78 logger.info('Migration rollback has successfully applied');79 },...

Full Screen

Full Screen

proxy-helper-specs.js

Source:proxy-helper-specs.js Github

copy

Full Screen

1import { errors } from 'appium-base-driver';2import sinon from 'sinon';3import chai from 'chai';4import chaiAsPromised from 'chai-as-promised';5import XCUITestDriver from '../../..';6chai.should();7chai.use(chaiAsPromised);8describe('proxy commands', () => {9 let driver = new XCUITestDriver();10 // give the driver a spy-able proxy object11 driver.wda = {jwproxy: {command: () => {}}};12 let proxyStub = sinon.stub(driver.wda.jwproxy, 'command');13 afterEach(() => {14 if (proxyStub) {15 proxyStub.reset();16 }17 });18 describe('proxyCommand', () => {19 it('should send command through WDA', async () => {20 proxyStub.returns({status: 0});21 await driver.proxyCommand('/some/endpoint', 'POST', {some: 'stuff'});22 proxyStub.calledOnce.should.be.true;23 proxyStub.firstCall.args[0].should.eql('/some/endpoint');24 proxyStub.firstCall.args[1].should.eql('POST');25 proxyStub.firstCall.args[2].some.should.eql('stuff');26 });27 it('should throw an error if no endpoint is given', async () => {28 await driver.proxyCommand(null, 'POST', {some: 'stuff'}).should.eventually.be.rejectedWith(/endpoint/);29 proxyStub.callCount.should.eql(0);30 });31 it('should throw an error if no endpoint is given', async () => {32 await driver.proxyCommand('/some/endpoint', null, {some: 'stuff'}).should.eventually.be.rejectedWith(/GET, POST/);33 proxyStub.callCount.should.eql(0);34 });35 it('should throw an error if wda returns an error (even if http status is 200)', async () => {36 proxyStub.returns({status: 13, value: 'WDA error occurred'});37 try {38 await driver.proxyCommand('/some/endpoint', 'POST', {some: 'stuff'});39 } catch (err) {40 err.jsonwpCode.should.eql(13);41 err.message.should.include('WDA error occurred');42 err.should.be.an.instanceof(errors.UnknownError);43 }44 proxyStub.calledOnce.should.be.true;45 });46 it('should not throw an error if no status is returned', async () => {47 proxyStub.returns({value: 'WDA error occurred'});48 await driver.proxyCommand('/some/endpoint', 'POST', {some: 'stuff'});49 proxyStub.calledOnce.should.be.true;50 });51 });...

Full Screen

Full Screen

ProxyManager.js

Source:ProxyManager.js Github

copy

Full Screen

1const execSync = require('child_process').execSync;2let window_proxy_command = function (command) {3 let ret = execSync(__dirname + "/../system/window/WindowProxyManager.exe "+ command);4};5let darwin_proxy_command = function (command) {6 command = command.replace(/\:/, " ");7 let ret = execSync(__dirname + "/../system/darwin/DarwinProxyManager.sh "+ command);8}9let linux_proxy_command = function (command) {10 // TODO: help me , please11 let arr = command.split(" ");12 if (arr[0] == 'start') {13 } else if (arr[0] == 'stop') {14 }15}16function setProxyOn (proxy_address) {17 console.log('attach proxy');18 if (process.platform == 'darwin') {19 darwin_proxy_command("start " + proxy_address);20 } else if (process.platform == 'win32') {21 window_proxy_command("start " + proxy_address);22 } else if (process.platform == 'linux') {23 linux_proxy_command("start " + proxy_address);24 }25}26function setProxyOff () {27 console.log('detach proxy');28 if (process.platform == 'darwin') {29 darwin_proxy_command("stop");30 } else if (process.platform == 'win32') {31 window_proxy_command("stop");32 } else if (process.platform == 'linux') {33 linux_proxy_command("stop");34 }35}36module.exports = {37 on : setProxyOn,38 off : setProxyOff...

Full Screen

Full Screen

lock.js

Source:lock.js Github

copy

Full Screen

1import B from 'bluebird';2let commands = {};3commands.lock = async function lock (seconds) {4 await this.proxyCommand('/wda/lock', 'POST');5 if (isNaN(seconds)) {6 return;7 }8 const floatSeconds = parseFloat(seconds);9 if (floatSeconds <= 0) {10 return;11 }12 await B.delay(floatSeconds * 1000);13 await this.proxyCommand('/wda/unlock', 'POST');14};15commands.unlock = async function unlock () {16 await this.proxyCommand('/wda/unlock', 'POST');17};18commands.isLocked = async function isLocked () {19 return await this.proxyCommand('/wda/locked', 'GET');20};21export { commands };...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wd = require('wd');2var assert = require('assert');3var async = require('async');4var path = require('path');5var fs = require('fs');6var desired = {7 chromeOptions: {8 }9};10var driver = wd.promiseChainRemote('localhost', 4723);11 .init(desired)12 .then(function() {13 })14 .then(function() {15 return driver.execute("mobile: proxyCommand", {16 proxy: {17 },18 });19 })20 .then(function() {21 console.log('done');22 })23 .fin(function() {24 return driver.quit();25 })26 .done();

Full Screen

Using AI Code Generation

copy

Full Screen

1var webdriver = require('selenium-webdriver');2var proxy = require('selenium-webdriver/proxy');3var driver = new webdriver.Builder()4 .forBrowser('chrome')5 .setProxy(proxy.manual({http: 'localhost:8001'}))6 .build();

Full Screen

Using AI Code Generation

copy

Full Screen

1var wd = require('wd');2var assert = require('assert');3var desired = {4};5var driver = wd.remote('localhost', 4723);6driver.init(desired, function() {7 driver.title(function(err, title) {8 console.log("title is: " + title);9 driver.quit();10 });11 });12});13var wd = require('wd');14var assert = require('assert');15var desired = {16};17var driver = wd.remote('localhost', 4723);18driver.init(desired, function() {19 driver.title(function(err, title) {20 console.log("title is: " + title);21 driver.quit();22 });23 });24});25var wd = require('wd');26var assert = require('assert');27var desired = {28};29var driver = wd.remote('localhost', 4723);30driver.init(desired, function() {31 driver.title(function(err, title) {32 console.log("title is: " + title);33 driver.quit();34 });35 });36});37var wd = require('wd');38var assert = require('assert');39var desired = {40};41var driver = wd.remote('localhost', 4723);42driver.init(desired, function() {43 driver.title(function(err, title) {44 console.log("title is: " + title);45 driver.quit();46 });47 });48});49var wd = require('wd');50var assert = require('assert');51var desired = {52};53var driver = wd.remote('localhost', 4723);54driver.init(desired, function() {55 driver.title(function(err, title) {

Full Screen

Using AI Code Generation

copy

Full Screen

1import { AppiumDriver, createDriver, SearchOptions } from "nativescript-dev-appium";2describe("sample scenario", () => {3 let driver: AppiumDriver;4 before(async () => {5 driver = await createDriver();6 });7 after(async () => {8 await driver.quit();9 });10 it("should find an element", async () => {11 const searchOptions: SearchOptions = {12 };13 const helloLabel = await driver.findElementByText("hello", searchOptions);14 await helloLabel.click();15 const textField = await driver.findElementByText("TextField", SearchOptions.exact);16 await textField.sendKeys("hello world");17 });18});19import { AppiumDriver, createDriver, SearchOptions } from "nativescript-dev-appium";20describe("sample scenario", () => {21 let driver: AppiumDriver;22 before(async () => {23 driver = await createDriver();24 });25 after(async () => {26 await driver.quit();27 });28 it("should find an element", async () => {29 const searchOptions: SearchOptions = {30 };31 const helloLabel = await driver.findElementByText("hello", searchOptions);32 await helloLabel.click();33 const textField = await driver.findElementByText("TextField", SearchOptions.exact);34 await textField.sendKeys("hello world");35 const deviceTime = await driver.proxy.command("GET", "/session/:sessionId/appium/device/system_time", []);36 console.log(deviceTime);37 });38});

Full Screen

Using AI Code Generation

copy

Full Screen

1var wd = require('wd');2var assert = require('assert');3var path = require('path');4var desired = {5 "app": path.resolve('path/to/android/app'),6};

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 Base 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