Repositorio del curso CCOM4030 el semestre B91 del proyecto kilometro0

common.spec.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /*jshint node: true, jasmine: true, browser: true */
  2. /*global ContactFindOptions, ContactName, Q*/
  3. /*
  4. *
  5. * Licensed to the Apache Software Foundation (ASF) under one
  6. * or more contributor license agreements. See the NOTICE file
  7. * distributed with this work for additional information
  8. * regarding copyright ownership. The ASF licenses this file
  9. * to you under the Apache License, Version 2.0 (the
  10. * "License"); you may not use this file except in compliance
  11. * with the License. You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing,
  16. * software distributed under the License is distributed on an
  17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18. * KIND, either express or implied. See the License for the
  19. * specific language governing permissions and limitations
  20. * under the License.
  21. *
  22. */
  23. // these tests are meant to be executed by Cordova Medic Appium runner
  24. // you can find it here: https://github.com/apache/cordova-medic/
  25. // it is not necessary to do a full CI setup to run these tests, just run:
  26. // node cordova-medic/medic/medic.js appium --platform android --plugins cordova-plugin-contacts
  27. 'use strict';
  28. var wdHelper = global.WD_HELPER;
  29. var screenshotHelper = global.SCREENSHOT_HELPER;
  30. var contactsHelper = require('../helpers/contactsHelper');
  31. var MINUTE = 60 * 1000;
  32. var PLATFORM = global.PLATFORM;
  33. var UNORM = global.UNORM;
  34. describe('Contacts UI Automation Tests', function () {
  35. var driver;
  36. var webviewContext;
  37. var promiseCount = 0;
  38. // going to set this to false if session is created successfully
  39. var failedToStart = true;
  40. function getNextPromiseId() {
  41. return 'appium_promise_' + promiseCount++;
  42. }
  43. function saveScreenshotAndFail(error) {
  44. fail(error);
  45. return screenshotHelper
  46. .saveScreenshot(driver)
  47. .quit()
  48. .then(function () {
  49. return getDriver();
  50. });
  51. }
  52. function getDriver() {
  53. driver = wdHelper.getDriver(PLATFORM);
  54. return wdHelper.getWebviewContext(driver, 2)
  55. .then(function (context) {
  56. webviewContext = context;
  57. return driver.context(webviewContext);
  58. })
  59. .then(function () {
  60. return wdHelper.waitForDeviceReady(driver);
  61. })
  62. .then(function () {
  63. return wdHelper.injectLibraries(driver);
  64. });
  65. }
  66. function addContact(firstName, lastName, bday) {
  67. var bdayString = bday ? bday.toDateString() : undefined;
  68. var contactName = contactsHelper.getContactName(firstName, lastName);
  69. return driver
  70. .context(webviewContext)
  71. .setAsyncScriptTimeout(MINUTE)
  72. .executeAsync(function (contactname, bday, callback) {
  73. navigator.contacts.create({
  74. 'displayName': contactname.formatted,
  75. 'name': contactname,
  76. 'note': 'DeleteMe',
  77. 'birthday': new Date(bday)
  78. }).save(function (successResult) {
  79. callback(successResult);
  80. }, function (failureResult) {
  81. callback(failureResult);
  82. });
  83. }, [contactName, bdayString])
  84. .then(function (result) {
  85. if (result && result.hasOwnProperty('code')) {
  86. throw result;
  87. }
  88. return result;
  89. });
  90. }
  91. function pickContact(name) {
  92. var promiseId = getNextPromiseId();
  93. return driver
  94. .context(webviewContext)
  95. .execute(function (pID) {
  96. navigator._appiumPromises[pID] = Q.defer();
  97. navigator.contacts.pickContact(function (contact) {
  98. navigator._appiumPromises[pID].resolve(contact);
  99. }, function (err) {
  100. navigator._appiumPromises[pID].reject(err);
  101. });
  102. }, [promiseId])
  103. .context('NATIVE_APP')
  104. .then(function () {
  105. switch (PLATFORM) {
  106. case 'ios':
  107. return driver
  108. .waitForElementByAccessibilityId(name, 20000)
  109. .elementByAccessibilityId(name);
  110. case 'android':
  111. return driver
  112. .waitForElementByXPath('//android.widget.TextView[@text="' + name + '"]', MINUTE);
  113. }
  114. })
  115. .click()
  116. .context(webviewContext)
  117. .executeAsync(function (pID, cb) {
  118. navigator._appiumPromises[pID].promise
  119. .then(function (contact) {
  120. // for some reason Appium cannot get Date object
  121. // let's make birthday a string then
  122. contact.birthday = contact.birthday.toDateString();
  123. cb(contact);
  124. }, function (err) {
  125. cb('ERROR: ' + err);
  126. });
  127. }, [promiseId])
  128. .then(function (result) {
  129. if (typeof result === 'string' && result.indexOf('ERROR:') === 0) {
  130. throw result;
  131. }
  132. return result;
  133. });
  134. }
  135. function renameContact(oldName, newGivenName, newFamilyName) {
  136. return driver
  137. .context(webviewContext)
  138. .setAsyncScriptTimeout(7 * MINUTE)
  139. .executeAsync(function (oldname, newgivenname, newfamilyname, callback) {
  140. var obj = new ContactFindOptions();
  141. obj.filter = oldname;
  142. obj.multiple = false;
  143. navigator.contacts.find(['displayName', 'name'], function (contacts) {
  144. if (contacts.length === 0) {
  145. callback({ 'code': -35142 });
  146. return;
  147. }
  148. var contact = contacts[0];
  149. contact.displayName = newgivenname + ' ' + newfamilyname;
  150. var name = new ContactName();
  151. name.givenName = newgivenname;
  152. name.familyName = newfamilyname;
  153. contact.name = name;
  154. contact.save(callback, callback);
  155. }, function (result) {
  156. callback(result);
  157. }, obj);
  158. }, [oldName, newGivenName, newFamilyName])
  159. .then(function (result) {
  160. if (result && result.hasOwnProperty('code')) {
  161. if (result.code === -35142) {
  162. throw 'Couldn\'t find the contact "' + oldName + '"';
  163. }
  164. throw result;
  165. }
  166. return result;
  167. });
  168. }
  169. function removeTestContacts() {
  170. return driver
  171. .context(webviewContext)
  172. .setAsyncScriptTimeout(MINUTE)
  173. .executeAsync(function (callback) {
  174. var obj = new ContactFindOptions();
  175. obj.filter = 'DeleteMe';
  176. obj.multiple = true;
  177. navigator.contacts.find(['note'], function (contacts) {
  178. var removes = [];
  179. contacts.forEach(function (contact) {
  180. removes.push(contact);
  181. });
  182. if (removes.length === 0) {
  183. return;
  184. }
  185. var nextToRemove;
  186. if (removes.length > 0) {
  187. nextToRemove = removes.shift();
  188. }
  189. function removeNext(item) {
  190. if (typeof item === 'undefined') {
  191. callback();
  192. return;
  193. }
  194. if (removes.length > 0) {
  195. nextToRemove = removes.shift();
  196. } else {
  197. nextToRemove = undefined;
  198. }
  199. item.remove(function removeSucceeded() {
  200. removeNext(nextToRemove);
  201. }, function removeFailed() {
  202. removeNext(nextToRemove);
  203. });
  204. }
  205. removeNext(nextToRemove);
  206. }, function (failureResult) {
  207. callback(failureResult);
  208. }, obj);
  209. }, [])
  210. .then(function (result) {
  211. if (typeof result !== 'undefined') {
  212. throw result;
  213. }
  214. });
  215. }
  216. function checkSession(done) {
  217. if (failedToStart) {
  218. fail('Failed to start a session');
  219. done();
  220. }
  221. }
  222. afterAll(function (done) {
  223. checkSession(done);
  224. driver
  225. .quit()
  226. .done(done);
  227. }, MINUTE);
  228. it('should connect to an appium endpoint properly', function (done) {
  229. // retry up to 3 times
  230. getDriver()
  231. .fail(function () {
  232. return getDriver()
  233. .fail(function () {
  234. return getDriver()
  235. .fail(fail);
  236. });
  237. })
  238. .then(function () {
  239. failedToStart = false;
  240. }, fail)
  241. .then(function () {
  242. // on iOS and Android >= 6, first interaction with contacts API will trigger the permission dialog.
  243. // We will attempt to bust it manually here, by triggering the contacts API
  244. // and waiting for the native dialog to show up, then dismissing the alert.
  245. // This only needs to be done once.
  246. // NOTE: in earlier versions of iOS (9.3 and below), using the older UI testing library
  247. // (UIAutomation), Appium's autoAcceptAlerts capability handles this for us. This logic
  248. // is here as a transition between UIAutomation and XCUITest and is compatible with both.
  249. // More details in the comment below.
  250. var promiseId = getNextPromiseId();
  251. var contactName = contactsHelper.getContactName('Permission', 'Buster');
  252. return driver
  253. .context(webviewContext)
  254. .execute(function (pID, contactname) {
  255. navigator._appiumPromises[pID] = Q.defer();
  256. navigator.contacts.create({
  257. 'displayName': contactname.formatted,
  258. 'name': contactname,
  259. 'note': 'DeleteMe'
  260. }).save(function (contact) {
  261. navigator._appiumPromises[pID].resolve(contact);
  262. }, function (err) {
  263. navigator._appiumPromises[pID].reject(err);
  264. });
  265. }, [promiseId, contactName])
  266. .context('NATIVE_APP')
  267. .then(function () {
  268. // iOS
  269. if (PLATFORM === 'ios') {
  270. return driver.acceptAlert()
  271. .then(function alertDismissed() {
  272. // TODO: once we move to only XCUITest-based (which is force on you in either iOS 10+ or Xcode 8+)
  273. // UI tests, we will have to:
  274. // a) remove use of autoAcceptAlerts appium capability since it no longer functions in XCUITest
  275. // b) can remove this entire then() clause, as we do not need to explicitly handle the acceptAlert
  276. // failure callback, since we will be guaranteed to hit the permission dialog on startup.
  277. }, function noAlert() {
  278. // in case the contacts permission alert never showed up: no problem, don't freak out.
  279. // This can happen if:
  280. // a) The application-under-test already had contacts permissions granted to it
  281. // b) Appium's autoAcceptAlerts capability is provided (and functioning)
  282. });
  283. }
  284. // Android
  285. return driver
  286. .elementByXPath('//android.widget.Button[translate(@text, "alow", "ALOW")="ALLOW"]')
  287. .click()
  288. .fail(function noAlert() { });
  289. })
  290. .context(webviewContext)
  291. .executeAsync(function (pID, cb) {
  292. navigator._appiumPromises[pID].promise
  293. .then(function (result) {
  294. cb(result);
  295. }, function (err) {
  296. cb('ERROR: ' + err);
  297. });
  298. }, [promiseId])
  299. .then(function (result) {
  300. if (typeof result === 'string' && result.indexOf('ERROR:') === 0) {
  301. throw result;
  302. }
  303. return result;
  304. });
  305. })
  306. .done(done);
  307. }, 30 * MINUTE);
  308. describe('Picking contacts', function () {
  309. afterEach(function (done) {
  310. checkSession(done);
  311. removeTestContacts()
  312. .finally(done);
  313. }, MINUTE);
  314. it('contacts.ui.spec.1 Pick a contact', function (done) {
  315. checkSession(done);
  316. var bday = new Date(1991, 1, 1);
  317. driver
  318. .then(function () {
  319. return addContact('Test', 'Contact', bday);
  320. })
  321. .then(function () {
  322. return pickContact('Test Contact');
  323. })
  324. .then(function (contact) {
  325. expect(contact.name.givenName).toBe('Test');
  326. expect(contact.name.familyName).toBe('Contact');
  327. expect(contact.birthday).toBe(bday.toDateString());
  328. })
  329. .fail(saveScreenshotAndFail)
  330. .done(done);
  331. }, 5 * MINUTE);
  332. it('contacts.ui.spec.2 Update an existing contact', function (done) {
  333. checkSession(done);
  334. driver
  335. .then(function () {
  336. return addContact('Dooney', 'Evans');
  337. })
  338. .then(function () {
  339. return renameContact('Dooney Evans', 'Urist', 'McContact');
  340. })
  341. .then(function () {
  342. return pickContact('Urist McContact');
  343. })
  344. .then(function (contact) {
  345. expect(contact.name.givenName).toBe('Urist');
  346. expect(contact.name.familyName).toBe('McContact');
  347. })
  348. .fail(saveScreenshotAndFail)
  349. .done(done);
  350. }, 10 * MINUTE);
  351. it('contacts.ui.spec.3 Create a contact with no name', function (done) {
  352. checkSession(done);
  353. driver
  354. .then(function () {
  355. return addContact();
  356. })
  357. .then(function () {
  358. switch (PLATFORM) {
  359. case 'android':
  360. return pickContact('(No name)');
  361. case 'ios':
  362. return pickContact('No Name');
  363. }
  364. })
  365. .then(function (contact) {
  366. if (contact.name) {
  367. expect(contact.name.givenName).toBeFalsy();
  368. expect(contact.name.middleName).toBeFalsy();
  369. expect(contact.name.familyName).toBeFalsy();
  370. expect(contact.name.formatted).toBeFalsy();
  371. } else {
  372. expect(contact.name).toBeFalsy();
  373. }
  374. })
  375. .fail(saveScreenshotAndFail)
  376. .done(done);
  377. }, 5 * MINUTE);
  378. it('contacts.ui.spec.4 Create a contact with Unicode characters in name', function (done) {
  379. checkSession(done);
  380. driver
  381. .then(function () {
  382. return addContact('Н€йромонах', 'ФеофаЊ');
  383. })
  384. .then(function () {
  385. return pickContact('Н€йромонах ФеофаЊ');
  386. })
  387. .then(function (contact) {
  388. expect(contact.name.givenName).toBe('Н€йромонах');
  389. expect(contact.name.familyName).toBe('ФеофаЊ');
  390. })
  391. .fail(saveScreenshotAndFail)
  392. .done(done);
  393. }, 5 * MINUTE);
  394. });
  395. });