How to use installations method in argos

Best JavaScript code snippet using argos

PushController.spec.js

Source:PushController.spec.js Github

copy

Full Screen

1"use strict";2var PushController = require('../src/Controllers/PushController').PushController;3var StatusHandler = require('../src/StatusHandler');4var Config = require('../src/Config');5var validatePushType = require('../src/Push/utils').validatePushType;6const successfulTransmissions = function(body, installations) {7 const promises = installations.map((device) => {8 return Promise.resolve({9 transmitted: true,10 device: device,11 })12 });13 return Promise.all(promises);14}15const successfulIOS = function(body, installations) {16 const promises = installations.map((device) => {17 return Promise.resolve({18 transmitted: device.deviceType == "ios",19 device: device,20 })21 });22 return Promise.all(promises);23}24describe('PushController', () => {25 it('can validate device type when no device type is set', (done) => {26 // Make query condition27 var where = {28 };29 var validPushTypes = ['ios', 'android'];30 expect(function(){31 validatePushType(where, validPushTypes);32 }).not.toThrow();33 done();34 });35 it('can validate device type when single valid device type is set', (done) => {36 // Make query condition37 var where = {38 'deviceType': 'ios'39 };40 var validPushTypes = ['ios', 'android'];41 expect(function(){42 validatePushType(where, validPushTypes);43 }).not.toThrow();44 done();45 });46 it('can validate device type when multiple valid device types are set', (done) => {47 // Make query condition48 var where = {49 'deviceType': {50 '$in': ['android', 'ios']51 }52 };53 var validPushTypes = ['ios', 'android'];54 expect(function(){55 validatePushType(where, validPushTypes);56 }).not.toThrow();57 done();58 });59 it('can throw on validateDeviceType when single invalid device type is set', (done) => {60 // Make query condition61 var where = {62 'deviceType': 'osx'63 };64 var validPushTypes = ['ios', 'android'];65 expect(function(){66 validatePushType(where, validPushTypes);67 }).toThrow();68 done();69 });70 it('can throw on validateDeviceType when single invalid device type is set', (done) => {71 // Make query condition72 var where = {73 'deviceType': 'osx'74 };75 var validPushTypes = ['ios', 'android'];76 expect(function(){77 validatePushType(where, validPushTypes);78 }).toThrow();79 done();80 });81 it('can get expiration time in string format', (done) => {82 // Make mock request83 var timeStr = '2015-03-19T22:05:08Z';84 var body = {85 'expiration_time': timeStr86 }87 var time = PushController.getExpirationTime(body);88 expect(time).toEqual(new Date(timeStr).valueOf());89 done();90 });91 it('can get expiration time in number format', (done) => {92 // Make mock request93 var timeNumber = 1426802708;94 var body = {95 'expiration_time': timeNumber96 }97 var time = PushController.getExpirationTime(body);98 expect(time).toEqual(timeNumber * 1000);99 done();100 });101 it('can throw on getExpirationTime in invalid format', (done) => {102 // Make mock request103 var body = {104 'expiration_time': 'abcd'105 }106 expect(function(){107 PushController.getExpirationTime(body);108 }).toThrow();109 done();110 });111 it('can get push time in string format', (done) => {112 // Make mock request113 var timeStr = '2015-03-19T22:05:08Z';114 var body = {115 'push_time': timeStr116 }117 var { date } = PushController.getPushTime(body);118 expect(date).toEqual(new Date(timeStr));119 done();120 });121 it('can get push time in number format', (done) => {122 // Make mock request123 var timeNumber = 1426802708;124 var body = {125 'push_time': timeNumber126 }127 var { date } = PushController.getPushTime(body);128 expect(date.valueOf()).toEqual(timeNumber * 1000);129 done();130 });131 it('can throw on getPushTime in invalid format', (done) => {132 // Make mock request133 var body = {134 'push_time': 'abcd'135 }136 expect(function(){137 PushController.getPushTime(body);138 }).toThrow();139 done();140 });141 it('properly increment badges', (done) => {142 var pushAdapter = {143 send: function(body, installations) {144 var badge = body.data.badge;145 installations.forEach((installation) => {146 expect(installation.badge).toEqual(badge);147 expect(installation.originalBadge + 1).toEqual(installation.badge);148 })149 return successfulTransmissions(body, installations);150 },151 getValidPushTypes: function() {152 return ["ios", "android"];153 }154 }155 var payload = {data:{156 alert: "Hello World!",157 badge: "Increment",158 }}159 var installations = [];160 while(installations.length != 10) {161 const installation = new Parse.Object("_Installation");162 installation.set("installationId", "installation_" + installations.length);163 installation.set("deviceToken","device_token_" + installations.length)164 installation.set("badge", installations.length);165 installation.set("originalBadge", installations.length);166 installation.set("deviceType", "ios");167 installations.push(installation);168 }169 while(installations.length != 15) {170 const installation = new Parse.Object("_Installation");171 installation.set("installationId", "installation_" + installations.length);172 installation.set("deviceToken","device_token_" + installations.length);173 installation.set("badge", installations.length);174 installation.set("originalBadge", installations.length);175 installation.set("deviceType", "android");176 installations.push(installation);177 }178 var config = new Config(Parse.applicationId);179 var auth = {180 isMaster: true181 }182 var pushController = new PushController();183 reconfigureServer({184 push: { adapter: pushAdapter }185 }).then(() => {186 return Parse.Object.saveAll(installations)187 }).then(() => {188 return pushController.sendPush(payload, {}, config, auth);189 }).then(() => {190 // Wait so the push is completed.191 return new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); });192 }).then(() => {193 // Check we actually sent 15 pushes.194 const query = new Parse.Query('_PushStatus');195 return query.find({ useMasterKey: true })196 }).then((results) => {197 expect(results.length).toBe(1);198 const pushStatus = results[0];199 expect(pushStatus.get('numSent')).toBe(15);200 }).then(() => {201 // Check that the installations were actually updated.202 const query = new Parse.Query('_Installation');203 return query.find({ useMasterKey: true })204 }).then((results) => {205 expect(results.length).toBe(15);206 for (var i = 0; i < 15; i++) {207 const installation = results[i];208 expect(installation.get('badge')).toBe(parseInt(installation.get('originalBadge')) + 1);209 }210 done()211 }).catch((err) => {212 jfail(err);213 done();214 });215 });216 it('properly set badges to 1', (done) => {217 var pushAdapter = {218 send: function(body, installations) {219 var badge = body.data.badge;220 installations.forEach((installation) => {221 expect(installation.badge).toEqual(badge);222 expect(1).toEqual(installation.badge);223 })224 return successfulTransmissions(body, installations);225 },226 getValidPushTypes: function() {227 return ["ios"];228 }229 }230 var payload = {data: {231 alert: "Hello World!",232 badge: 1,233 }}234 var installations = [];235 while(installations.length != 10) {236 var installation = new Parse.Object("_Installation");237 installation.set("installationId", "installation_" + installations.length);238 installation.set("deviceToken","device_token_" + installations.length)239 installation.set("badge", installations.length);240 installation.set("originalBadge", installations.length);241 installation.set("deviceType", "ios");242 installations.push(installation);243 }244 var config = new Config(Parse.applicationId);245 var auth = {246 isMaster: true247 }248 var pushController = new PushController();249 reconfigureServer({250 push: { adapter: pushAdapter }251 }).then(() => {252 return Parse.Object.saveAll(installations)253 }).then(() => {254 return pushController.sendPush(payload, {}, config, auth);255 }).then(() => {256 // Wait so the push is completed.257 return new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); });258 }).then(() => {259 // Check we actually sent the pushes.260 const query = new Parse.Query('_PushStatus');261 return query.find({ useMasterKey: true })262 }).then((results) => {263 expect(results.length).toBe(1);264 const pushStatus = results[0];265 expect(pushStatus.get('numSent')).toBe(10);266 }).then(() => {267 // Check that the installations were actually updated.268 const query = new Parse.Query('_Installation');269 return query.find({ useMasterKey: true })270 }).then((results) => {271 expect(results.length).toBe(10);272 for (var i = 0; i < 10; i++) {273 const installation = results[i];274 expect(installation.get('badge')).toBe(1);275 }276 done()277 }).catch((err) => {278 jfail(err);279 done();280 });281 });282 it('properly set badges to 1 with complex query #2903 #3022', (done) => {283 var payload = {284 data: {285 alert: "Hello World!",286 badge: 1,287 }288 }289 var installations = [];290 while(installations.length != 10) {291 var installation = new Parse.Object("_Installation");292 installation.set("installationId", "installation_" + installations.length);293 installation.set("deviceToken","device_token_" + installations.length)294 installation.set("badge", installations.length);295 installation.set("originalBadge", installations.length);296 installation.set("deviceType", "ios");297 installations.push(installation);298 }299 let matchedInstallationsCount = 0;300 var pushAdapter = {301 send: function(body, installations) {302 matchedInstallationsCount += installations.length;303 var badge = body.data.badge;304 installations.forEach((installation) => {305 expect(installation.badge).toEqual(badge);306 expect(1).toEqual(installation.badge);307 })308 return successfulTransmissions(body, installations);309 },310 getValidPushTypes: function() {311 return ["ios"];312 }313 }314 var config = new Config(Parse.applicationId);315 var auth = {316 isMaster: true317 }318 var pushController = new PushController();319 reconfigureServer({320 push: {321 adapter: pushAdapter322 }323 }).then(() => {324 return Parse.Object.saveAll(installations)325 }).then((installations) => {326 const objectIds = installations.map(installation => {327 return installation.id;328 })329 const where = {330 objectId: {'$in': objectIds.slice(0, 5)}331 }332 return pushController.sendPush(payload, where, config, auth);333 }).then(() => {334 return new Promise((res) => {335 setTimeout(res, 300);336 });337 }).then(() => {338 expect(matchedInstallationsCount).toBe(5);339 const query = new Parse.Query(Parse.Installation);340 query.equalTo('badge', 1);341 return query.find({useMasterKey: true});342 }).then((installations) => {343 expect(installations.length).toBe(5);344 done();345 }).catch(() => {346 fail("should not fail");347 done();348 });349 });350 it('properly creates _PushStatus', (done) => {351 const pushStatusAfterSave = {352 handler: function() {}353 };354 const spy = spyOn(pushStatusAfterSave, 'handler').and.callThrough();355 Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler);356 var installations = [];357 while(installations.length != 10) {358 const installation = new Parse.Object("_Installation");359 installation.set("installationId", "installation_" + installations.length);360 installation.set("deviceToken","device_token_" + installations.length)361 installation.set("badge", installations.length);362 installation.set("originalBadge", installations.length);363 installation.set("deviceType", "ios");364 installations.push(installation);365 }366 while(installations.length != 15) {367 const installation = new Parse.Object("_Installation");368 installation.set("installationId", "installation_" + installations.length);369 installation.set("deviceToken","device_token_" + installations.length)370 installation.set("deviceType", "android");371 installations.push(installation);372 }373 var payload = {data: {374 alert: "Hello World!",375 badge: 1,376 }}377 var pushAdapter = {378 send: function(body, installations) {379 return successfulIOS(body, installations);380 },381 getValidPushTypes: function() {382 return ["ios"];383 }384 }385 var config = new Config(Parse.applicationId);386 var auth = {387 isMaster: true388 }389 var pushController = new PushController();390 reconfigureServer({391 push: { adapter: pushAdapter }392 }).then(() => {393 return Parse.Object.saveAll(installations);394 })395 .then(() => {396 return pushController.sendPush(payload, {}, config, auth);397 }).then(() => {398 // it is enqueued so it can take time399 return new Promise((resolve) => {400 setTimeout(() => {401 resolve();402 }, 1000);403 });404 }).then(() => {405 const query = new Parse.Query('_PushStatus');406 return query.find({useMasterKey: true});407 }).then((results) => {408 expect(results.length).toBe(1);409 const result = results[0];410 expect(result.createdAt instanceof Date).toBe(true);411 expect(result.updatedAt instanceof Date).toBe(true);412 expect(result.id.length).toBe(10);413 expect(result.get('source')).toEqual('rest');414 expect(result.get('query')).toEqual(JSON.stringify({}));415 expect(typeof result.get('payload')).toEqual("string");416 expect(JSON.parse(result.get('payload'))).toEqual(payload.data);417 expect(result.get('status')).toEqual('succeeded');418 expect(result.get('numSent')).toEqual(10);419 expect(result.get('sentPerType')).toEqual({420 'ios': 10 // 10 ios421 });422 expect(result.get('numFailed')).toEqual(5);423 expect(result.get('failedPerType')).toEqual({424 'android': 5 // android425 });426 // Try to get it without masterKey427 const query = new Parse.Query('_PushStatus');428 return query.find();429 }).catch((error) => {430 expect(error.code).toBe(119);431 })432 .then(() => {433 function getPushStatus(callIndex) {434 return spy.calls.all()[callIndex].args[0].object;435 }436 expect(spy).toHaveBeenCalled();437 expect(spy.calls.count()).toBe(4);438 const allCalls = spy.calls.all();439 allCalls.forEach((call) => {440 expect(call.args.length).toBe(2);441 const object = call.args[0].object;442 expect(object instanceof Parse.Object).toBe(true);443 });444 expect(getPushStatus(0).get('status')).toBe('pending');445 expect(getPushStatus(1).get('status')).toBe('running');446 expect(getPushStatus(1).get('numSent')).toBe(0);447 expect(getPushStatus(2).get('status')).toBe('running');448 expect(getPushStatus(2).get('numSent')).toBe(10);449 expect(getPushStatus(2).get('numFailed')).toBe(5);450 // Those are updated from a nested . operation, this would451 // not render correctly before452 expect(getPushStatus(2).get('failedPerType')).toEqual({453 android: 5454 });455 expect(getPushStatus(2).get('sentPerType')).toEqual({456 ios: 10457 });458 expect(getPushStatus(3).get('status')).toBe('succeeded');459 })460 .then(done).catch(done.fail);461 });462 it('properly creates _PushStatus without serverURL', (done) => {463 const pushStatusAfterSave = {464 handler: function() {}465 };466 Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler);467 const installation = new Parse.Object("_Installation");468 installation.set("installationId", "installation");469 installation.set("deviceToken","device_token")470 installation.set("badge", 0);471 installation.set("originalBadge", 0);472 installation.set("deviceType", "ios");473 var payload = {data: {474 alert: "Hello World!",475 badge: 1,476 }}477 var pushAdapter = {478 send: function(body, installations) {479 return successfulIOS(body, installations);480 },481 getValidPushTypes: function() {482 return ["ios"];483 }484 }485 var config = new Config(Parse.applicationId);486 var auth = {487 isMaster: true488 }489 var pushController = new PushController();490 return installation.save().then(() => {491 return reconfigureServer({492 serverURL: 'http://localhost:8378/', // server with borked URL493 push: { adapter: pushAdapter }494 })495 })496 .then(() => {497 return pushController.sendPush(payload, {}, config, auth);498 }).then(() => {499 // it is enqueued so it can take time500 return new Promise((resolve) => {501 setTimeout(() => {502 resolve();503 }, 1000);504 });505 }).then(() => {506 Parse.serverURL = 'http://localhost:8378/1'; // GOOD url507 const query = new Parse.Query('_PushStatus');508 return query.find({useMasterKey: true});509 }).then((results) => {510 expect(results.length).toBe(1);511 })512 .then(done).catch(done.fail);513 });514 it('should properly report failures in _PushStatus', (done) => {515 var pushAdapter = {516 send: function(body, installations) {517 return installations.map((installation) => {518 return Promise.resolve({519 deviceType: installation.deviceType520 })521 })522 },523 getValidPushTypes: function() {524 return ["ios"];525 }526 }527 const where = { 'channels': {528 '$ins': ['Giants', 'Mets']529 }};530 var payload = {data: {531 alert: "Hello World!",532 badge: 1,533 }}534 var config = new Config(Parse.applicationId);535 var auth = {536 isMaster: true537 }538 var pushController = new PushController();539 reconfigureServer({540 push: { adapter: pushAdapter }541 }).then(() => {542 return pushController.sendPush(payload, where, config, auth)543 }).then(() => {544 fail('should not succeed');545 done();546 }).catch(() => {547 const query = new Parse.Query('_PushStatus');548 query.find({useMasterKey: true}).then((results) => {549 expect(results.length).toBe(1);550 const pushStatus = results[0];551 expect(pushStatus.get('status')).toBe('failed');552 done();553 });554 });555 });556 it('should support full RESTQuery for increment', (done) => {557 var payload = {data: {558 alert: "Hello World!",559 badge: 'Increment',560 }}561 var pushAdapter = {562 send: function(body, installations) {563 return successfulTransmissions(body, installations);564 },565 getValidPushTypes: function() {566 return ["ios"];567 }568 }569 var config = new Config(Parse.applicationId);570 var auth = {571 isMaster: true572 }573 const where = {574 'deviceToken': {575 '$in': ['device_token_0', 'device_token_1', 'device_token_2']576 }577 }578 var pushController = new PushController();579 reconfigureServer({580 push: { adapter: pushAdapter }581 }).then(() => {582 var installations = [];583 while (installations.length != 5) {584 const installation = new Parse.Object("_Installation");585 installation.set("installationId", "installation_" + installations.length);586 installation.set("deviceToken", "device_token_" + installations.length)587 installation.set("badge", installations.length);588 installation.set("originalBadge", installations.length);589 installation.set("deviceType", "ios");590 installations.push(installation);591 }592 return Parse.Object.saveAll(installations);593 }).then(() => {594 return pushController.sendPush(payload, where, config, auth);595 }).then(() => {596 // Wait so the push is completed.597 return new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); });598 }).then(() => {599 const query = new Parse.Query('_PushStatus');600 return query.find({ useMasterKey: true })601 }).then((results) => {602 expect(results.length).toBe(1);603 const pushStatus = results[0];604 expect(pushStatus.get('numSent')).toBe(3);605 done();606 }).catch((err) => {607 jfail(err);608 done();609 });610 });611 it('should support object type for alert', (done) => {612 var payload = {data: {613 alert: {614 'loc-key': 'hello_world',615 },616 }}617 var pushAdapter = {618 send: function(body, installations) {619 return successfulTransmissions(body, installations);620 },621 getValidPushTypes: function() {622 return ["ios"];623 }624 }625 var config = new Config(Parse.applicationId);626 var auth = {627 isMaster: true628 }629 const where = {630 'deviceType': 'ios'631 }632 var pushController = new PushController();633 reconfigureServer({634 push: { adapter: pushAdapter }635 }).then(() => {636 var installations = [];637 while (installations.length != 5) {638 const installation = new Parse.Object("_Installation");639 installation.set("installationId", "installation_" + installations.length);640 installation.set("deviceToken", "device_token_" + installations.length)641 installation.set("badge", installations.length);642 installation.set("originalBadge", installations.length);643 installation.set("deviceType", "ios");644 installations.push(installation);645 }646 return Parse.Object.saveAll(installations);647 }).then(() => {648 return pushController.sendPush(payload, where, config, auth)649 }).then(() => {650 // Wait so the push is completed.651 return new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); });652 }).then(() => {653 const query = new Parse.Query('_PushStatus');654 return query.find({ useMasterKey: true })655 }).then((results) => {656 expect(results.length).toBe(1);657 const pushStatus = results[0];658 expect(pushStatus.get('numSent')).toBe(5);659 done();660 }).catch(() => {661 fail('should not fail');662 done();663 });664 });665 it('should flatten', () => {666 var res = StatusHandler.flatten([1, [2], [[3, 4], 5], [[[6]]]])667 expect(res).toEqual([1,2,3,4,5,6]);668 });669 it('properly transforms push time', () => {670 expect(PushController.getPushTime()).toBe(undefined);671 expect(PushController.getPushTime({672 'push_time': 1000673 }).date).toEqual(new Date(1000 * 1000));674 expect(PushController.getPushTime({675 'push_time': '2017-01-01'676 }).date).toEqual(new Date('2017-01-01'));677 expect(() => {PushController.getPushTime({678 'push_time': 'gibberish-time'679 })}).toThrow();680 expect(() => {PushController.getPushTime({681 'push_time': Number.NaN682 })}).toThrow();683 expect(PushController.getPushTime({684 push_time: '2017-09-06T13:42:48.369Z'685 })).toEqual({686 date: new Date('2017-09-06T13:42:48.369Z'),687 isLocalTime: false,688 });689 expect(PushController.getPushTime({690 push_time: '2007-04-05T12:30-02:00',691 })).toEqual({692 date: new Date('2007-04-05T12:30-02:00'),693 isLocalTime: false,694 });695 expect(PushController.getPushTime({696 push_time: '2007-04-05T12:30',697 })).toEqual({698 date: new Date('2007-04-05T12:30'),699 isLocalTime: true,700 });701 });702 it('should not schedule push when not configured', (done) => {703 var config = new Config(Parse.applicationId);704 var auth = {705 isMaster: true706 }707 var pushAdapter = {708 send: function(body, installations) {709 return successfulTransmissions(body, installations);710 },711 getValidPushTypes: function() {712 return ["ios"];713 }714 }715 var pushController = new PushController();716 const payload = {717 data: {718 alert: 'hello',719 },720 push_time: new Date().getTime()721 }722 var installations = [];723 while(installations.length != 10) {724 const installation = new Parse.Object("_Installation");725 installation.set("installationId", "installation_" + installations.length);726 installation.set("deviceToken","device_token_" + installations.length)727 installation.set("badge", installations.length);728 installation.set("originalBadge", installations.length);729 installation.set("deviceType", "ios");730 installations.push(installation);731 }732 reconfigureServer({733 push: { adapter: pushAdapter }734 }).then(() => {735 return Parse.Object.saveAll(installations).then(() => {736 return pushController.sendPush(payload, {}, config, auth);737 }).then(() => new Promise(resolve => setTimeout(resolve, 300)));738 }).then(() => {739 const query = new Parse.Query('_PushStatus');740 return query.find({useMasterKey: true}).then((results) => {741 expect(results.length).toBe(1);742 const pushStatus = results[0];743 expect(pushStatus.get('status')).not.toBe('scheduled');744 done();745 });746 }).catch((err) => {747 console.error(err);748 fail('should not fail');749 done();750 });751 });752 it('should schedule push when configured', (done) => {753 var auth = {754 isMaster: true755 }756 var pushAdapter = {757 send: function(body, installations) {758 const promises = installations.map((device) => {759 if (!device.deviceToken) {760 // Simulate error when device token is not set761 return Promise.reject();762 }763 return Promise.resolve({764 transmitted: true,765 device: device,766 })767 });768 return Promise.all(promises);769 },770 getValidPushTypes: function() {771 return ["ios"];772 }773 }774 var pushController = new PushController();775 const payload = {776 data: {777 alert: 'hello',778 },779 push_time: new Date().getTime() / 1000780 }781 var installations = [];782 while(installations.length != 10) {783 const installation = new Parse.Object("_Installation");784 installation.set("installationId", "installation_" + installations.length);785 installation.set("deviceToken","device_token_" + installations.length)786 installation.set("badge", installations.length);787 installation.set("originalBadge", installations.length);788 installation.set("deviceType", "ios");789 installations.push(installation);790 }791 reconfigureServer({792 push: { adapter: pushAdapter },793 scheduledPush: true794 }).then(() => {795 var config = new Config(Parse.applicationId);796 return Parse.Object.saveAll(installations).then(() => {797 return pushController.sendPush(payload, {}, config, auth);798 }).then(() => new Promise(resolve => setTimeout(resolve, 300)));799 }).then(() => {800 const query = new Parse.Query('_PushStatus');801 return query.find({useMasterKey: true}).then((results) => {802 expect(results.length).toBe(1);803 const pushStatus = results[0];804 expect(pushStatus.get('status')).toBe('scheduled');805 });806 }).then(done).catch(done.err);807 });808 it('should not enqueue push when device token is not set', (done) => {809 var auth = {810 isMaster: true811 }812 var pushAdapter = {813 send: function(body, installations) {814 const promises = installations.map((device) => {815 if (!device.deviceToken) {816 // Simulate error when device token is not set817 return Promise.reject();818 }819 return Promise.resolve({820 transmitted: true,821 device: device,822 })823 });824 return Promise.all(promises);825 },826 getValidPushTypes: function() {827 return ["ios"];828 }829 }830 var pushController = new PushController();831 const payload = {832 data: {833 alert: 'hello',834 },835 push_time: new Date().getTime() / 1000836 }837 var installations = [];838 while(installations.length != 5) {839 const installation = new Parse.Object("_Installation");840 installation.set("installationId", "installation_" + installations.length);841 installation.set("deviceToken","device_token_" + installations.length)842 installation.set("badge", installations.length);843 installation.set("originalBadge", installations.length);844 installation.set("deviceType", "ios");845 installations.push(installation);846 }847 while(installations.length != 15) {848 const installation = new Parse.Object("_Installation");849 installation.set("installationId", "installation_" + installations.length);850 installation.set("badge", installations.length);851 installation.set("originalBadge", installations.length);852 installation.set("deviceType", "ios");853 installations.push(installation);854 }855 reconfigureServer({856 push: { adapter: pushAdapter }857 }).then(() => {858 var config = new Config(Parse.applicationId);859 return Parse.Object.saveAll(installations).then(() => {860 return pushController.sendPush(payload, {}, config, auth);861 }).then(() => new Promise(resolve => setTimeout(resolve, 100)));862 }).then(() => {863 const query = new Parse.Query('_PushStatus');864 return query.find({useMasterKey: true}).then((results) => {865 expect(results.length).toBe(1);866 const pushStatus = results[0];867 expect(pushStatus.get('numSent')).toBe(5);868 expect(pushStatus.get('status')).toBe('succeeded');869 done();870 });871 }).catch((err) => {872 console.error(err);873 fail('should not fail');874 done();875 });876 });877 it('should mark the _PushStatus as failed when audience has no deviceToken', (done) => {878 var auth = {879 isMaster: true880 }881 var pushAdapter = {882 send: function(body, installations) {883 const promises = installations.map((device) => {884 if (!device.deviceToken) {885 // Simulate error when device token is not set886 return Promise.reject();887 }888 return Promise.resolve({889 transmitted: true,890 device: device,891 })892 });893 return Promise.all(promises);894 },895 getValidPushTypes: function() {896 return ["ios"];897 }898 }899 var pushController = new PushController();900 const payload = {901 data: {902 alert: 'hello',903 },904 push_time: new Date().getTime() / 1000905 }906 var installations = [];907 while(installations.length != 5) {908 const installation = new Parse.Object("_Installation");909 installation.set("installationId", "installation_" + installations.length);910 installation.set("badge", installations.length);911 installation.set("originalBadge", installations.length);912 installation.set("deviceType", "ios");913 installations.push(installation);914 }915 reconfigureServer({916 push: { adapter: pushAdapter }917 }).then(() => {918 var config = new Config(Parse.applicationId);919 return Parse.Object.saveAll(installations).then(() => {920 return pushController.sendPush(payload, {}, config, auth)921 .then(() => { done.fail('should not success') })922 .catch(() => {})923 }).then(() => new Promise(resolve => setTimeout(resolve, 100)));924 }).then(() => {925 const query = new Parse.Query('_PushStatus');926 return query.find({useMasterKey: true}).then((results) => {927 expect(results.length).toBe(1);928 const pushStatus = results[0];929 expect(pushStatus.get('numSent')).toBe(0);930 expect(pushStatus.get('status')).toBe('failed');931 done();932 });933 }).catch((err) => {934 console.error(err);935 fail('should not fail');936 done();937 });938 });939 it('should support localized payload data', (done) => {940 var payload = {data: {941 alert: 'Hello!',942 'alert-fr': 'Bonjour',943 'alert-es': 'Ola'944 }}945 var pushAdapter = {946 send: function(body, installations) {947 return successfulTransmissions(body, installations);948 },949 getValidPushTypes: function() {950 return ["ios"];951 }952 }953 var config = new Config(Parse.applicationId);954 var auth = {955 isMaster: true956 }957 const where = {958 'deviceType': 'ios'959 }960 spyOn(pushAdapter, 'send').and.callThrough();961 var pushController = new PushController();962 reconfigureServer({963 push: { adapter: pushAdapter }964 }).then(() => {965 var installations = [];966 while (installations.length != 5) {967 const installation = new Parse.Object("_Installation");968 installation.set("installationId", "installation_" + installations.length);969 installation.set("deviceToken", "device_token_" + installations.length)970 installation.set("badge", installations.length);971 installation.set("originalBadge", installations.length);972 installation.set("deviceType", "ios");973 installations.push(installation);974 }975 installations[0].set('localeIdentifier', 'fr-CA');976 installations[1].set('localeIdentifier', 'fr-FR');977 installations[2].set('localeIdentifier', 'en-US');978 return Parse.Object.saveAll(installations);979 }).then(() => {980 return pushController.sendPush(payload, where, config, auth)981 }).then(() => {982 // Wait so the push is completed.983 return new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); });984 }).then(() => {985 expect(pushAdapter.send.calls.count()).toBe(2);986 const firstCall = pushAdapter.send.calls.first();987 expect(firstCall.args[0].data).toEqual({988 alert: 'Hello!'989 });990 expect(firstCall.args[1].length).toBe(3); // 3 installations991 const lastCall = pushAdapter.send.calls.mostRecent();992 expect(lastCall.args[0].data).toEqual({993 alert: 'Bonjour'994 });995 expect(lastCall.args[1].length).toBe(2); // 2 installations996 // No installation is in es so only 1 call for fr, and another for default997 done();998 }).catch(done.fail);999 });1000 it('should update audiences', (done) => {1001 var pushAdapter = {1002 send: function(body, installations) {1003 return successfulTransmissions(body, installations);1004 },1005 getValidPushTypes: function() {1006 return ["ios"];1007 }1008 }1009 var config = new Config(Parse.applicationId);1010 var auth = {1011 isMaster: true1012 }1013 var audienceId = null;1014 var now = new Date();1015 var timesUsed = 0;1016 const where = {1017 'deviceType': 'ios'1018 }1019 spyOn(pushAdapter, 'send').and.callThrough();1020 var pushController = new PushController();1021 reconfigureServer({1022 push: { adapter: pushAdapter }1023 }).then(() => {1024 var installations = [];1025 while (installations.length != 5) {1026 const installation = new Parse.Object("_Installation");1027 installation.set("installationId", "installation_" + installations.length);1028 installation.set("deviceToken","device_token_" + installations.length)1029 installation.set("badge", installations.length);1030 installation.set("originalBadge", installations.length);1031 installation.set("deviceType", "ios");1032 installations.push(installation);1033 }1034 return Parse.Object.saveAll(installations);1035 }).then(() => {1036 // Create an audience1037 const query = new Parse.Query("_Audience");1038 query.descending("createdAt");1039 query.equalTo("query", JSON.stringify(where));1040 const parseResults = (results) => {1041 if (results.length > 0) {1042 audienceId = results[0].id;1043 timesUsed = results[0].get('timesUsed');1044 if (!isFinite(timesUsed)) {1045 timesUsed = 0;1046 }1047 }1048 }1049 const audience = new Parse.Object("_Audience");1050 audience.set("name", "testAudience")1051 audience.set("query", JSON.stringify(where));1052 return Parse.Object.saveAll(audience).then(() => {1053 return query.find({ useMasterKey: true }).then(parseResults);1054 });1055 }).then(() => {1056 var body = {1057 data: { alert: 'hello' },1058 audience_id: audienceId1059 }1060 return pushController.sendPush(body, where, config, auth)1061 }).then(() => {1062 // Wait so the push is completed.1063 return new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); });1064 }).then(() => {1065 expect(pushAdapter.send.calls.count()).toBe(1);1066 const firstCall = pushAdapter.send.calls.first();1067 expect(firstCall.args[0].data).toEqual({1068 alert: 'hello'1069 });1070 expect(firstCall.args[1].length).toBe(5);1071 }).then(() => {1072 // Get the audience we used above.1073 const query = new Parse.Query("_Audience");1074 query.equalTo("objectId", audienceId);1075 return query.find({ useMasterKey: true })1076 }).then((results) => {1077 const audience = results[0];1078 expect(audience.get('query')).toBe(JSON.stringify(where));1079 expect(audience.get('timesUsed')).toBe(timesUsed + 1);1080 expect(audience.get('lastUsed')).not.toBeLessThan(now);1081 }).then(() => {1082 done();1083 }).catch(done.fail);1084 });1085 describe('pushTimeHasTimezoneComponent', () => {1086 it('should be accurate', () => {1087 expect(PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048Z'))1088 .toBe(true, 'UTC time');1089 expect(PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30-02:00'))1090 .toBe(true, 'Timezone offset');1091 expect(PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30:00.000Z-02:00'))1092 .toBe(true, 'Seconds + Milliseconds + Timezone offset');1093 expect(PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048'))1094 .toBe(false, 'No timezone');1095 expect(PushController.pushTimeHasTimezoneComponent('2017-09-06'))1096 .toBe(false, 'YY-MM-DD');1097 });1098 });1099 describe('formatPushTime', () => {1100 it('should format as ISO string', () => {1101 expect(PushController.formatPushTime({1102 date: new Date('2017-09-06T17:14:01.048Z'),1103 isLocalTime: false,1104 })).toBe('2017-09-06T17:14:01.048Z', 'UTC time');1105 expect(PushController.formatPushTime({1106 date: new Date('2007-04-05T12:30-02:00'),1107 isLocalTime: false1108 })).toBe('2007-04-05T14:30:00.000Z', 'Timezone offset');1109 expect(PushController.formatPushTime({1110 date: new Date('2017-09-06T17:14:01.048'),1111 isLocalTime: true,1112 })).toBe('2017-09-06T17:14:01.048', 'No timezone');1113 expect(PushController.formatPushTime({1114 date: new Date('2017-09-06'),1115 isLocalTime: true1116 })).toBe('2017-09-06T00:00:00.000', 'YY-MM-DD');1117 });1118 });1119 describe('Scheduling pushes in local time', () => {1120 it('should preserve the push time', (done) => {1121 const auth = {isMaster: true};1122 const pushAdapter = {1123 send(body, installations) {1124 return successfulTransmissions(body, installations);1125 },1126 getValidPushTypes() {1127 return ["ios"];1128 }1129 };1130 const pushTime = '2017-09-06T17:14:01.048';1131 reconfigureServer({1132 push: {adapter: pushAdapter},1133 scheduledPush: true1134 })1135 .then(() => {1136 const config = new Config(Parse.applicationId);1137 return new Promise((resolve, reject) => {1138 const pushController = new PushController();1139 pushController.sendPush({1140 data: {1141 alert: "Hello World!",1142 badge: "Increment",1143 },1144 push_time: pushTime1145 }, {}, config, auth, resolve)1146 .catch(reject);1147 })1148 })1149 .then((pushStatusId) => {1150 const q = new Parse.Query('_PushStatus');1151 return q.get(pushStatusId, {useMasterKey: true});1152 })1153 .then((pushStatus) => {1154 expect(pushStatus.get('status')).toBe('scheduled');1155 expect(pushStatus.get('pushTime')).toBe('2017-09-06T17:14:01.048');1156 })1157 .then(done, done.fail);1158 });1159 });...

Full Screen

Full Screen

page-installations.js

Source:page-installations.js Github

copy

Full Screen

1var installationsList = [];2var bannerInstallations = [];3var areInstallationsLoaded = false;4var arePageInstallationsBuild = false;5myApp.onPageInit('installations', function (page)6{7 myApp.initImagesLazyLoad(mainView.activePage.container);8 9 if(page.fromPage.name == 'about'){10 $$('#page-installations .page-content').scrollTop(0);11 }12});13myApp.onPageBeforeAnimation('installations', function (page)14{15 myApp.params.swipePanel = false;16 trackPageGA("Instalaciones");17});18/*function loadInstallations(){19 showLoadSpinnerWS();20 //bannerInstallations = response.banner;21 //installationsList = response;22 console.log(installationsList);23 //if (installationsList){24 //console.log('areInstallationsLoaded set to true');25 // areInstallationsLoaded = true;26 //}27 builderInstallationsList();28 hideLoadSpinnerWS();29}*/30function builderInstallationsList(response){31 installationsList = response;32 console.log(installationsList);33 console.log(areInstallationsLoaded);34 console.log(arePageInstallationsBuild);35 var strBuilderInstallationsContent = [];36 //if(areInstallationsLoaded == true){37 if(installationsList.length == 0){38 showMessage(messageNotInstallations);39 }40 else if(arePageInstallationsBuild == false){41 $('#installations-list').html('');42 $.each( installationsList, function( i, item ){43 strBuilderInstallationsContent.push('<div class="card card-installations"><div class="card-content"><div class="list-block list-block-about media-list">');44 strBuilderInstallationsContent.push('<ul><li class="item-content">');45 strBuilderInstallationsContent.push('<a onclick="builderInstallationDetails('+item.id+')" href="#" class="item-link item-content">');46 47 strBuilderInstallationsContent.push('<div class="item-media">');48 strBuilderInstallationsContent.push('<img class="lazy lazy-fadeIn imgCardInstallation" data-src="'+item.imagenPrincipalMin+'" alt="'+item.imagenPrincipalMin+'" />');49 strBuilderInstallationsContent.push('</div>');50 51 strBuilderInstallationsContent.push('<div class="item-inner">');52 strBuilderInstallationsContent.push('<div class="item-title-row">');53 strBuilderInstallationsContent.push('<div class="item-title">'+item.nombre+'</div>');54 strBuilderInstallationsContent.push('</div>');55 strBuilderInstallationsContent.push('<div class="item-text">'+item.descripcion+'</div>');56 strBuilderInstallationsContent.push('</div>');57 58 strBuilderInstallationsContent.push('</a>');59 strBuilderInstallationsContent.push('</li></ul>');60 strBuilderInstallationsContent.push('</div></div></div>');61 });62 63 $('#installations-list').append(strBuilderInstallationsContent.join(""));64 mainView.router.load({pageName: 'installations'});65 myApp.initImagesLazyLoad(mainView.activePage.container);66 67 arePageInstallationsBuild = true;68 69 } else {70 mainView.router.load({pageName: 'installations'});71 }72 /*} else {73 loadInstallations();74 }*/...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var argosy = require('argosy')2var argosyPattern = require('argosy-pattern')3var installations = require('argosy-installations')4var service = argosy()5service.pipe(installations()).pipe(service)6service.accept(argosyPattern({role: 'math', cmd: 'sum'}), function (msg, cb) {7 cb(null, {answer: msg.left + msg.right})8})9service.install({role: 'math', cmd: 'sum'}, function (err) {10 if (err) throw err11 console.log('service installed')12})13var argosy = require('argosy')14var argosyPattern = require('argosy-pattern')15var client = require('argosy-client')16var service = argosy()17service.pipe(client()).pipe(service)18service.request(argosyPattern({role: 'math', cmd: 'sum'}), {left: 1, right: 2}, function (err, response) {19 if (err) throw err20 console.log('1 + 2 =', response.answer)21})22`installations([opts])`23`service.install(opts, [cb])`24* `opts` - options object to match against for installation, see [argosy-pattern](

Full Screen

Using AI Code Generation

copy

Full Screen

1var argos = require('argos');2argos.installations(function (err, installations) {3 if (err) {4 console.log(err);5 return;6 }7 console.log(installations);8});9var argos = require('argos');10argos.installations(function (err, installations) {11 if (err) {12 console.log(err);13 return;14 }15 console.log(installations);16});17var argos = require('argos');18argos.installations(function (err, installations) {19 if (err) {20 console.log(err);21 return;22 }23 console.log(installations);24});25var argos = require('argos');26argos.installations(function (err, installations) {27 if (err) {28 console.log(err);29 return;30 }31 console.log(installations);32});33var argos = require('argos');34argos.installations(function (err, installations) {35 if (err) {36 console.log(err);37 return;38 }39 console.log(installations);40});41var argos = require('argos');42argos.installations(function (err, installations) {43 if (err) {44 console.log(err);45 return;46 }47 console.log(installations);48});49var argos = require('argos');50argos.installations(function (err, installations) {51 if (err) {52 console.log(err);53 return;54 }55 console.log(installations);56});57var argos = require('argos');58argos.installations(function (err, installations) {59 if (err) {60 console.log(err);61 return;62 }63 console.log(installations);64});65var argos = require('argos');66argos.installations(function (err, installations

Full Screen

Using AI Code Generation

copy

Full Screen

1var argos = require('argos');2argos.installations(function(err, data){3 console.log(data);4});5var argos = require('argos');6argos.installations('id', function(err, data){7 console.log(data);8});9var argos = require('argos');10argos.installations('id', 'id', function(err, data){11 console.log(data);12});13var argos = require('argos');14argos.installations('id', 'id', 'id', function(err, data){15 console.log(data);16});17var argos = require('argos');18argos.installations('id', 'id', 'id', 'id', function(err, data){19 console.log(data);20});21var argos = require('argos');22argos.installations('id', 'id', 'id', 'id', 'id', function(err, data){23 console.log(data);24});25var argos = require('argos');26argos.installations('id', 'id', 'id', 'id', 'id', 'id', function(err, data){27 console.log(data);28});29var argos = require('argos');30argos.installations('id', 'id', 'id', 'id', 'id', 'id', 'id', function(err, data){31 console.log(data);32});33var argos = require('argos');34argos.installations('id', 'id', 'id', 'id', 'id', 'id', 'id', 'id', function(err, data){35 console.log(data);36});37var argos = require('argos');38argos.installations('id', 'id', 'id', 'id', 'id', 'id

Full Screen

Using AI Code Generation

copy

Full Screen

1const argosSDK = require('@axway/argos-sdk');2const installations = argosSDK.installations;3const argosSDK = require('@axway/argos-sdk');4const installations = argosSDK.installations;5installations.getInstallations().then((installations) => {6 console.log(installations);7});8installations.getInstallations().then((installations) => {9 console.log(installations);10});11const argosSDK = require('@axway/argos-sdk');12const installations = argosSDK.installations;13installations.getInstallations().then((installations) => {14 console.log(installations);15});16const argosSDK = require('@axway/argos-sdk');17const installations = argosSDK.installations;18installations.getInstallations().then((installations) => {19 console.log(installations);20});21const argosSDK = require('@axway/argos-sdk');22const installations = argosSDK.installations;23installations.getInstallations().then((installations) => {24 console.log(installations);25});26const argosSDK = require('@axway/argos-sdk');27const installations = argosSDK.installations;28installations.getInstallations().then((installations) => {29 console.log(installations);30});31const argosSDK = require('@axway/argos-sdk');32const installations = argosSDK.installations;33installations.getInstallations().then((installations) => {34 console.log(installations);35});36const argosSDK = require('@axway/argos-sdk');37const installations = argosSDK.installations;

Full Screen

Using AI Code Generation

copy

Full Screen

1var argosy = require('argosy')2var argosyPatterns = require('argosy-patterns')3var argosyService = argosy()4var argosyClient = argosy()5argosyService.use(argosyPatterns.installations())6argosyClient.use(argosyPatterns.installations())7argosyService.listen(8000)8argosyClient.connect(8000)9argosyClient.installations().install('test', function (err) {10 if (err) {11 console.log(err)12 }13 argosyClient.installations().list(function (err, list) {14 if (err) {15 console.log(err)16 }17 console.log(list)18 })19})20var argosy = require('argosy')21var argosyPatterns = require('argosy-patterns')22var argosyService = argosy()23var argosyClient = argosy()24argosyService.use(argosyPatterns.installations())25argosyClient.use(argosyPatterns.installations())26argosyService.listen(8000)27argosyClient.connect(8000)28argosyClient.installations().install('test', function (err) {29 if (err) {30 console.log(err)31 }32 argosyClient.installations().list(function (err, list) {33 if (err) {34 console.log(err)35 }36 console.log(list)37 })38})39installations(patterns)40installations(patterns)41installations(patterns)42installations(patterns)

Full Screen

Using AI Code Generation

copy

Full Screen

1var argos = require('argos-sdk');2var installations = argos.installations;3installations.getInstallations().then(function(installations) {4 console.log(installations);5});6### getInstallations()7### getInstallation(installationId)8### getInstallationByDevice(deviceId)9### getInstallationByUser(userId)10### getInstallationByDeviceAndUser(deviceId, userId)11### createInstallation(installation)12### updateInstallation(installationId, installation)13### deleteInstallation(installationId)14### getDevices(installationId)15### getDevice(installationId, deviceId)16### createDevice(installationId, device)17### updateDevice(installationId, deviceId, device)18### deleteDevice(installationId, deviceId)19### getUsers(installationId)20### getUser(installationId, userId)21### createUser(installationId, user)22### updateUser(installationId, userId, user)23### deleteUser(installationId, userId)24### getInstallationByDeviceAndUser(deviceId, userId)25### getInstallationByDevice(deviceId)26### getInstallationByUser(userId)

Full Screen

Using AI Code Generation

copy

Full Screen

1const argosy = require('argosy')2const argosyService = require('argosy-service')3const services = argosy()4services.use(argosyService())5services.accept({role: 'math', cmd: 'sum'}, (msg, cb) => {6 cb(null, {answer: msg.left + msg.right})7})8services.accept({role: 'math', cmd: 'product'}, (msg, cb) => {9 cb(null, {answer: msg.left * msg.right})10})11services.accept({role: 'math', cmd: 'difference'}, (msg, cb) => {12 cb(null, {answer: msg.left - msg.right})13})14services.accept({role: 'math', cmd: 'quotient'}, (msg, cb) => {15 cb(null, {answer: msg.left / msg.right})16})17services.accept({role: 'math', cmd: 'exponent'}, (msg, cb) => {18 cb(null, {answer: Math.pow(msg.left, msg.right)})19})20services.accept({role: 'math', cmd: 'squareRoot'}, (msg, cb) => {21 cb(null, {answer: Math.sqrt(msg.left)})22})23services.accept({role: 'math', cmd: 'cubeRoot'}, (msg, cb) => {24 cb(null, {answer: Math.cbrt(msg.left)})25})26services.accept({role: 'math', cmd: 'logarithm'}, (msg, cb) => {27 cb(null, {answer: Math.log(msg.left)})28})29services.accept({role: 'math', cmd: 'logarithmBase10'}, (msg, cb) => {30 cb(null, {answer: Math.log10(msg.left)})31})32services.accept({role: 'math', cmd: 'logarithmBase2'}, (msg, cb) => {33 cb(null, {answer: Math.log2(msg.left)})34})35services.accept({role: 'math', cmd: 'sin'}, (msg, cb)

Full Screen

Using AI Code Generation

copy

Full Screen

1var argosy = require('argosy')2var installations = require('argosy-installations')3var argosyService = argosy()4argosyService.pipe(installations()).pipe(argosyService)5argosyService.service('test').invoke('test', 'hello world', function (err, result) {6 if (err) throw err7 console.log(result)8})9var argosy = require('argosy')10var service = require('argosy-service')11var argosyService = argosy()12argosyService.pipe(service()).pipe(argosyService)13argosyService.service('test').accept('test', function (message, callback) {14 callback(null, 'hello ' + message)15})16var argosy = require('argosy')17var service = require('argosy-service')18var argosyService = argosy()19argosyService.pipe(service()).pipe(argosyService)20argosyService.service('test').accept('test', function (message, callback) {21 callback(null, 'hello ' + message)22})23var argosy = require('argosy')24var service = require('argosy-service')25var argosyService = argosy()26argosyService.pipe(service()).pipe(argosyService)27argosyService.service('test').accept('test', function (message, callback) {28 callback(null, 'hello ' + message)29})

Full Screen

Using AI Code Generation

copy

Full Screen

1argosy.installations().then(function() {2 argosy.start();3});4argosy.install('testService').then(function() {5 argosy.start();6});7argosy.start();8argosy.stop();9argosy.send('testService', 'test message').then(function(response) {10 console.log(response);11});12argosy.send('testService', { message: 'test message' }).then(function(response) {13 console.log(response);14});15argosy.send('testService', { message: 'test message' }, { timeout: 1000, retry: 2 }).then(function(response) {16 console.log(response);17});

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