How to use emulate method in chromy

Best JavaScript code snippet using chromy

drag-and-drop-integrated.spec.ts

Source:drag-and-drop-integrated.spec.ts Github

copy

Full Screen

1/*2 * Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved.3 * This software is released under MIT license.4 * The full license information can be found in LICENSE in the root directory of this project.5 */6import { Component, ElementRef } from '@angular/core';7import { ComponentFixture, TestBed } from '@angular/core/testing';8import { By } from '@angular/platform-browser';9import { NoopAnimationsModule } from '@angular/platform-browser/animations';10import { ClrDragAndDropModule } from './drag-and-drop.module';11import { ClrDraggable } from './draggable/draggable';12import { emulateDragEvent } from './helpers.spec';13// Here, we test drag and drop directives and components altogether in an integrated way.14// Unlike other .spec test files, we use actual events in the following tests.15const MOCK_DATA_PAYLOAD = {16 data: 'test_drag_data_transfer',17};18export default function(): void {19 describe('With ClrDraggable', function() {20 let fixture: ComponentFixture<any>;21 let testComponent: WithDraggableTest;22 let testElement: HTMLElement;23 let draggable: ElementRef;24 beforeEach(function() {25 TestBed.configureTestingModule({26 imports: [ClrDragAndDropModule, NoopAnimationsModule],27 declarations: [WithDraggableTest],28 });29 fixture = TestBed.createComponent(WithDraggableTest);30 testComponent = fixture.componentInstance;31 testElement = fixture.nativeElement;32 draggable = fixture.debugElement.queryAll(By.directive(ClrDraggable))[1];33 fixture.detectChanges();34 });35 afterEach(function() {36 fixture.destroy();37 });38 describe('Template API', function() {39 const checkStaticProps = function(dragEvent: any) {40 // the following properties don't change accross different drag events41 // and expected to stay the same in all of them.42 expect(dragEvent.group).toBe('draggable-1');43 expect(dragEvent.dragDataTransfer).toEqual(MOCK_DATA_PAYLOAD);44 };45 it('should emit event with proper properties on dragStart', function() {46 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);47 emulateDragEvent('mousemove', 10, 20);48 checkStaticProps(testComponent.dragStartEvent);49 expect(testComponent.dragStartEvent.dragPosition).toEqual({ pageX: 10, pageY: 20, moveX: 10, moveY: 20 });50 expect(testComponent.dragStartEvent.dropPointPosition).toBeUndefined();51 });52 it('should emit event with proper properties on dragMove', function() {53 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);54 emulateDragEvent('mousemove', 10, 20);55 emulateDragEvent('mousemove', 100, 200);56 checkStaticProps(testComponent.dragMoveEvent);57 expect(testComponent.dragMoveEvent.dragPosition).toEqual({ pageX: 100, pageY: 200, moveX: 100, moveY: 200 });58 // offsetX = dragStart.pageX - draggable.pageX59 // offsetY = dragStart.pageY - draggable.pageY60 // dropPointPosition.pageX = dragMove.pageX + draggable.width/2 - offsetX61 // dropPointPosition.pageY = dragMove.pageY + draggable.height/2 - offsetY62 expect(testComponent.dragMoveEvent.dropPointPosition).toEqual({ pageX: 140, pageY: 205 });63 });64 it('should emit event with proper properties on dragEnter', function() {65 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);66 emulateDragEvent('mousemove', 10, 20);67 emulateDragEvent('mousemove', 500, 300);68 checkStaticProps(testComponent.dragEnterEvent);69 expect(testComponent.dragEnterEvent.dragPosition).toEqual({ pageX: 500, pageY: 300, moveX: 500, moveY: 300 });70 expect(testComponent.dragEnterEvent.dropPointPosition).toEqual({ pageX: 540, pageY: 305 });71 });72 it('should emit event with proper properties on dragLeave', function() {73 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);74 emulateDragEvent('mousemove', 10, 20);75 emulateDragEvent('mousemove', 500, 300);76 emulateDragEvent('mousemove', 100, 200);77 checkStaticProps(testComponent.dragLeaveEvent);78 expect(testComponent.dragLeaveEvent.dragPosition).toEqual({ pageX: 100, pageY: 200, moveX: 100, moveY: 200 });79 expect(testComponent.dragLeaveEvent.dropPointPosition).toEqual({ pageX: 140, pageY: 205 });80 });81 it('should emit event with proper properties on dragEnd', function() {82 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);83 emulateDragEvent('mousemove', 10, 20);84 emulateDragEvent('mousemove', 500, 300);85 emulateDragEvent('mousemove', 100, 200);86 emulateDragEvent('mouseup', 100, 200);87 checkStaticProps(testComponent.dragEndEvent);88 expect(testComponent.dragEndEvent.dragPosition).toEqual({ pageX: 100, pageY: 200, moveX: 100, moveY: 200 });89 expect(testComponent.dragEndEvent.dropPointPosition).toEqual({ pageX: 140, pageY: 205 });90 });91 it('should emit event with proper properties on drop', function() {92 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);93 emulateDragEvent('mousemove', 10, 20);94 emulateDragEvent('mousemove', 500, 300);95 emulateDragEvent('mouseup', 500, 300);96 checkStaticProps(testComponent.dropEvent);97 expect(testComponent.dropEvent.dragPosition).toEqual({ pageX: 500, pageY: 300, moveX: 500, moveY: 300 });98 expect(testComponent.dropEvent.dropPointPosition).toEqual({ pageX: 540, pageY: 305 });99 });100 });101 describe('View', function() {102 it('should have proper number of draggables and droppables', function() {103 expect(testElement.querySelectorAll('.draggable').length).toBe(2);104 expect(testElement.querySelectorAll('.droppable').length).toBe(3);105 expect(testElement.querySelectorAll('clr-draggable-ghost').length).toBe(0);106 });107 it('should add being-dragged class to draggable being dragged', function() {108 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);109 emulateDragEvent('mousemove', 10, 20);110 fixture.detectChanges();111 const draggableQueries = testElement.querySelectorAll('.being-dragged');112 expect(draggableQueries.length).toBe(1, `Only one draggable should have the "being-dragged" class at a time.`);113 expect(draggableQueries[0]).toBe(draggable.nativeElement);114 });115 it('should show ghost next to proper draggable on dragStart', function() {116 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);117 emulateDragEvent('mousemove', 10, 20);118 fixture.detectChanges();119 const draggableQueries = testElement.querySelectorAll('.being-dragged');120 expect(draggableQueries[0].nextElementSibling.tagName).toBe('CLR-DRAGGABLE-GHOST');121 expect(draggableQueries[0].nextElementSibling.classList.contains('draggable-ghost')).toBeTruthy();122 });123 it('should add draggable-match to all droppables with matching group', function() {124 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);125 emulateDragEvent('mousemove', 10, 20);126 fixture.detectChanges();127 const matchingDroppablesQueries = testElement.querySelectorAll('.draggable-match');128 expect(matchingDroppablesQueries.length).toBe(2);129 const overDroppablesQueries = testElement.querySelectorAll('.draggable-over');130 expect(overDroppablesQueries.length).toBe(0);131 });132 it('should add draggable-over to proper droppable on dragEnter', function() {133 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);134 emulateDragEvent('mousemove', 10, 20);135 emulateDragEvent('mousemove', 500, 300);136 fixture.detectChanges();137 const matchingDroppablesQueries = testElement.querySelectorAll('.draggable-match');138 expect(matchingDroppablesQueries.length).toBe(2);139 const overDroppablesQueries = testElement.querySelectorAll('.draggable-over');140 expect(overDroppablesQueries.length).toBe(1);141 });142 it('should remove draggable-over to on dragLeave', function() {143 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);144 emulateDragEvent('mousemove', 10, 20);145 emulateDragEvent('mousemove', 500, 300);146 emulateDragEvent('mousemove', 100, 200);147 fixture.detectChanges();148 const matchingDroppablesQueries = testElement.querySelectorAll('.draggable-match');149 expect(matchingDroppablesQueries.length).toBe(2);150 const overDroppablesQueries = testElement.querySelectorAll('.draggable-over');151 expect(overDroppablesQueries.length).toBe(0);152 });153 it('should have no droppables with draggable-over and draggable-match class on dragEnd', function() {154 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);155 emulateDragEvent('mousemove', 10, 20);156 emulateDragEvent('mousemove', 500, 300);157 emulateDragEvent('mousemove', 100, 200);158 emulateDragEvent('mouseup', 100, 200);159 fixture.detectChanges();160 const matchingDroppablesQueries = testElement.querySelectorAll('.draggable-match');161 expect(matchingDroppablesQueries.length).toBe(0);162 const overDroppablesQueries = testElement.querySelectorAll('.draggable-over');163 expect(overDroppablesQueries.length).toBe(0);164 });165 it('should add dropped class to ghost on successful drop event', function() {166 emulateDragEvent('mousedown', 0, 0, draggable.nativeElement);167 emulateDragEvent('mousemove', 10, 20);168 const ghost = testElement.querySelector('.draggable-ghost');169 emulateDragEvent('mousemove', 500, 300);170 expect(ghost.classList.contains('dropped')).toBeFalsy();171 emulateDragEvent('mouseup', 500, 300);172 expect(ghost.classList.contains('dropped')).toBeTruthy();173 });174 });175 });176}177@Component({178 styles: [179 `180 .droppable-test-state {181 position: absolute;182 left: 400px;183 top: 200px;184 width: 200px;185 height: 400px;186 }187 .draggable-test-state {188 position: absolute;189 left: 0px;190 top: 0px;191 width: 100px;192 height: 50px;193 }194 `,195 ],196 template: `<button clrDraggable>draggable button</button><div class="draggable-test-state" clrGroup="draggable-1" [clrDraggable]="mockDataPayload">draggable div</div>197 <div clrDroppable clrGroup="draggable-0"></div> 198 <div class="droppable-test-state" clrDroppable 199 [clrGroup]="['draggable-1', 'draggable-2']"200 (clrDragStart)="dragStartEvent = $event;"201 (clrDragMove)="dragMoveEvent = $event;"202 (clrDragEnd)="dragEndEvent = $event;"203 (clrDragLeave)="dragLeaveEvent = $event;"204 (clrDragEnter)="dragEnterEvent = $event;"205 (clrDrop)="dropEvent = $event;">206 </div>207 <div clrDroppable clrGroup="draggable-1"></div>`,208})209class WithDraggableTest {210 public mockDataPayload = MOCK_DATA_PAYLOAD;211 public dragStartEvent: any;212 public dragMoveEvent: any;213 public dragEndEvent: any;214 public dragLeaveEvent: any;215 public dragEnterEvent: any;216 public dropEvent: any;...

Full Screen

Full Screen

Backbone.CrossDomain.js

Source:Backbone.CrossDomain.js Github

copy

Full Screen

1$(document).ready(function() {23 var Library = Backbone.Collection.extend({4 url : function() { return '/library'; }5 });6 var library;78 var attrs = {9 title : "The Tempest",10 author : "Bill Shakespeare",11 length : 12312 };1314 module('Backbone.CrossDomain', _.extend(new Environment, {1516 setup : function() {17 Environment.prototype.setup.apply(this, arguments);18 library = new Library;19 library.create(attrs, {wait: false});20 },2122 teardown: function() {23 Environment.prototype.teardown.apply(this, arguments);24 Backbone.emulateHTTP = false;25 }26 }));2728 test("initialize", function() {29 var xdm = new Backbone.Model();30 ok(xdm instanceof Backbone.Model, 'Backbone.Model created');31 });3233 test("urlError", 2, function() {34 var model = new Backbone.Model();35 raises(function() {36 model.fetch();37 });38 model.fetch({url: '/one/two'});39 equal(this.ajaxSettings.url, '/one/two');40 });4142 test("#1052 - `options` is optional.", 0, function() {43 var model = new Backbone.Model();44 model.url = '/test';45 Backbone.sync('create', model);46 });4748 test("Backbone.ajax", 1, function() {49 Backbone.ajax = function(settings){50 strictEqual(settings.url, '/test');51 };52 var model = new Backbone.Model();53 model.url = '/test';54 Backbone.sync('create', model);55 });5657 test("Call provided error callback on error.", 1, function() {58 var model = new Backbone.Model;59 model.url = '/test';60 Backbone.sync('read', model, {61 error: function() { ok(true); }62 });63 this.ajaxSettings.error();64 });6566 test('Use Backbone.emulateHTTP as default.', 2, function() {67 var model = new Backbone.Model;68 model.url = '/test';6970 Backbone.emulateHTTP = true;71 model.sync('create', model);72 strictEqual(this.ajaxSettings.emulateHTTP, true);7374 Backbone.emulateHTTP = false;75 model.sync('create', model);76 strictEqual(this.ajaxSettings.emulateHTTP, false);77 });7879 test('Use Backbone.emulateJSON as default.', 2, function() {80 var model = new Backbone.Model;81 model.url = '/test';8283 Backbone.emulateJSON = true;84 model.sync('create', model);85 strictEqual(this.ajaxSettings.emulateJSON, true);8687 Backbone.emulateJSON = false;88 model.sync('create', model);89 strictEqual(this.ajaxSettings.emulateJSON, false);90 });9192 // Perform these tests only for IE93 if (window.XDomainRequest) {9495 // Make sure non-cross domain requests work fine and as normal96 test("Backbone.ajax", 1, function() {97 Backbone.ajax = function(settings){98 strictEqual(settings.url, '/test');99 };100 var model = new Backbone.Model();101 model.url = '/test';102 Backbone.sync('create', model);103 });104105 test("Call provided error callback on error.", 1, function() {106 var model = new Backbone.Model;107 model.url = '/test';108 Backbone.sync('read', model, {109 error: function() { ok(true); }110 });111 this.ajaxSettings.error();112 });113114 test('Use Backbone.emulateHTTP as default.', 2, function() {115 var model = new Backbone.Model;116 model.url = '/test';117118 Backbone.emulateHTTP = true;119 model.sync('create', model);120 strictEqual(this.ajaxSettings.emulateHTTP, true);121122 Backbone.emulateHTTP = false;123 model.sync('create', model);124 strictEqual(this.ajaxSettings.emulateHTTP, false);125 });126127 test('Use Backbone.emulateJSON as default.', 2, function() {128 var model = new Backbone.Model;129 model.url = '/test';130131 Backbone.emulateJSON = true;132 model.sync('create', model);133 strictEqual(this.ajaxSettings.emulateJSON, true);134135 Backbone.emulateJSON = false;136 model.sync('create', model);137 strictEqual(this.ajaxSettings.emulateJSON, false);138 });139140 test("#1756 - Call user provided beforeSend function.", 4, function() {141 Backbone.emulateHTTP = true;142 var model = new Backbone.Model;143 model.url = '/test';144 var xhr = {145 setRequestHeader: function(header, value) {146 strictEqual(header, 'X-HTTP-Method-Override');147 strictEqual(value, 'DELETE');148 }149 };150 model.sync('delete', model, {151 beforeSend: function(_xhr) {152 ok(_xhr === xhr);153 return false;154 }155 });156 strictEqual(this.ajaxSettings.beforeSend(xhr), false);157 });158159 // Make sure cross domain requests to DELETE, PATCH, and PUT fail with emulateHTTP off160 test("Try Forbidden requests with emulateHTTP on.", 3, function() {161 Backbone.emulateHTTP = true;162 var model = new Backbone.Model;163 model.url = 'http://example.com/test';164165 model.sync('delete', model);166 strictEqual(this.ajaxSettings.emulateHTTP,167 true,168 "CrossDomain Sync appropriately allowed DELETE with emulateHTTP on");169170 model.sync('patch', model);171 strictEqual(this.ajaxSettings.emulateHTTP,172 true,173 "CrossDomain Sync appropriately allowed PATCH with emulateHTTP on");174175 model.sync('update', model);176 strictEqual(this.ajaxSettings.emulateHTTP,177 true,178 "CrossDomain Sync appropriately allowed PUT with emulateHTTP on");179 }); 180181 // Make sure cross domain requests to DELETE, PATCH, and PUT work with emulateHTTP on182 test("Try Forbidden requests with emulateHTTP off.", 3, function() {183 Backbone.emulateHTTP = false;184 var model = new Backbone.Model;185 model.url = 'http://example.com/test';186187 try {188 // This should fail and throw an exception.189 model.sync('delete', model);190 } catch (x) {191 strictEqual(x.message,192 "Backbone.CrossDomain cannot use PUT, PATCH, DELETE with XDomainRequest (IE) and emulateHTTP=false",193 "CrossDomain Sync appropriately denied DELETE request with emulateHTTP off on IE.");194 }195196 try {197 // This should fail and throw an exception.198 model.sync('patch', model);199 } catch (x) {200 strictEqual(x.message,201 "Backbone.CrossDomain cannot use PUT, PATCH, DELETE with XDomainRequest (IE) and emulateHTTP=false",202 "CrossDomain Sync appropriately denied PATCH request with emulateHTTP off on IE.");203 }204205 try {206 // This should fail and throw an exception.207 model.sync('update', model);208 } catch (x) {209 strictEqual(x.message,210 "Backbone.CrossDomain cannot use PUT, PATCH, DELETE with XDomainRequest (IE) and emulateHTTP=false",211 "CrossDomain Sync appropriately denied PUT request with emulateHTTP off on IE.");212 }213 });214215 test("Test out different combos of protocols", 2, function() {216 Backbone.emulateHTTP = false;217 var model = new Backbone.Model;218 model.url = 'https://example.com/test';219220 try {221 // This should fail and throw an exception.222 model.sync('read', model);223 } catch (x) {224 strictEqual(x.message,225 "Backbone.CrossDomain only works for same protocol requests (HTTP -> HTTP, HTTPS -> HTTPS) cannot mix.",226 "CrossDomain Sync appropriately threw error when met with mixed protocols on IE.");227 }228229 model.url = '//example.com/test';230231 model.sync('read', model);232 ok(model, "Model read worked without a protocol specified and no errors thrown.");233 });234235 }236 else {237 test("#1756 - Call user provided beforeSend function.", 4, function() {238 Backbone.emulateHTTP = true;239 var model = new Backbone.Model;240 model.url = '/test';241 var xhr = {242 setRequestHeader: function(header, value) {243 strictEqual(header, 'X-HTTP-Method-Override');244 strictEqual(value, 'DELETE');245 }246 };247 model.sync('delete', model, {248 beforeSend: function(_xhr) {249 ok(_xhr === xhr);250 return false;251 }252 });253 strictEqual(this.ajaxSettings.beforeSend(xhr), false);254 }); 255 } ...

Full Screen

Full Screen

sync.js

Source:sync.js Github

copy

Full Screen

1(function(QUnit) {2 var Library = Backbone.Collection.extend({3 url: function() { return '/library'; }4 });5 var library;6 var attrs = {7 title: 'The Tempest',8 author: 'Bill Shakespeare',9 length: 12310 };11 QUnit.module('Backbone.sync', {12 beforeEach: function(assert) {13 library = new Library;14 library.create(attrs, {wait: false});15 },16 afterEach: function(assert) {17 Backbone.emulateHTTP = false;18 }19 });20 QUnit.test('read', function(assert) {21 assert.expect(4);22 library.fetch();23 assert.equal(this.ajaxSettings.url, '/library');24 assert.equal(this.ajaxSettings.type, 'GET');25 assert.equal(this.ajaxSettings.dataType, 'json');26 assert.ok(_.isEmpty(this.ajaxSettings.data));27 });28 QUnit.test('passing data', function(assert) {29 assert.expect(3);30 library.fetch({data: {a: 'a', one: 1}});31 assert.equal(this.ajaxSettings.url, '/library');32 assert.equal(this.ajaxSettings.data.a, 'a');33 assert.equal(this.ajaxSettings.data.one, 1);34 });35 QUnit.test('create', function(assert) {36 assert.expect(6);37 assert.equal(this.ajaxSettings.url, '/library');38 assert.equal(this.ajaxSettings.type, 'POST');39 assert.equal(this.ajaxSettings.dataType, 'json');40 var data = JSON.parse(this.ajaxSettings.data);41 assert.equal(data.title, 'The Tempest');42 assert.equal(data.author, 'Bill Shakespeare');43 assert.equal(data.length, 123);44 });45 QUnit.test('update', function(assert) {46 assert.expect(7);47 library.first().save({id: '1-the-tempest', author: 'William Shakespeare'});48 assert.equal(this.ajaxSettings.url, '/library/1-the-tempest');49 assert.equal(this.ajaxSettings.type, 'PUT');50 assert.equal(this.ajaxSettings.dataType, 'json');51 var data = JSON.parse(this.ajaxSettings.data);52 assert.equal(data.id, '1-the-tempest');53 assert.equal(data.title, 'The Tempest');54 assert.equal(data.author, 'William Shakespeare');55 assert.equal(data.length, 123);56 });57 QUnit.test('update with emulateHTTP and emulateJSON', function(assert) {58 assert.expect(7);59 library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {60 emulateHTTP: true,61 emulateJSON: true62 });63 assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');64 assert.equal(this.ajaxSettings.type, 'POST');65 assert.equal(this.ajaxSettings.dataType, 'json');66 assert.equal(this.ajaxSettings.data._method, 'PUT');67 var data = JSON.parse(this.ajaxSettings.data.model);68 assert.equal(data.id, '2-the-tempest');69 assert.equal(data.author, 'Tim Shakespeare');70 assert.equal(data.length, 123);71 });72 QUnit.test('update with just emulateHTTP', function(assert) {73 assert.expect(6);74 library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {75 emulateHTTP: true76 });77 assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');78 assert.equal(this.ajaxSettings.type, 'POST');79 assert.equal(this.ajaxSettings.contentType, 'application/json');80 var data = JSON.parse(this.ajaxSettings.data);81 assert.equal(data.id, '2-the-tempest');82 assert.equal(data.author, 'Tim Shakespeare');83 assert.equal(data.length, 123);84 });85 QUnit.test('update with just emulateJSON', function(assert) {86 assert.expect(6);87 library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {88 emulateJSON: true89 });90 assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');91 assert.equal(this.ajaxSettings.type, 'PUT');92 assert.equal(this.ajaxSettings.contentType, 'application/x-www-form-urlencoded');93 var data = JSON.parse(this.ajaxSettings.data.model);94 assert.equal(data.id, '2-the-tempest');95 assert.equal(data.author, 'Tim Shakespeare');96 assert.equal(data.length, 123);97 });98 QUnit.test('read model', function(assert) {99 assert.expect(3);100 library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});101 library.first().fetch();102 assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');103 assert.equal(this.ajaxSettings.type, 'GET');104 assert.ok(_.isEmpty(this.ajaxSettings.data));105 });106 QUnit.test('destroy', function(assert) {107 assert.expect(3);108 library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});109 library.first().destroy({wait: true});110 assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');111 assert.equal(this.ajaxSettings.type, 'DELETE');112 assert.equal(this.ajaxSettings.data, null);113 });114 QUnit.test('destroy with emulateHTTP', function(assert) {115 assert.expect(3);116 library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});117 library.first().destroy({118 emulateHTTP: true,119 emulateJSON: true120 });121 assert.equal(this.ajaxSettings.url, '/library/2-the-tempest');122 assert.equal(this.ajaxSettings.type, 'POST');123 assert.equal(JSON.stringify(this.ajaxSettings.data), '{"_method":"DELETE"}');124 });125 QUnit.test('urlError', function(assert) {126 assert.expect(2);127 var model = new Backbone.Model();128 assert.raises(function() {129 model.fetch();130 });131 model.fetch({url: '/one/two'});132 assert.equal(this.ajaxSettings.url, '/one/two');133 });134 QUnit.test('#1052 - `options` is optional.', function(assert) {135 assert.expect(0);136 var model = new Backbone.Model();137 model.url = '/test';138 Backbone.sync('create', model);139 });140 QUnit.test('Backbone.ajax', function(assert) {141 assert.expect(1);142 Backbone.ajax = function(settings) {143 assert.strictEqual(settings.url, '/test');144 };145 var model = new Backbone.Model();146 model.url = '/test';147 Backbone.sync('create', model);148 });149 QUnit.test('Call provided error callback on error.', function(assert) {150 assert.expect(1);151 var model = new Backbone.Model;152 model.url = '/test';153 Backbone.sync('read', model, {154 error: function() { assert.ok(true); }155 });156 this.ajaxSettings.error();157 });158 QUnit.test('Use Backbone.emulateHTTP as default.', function(assert) {159 assert.expect(2);160 var model = new Backbone.Model;161 model.url = '/test';162 Backbone.emulateHTTP = true;163 model.sync('create', model);164 assert.strictEqual(this.ajaxSettings.emulateHTTP, true);165 Backbone.emulateHTTP = false;166 model.sync('create', model);167 assert.strictEqual(this.ajaxSettings.emulateHTTP, false);168 });169 QUnit.test('Use Backbone.emulateJSON as default.', function(assert) {170 assert.expect(2);171 var model = new Backbone.Model;172 model.url = '/test';173 Backbone.emulateJSON = true;174 model.sync('create', model);175 assert.strictEqual(this.ajaxSettings.emulateJSON, true);176 Backbone.emulateJSON = false;177 model.sync('create', model);178 assert.strictEqual(this.ajaxSettings.emulateJSON, false);179 });180 QUnit.test('#1756 - Call user provided beforeSend function.', function(assert) {181 assert.expect(4);182 Backbone.emulateHTTP = true;183 var model = new Backbone.Model;184 model.url = '/test';185 var xhr = {186 setRequestHeader: function(header, value) {187 assert.strictEqual(header, 'X-HTTP-Method-Override');188 assert.strictEqual(value, 'DELETE');189 }190 };191 model.sync('delete', model, {192 beforeSend: function(_xhr) {193 assert.ok(_xhr === xhr);194 return false;195 }196 });197 assert.strictEqual(this.ajaxSettings.beforeSend(xhr), false);198 });199 QUnit.test('#2928 - Pass along `textStatus` and `errorThrown`.', function(assert) {200 assert.expect(2);201 var model = new Backbone.Model;202 model.url = '/test';203 model.on('error', function(m, xhr, options) {204 assert.strictEqual(options.textStatus, 'textStatus');205 assert.strictEqual(options.errorThrown, 'errorThrown');206 });207 model.fetch();208 this.ajaxSettings.error({}, 'textStatus', 'errorThrown');209 });...

Full Screen

Full Screen

emulatetab.joelpurra.js

Source:emulatetab.joelpurra.js Github

copy

Full Screen

1/*!2* @license EmulateTab3* Copyright (c) 2011, 2012 The Swedish Post and Telecom Authority (PTS)4* Developed for PTS by Joel Purra <http://joelpurra.se/>5* Released under the BSD license.6*7* A jQuery plugin to emulate tabbing between elements on a page.8*/9/*jslint vars: true, white: true, browser: true*/10/*global jQuery*/11// Set up namespace, if needed12var JoelPurra = JoelPurra || {};13(function ($, namespace)14{15 namespace.EmulateTab = function ()16 {17 };18 var eventNamespace = ".EmulateTab";19 // TODO: get code for :focusable, :tabbable from jQuery UI?20 var focusable = ":input, a[href]";21 // Keep a reference to the last focused element, use as a last resort.22 var lastFocusedElement = null;23 // Private methods24 {25 function escapeSelectorName(str) {26 // Based on http://api.jquery.com/category/selectors/27 // Still untested28 return str.replace(/(!"#$%&'\(\)\*\+,\.\/:;<=>\?@\[\]^`\{\|\}~)/g, "\\\\$1");29 }30 function findNextFocusable($from, offset) {31 var $focusable = $(focusable)32 .not(":disabled")33 .not(":hidden")34 .not("a[href]:empty");35 if ($from[0].tagName === "INPUT"36 && $from[0].type === "radio"37 && $from[0].name !== "") {38 var name = escapeSelectorName($from[0].name);39 $focusable = $focusable40 .not("input[type=radio][name=" + name + "]")41 .add($from);42 }43 var currentIndex = $focusable.index($from);44 var nextIndex = (currentIndex + offset) % $focusable.length;45 if (nextIndex <= -1) {46 nextIndex = $focusable.length + nextIndex;47 }48 var $next = $focusable.eq(nextIndex);49 return $next;50 }51 function focusInElement(event) {52 lastFocusedElement = event.target;53 }54 function tryGetElementAsNonEmptyJQueryObject(selector) {55 try {56 var $element = $(selector);57 if (!!$element58 && $element.size() !== 0) {59 return $element;60 }61 } catch (e) {62 // Could not use element. Do nothing.63 }64 return null;65 }66 // Fix for EmulateTab Issue #267 // https://github.com/joelpurra/emulatetab/issues/268 // Combined function to get the focused element, trying as long as possible.69 // Extra work done trying to avoid problems with security features around70 // <input type="file" /> in Firefox (tested using 10.0.1).71 // http://stackoverflow.com/questions/9301310/focus-returns-no-element-for-input-type-file-in-firefox72 // Problem: http://jsfiddle.net/joelpurra/bzsv7/73 // Fixed: http://jsfiddle.net/joelpurra/bzsv7/2/74 function getFocusedElement() {75 var $focused = null76 // Try the well-known, recommended method first.77 || tryGetElementAsNonEmptyJQueryObject(':focus')78 // Fall back to a fast method that might fail.79 // Known to fail for Firefox (tested using 10.0.1) with80 // Permission denied to access property 'nodeType'.81 || tryGetElementAsNonEmptyJQueryObject(document.activeElement)82 // As a last resort, use the last known focused element.83 // Has not been tested enough to be sure it works as expected84 // in all browsers and scenarios.85 || tryGetElementAsNonEmptyJQueryObject(lastFocusedElement)86 87 // Empty fallback88 || $();89 return $focused;90 }91 function emulateTabbing($from, offset) {92 var $next = findNextFocusable($from, offset);93 $next.focus();94 }95 function initializeAtLoad() {96 // Start listener that keep track of the last focused element.97 $(document)98 .on("focusin" + eventNamespace, focusInElement);99 }100 }101 // Public functions102 {103 namespace.EmulateTab.forwardTab = function ($from) {104 return namespace.EmulateTab.tab($from, +1);105 };106 namespace.EmulateTab.reverseTab = function ($from) {107 return namespace.EmulateTab.tab($from, -1);108 };109 namespace.EmulateTab.tab = function ($from, offset) {110 // Tab from focused element with offset, .tab(-1)111 if ($.isNumeric($from)) {112 offset = $from;113 $from = undefined;114 }115 $from = $from || namespace.EmulateTab.getFocused();116 offset = offset || +1;117 emulateTabbing($from, offset);118 };119 namespace.EmulateTab.getFocused = function () {120 return getFocusedElement();121 };122 $.extend({123 emulateTab: function ($from, offset) {124 return namespace.EmulateTab.tab($from, offset);125 }126 });127 $.fn.extend({128 emulateTab: function (offset) {129 return namespace.EmulateTab.tab(this, offset);130 }131 });132 }133 // EmulateTab initializes listener(s) when jQuery is ready134 $(initializeAtLoad);...

Full Screen

Full Screen

jest-runner.ts

Source:jest-runner.ts Github

copy

Full Screen

1import * as d from '../../declarations';2import { buildJestArgv, getProjectListFromCLIArgs } from './jest-config';3import { setScreenshotEmulateData } from '../puppeteer/puppeteer-emulate';4export async function runJest(config: d.Config, env: d.E2EProcessEnv) {5 let success = false;6 try {7 // set all of the emulate configs to the process.env to be read later on8 const emulateConfigs = getEmulateConfigs(config.testing, config.flags);9 env.__STENCIL_EMULATE_CONFIGS__ = JSON.stringify(emulateConfigs);10 if (config.flags.ci || config.flags.e2e) {11 env.__STENCIL_DEFAULT_TIMEOUT__ = '30000';12 } else {13 env.__STENCIL_DEFAULT_TIMEOUT__ = '15000';14 }15 if (config.flags.devtools) {16 env.__STENCIL_DEFAULT_TIMEOUT__ = '300000000';17 }18 // build up our args from our already know list of args in the config19 const jestArgv = buildJestArgv(config);20 // build up the project paths, which is basically the app's root dir21 const projects = getProjectListFromCLIArgs(config, jestArgv);22 // run the jest-cli with our data rather than letting the23 // jest-cli parse the args itself24 const { runCLI } = require('jest-cli');25 const cliResults = await runCLI(jestArgv, projects);26 success = !!cliResults.results.success;27 } catch (e) {28 config.logger.error(`runJest: ${e}`);29 }30 return success;31}32export function createTestRunner(): any {33 const TestRunner = require('jest-runner');34 class StencilTestRunner extends TestRunner {35 async runTests(tests: { path: string }[], watcher: any, onStart: any, onResult: any, onFailure: any, options: any) {36 const env = (process.env as d.E2EProcessEnv);37 // filter out only the tests the flags said we should run38 tests = tests.filter(t => includeTestFile(t.path, env));39 if (env.__STENCIL_SCREENSHOT__ === 'true') {40 // we're doing e2e screenshots, so let's loop through41 // each of the emulate configs for each test42 // get the emulate configs from the process env43 // and parse the emulate config data44 const emulateConfigs = JSON.parse(env.__STENCIL_EMULATE_CONFIGS__) as d.EmulateConfig[];45 // loop through each emulate config to re-run the tests per config46 for (let i = 0; i < emulateConfigs.length; i++) {47 const emulateConfig = emulateConfigs[i];48 // reset the environment for each emulate config49 setScreenshotEmulateData(emulateConfig, env);50 // run the test for each emulate config51 await super.runTests(tests, watcher, onStart, onResult, onFailure, options);52 }53 } else {54 // not doing e2e screenshot tests55 // so just run each test once56 await super.runTests(tests, watcher, onStart, onResult, onFailure, options);57 }58 }59 }60 return StencilTestRunner;61}62export function includeTestFile(testPath: string, env: d.E2EProcessEnv) {63 testPath = testPath.toLowerCase().replace(/\\/g, '/');64 const hasE2E = testPath.includes('.e2e.') || testPath.includes('/e2e.');65 if (env.__STENCIL_E2E_TESTS__ === 'true' && hasE2E) {66 // keep this test if it's an e2e file and we should be testing e2e67 return true;68 }69 if (env.__STENCIL_SPEC_TESTS__ === 'true' && !hasE2E) {70 // keep this test if it's a spec file and we should be testing unit tests71 return true;72 }73 return false;74}75export function getEmulateConfigs(testing: d.TestingConfig, flags: d.ConfigFlags) {76 let emulateConfigs = testing.emulate.slice();77 if (typeof flags.emulate === 'string') {78 const emulateFlag = flags.emulate.toLowerCase();79 emulateConfigs = emulateConfigs.filter(emulateConfig => {80 if (typeof emulateConfig.device === 'string' && emulateConfig.device.toLowerCase() === emulateFlag) {81 return true;82 }83 if (typeof emulateConfig.userAgent === 'string' && emulateConfig.userAgent.toLowerCase().includes(emulateFlag)) {84 return true;85 }86 return false;87 });88 }89 return emulateConfigs;...

Full Screen

Full Screen

mfp-before-emulate.js

Source:mfp-before-emulate.js Github

copy

Full Screen

1/*2 Licensed Materials - Property of IBM3 (C) Copyright 2015, 2016 IBM Corp.4 Unless required by applicable law or agreed to in writing, software5 distributed under the License is distributed on an "AS IS" BASIS,6 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.7 See the License for the specific language governing permissions and8 limitations under the License.9*/10// Public modules11var path = require('path');12var strings = require('ibm-strings');13var log = require('npmlog');14// MFP modules15var hookConsts = require('./../utils/hook-consts');16var externalizedStrings = require('./../externalizedStrings');17var MFPHook = require('./mfp-hook');18var BrowserBeforeEmulate = require('./../browser/browser-before-emulate');19/*20This class determines which platform specific before-emulate hook to21instantiate, and invoke.22 */23function MFPBeforeEmulate(context) {24 var platformPath; // Path to platform25 var currentPlatforms; // Installed platforms26 var projectRoot; // Path to project27 var args; // User arguments28 var pluginName; // Name of plugin29 MFPHook.apply(this);30 MFPBeforeEmulate.prototype = MFPHook.prototype;31 currentPlatforms = context.opts.cordova.platforms;32 projectRoot = path.resolve(context.opts.projectRoot);33 args = MFPBeforeEmulate.prototype.getArguments(context.cmdLine);34 pluginName = context.opts.plugin.id;35 // If the user did not supply any platforms, use all the installed36 // platforms37 if (currentPlatforms.length === 0) {38 currentPlatforms =39 MFPBeforeEmulate.prototype.getInstalledPlatforms(40 path.join(projectRoot, 'platforms')41 );42 }43 MFPBeforeEmulate.prototype.setLogLevel(args);44 logSilly('Cordova context: ' + JSON.stringify(context, null, 2));45 logSilly('Project root: ' + projectRoot);46 logSilly('Current platforms: ' + currentPlatforms);47 logSilly('Arguments: ' + args);48 /*49 Displays a log silly message. The log level must be set to silly.50 message - The message to log51 */52 function logSilly(message) {53 log.silly(hookConsts.MFP_BEFORE_EMULATE, message);54 }55 /*56 Displays a log verbose message. The log level must be set to verbose.57 message - The message to log58 */59 function logVerbose(message) {60 log.verbose(hookConsts.MFP_BEFORE_EMULATE, message);61 }62 /*63 Calls the platform specific hooks bassed on the platforms based. If an64 unsupported platform is passed, a warning message is displayed.65 currentPlatforms - Platforms to invoke hooks for66 */67 function invokePlatformHooks(currentPlatforms) {68 logVerbose('Invoking platform specific hooks.');69 // For each installed platform, invoke platform specific hook70 currentPlatforms.forEach(71 function(platformId) {72 platformPath = path.join(projectRoot, 'platforms', platformId);73 if (platformId === hookConsts.BROWSER)74 new BrowserBeforeEmulate(projectRoot).invokeHook();75 }76 );77 }78 /*79 Determines which hook platform specific before_emulate hook to80 instantiate, and invoke.81 */82 this.invokeHook = function() {83 logVerbose('Performing MFP before emulate hook.');84 invokePlatformHooks(currentPlatforms);85 };86}...

Full Screen

Full Screen

puppeteer-emulate.ts

Source:puppeteer-emulate.ts Github

copy

Full Screen

1import * as d from '../../declarations';2import * as puppeteer from 'puppeteer';3export function setScreenshotEmulateData(userEmulateConfig: d.EmulateConfig, env: d.E2EProcessEnv) {4 const screenshotEmulate: d.EmulateConfig = {5 userAgent: 'default',6 viewport: {7 width: 800,8 height: 600,9 deviceScaleFactor: 1,10 isMobile: false,11 hasTouch: false,12 isLandscape: false13 }14 };15 if (typeof userEmulateConfig.device === 'string') {16 try {17 const deviceDescriptors = require(env.__STENCIL_PUPPETEER_MODULE__ + '/DeviceDescriptors');18 const puppeteerEmulateOpts = deviceDescriptors[userEmulateConfig.device] as puppeteer.EmulateOptions;19 if (!puppeteerEmulateOpts) {20 console.error(`invalid emulate device: ${userEmulateConfig.device}`);21 return;22 }23 screenshotEmulate.device = userEmulateConfig.device;24 screenshotEmulate.userAgent = puppeteerEmulateOpts.userAgent;25 screenshotEmulate.viewport = puppeteerEmulateOpts.viewport;26 } catch (e) {27 console.error('error loading puppeteer DeviceDescriptors', e);28 return;29 }30 }31 if (userEmulateConfig.viewport) {32 if (typeof userEmulateConfig.viewport.width === 'number') {33 screenshotEmulate.viewport.width = userEmulateConfig.viewport.width;34 }35 if (typeof userEmulateConfig.viewport.height === 'number') {36 screenshotEmulate.viewport.height = userEmulateConfig.viewport.height;37 }38 if (typeof userEmulateConfig.viewport.deviceScaleFactor === 'number') {39 screenshotEmulate.viewport.deviceScaleFactor = userEmulateConfig.viewport.deviceScaleFactor;40 }41 if (typeof userEmulateConfig.viewport.hasTouch === 'boolean') {42 screenshotEmulate.viewport.hasTouch = userEmulateConfig.viewport.hasTouch;43 }44 if (typeof userEmulateConfig.viewport.isLandscape === 'boolean') {45 screenshotEmulate.viewport.isLandscape = userEmulateConfig.viewport.isLandscape;46 }47 if (typeof userEmulateConfig.viewport.isMobile === 'boolean') {48 screenshotEmulate.viewport.isMobile = userEmulateConfig.viewport.isMobile;49 }50 if (typeof userEmulateConfig.userAgent === 'string') {51 screenshotEmulate.userAgent = userEmulateConfig.userAgent;52 }53 }54 env.__STENCIL_EMULATE__ = JSON.stringify(screenshotEmulate);...

Full Screen

Full Screen

environment.js

Source:environment.js Github

copy

Full Screen

1(function(QUnit) {2 var sync = Backbone.sync;3 var ajax = Backbone.ajax;4 var emulateHTTP = Backbone.emulateHTTP;5 var emulateJSON = Backbone.emulateJSON;6 var history = window.history;7 var pushState = history.pushState;8 var replaceState = history.replaceState;9 QUnit.config.noglobals = true;10 QUnit.testStart(function() {11 var env = QUnit.config.current.testEnvironment;12 // We never want to actually call these during tests.13 history.pushState = history.replaceState = function() {};14 // Capture ajax settings for comparison.15 Backbone.ajax = function(settings) {16 env.ajaxSettings = settings;17 };18 // Capture the arguments to Backbone.sync for comparison.19 Backbone.sync = function(method, model, options) {20 env.syncArgs = {21 method: method,22 model: model,23 options: options24 };25 sync.apply(this, arguments);26 };27 });28 QUnit.testDone(function() {29 Backbone.sync = sync;30 Backbone.ajax = ajax;31 Backbone.emulateHTTP = emulateHTTP;32 Backbone.emulateJSON = emulateJSON;33 history.pushState = pushState;34 history.replaceState = replaceState;35 });...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1chromy.emulate({2 'viewport': {3 },4 'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'5});6chromy.screenshot('google.png');7chromy.screenshot('google.png', '#hplogo');

Full Screen

Using AI Code Generation

copy

Full Screen

1chromy.emulate({2 viewport: {width: 1024, height: 768, deviceScaleFactor: 1, mobile: false, fitWindow: false}3})4chromy.emulate({5 viewport: {width: 1024, height: 768, deviceScaleFactor: 1, mobile: false, fitWindow: false}6})7chromy.emulate({8 viewport: {width: 1024, height: 768, deviceScaleFactor: 1, mobile: false, fitWindow: false}9})10chromy.emulate({11 viewport: {width: 1024, height: 768, deviceScaleFactor: 1, mobile: false, fitWindow: false}12})13chromy.emulate({14 viewport: {width: 1024, height: 768, deviceScaleFactor: 1, mobile: false, fitWindow: false}15})16chromy.emulate({17 viewport: {width: 1024, height: 768, deviceScaleFactor: 1, mobile: false, fitWindow: false}18})19chromy.emulate({20 viewport: {width: 1024, height: 768, deviceScaleFactor: 1, mobile: false, fitWindow: false}21})22chromy.emulate({

Full Screen

Using AI Code Generation

copy

Full Screen

1chromy.emulate({2 viewport: {3 },4})5chromy.chain()6 .wait('#hplogo')7 .screenshot()8 .result(function(screenshot) {9 fs.writeFileSync('screenshot.png', screenshot)10 })11 .end()12 .then(function() {13 console.log('ok')14 })15 .catch(function(e) {16 console.log('error', e)17 })

Full Screen

Using AI Code Generation

copy

Full Screen

1chromy.emulate({viewport: {width: 1000, height: 1000}, userAgent: 'Chrome'});2chromy.chain()3 .wait('#lst-ib')4 .type('#lst-ib', 'Chromy')5 .click('#tsbb')6 .wait('#resultStats')7 .result(function (result) {8 console.log(result);9 })10 .end()11 .then(function (result) {12 console.log(result);13 })14 .catch(function (e) {15 console.log(e);16 });17chromy.close();18{ viewport: { width: 1000, height: 1000 },19 scale: 1 }20{ viewport: { width: 1000, height: 1000 },21 scale: 1 }

Full Screen

Using AI Code Generation

copy

Full Screen

1var chromy = new Chromy({port:9222})2chromy.emulate({3 'viewport': {width: 1000, height: 1000},4})5chromy.chain()6 .wait(1000)7 .screenshot('screenshot.png')8 .end()9 .then(function() {10 console.log('done')11 })12 .catch(function(e) {13 console.log(e)14 })15 .then(function() {16 chromy.close()17 })

Full Screen

Using AI Code Generation

copy

Full Screen

1Chromy.prototype.emulate = function (deviceMetrics, useragent) {2 return this._client.send('Emulation.setDeviceMetricsOverride', deviceMetrics)3 .then(() => this._client.send('Emulation.setUserAgentOverride', {userAgent: useragent}))4}5Chromy.prototype.emulateDevice = function (deviceName) {6 if (!device) {7 throw new Error('Unknown device: ' + deviceName)8 }9 const {width, height, deviceScaleFactor, mobile, userAgent} = device10 return this.emulate({width, height, deviceScaleFactor, mobile}, userAgent)11}12Chromy.prototype.emulateMedia = function (mediaType) {13 return this._client.send('Emulation.setEmulatedMedia', {media: mediaType})14}15Chromy.prototype.emulate = function (deviceMetrics, useragent) {16 return this._client.send('Emulation.setDeviceMetricsOverride', deviceMetrics)17 .then(() => this._client.send('Emulation.setUserAgentOverride', {userAgent: useragent}))18}19Chromy.prototype.emulateDevice = function (deviceName) {20 if (!device) {21 throw new Error('Unknown device: ' + deviceName)22 }23 const {width, height, deviceScaleFactor, mobile, userAgent} = device24 return this.emulate({width, height, deviceScaleFactor, mobile}, userAgent)25}26Chromy.prototype.emulateMedia = function (mediaType) {27 return this._client.send('Emulation.setEmulatedMedia', {media: mediaType})28}

Full Screen

Using AI Code Generation

copy

Full Screen

1chromy.emulate({2 userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',3 viewport: {4 }5});6chromy.emulateMedia('screen');7chromy.emulateDevice('iPhone 6');8chromy.emulateOrientation('landscape');

Full Screen

Using AI Code Generation

copy

Full Screen

1goto()2chromy.goto(url);3wait()4chromy.wait(time);5evaluate()6chromy.evaluate(function);7evaluateNow()8chromy.evaluateNow(function);9evaluateAsync()10chromy.evaluateAsync(function);11click()12chromy.click(selector);13mouseEvent()14chromy.mouseEvent(type, selector, x, y, options);

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