How to use this.setupServer method in Cypress

Best JavaScript code snippet using cypress

index.js

Source:index.js Github

copy

Full Screen

1'use strict'; /* globals describe, it, beforeEach, afterEach, before, after, __dirname, module, global */2const chai = require('chai');3chai.use(require('chai-as-promised'));4chai.should();5global.expect = chai.expect;6const {7 concurrent: { _async, asyncClass, spawn, promisify, },8 fs: { FS, Path, },9} = require('es6lib');10const HttpServer = require('../server/index.js');11const buildExt = require('../../build.js');12const makeTempDir = promisify(require('temp').track().mkdir);13const eventToPromise = require('event-to-promise');14const Https = require('https'), Http = require('http');15const getBody = require('raw-body');16const { Builder, } = require('selenium-webdriver');17const Chrome = require('selenium-webdriver/chrome');18const Firefox = require('selenium-webdriver/firefox');19const { Key, } = require('selenium-webdriver/lib/input');20const Test = asyncClass({21 constructor: function*({ noExt = false, server, } = { }) {22 if (!noExt) {23 // start a web server which the extension will contact during startup24 this.setupServer = new Http.Server(this._onSetupRequest.bind(this));25 (yield eventToPromise(this.setupServer.listen(0), 'listening'));26 this.setupServer.close = promisify(this.setupServer.close);27 this.setupPort = this.setupServer.address().port;28 // build add-on and create a firefox builder with it29 this.tempDir = (yield makeTempDir('add-on-build'));30 this.extName = (yield buildExt({31 icons: false, tld: false, // no need to rebuild these every time32 selenium: { setupPort: this.setupPort, },33 xpi: true,34 outDir: this.tempDir,35 }));36 this.extPath = Path.join(this.tempDir, this.extName +'.xpi');37 }38 this._makeBuilder();39 const log = this.serverLogs = [ ];40 this.server = (yield new HttpServer(Object.assign({41 httpPorts: [ 0, ],42 httpsPorts: [ 0, ],43 upgradePorts: { },44 log(header) { log.push(header); },45 serveFromFS: false,46 favicon: 'ignore',47 }, server)));48 this.pendingStartUp = null; // holds a PromiseCapability with additional options while the browser is starting49 this.browser = null; // reference to the browser driver, while it runs50 return this;51 },52 // start browser with config, wait for the add-on to have started, clear server logs53 start: function*({ storage, }) {54 if (this.browser || this.pendingStartUp) { throw new Error('Browser already started'); }55 const ctx = this.pendingStartUp = { options: { storage, token: Math.random().toString(36).slice(2), }, };56 const done = ctx.promise = this.setupServer ? new Promise((y, n) => { ctx.resolve = y; ctx.reject = n; }) : Promise.resolve();57 this.browser = (yield this.builder.buildAsync());58 (yield done);59 this.pendingStartUp = null;60 this.takeLogs();61 return this.browser;62 },63 // close browser, clear server logs64 stop: function*() { // TODO: add 'force' option65 this.pendingStartUp && (yield this.pendingStartUp.promise);66 this.browser && (yield this.browser.quit());67 this.browser = null;68 this.takeLogs();69 },70 // stop servers, calls .stop(true)71 destroy: function*() {72 (yield this.stop(true));73 this.server && (yield this.server.close());74 this.server = null;75 this.setupServer && (yield this.setupServer.close());76 this.setupServer = null;77 // TODO: destroy builder78 this.builder = null;79 },80 takeLogs() {81 return this.serverLogs.splice(0, Infinity);82 },83 peekLogs() {84 return this.serverLogs.slice(0, Infinity);85 },86/*87 openTab: function*(url) {88 (yield this.browser.executeScript('window.open()')); console.log('opened');89 // TODO: wait?90 (yield this.focusTab()); console.log('focused');91 url != null && (yield this.browser.get(url)); console.log('navigated');92 },93 focusTab: function*(index) {94 const tabs = (yield this.browser.getAllWindowHandles()); console.log('tabs', tabs);95 index = index == null ? tabs.length - 1 : index < 0 ? tabs.length - 1 + length : length;96 index = Math.max(0, Math.min(index, tabs.length - 1)); console.log('index', index);97 (yield this.browser.switchTo().window(tabs[index]));98 },99 closeTab: function*(index) {100 index != null && (yield this.focusTab(index));101 (yield this.browser.close());102 // (yield this.browser.executeScript('window.close()'));103 (yield this.focusTab(0));104 },105*/106 _makeBuilder: function() {107 const chromeOpts = new Chrome.Options();108 chromeOpts.addArguments(`load-extension=${ this.tempDir }/webextension`);109 const ffProfile = new Firefox.Profile();110 this.extPath && ffProfile.addExtension(this.extPath);111 ffProfile.setPreference('extensions.@stop-fingerprinting.sdk.console.logLevel', 'all');112 ffProfile.setPreference('xpinstall.signatures.required', false); // allow unsigned add-on (requires alpha/devEdition/unbranded build)113 ffProfile.setPreference('extensions.checkCompatibility.51.0a', false); // FF51 devEdition114 ffProfile.setPreference('extensions.checkCompatibility.51.0b', false); // FF51 beta115 // disable all caching, for now this is an acceptable way to handle caching in these tests,116 // but it needs to be removed once this extension affects caching itself117 ffProfile.setPreference('browser.cache.disk.enable', false);118 ffProfile.setPreference('browser.cache.memory.enable', false);119 ffProfile.setPreference('browser.cache.offline.enable', false);120 ffProfile.setPreference('network.http.use-cache', false);121 // enable all debugging output within Firefox122 ffProfile.setPreference('extensions.@stop-fingerprinting.sdk.console.logLevel', 'all');123 // these don't seem to work124 ffProfile.setAcceptUntrustedCerts(true);125 ffProfile.setAssumeUntrustedCertIssuer(true);126 const ffOpts = new Firefox.Options();127 // ffOpts.setBinary(new Firefox.Binary().useDevEdition(true));128 ffOpts.setBinary(String.raw`C:\Program Files\Firefox Developer Edition\firefox.exe`);129 ffOpts.setProfile(ffProfile);130 this.builder = new Builder()131 .forBrowser('firefox')132 // .setChromeOptions(chromeOpts)133 .setFirefoxOptions(ffOpts)134 .disableEnvironmentOverrides()135 ;136 },137 _onSetupRequest({ url, body, }, out) {138 const ctx = this.pendingStartUp;139 if (!ctx) {140 console.error('Unexpected startup request:', url);141 out.writeHead(404);142 out.end(); return;143 }144 switch (url.slice(1)) {145 case 'get-options': {146 if (ctx.options) {147 out.write(JSON.stringify(ctx.options));148 } else {149 out.writeHead(404);150 }151 ctx.options = null;152 } break;153 case 'statup-done': {154 ctx.resolve();155 } break;156 case 'statup-failed': {157 getBody(arguments[0])158 .catch(() => '<unknown>')159 .then(error => ctx.reject(new Error('Failed to start extension: '+ error)));160 } break;161 default: {162 console.error('request to unknown path:', url);163 out.writeHead(404);164 }165 }166 out.end();167 },168 [Symbol.toStringTag]: 'Test',169});170Test.register = function(options, done) {171 let test, getTest = new Test(options), port;172 before(_async(function*() {173 this.timeout(6000);174 test = (yield getTest);175 port = test.server.http[test.server.http.length - 1].address().port;176 (yield done(test));177 }));178 beforeEach(_async(function*() {179 test.server.files = { 'reset.html': 'Hey Ho!', };180 (yield test.browser.get(`http://localhost:${ port }/reset.html`));181 test.server.files = null;182 test.takeLogs();183 }));184 afterEach(_async(function*() {185 }));186 after(_async(function*() {187 (yield test.destroy());188 }));189};...

Full Screen

Full Screen

server.js

Source:server.js Github

copy

Full Screen

...49 this._super();50 // Start up the port store.51 portStore.start();52 // Set up the server for master -- proxy (net server) if multiple ports, express if not.53 this.setupServer();54 };55 Server.prototype.setupServer = function() {56 if (this.server || (this.isMaster && this.numProcesses)) {57 return;58 }59 this._setupExpressServer();60 };61 // Slave62 // http://expressjs.com/en/api.html#req.body63 Server.prototype._setupExpressServer = function() {64 this.app = express();65 // Use a cookie parser.66 this.app.use(cookieParser(credentials.cookieSecret));67 // Use a body parser for POSTS.68 this.app.use(bodyParser.json());69 // For parsing application/x-www-form-urlencoded70 this.app.use(bodyParser.urlencoded({extend: true}));71 // Setup the session.72 this.app.use(this._createSession());73 // Setup the public routes.74 this._publicRoutes = [];75 for (var route in this.publicRoutes) {76 var serverPath = this.publicRoutes[route];77 this._publicRoutes.push(route);78 this.app.use(route, express.static(serverPath));79 }80 // Setup the endpoints.81 for (var i=0; i< this.routes.length; i++) {82 this.addEndpoint(this.routes[i]);83 }84 // Callback to call once express app is listening.85 var cb = function() { console.log('Server (', this.__className, ') has started.'); }.bind(this);86 // Have it listen to localhost87 //if (this.isMaster) {88 this.server = this.app.listen(this.port, cb);89 //} else {90 //this.server = this.app.listen(0, 'localhost', cb);91 //}92 // Setup the sockets.93 for (var i=0; i< this.sockets.length; i++) {94 this.addSocket(this.sockets[i]);95 }96 };97 Server.prototype._createSession = function() {98 var RedisStore = connectRedis(expressSession);99 return expressSession({100 'store': new RedisStore({ 'client': redisManager.getCluster() }),101 'secret': credentials.cookieSecret,102 'cookie': this._getCookieParams(),103 'resave': false,104 'saveUninitialized': false105 });106 };107 Server.prototype._getCookieParams = function() {108 // https://github.com/expressjs/session#cookie-options109 // Note: for https sites, read that.110 return {111 'path': '/',112 'httpOnly': true,113 'maxAge': 86400*90, // 90 days114 // 'domain': 'your domain so you can have multiple sub-domains using the same cookie'115 'secure': false116 }117 };118 Server.prototype.addEndpoint = function(endpointOrOptions) {119 if (!endpointOrOptions) return;120 var ep;121 if (endpointOrOptions.prototype.__templateType === 'endpoint') {122 ep = new endpointOrOptions({'app': this.app});123 } else {124 endpointOrOptions['app'] = this.app;125 ep = new Endpoint(endpointOrOptions);126 }127 this.endpoints[ep.url] = ep;128 };129 Server.prototype.addSocket = function(socket) {130 var publicRoute = this._publicRoutes[0] || '';131 var s = new socket({'server': this.server, 'publicRoute': publicRoute, 'app': this.app});132 this.sockets[socket.__id] = s;133 };134 Server.prototype.onFork = function() {135 if (this.isMaster) return;136 this._super.apply(this, arguments);137 this.setupServer();138 };139 // Listening to messages sent by master -- if it's a connection event, then140 // emulate the connection event on the server by emitting the event that master sent us.141 Server.prototype.onProcessMessage = function(message, connection) {142 console.log(message, connection);143 if (this.isMaster || message !== (config.stickySessionEvent || 'sticky-session:connection')) return;144 connection.resume();145 this.server.emit('connection', connection);146 };147 return extend(MicroService, Server);...

Full Screen

Full Screen

pyserialclient.js

Source:pyserialclient.js Github

copy

Full Screen

...7 this.pySerialServer = undefined8 this.socket = undefined9 this.socketIoConnected = false10 this.serialPortConnected = false11 this.setupServer(callbacks)12 this.setupSocketIO(callbacks)13 }14 setupServer(callbacks) {15 let options = {16 mode: 'text',17 pythonOptions: ['-u'],18 scriptPath: path.join(__dirname, 'python')19 }20 if (this.pySerialServer) {21 delete this.pySerialServer22 }23 this.pySerialServer = new PythonShell('pyserialserver.py', options)24 this.pySerialServer.on('message', (message) => {25 console.log("pyserialserver.py - stdout:", message)26 })27 this.pySerialServer.end( (err) => {28 console.log('pyserialserver.py finished')29 // If we didn't receive a disconnect event from socketio, we know30 // the python script crashed. We will attempt to restart it31 if (this.socketIoConnected === true) {32 console.log(err)33 console.log("Ungraceful Disconnection, restarting pyserialserver")34 this.socketIoConnected = false35 this.serialPortConnected = false36 callbacks.onServerError.call(callbacks.obj)37 this.setupServer(callbacks)38 }39 })40 }41 setupSocketIO(callbacks) {42 this.socket = io.connect('http://localhost:8000', {reconnect: true,43 transports: ['websocket']} )44 // Socket.io built in event listneres45 this.socket.on('connect', () => {46 console.log("Socket IO connected.")47 this.socketIoConnected = true48 callbacks.onServerConnected.call(callbacks.obj)49 this.getPortList()50 })51 this.socket.on('disconnect', () => {...

Full Screen

Full Screen

SocketService.js

Source:SocketService.js Github

copy

Full Screen

...65 ],66 methods: [67 function init() {68 if ( ! this.listen ) return;69 this.setupServer(this.port);70 },71 function setupServer(port) {72 var server = this.server = new require('net').Server();73 this.server.on('connection', this.onConnection);74 this.server.on('error', function(error) {75 this.error('foam.net.node.SocketService: Server error', error);76 server.unref();77 if ( error.code === 'EADDRINUSE' ) {78 var port = Math.floor( 10000 + ( Math.random() * 10000 ) );79 this.info('foam.net.node.SocketService: Retrying on port', port);80 this.setupServer(port);81 }82 }.bind(this));83 if ( this.listen ) {84 this.server.on('listening', function() {85 this.listening = true;86 }.bind(this));87 this.server.listen(this.port = port);88 }89 },90 function addSocket(socket) {91 var socketBox = this.RawSocketBox.create({92 socket: socket93 })94 var X = this.creationContext.createSubContext({...

Full Screen

Full Screen

instrumentation-middleware.js

Source:instrumentation-middleware.js Github

copy

Full Screen

1'use strict';2const pino = require('pino')();3const cpuProfiler = require('./v8-cpu-profiler');4const heapProfiler = require('heap-profile');5heapProfiler.start();6class InstrumentationMiddleware {7 constructor(app) {8 this.app = app;9 this.app.addHook('preHandler', async (request, reply) => {10 const route = request.req.url.replace(new RegExp(/\//, 'g'), "");11 if (this.isCpuProfiling(request.req.url))12 cpuProfiler.startProfiling();13 if (this.isMemoryProfiling(request.req.url))14 heapProfiler.write(`./out/${route}-${request.id}-before.heapprofile`);15 if (this.isLogging(request.req.url))16 pino.info(request.req);17 });18 this.app.addHook('onSend', async (request, reply, payload) => {19 const route = request.req.url.replace(new RegExp(/\//, 'g'), "");20 if (this.isLogging(request.req.url))21 pino.info(reply.res);22 if (this.isMemoryProfiling(request.req.url))23 heapProfiler.write(`./out/${route}-${request.id}-after.heapprofile`);24 if (this.isCpuProfiling(request.req.url))25 cpuProfiler.stopProfiling(`./out/${route}-${request.id}.cpuprofile`);26 });27 this.app.addHook('onRoute', (routeOptions) => {28 if (routeOptions.url in this.routes) return;29 this.routes.push(routeOptions.url);30 })31 this.routes = []32 this.instrumenting = {};33 this.setupServer = require('express')();34 this.setupServer.use(require('express').json());35 this.setupServer.get('/', (req, res) => {36 res.json(this.routes.map((route) => {37 return {38 path: route,39 instrumented: this.instrumenting[route] || [],40 instrumentations: this.availableInstrumentations(),41 }42 }));43 });44 this.setupServer.post('/', (req, res) => {45 this.instrumenting = req.body;46 res.send();47 });48 this.setupServer.listen('/tmp/instrumentation.sock')49 }50 isCpuProfiling(route) {51 return this.isInstrumenting(route, 'cpu-profiler');52 }53 isMemoryProfiling(route) {54 return this.isInstrumenting(route, 'memory-profiler');55 }56 isLogging(route) {57 return this.isInstrumenting(route, 'logging');58 }59 isInstrumenting(route, instrumentation) {60 return (this.instrumenting[route] || []).includes(instrumentation);61 }62 availableInstrumentations() {63 return [64 'cpu-profiler',65 'memory-profiler',66 'logging',67 ];68 }69}70module.exports = {71 InstrumentationMiddleware...

Full Screen

Full Screen

Application.js

Source:Application.js Github

copy

Full Screen

1"use strict";2Object.defineProperty(exports, "__esModule", { value: true });3const express = require("express");4const mongoose_1 = require("mongoose");5class Application {6 constructor(opt) {7 this.app = express();8 this.port = opt.port;9 this.db_address = opt.db_address;10 this.middllewaresArray = opt.middllewaresArray;11 this.setUpServer();12 this.setDb();13 this.setUpMiddleWares();14 }15 setUpServer() {16 this.app.listen(this.port, () => {17 console.log("App is listening to port " + this.port);18 });19 }20 setDb() {21 this.db_address && (mongoose_1.default.connect(this.db_address, {22 useNewUrlParser: true, useUnifiedTopology: true23 })24 .then(_ => {25 console.log("Db connected");26 })27 .catch(err => {28 throw err;29 }));30 }31 setUpMiddleWares() {32 if (this.middllewaresArray) {33 this.middllewaresArray.map(m => {34 m.path ? this.app.use(m.path, m.cb) : this.app.use(m.cb);35 });36 }37 }38}...

Full Screen

Full Screen

Room.js

Source:Room.js Github

copy

Full Screen

...5class Room extends Component {6 constructor(props) {7 super(props);8 this.handleClick = this.handleClick.bind(this);9 this.setupServer();10 }11 /**12 * Tirggered when the user selects a room13 */14 handleClick = () => {15 if (16 !this.props.room ||17 (this.props.room !== undefined && this.props.room !== this.props.roomId)18 )19 this.props.socket.emit("updateRoom", this.props.roomId);20 };21 /**22 * Setup the real time server23 */...

Full Screen

Full Screen

setup.js

Source:setup.js Github

copy

Full Screen

1(function() {2 var app = angular.module('rcSetup', []);3 app.controller('rcSetupController', function() {4 this.setupServer = function() {5 // ....6 };7 });8 9 var flower = []...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1import { setupServer } from 'msw/node'2import { handlers } from './handlers'3const server = setupServer(...handlers)4beforeAll(() => server.listen())5afterEach(() => server.resetHandlers())6afterAll(() => server.close())7describe('test', () => {8 it('test', () => {9 })10})11import { rest } from 'msw'12import { setupServer } from 'msw/node'13 rest.get('/test', (req, res, ctx) => {14 return res(15 ctx.json({16 })17 }),18{19}

Full Screen

Using AI Code Generation

copy

Full Screen

1import { setupServer } from 'msw/node'2import { handlers } from './handlers'3export const server = setupServer(...handlers)4before(() => {5 server.listen()6})7after(() => {8 server.close()9})10import { rest } from 'msw'11 rest.get('/greeting', (req, res, ctx) => {12 return res(13 ctx.json({14 }),15 }),16describe('Example', () => {17 it('works', () => {18 cy.request('/greeting').should((response) => {19 expect(response.body.greeting).to.eq('hello there')20 })21 })22})

Full Screen

Using AI Code Generation

copy

Full Screen

1import './commands'2import './commands'3import './commands'4import './commands'5import './commands'6import './commands'7import './commands'8import './commands'9import './commands'10import './commands'11import './commands'12import './commands'13import './commands'14import './commands'15import './commands'16import './commands'17import './commands'18import './commands'

Full Screen

Using AI Code Generation

copy

Full Screen

1import { setupServer } from 'msw/node'2import { handlers } from './handlers'3export const server = setupServer(...handlers)4beforeEach(() => {5 server.listen()6})7afterEach(() => {8 server.resetHandlers()9})10afterAll(() => {11 server.close()12})13import { server } from './test'14Cypress.Commands.add('login', (overrides = {}) => {15 const user = {

Full Screen

Using AI Code Generation

copy

Full Screen

1import {setupServer} from 'cypress-server-mock';2import {mockServer} from 'cypress-server-mock';3const mockServer = require('cypress-server-mock');4const setupServer = require('cypress-server-mock');5const mockServer = require('cypress-server-mock').mockServer;6const setupServer = require('cypress-server-mock').setupServer;7const mockServer = require('cypress-server-mock').mockServer;8const setupServer = require('cypress-server-mock').setupServer;9const mockServer = require('cypress-server-mock').mockServer;10const setupServer = require('cypress-server-mock').setupServer;11const mockServer = require('cypress-server-mock').mockServer;12const setupServer = require('cypress-server-mock').setupServer;13const mockServer = require('cypress-server-mock').mockServer;14const setupServer = require('cypress-server-mock').setupServer;15const mockServer = require('cypress-server-mock').mockServer;16const setupServer = require('cypress-server-mock').setupServer;17const mockServer = require('cypress-server-mock').mockServer;18const setupServer = require('cypress-server-mock').setupServer;19const mockServer = require('cypress-server-mock').mockServer;20const setupServer = require('cypress-server-mock').setupServer;

Full Screen

Using AI Code Generation

copy

Full Screen

1describe("My First Test", function() {2 it("Visits the Kitchen Sink", function() {3 cy.contains("type").click();4 cy.url().should("include", "/commands/actions");5 cy.get(".action-email")6 .type("

Full Screen

Using AI Code Generation

copy

Full Screen

1import { setupServer } from 'msw/node'2import { rest } from 'msw'3 { id: 1, text: 'todo 1', completed: false },4 { id: 2, text: 'todo 2', completed: false },5const server = setupServer(6 rest.get('/api/todos', (req, res, ctx) => {7 return res(ctx.json(todos))8 }),9 rest.post('/api/todos', (req, res, ctx) => {10 todos.push(todo)11 return res(ctx.status(201), ctx.json(todo))12 })13beforeAll(() => server.listen())14afterEach(() => server.resetHandlers())15afterAll(() => server.close())16it('should fetch todos', () => {17 cy.request('/api/todos').should((response) => {18 expect(response.body).to.deep.equal(todos)19 })20})21it('should create a todo', () => {22 const todo = { text: 'todo 3', completed: false }23 cy.request('POST', '/api/todos', todo).should((response) => {24 expect(response.status).to.equal(201)25 expect(response.body).to.deep.equal({26 })27 })28 cy.request('/api/todos').should((response) => {29 expect(response.body).to.deep.equal([...todos, todo])30 })31})

Full Screen

Using AI Code Generation

copy

Full Screen

1const PORT = 3000;2describe("My First Test", function() {3 beforeEach(function() {4 this.setupServer();5 });6 it("Visits the app root url", function() {7 cy.visit("/");8 cy.title().should("include", "Welcome to Your Vue.js App");9 });10});11describe("My Second Test", function() {12 it("Visits the app root url", function() {13 cy.visit("/");

Full Screen

Cypress Tutorial

Cypress is a renowned Javascript-based open-source, easy-to-use end-to-end testing framework primarily used for testing web applications. Cypress is a relatively new player in the automation testing space and has been gaining much traction lately, as evidenced by the number of Forks (2.7K) and Stars (42.1K) for the project. LambdaTest’s Cypress Tutorial covers step-by-step guides that will help you learn from the basics till you run automation tests on LambdaTest.

Chapters:

  1. What is Cypress? -
  2. Why Cypress? - Learn why Cypress might be a good choice for testing your web applications.
  3. Features of Cypress Testing - Learn about features that make Cypress a powerful and flexible tool for testing web applications.
  4. Cypress Drawbacks - Although Cypress has many strengths, it has a few limitations that you should be aware of.
  5. Cypress Architecture - Learn more about Cypress architecture and how it is designed to be run directly in the browser, i.e., it does not have any additional servers.
  6. Browsers Supported by Cypress - Cypress is built on top of the Electron browser, supporting all modern web browsers. Learn browsers that support Cypress.
  7. Selenium vs Cypress: A Detailed Comparison - Compare and explore some key differences in terms of their design and features.
  8. Cypress Learning: Best Practices - Take a deep dive into some of the best practices you should use to avoid anti-patterns in your automation tests.
  9. How To Run Cypress Tests on LambdaTest? - Set up a LambdaTest account, and now you are all set to learn how to run Cypress tests.

Certification

You can elevate your expertise with end-to-end testing using the Cypress automation framework and stay one step ahead in your career by earning a Cypress certification. Check out our Cypress 101 Certification.

YouTube

Watch this 3 hours of complete tutorial to learn the basics of Cypress and various Cypress commands with the Cypress testing at LambdaTest.

Run Cypress 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