diff --git a/.travis.yml b/.travis.yml index 9281a015c..e1928a9c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,23 @@ env: - INTEGRATION_TESTS_CORDOVA_VERSION='8.0.0' - UNIT_TESTS_NODEJS_VERSION='9' - RUN_INTEGRATION_TESTS_FILE_LOCATION='./test/integration/run-tests' + - secure: IuywIkmD6t8wxh1OpjMgHvvGIzDBDXXahkhlTFsuxN0kowqlWrF9F1gCyMdg7dALJ+rtInISGIm+7UrTbyoNSBPHRkzXyU0p1eDx8shBLYOg8dKxTwUn61zb3ckOMt1sYwOy2x00d6MUIayB0uu5haYUyi4zt8bqs2Mp548oO4r5+Y4/Tnt3lP5/dGFyxOER7HZdb+Ka9KguzhgJeWSF7dRMjQCHCccUO4i2Gp6cQVE8xI2e9nN6SXDwuQrIq8zRcbCh/5sH4R1uEHEKDT5u0gBIN5eBQSkjG7rtBi3Vaif7TsSqRTGUV2njKCIO7Cd8HLHeY9D7WHFMsLXOGo2hGZvdaeq9QXoJ3mswNO0AUP6Smam+Bg8YeHy+x6+/yGIT7N0J0wrZUY3WeEpq3b14+f2YtDhhGS0m3K6uLllOQ+NVHvkgEEVp7y4pxJhTf8e9Gc6ZCoJOAY+NCjI7wnDQ0DbyanoE5JbhyL6oDv1P+sZE9Ixm3fRIZVrUf4g3P31AXL4O4UUdcTjMwUmTktweXGLCKqbtS/XepxtaeUTthacoTYALkjpgDeB3jrOLiEUxVjI2GYH4IRjdutAohVfgEFy8m5KiTdw5pQQFzT+u+T+YRf9N3o5O/BSSJuzpa57nmPa9ShCHNLLahEAVi65fRo1Ols7pU8ZnLxZ7r6PrrwQ= + - secure: ConvbHNWuW4iMz/8vS2Gwe/lipCcC+JgmVz/baXvsH8CP/fw4PcWbXyYmCq9uVcibernp21AD3vzwy00f2sbG/gDa4uGW4FE0L87Pl+lbKoQeBPBvSVvt2rWFGJc1wuXbxwgTRJT3KGXgDFFOQGtwGWjvqT2ScTwKxeNuMICA7h5PPvTrVwO1NatKoLXz+kh/+9+mXuCTU1nfOvfH7qFWxSE19wKoNOe8xRdeCAb0GcHthp9jbxqW5gOBcIjv/7Fu7yGwUx1+khx3v4QtCiQxytr0GooiBSssogmYTyueFp+jOwY2c2+2rzwhtWBHWFq+kOdIs53heq4ccs1Nnb3j00THToxnelpA/kC2JF6lEuPyjnAeZoatEbGicsWiDMCmBOtAtNTIsfNbBKu53MMag6KHbtDMLL+Ma5DTzzQgkr58k/bZzVlPZkMShiKHaHPBdhTflIm1X0moHmO3rinMJN3MAT9TtmVBD2KJcF/a6S9ULsQEZG/GMLzaTCorf8Qo+dSL4+B3S7QEkZSJQw8MGczvPqATnxA8hOMzQpP3HuVxXAu+ZoTF9tybo4v4i2yPe9imIPnhoRYz1JjNGQZ0XenNRh3vQyuTs/tlB9CyVKCqZYzeNwvuI6aSVi4egEUuTEOdqW+vCKoGdypLVskiuibNvAQxmPX0bx7qGKtDc0= + - secure: qGmXdn37nleSygbfv5ioR0mKJ9l289MNM2x1gmvWeXZRLaocL0JmOuHWwOMiT6peJu1ImF+hnWEl5ueyNPxG2jBLwxyRG9yLoHhqvXttRlwz8w12XDuhQ9LWBbiOge/SZrF6/IvUSAiw4FADZBiK3raH0Iu3vYBEzC2Ud9bfHcwuaqzt2ZpNa39XeJo1Ammw/xGcywB/KbEhMxNSROocSfqHyKINWJw6qvOs8Q+/0pWedvUkBTdqWJx5UxnF+v4iGMvDZ8FdNDyObDNjvIsrf8gKnc/e9I93Q86Frvkfo02CLXzV5s+CNwXBADCE/Bi7sFPaq/ExGwd+fZSkq7/6IoQanqW0y3CBnO5YSvbN8AG4+Mvj9Ty8vAlRYoljuQgMipINWmOo80nIogySp0kFJPcjqLPW0SxCrbH2nw7+vLm2Wvmz0aCC+WXdXqxBZcj/wIe+4P519jE5lXz3G4HPApi3DJl5vUnMqGqV4UmI+OL3tsppb1a8bsJhFi3cvzfYaM+iYMn4PyRpgXhbMdcDnzpHvULa+hse+rawXR9BeXi3N46FPEVNG1CBiqKRRFup3+OVuzEpYvB52Wf/XMxhYMgcqwjNd5tfuJ952Qkg67VFe09N9dW6NiG/ac5boczTjhHuHqkR5CDlS8LDNzqzp4YZhIabJvXL9q8gLfqbo24= + - secure: VPFeeGOS5BVZ5pM6fkr7nVAj4+3kjtA2ivov7jFUw0rmBQaXCC8+y88idQPR2J4VtJWy9y70uVyVrc9YUxzVB4kx4N0a6RJgeLp809sFladenLfNrfOC213k6ucpqmuFKIaWAFnUkwgPmqix8UV/DunW05e93LyK/E9XqkDyjk3fO6fnI3PKloMJyWH+79KinESwlxcab83K0cSzW1eElmr0iejNcw9gVTudvLWjHzK/y6ndBWjM7eUGJ+NTXsV+Jm492ATbi9ObhAiPTOOYBMDdZ013bvtsUh0U8qSaLq4I/80QG1q5E95F6CWf/wMyQdbWfYLwXC1HL7OIrFlD0pBEA3PPWpR+trpADV8fa695GAqi6Ky8kEdZGa8NT9+9M4Swxya/VnEhV6PupOen4BoryEI0Q9DtaPhZIuC8C5eLH6BRpY96s9kTLcJg0urH6b3+Sj368FVZGorL2vx+9y0GYEDKvcfkG6hIaZNGSArM/wGyT94dFq+NJNKZOqAGuHYzLd6c/kE0nHT9HR9irVYrmrvRHAawMeMn38QmfbX9jjt9fHARQR6SzUWBfVEmg2CpGIjcb8GS/7X3R7OsdwmBrQ+VvOuea7/LByGFQjufEKXMkFtdcai4C4NelV9Rjn0kWJmpB07ZJoFg2+Y7sJQvw/USG67f+vWtdvgYsH8= + - secure: Y75Wkt90XA86u/UchXcWmVkE08FmCccK3oSquNcJG0/2xPkpmOXZnEQ5wG50cKw0kBqrrhm0a8WjBLyNuc2hPE1s7wtVct/CCbBot//ahZW9PrFIoUGuH2f6VvKmKtU8qjKm3vqju1dMMij0Usg2dWnmkfharqAsWMuZBpCusrpqNA8ZGQlRIkVh34A54ebH/ZMEKWDwFBSSPNL7VPhIkV+zzwVc7OY40iO8+Xxy8PfxqU2WDFVKkFBBxvycQiF9byhbEl3xGmwLZcme1b0ZgBS5Ev/3DNr+nddlHtTyJY1R8QFWJ7eLC7pXr4U0AW5xvTEWH+okB19WTmPzlbEIdGD5GEar5NWG3SaL1QNxSbWkit32lkOB1DdPaD1R4sy6H2k9fOBcUPa86bQO2etQvhjXnn8yQ56OSSVH20ccqFwTaKVurP0tARo8Elc48LI0oXxqetPn7hpdS1hgNK7knEJtXI9IvCbPC9+MOGprYLLZ4YKK648kiIFWM/pVLe9QyRaG2r4Om6QJdbgqwRIFDo3FP5RfBNKUn3ChGkaeXjKV2EWTYQ0DIlDc3lkCJYnazr71u6LDdq8J/MZYBq2zfOerVIv+67HJB6PpYomJhiNLTpi5aPxdwVmmuss5gzSm5Ju5B0Nq/pQzxOoIlB5yBetw80mefG3EB00UsGuAsiI= + - secure: hSfirFJx/nZLMVavRuTJlrujECqTKuBFh0cC6adEmaFL7WhoHnS+cZv2KtGI5eYuQ470+pg5LUWCF1nwCUhqwIV+9rhOEB1cvD1fRSHa4rEM3S1lkBWalIwcRlGPUwxgssqKr0zJq6CuwK/XSzRdGtKyaUJHHBpVpIMGnIVtm9iFrh7MM8GY1ztnDPUbvLG6Z2rQ8qq+8IqcQlth/xcDxjEglTD1tJe99nB0VAi71M3UYel64Hs8RFnk2V0ARoK/MEpNkEXBYSJeeLGA05RqKNTPKTN3WeOLgyakHGl9OUnZJjIy21V/r1pojeHhoDwFtGuEpDTX/aaaX8codyv3mdEZgUK4D1kJtgQJxFJi3shjB9O7kp9dECHB3p4XklOmZcQXR9Zdqovdb2y6o0GLbOOEKbUKM4pXvk2XCNmtrOmMEMIkzz7ejxL43WdvZ2irW8fVQVgiZcTFQ5ti4Apq76h3wqNRVIcypXJi+dGV3Xf7V6qfOOaW6GvQn3OlZTmpjJ6JStv49zfF3lD5uJipxGAsLSP6+oMMKOUJlN/qwvdeVaU7LTA2L3zHqzmTOdkUg8XO6e2Bl/DuArLk9j6uYbZE/v9QKJTWxbSF+vYb33+x2hcgmtgcsKP6gnearwka1kEfw4/8xYtwWED4zkfvLIsDgsNoPfG4EiqH35FdsgA= + - secure: V37fo5zt3oLuj/QSVyyFI9z+R1u+m/4o+rkz6JKEesJzKO8aq/Goi/uKwTQVsK+zJ7c6swe1uhTH9A6VqaJKowqfXUxyO0XhXZnAUUenjAxG5OaLqtkfluQhPBPcXPHlatLPW/Oqqlswv05TKgaozBc55hdhH0qpQWLaxS2Gt11MbrbF7HoQevB9UJfO9Mn/F38KlFp5BiRP+zRKogAPJdyGnpd6ndLpa9Qvs60JCZXY66sav/BbMniQAZ4guOGORaxueKlpdX1z2xnCmD4jGuQnLdikx9/RVl2kutGCNfx73tsSAdMRKXI5YT9L9R/CB9fUpGEfdIADTbeOMOaFH96tUkelTErtt28mznoA2Wjif97TD5XpMxPc5nE+Sx1BPDb2+O+1wcutior+3eZUnjdoxo8ccfcf7rRmRDqqqkblMrASYPSmAlrHAukuC5EUizF0WVUCQbRgt0JJnDFfFYUXHSnRPedYgdFJmvrb7+GkbtJtcCPJfkK6sbYCpJwQJibkUJJYIzvmyQUyjeeQ0Z+KWK85F/G7BonLi1wAic1rBPvDgbQJwcGKaoYexTI23IZSYKc2rrPW7TPiUMiIPV49TMQez6cIFrdLjDTWvFyGqlOFnRES4FFASZR4P9HWRKNJcqCTwKg+Q61HdIaQdXoC5zwXGA0stXH2xNX6A+4= + - secure: jSimf2X4zkaNaEEjOtNJW29F3XI3Jl7d3/0N3oShc7vjErgItB/eZintEhfpvPPthuv1f7vYU4uKtxHepwBjVReL6rw0zEyaRvHDB/YrspaZN+UzlBKV06NkCiFcTJtgX8i0UCARn99JZCE/FSp/kEKIe66Fv3uC6JuZctKPpKj8IvtpPKNq5/UxbDb+2Es0oa9vWjos0zsZuKf1OxA0D56hYXzrm+Pu2NqZAJNXbmKtNvYVE1tmljTz4kpJ4VIgJqrsXIpVaL2ZloO5Nn/pbvkdaXkFqkVOgfMdpnzpKp5CYCySfjVEFUMTbREmLwUuhl4kqQD63/pKvUDg5LG7HmCnmuyCWjNF2U/Z1hUVLHiIiPGGLcgJS/xloWIIB9VZVuq0DP6wa+VABXpfNVmpMKpLQuJRKzKo4TcJaMWYeUG72KqiQLzXA9GzaBspn4UEuoTlGZpm5TLDpYiLeqDQQQplA95nkDOdYDL1NGs/DKMAgyx1PT6MQWPSGPIUmxXX9dTA5S8I1EfMQdy94RWRkgIF0gKwgAB+ig2JeHJKJ+NoguXlryCQsrLrbttkYi85ZfZWxZSg2l++ZMwheDmNvmWiilHg2Rnu2b6q2u4H+NB/1xSnIGJ0HTAN9K/UHieoSZtpuF2eghDl7FUXHgCF/Diok48AUrerz98crsr0PT8= + - secure: QKvABc+mqwcQfROecTjsvDMKu+NenSJmY05l0oHBZRHyWkzLauWHK0YAqHd9PVaejeBDZD167LJHK7PdxrKrLxH02tklpQKOGzI1XcAyRJn7YawyTKeUP/znaXC37xdhtKq+2SBRJoZPVzgbPYpZAlOM50y8BVWQ4ETKVZPw472B7GvE/DCS5I9aLFJQ+hb0v8/oPt87iR3biIZgOiswGxbk8TrWpNSBim13OWx+h7/x89HMjqal2i1fbHMsZtBtoE8n1IF2h8Jt9O8yujYmxOsT7iUIN/NffQ98RkrtIGWbO5tPVWWtTsbyT96kV0gWyUIN4QFHNoHNPwQOvrMj/7Faxx4Mfa5tnRnf5px5ZmjDfuh5ROzX/npgI7TT94uDJtv8IeyqyAOmbzNSiZpn9E72/yJY2cD+nOIMfywkcucZXJxrTqUUQk6YH3w+jVMyWPmUys0sD54NPkedPiI18/Esqc0L1arco+Slert55X+aavjBzfNatAt7g0E5ogKc3rJOerPK3n7M0j2XjUlTv82xZ436bX4UjN06D7V6QSSJ1bNS19TTanLBa1NvUQa533p1I0OZTYeVh1DE0yDgYXuthZP7rf8Q6ZSSf0G8UJiu8c+3feDqHXPbqr9pzWep+cFlLtL/MqjlKHVRMXs4OuHPKPWTXmjfxmoMNLQ81kE= + - secure: pomAHaSYxqUTx+Zuv9AVbdHP09FUikn+UNCsxvJNwISLm/9mvv4uthalD9h6A4aHHKZVvIAAf1pnIDiO096A6eIDCqc0cH4JUEDJiqJlBMMg7X4qWhRx7A2oLUtfY+YfyO6ZvGmByuRzV7NgE9PLnrdPQTpsTNSt0/XAkzfXbBcCvsiygl7cJNchqpJu1+ojZnvTy1mEFBkc3MraisoYNq+auNa+lD/KSsiGS/zJWCL53l6yXg2yb65hrH0ov0PDoRSSLl/dedfgyQVa5lCCFskjE3wCIWucrtTuKhIek6XUbIQpcABE5hGfVdAODfLxT+rWc05/UHHbnZ+h2fNDnHndF5wyOtdmct652n9LOFIs2oANMxZqJuNFrNm5iuFj4MPheg9M4FowJ5Am46alkJ1stsUlT+FVR9wkSN/wWIElC8+PhW4EsCCO2ZytRt2loQKUk+XL+GV+XNYNwi7wdykqKN+kLKVnrl7tXzrE9JXoPTdhStIwbZ/VCntlIEt8FTinZ+E+EntNdUnDlmPZd/eGcDhpy+fCRohuTEwEaXPMkIUxlfBF21Z+TPK0bEkvwT+x7UFr/hus9wePNMDcA05SjftrjTyJutAw6QJ1MokF5MiBPRiZpwIA3/qMYtsU6Sp6cwTJWvDWdMZuLqgq2MV6qMG4S/fxhAkKw7ycD9w= + - secure: UpvDkP4MgNeqUdFrxz/NOJjchd+R+QW9d+sUr/TdC34ukzNhKzgHeFSRc+abMOKjb6XjDfFOR30l6lKDad14v8gBE/xcakugkCeKeUrROJOvGqFZ+HJI2zl3XGrCbT4ZowhwmEYQxBuzxTijBGPIyXehXK3j9F+KhsQ9u+PxcKwWd43FKSB8Cte8gWFS6NV2Es5rZvmiSsrDj15TAGWxfPlZ2UbI0JFQ1/Y+exH9CNidW+VTuvqdyPZIkLgmVRcm5ZUgcR71iT1zw5B2wOhMWzxK8EY1k0ed3gbPSdkMy5D2J5sWALAEII5f9/IaAzCjgW/HZR6AnZ71CEjlQdtBQZZUYSOTtfJOR4jUjsA5ce3+bR5wy5/gzcA6hLrY9tS6OdWmNfHl/mY+cjaDcPhkmpKpOVjmdytEq7Z6M+vzM50Th8Uyv9FP0WWzdWqSaYaXMO9+3/joa8UnnByzCgq/FCLuVUIPGxGqOSkb/7um9HZyd+GeNxYWq9CtWwMKY1JjsT+FHceSEJMl5LbCHf6FGnimAeShegba5P0PlGKeII2sMnT50wGYviykdi7Z2bO5jCeegrCCL6kwxQHfX1Y4c5iAFl9qEykUwyPPo5Y+Kw5Ye01bZ8FHiI8nA8NY77byjOubivncKG5CEr+dM9Slu68psP8e69KuJ6Xw8LSr3Lw= + - secure: vuY4UsnnZhsvXN1hfuVvypUD1MazfmHNDD7sUhwOR5F02T4sR8aIe5HR5ZEAu+cbNdocydd9UYsYRE+VY5krM/z6g4+J2NBV9ZDxNKF/p3oK0R4C2vT6XfxTlEieqfzlE8Pit6f7q9n+/+xebKs4RDX9b+lXmB6NcCd4FjXZxtR00Jt70a3F0x0qHp4Qa8ikfsNcHoihFl3vnRBnd429N6Jj/3ftGN01Ofd91kvyqFvhbk3o1xqxIbkZCvkbj/CrXJ49mkKQVETkorXrYY/snrW+i7z7JpFmcxU+zwxxRDfULr02V5g82IJOQSMaXZYoIcNtFdn1G3zaRnWaOSBWCAMybwZzjl+3c0VDTtHPNrkdbsSRv2xPf+zMKMJ9PZ7w+UDC2L4r6sC1cbYDVg+6SrDO83bIeWeUJyK8BpSx0n2tvoQI7fxyz52wSFpshY+XQ/yT7EA4GGo7+XailtleqeQXyBe+deupW1tNVX0z/cyA3xTsrsZIALGOgSaZUHMdlXBg0pVRNdSdTyZB2NbHt3QbzmrrChIzQwk0BDjo8S/aoHuSCRVeC34quFdnuKZflDRrF0Z0AEO/CF9G81UJ7SRkz53J2aj18ssEFJ/2TNU3jtZAbdVoFtTiudd4uz00/ku6IcAq6sl3CkskEFkqLPZYlom0pOhfP3ef5pjKHK4= + - secure: DBqpYIXv/bT66KhhqEuwQbIdxCmqli8jg7Sk3m0ZnYs3Z2DVHkRte/Y3wswZiqE+nujX8qHcMzvGk9NN8l30Mv6jAfMgewNWW/Xu2oJ6ZAZm+cZRMskhMP/5TzVs3RmOz21rbODpHwA6DLVoebfg13JaFNMg8I+FoeBV8TqlAoElLztW9wkx7jZkmeXEJK1DWocGVtI08GSbCo6CqZDRIgf1SxqOQuHyCCAJ9EFVn6lxuUtnZNMWMPvbAsyh3WKsP97XxC3uzZq+7BUkynM9XlZ3mHYkYZWvoz/qn2sYXYV9y3Wn6+BHGvDW7R4coSp3dpSkvBna0aPO9wpBVkHss6lDYeRz65z6XOr2X2inLonoBmcg21ChIo45v1vs0OHP5jS2KroS+X2UVxhRbY4HuyN/0k3UmAnEO5h3864yXX00I/8oCbvARNl2pFjWObcBcGEUfue79t0jBz7hVlPHdfrSrRdDxKbxzeLxR9s1sKxqR2xLt/xEl72KTaK41liwAicc/2gvt3dGXQhrXEHRg0fGjU2HN6fywKOA2InnCAXRIA9ql5+0VsYiqZyy32AZaaXoXxF4zLAJlfh++wv+s05uzrwOAvonZqKIVS1eJNyLr51Xc0nV2lG5VCrm900raPw4h8toaA0zLuBNEiuOld+eFHowIRrD4o12JJ29eCs= + - secure: mL9ZdIOnl0g5II4rQhwKTeSvKEPonzaj2FUGKbSOEwx0Ol89PdkvbZccKgucnIzeDyijx0Qf/icCdtCQd1bm0m5AJGV+JPUfqKbOUV6kh/DIOSrI6xlnewyjVbwPfe00bgaiOcTvnP80aoy44JEQu6SyAiXqm8n+pllIyRksPjxAHq+Ozsf35OZBFXm5ZufYjGa4E4JPgxA/cGY+WBWWaYy6TfoB7vLASzWinYwJBj2KjmNjqSnz23RF3HqG4hUQWz5W0D0SONrtedTkmYZ5r/NKlDmD4Tzs/pVd1giM8Z4mU0lbIV+5hZEBXNyBmiFSCHB3C7utomflsmmG/KDUC3Okh8Xyh4F7L64QYs8IV4bjMKkE6YeiZOjnQHWwIFkRDOrIvgw/7mlj4Q4UFIi3c4LPrzrGcA+Q2BDTfk0KDi/p75+Nq4PQeZlN+wwj7/ViTTKCEnBtudlafqQHWA6RgUmyFwFsA5Gh8QKinB0o/M9RStAjPerjxmVfMtZt54o92OMFaPAS4xbvh2u2TWLDZsuBSsKBzU8pDkBMeQl3IypSIYCZxv1I/6g6yHSGbN+3SUNwWPnq8iv9U7aYqDw5/Ni9LhC79/xXr9s4LZTo51BrWT+rPOHzhev7s9rNJbM+6WgTscblHjK+OJxUkP0SzdklZEhLBF/5X6ATz8UoYlk= + - secure: Hkgc3LS2+ihYneHyG9ib7scO9eTvH3VhxHmlU1FI6rFjkbz6oLvM1SaJdnpo/4enIiPLSjVzd5p50zuEP46tJBBMRboJZb4/vTfb0QFA5VwtAiQoHyiyDO7x/6j5uGB7EJc102u/IUjC5EeM5MlsQYoE47E37K5ojNUNOQin7PLAWiDxwJ6C8DWzhhTT3D4T7v0zpdxyOQTDHhqE/CwwqaCGvBZxGxrjwtAIHXGekX2yknXgy4OHHvZBnInyHT+QP1oOXLsHp8ZtLaT4UZk96VSsjGM6CzavUm3OG8qx+sU/5Vin5Y/1pN/yVgzHayctKIw5q632FeyD/jtaLgsibUbszr0y7K+of/9LMzAYZEHdEmBXYjxdoqVDChxKgiLg1nCoQ2tETTJpmfrsSSgFmeCfBEFmZCpascigYnFqfbyBu9vwppvihdogm66VMqwU2FXPmOczNopYZYpO6yJcuMEbiEJdYYimpIByZukXdQX8q/h1kx6dcBK+kSnFoBWFcQbaPblOIVqHuO5O76b0Xl1AtS/VYIUfa6dlbzKNnyR+krxf4WFUep/X8vZpbegP4nE+VDX1aPMDTWy3ew6uP+lThFBJsmQGabX1PU9s1iVwb2Z+lgukPtdPfj2RLpLq/IzwuG64a4XstxRKnbhtHlfdvmrbRXCoq2uCerd+BBc= + - secure: vR9kk75nDGKTbBCaPLwH3bSkkUnu9o/Mr1rU7gBTyG9L3s6SzHQkUvxLVpjEuEgzx4mpqBaJIzbNA7txCnZkvQ5vfozcuPmrfXTaifw5MpIAi2a1uSN2ctZh32bhR5m+OfPGvmKyXMRvgQSlG3cO+lekWRvfNAwhU+vMwi9E2HBXVXBifJMWTg3aFU6wISGRea7OhFdfb5ROpFVqR3chAAevnd4UeOjY7VczZx/w8MeyhzYqNgiZJ5q132vpapAsKEBQBM6HJAdRgmlLYEZHtjh9ROD+dnZSmUAEj/NdHqglBMif0yW4W5X+SKWLF1xEUkYXT0/tPzYXHwcHsStnKUJhKmdlOuNYSzX+q75QNbi+yxNnqWcSeMNtN5E7n/MYsRDQVY1uo35q1C2iTOWHrWaGE8RJrSXPxdh7Lq0wFVSm8ofUW9+vBCFZ0vxspngJqIaJpZyPbOxKvWADd+r14j6cQFo+FL+QNUO1uQLz4XPrJW6sU6uT2MTBDDTQdH4tWtswpAuc475d389FRsnyiKd/c886rRULIUq6oRI5fMlFe+8WLw3QAzs22toH2xgXChSkHnBF71/FAW+79v+lOmy8J5kE7ZJcROq8meBWH4ASk089FRWv3wjjOVvTXx0O6XU+7jmS5fLPEtpoanuNdFfPGzN6e1JLbwElP2UwpY8= + - secure: ea6+BvenROTG7giVAJiMVsGPX2QLbl+NVEN09cxq6gZJIqsdr+UN6S8RGMTNAZibmeagj5gMF4fQ2tOwgCl5mUfFStk3dXeZYJ6bu358KLrDvqTELoQr6pMgsWc04L0dd/7xUBH0krMc4XCkOT8aNdNif+UCjPoIBEKuKBSviVoEfxVwh7cTb5GJ1UmozqTZlxf0/2lBVNC8EYylHWyhQ7Si94CNQbOlvqq97EOjA7qN7y3m6IpLvkOZ0JUqc5gQcdETtocIbgc6crrSv5c1cUAmryL61gA25qPyedydK2uRsgNgO+JfZdN3+JoKWhyKhfNzvnMITk0tneEYYgLSauEt1b/+loYbd5yNIhvKcynKsUTkzafyVV1dMmz/Bm9qS6DGjdixHK/UTzXThjyUVmzMpuAPrRdEHq1r/1R7rmgPrPR2TWCu3zDhvWSbh4jfJ7tQaue9+7pzJ0VOYxfnwLX1c2cP6CkZFgy9tUsmrLARS54f2BK8dWW2uqJFQre4+IlKKznn5eIuyxzH0dbkNU1gM9UX7RYV3IM5fM54H2ANoF2z9PsHmJw0f9bSp+Z4rAGZ8PVInx3gH6t7dp2cU3suK2+sJUhzkBDXzd/DqCdbM75IFTyPGrMOVb+MXQ+MqBwzp7NCb40nO6Ob+0VmmvmhzdZu/sjjyZ2rC+5c+qg= matrix: secure: aqcGPDGxdYhbW6Cq6JFKHmyjYBqkDgDlONgM43o5ES1yIiqdFG0LySL75XE2QBocyyG/a1tNMj6aX4nrLrsukmNlEgXieWjuQs3EaTjVHiw+JWhU6yDcI4PqF4ZoD3ANdghxkBWQ8YjL+tfHfwCSnewI0UnRBsrb76Yv81o/Frkr+1zAkw4kG/6ubS5eRGcA9xOOvo0VDkz8HtsWnbxfAfLMBhsPICmdj7KlZJZY2hjfD+okf3DcKQqZZPi7bqfDb7AGmTKufX2QUVFRbT1p6yp+xH5hOB5QK2/2YeSXNntkzO2NhXsiyVybBTAhnulTsQMbCv6RELmXIm0/BHS0bhPzrK8NbQJPrYHPVYV93Z3S4Lpg1rzPAPIDf124V7CAcTYZEXgjS6YRUMMPwA0GJJDbNuJnwGiVGL4J95dkllf9h2BSym0zsPhtU94OPQvWs3VyfaK3uz6z9X/CmeeCuhOcPYV9NWX5J+6swI/tttYdnNdOa5j5PCoVtRD6hIB0YsBpxLSOIOjugXyQXNn+rWQbvoTZjQMPInAk/Bhav5IGXC+Xa2MesXJh6Nu7WXdwXxXEwVNJ4/jJULNsP7NTer0G43akzkI4m4Fairw3mo8HXdhQ+nmMyUU9xqmPWvgSNsI9JiBxTMu8xmN36cdmNiBqW+GWa0iBzlXTq2zK9lQ= diff --git a/packages/kinvey-html5-sdk/runner-config.js b/packages/kinvey-html5-sdk/runner-config.js index e0d221dd0..7bd86d12f 100644 --- a/packages/kinvey-html5-sdk/runner-config.js +++ b/packages/kinvey-html5-sdk/runner-config.js @@ -24,6 +24,10 @@ const commonTests = walk(path.join(rootMonoRepoPath, 'test', 'integration', 'tes filter: jsFilesFilter, nodir: true }); +const shimSpecificTests = walk(path.join(__dirname, 'test', 'tests'), { + filter: jsFilesFilter, + nodir: true +}); function runPipeline() { const runner = new Runner({ @@ -38,7 +42,7 @@ function runPipeline() { processTemplateFile( path.join(__dirname, 'test', 'index.template.hbs'), () => ({ - tests: commonTests.map(f => + tests: shimSpecificTests.concat(commonTests).map(f => `./${path.relative( path.join(__dirname, 'test'), f.path diff --git a/packages/kinvey-html5-sdk/test/tasks/serveTests.js b/packages/kinvey-html5-sdk/test/tasks/serveTests.js index 9635fa4d5..c4571bf08 100644 --- a/packages/kinvey-html5-sdk/test/tasks/serveTests.js +++ b/packages/kinvey-html5-sdk/test/tasks/serveTests.js @@ -12,7 +12,7 @@ const serveTests = (appRoot, runner) => serve(req, res, done); }); - server.listen(0, () => { + server.listen(64320, () => { const staticPort = server.address().port; runner.emit('serve.static', staticPort); console.log(`Serving static files on port: ${staticPort}`); diff --git a/packages/kinvey-html5-sdk/test/tasks/webRunTests.js b/packages/kinvey-html5-sdk/test/tasks/webRunTests.js index 3d5d75d18..9ca67d1cd 100644 --- a/packages/kinvey-html5-sdk/test/tasks/webRunTests.js +++ b/packages/kinvey-html5-sdk/test/tasks/webRunTests.js @@ -8,12 +8,12 @@ const webRunTests = (staticPort, runner) => if (os.type() === 'Windows_NT') { opn(args[0], { - app: ['chrome', '--incognito'] + app: ['chrome', '--incognito', '--disable-web-security', '--disable-popup-blocking'] }) .then(resolve) .catch(reject); } else { - const chrome = spawnHeadlessChromium(args); + const chrome = spawnHeadlessChromium([args[0], '--incognito', '--disable-web-security', '--disable-popup-blocking']); chrome.stderr.on('data', d => reject(d.toString())); resolve(); } diff --git a/packages/kinvey-html5-sdk/test/tests/mic.tests.js b/packages/kinvey-html5-sdk/test/tests/mic.tests.js new file mode 100644 index 000000000..8dc8e6b09 --- /dev/null +++ b/packages/kinvey-html5-sdk/test/tests/mic.tests.js @@ -0,0 +1,263 @@ +function testFunc() { + + //the same redirect url should be configured on the server + const serverHost = 'auth.kinvey.com'; + const redirectUrl = 'http://localhost:64320/callback'; + const { authServiceId } = externalConfig; + const micDefaultVersion = 'v3'; + + //the used OAuth 2 provider is Facebook + const { fbEmail } = externalConfig; + const { fbPassword } = externalConfig; + const fbDevUrl = 'https://developers.facebook.com'; + const fbUserName = 'Gaco Baco'; + const fbCookieName = 'c_user'; + const fbCookieValue = '1172498488'; + + const { collectionName } = externalConfig; + const networkstore = Kinvey.DataStore.collection(collectionName, Kinvey.DataStoreType.Network); + + + // The configured access_token ttl is 3 seconds on the server for the default auth service + const defaultServiceAccessTokenTTL = 3000; + + // Currently the server returns refresh_token = 'null' if the auth service does not allow refresh tokens. + // The tests should be changed when this is fixed on the server + const notAllowedRefreshTokenValue = 'null'; + const invalidUrl = 'invalid_url'; + const shouldNotBeInvokedMessage = 'Should not happen'; + const cancelledLoginMessage = 'Login has been cancelled.'; + let winOpen; + let actualHref; + + const getExpectedInitialUrl = (appKey, micVersion, redirectUrl) => { + return `https://${serverHost}/${micVersion}/oauth/auth?client_id=${appKey}&redirect_uri=${redirectUrl}&response_type=code&scope=openid`; + }; + + const validateSuccessfulDataRead = (done) => { + return networkstore.find().toPromise() + .then((result) => { + expect(result).to.be.an('array'); + done(); + }); + }; + + const expireFBCookie = (fbWindow, cookieName, cookieValue, expiredDays) => { + const newDate = new Date(); + newDate.setTime(newDate.getTime() - (expiredDays * 24 * 60 * 60 * 1000)); + const expires = 'expires=' + newDate.toUTCString(); + const domain = 'domain=.facebook.com'; + const newValue = `${cookieName}=${cookieValue};${expires};${domain};path=/`; + fbWindow.document.cookie = newValue; + }; + + const validateMICUser = (user, allowRefreshTokens, explicitAuthServiceId) => { + expect(user).to.deep.equal(Kinvey.User.getActiveUser()); + + const userData = user.data; + const kinveyAuth = userData._socialIdentity.kinveyAuth; + const metadata = userData._kmd; + + expect(userData._id).to.exist; + expect(userData.username).to.exist; + expect(userData._acl.creator).to.exist; + expect(metadata.lmt).to.exist; + expect(metadata.ect).to.exist; + expect(metadata.authtoken).to.exist; + + expect(kinveyAuth.id).to.exist; + expect(kinveyAuth.name).to.equal(fbUserName); + expect(kinveyAuth.access_token).to.exist; + + if (allowRefreshTokens) { + expect(typeof kinveyAuth.refresh_token).to.equal('string'); + expect(kinveyAuth.refresh_token).to.not.equal(notAllowedRefreshTokenValue); + } + else { + expect(kinveyAuth.refresh_token).to.equal(notAllowedRefreshTokenValue); + } + + expect(kinveyAuth.token_type).to.equal('Bearer'); + expect(typeof kinveyAuth.expires_in).to.equal('number'); + expect(kinveyAuth.id_token).to.exist; + expect(kinveyAuth.identity).to.equal('kinveyAuth'); + if (explicitAuthServiceId) { + expect(kinveyAuth.client_id).to.equal(`${externalConfig.appKey}.${authServiceId}`); + } + else { + expect(kinveyAuth.client_id).to.equal(externalConfig.appKey); + } + expect(kinveyAuth.redirect_uri).to.equal(redirectUrl); + expect(kinveyAuth.protocol).to.equal('https:'); + expect(kinveyAuth.host).to.equal(serverHost); + + expect(user.client).to.exist; + }; + + const addLoginFacebookHandler = () => { + // monkey patch window.open - the function is reset back in the afterEach hook + window.open = function () { + const fbPopup = winOpen.apply(this, arguments); + fbPopup.addEventListener('load', function () { + const setIntervalVar = setInterval(function () { + const email = fbPopup.document.getElementById('email') + const pass = fbPopup.document.getElementById('pass') + const loginButton = fbPopup.document.getElementById('loginbutton') + if (email && pass && loginButton) { + email.value = fbEmail; + pass.value = fbPassword; + loginButton.click(); + clearInterval(setIntervalVar); + } + }, 1000); + }); + return fbPopup; + }; + }; + + const addCloseFacebookPopupHandler = (retrievePopupUrl) => { + window.open = function () { + const fbPopup = winOpen.apply(this, arguments); + fbPopup.addEventListener('load', function () { + if (retrievePopupUrl) { + actualHref = fbPopup.location.href; + } + fbPopup.close(); + }); + return fbPopup; + }; + }; + + const resolveAfter = (timeInMs) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, timeInMs); + }); + } + + describe('MIC Integration', () => { + + beforeEach((done) => { + Kinvey.User.logout() + .then(() => { + var fbWindow = window.open(fbDevUrl); + fbWindow.addEventListener('load', function () { + expireFBCookie(fbWindow, fbCookieName, fbCookieValue, 3); + fbWindow.close(); + winOpen = window.open; + actualHref = null; + done(); + }); + }); + }); + + afterEach((done) => { + window.open = winOpen; + done(); + }); + + + it('should login the user, using the default Auth service, which allows refresh tokens', (done) => { + addLoginFacebookHandler(); + Kinvey.User.loginWithMIC(redirectUrl) + .then((user) => { + validateMICUser(user, true); + return Kinvey.User.exists(user.username) + }) + .then((existsOnServer) => { + expect(existsOnServer).to.be.true; + return validateSuccessfulDataRead(done); + }) + .catch(done); + }); + + it('should login the user, using the specified Auth service, which does not allow refresh tokens', (done) => { + addLoginFacebookHandler(); + Kinvey.User.loginWithMIC(redirectUrl, Kinvey.AuthorizationGrant.AuthorizationCodeLoginPage, { micId: authServiceId }) + .then((user) => { + validateMICUser(user, false, true); + return validateSuccessfulDataRead(done); + }) + .catch(done); + }); + + it('should refresh an expired access_token and not logout the user', (done) => { + addLoginFacebookHandler(); + Kinvey.User.loginWithMIC(redirectUrl) + .then((user) => { + expect(user).to.exist; + + // the test waits for the expiration of the access_token + return resolveAfter(defaultServiceAccessTokenTTL + 100) + }) + .then(() => { + return validateSuccessfulDataRead(done); + }) + .catch(done); + }); + + it(`should make a correct request to KAS with the default ${micDefaultVersion} version`, (done) => { + // Currently the error function is not called when the server returns an error - MLIBZ-2423, + // so the test is closing the popup in order to resume execution and validate the request url + addCloseFacebookPopupHandler(true); + + Kinvey.User.loginWithMIC(invalidUrl) + .then(() => done(new Error(shouldNotBeInvokedMessage))) + .catch(() => { + expect(actualHref).to.equal(getExpectedInitialUrl(externalConfig.appKey, micDefaultVersion, invalidUrl)); + done(); + }).catch(done); + }); + + it(`should make a correct request to KAS with the supplied options.version`, (done) => { + // Currently the error function is not called when the server returns an error - MLIBZ-2423, + // so the test is closing the popup in order to resume execution and validate the request url + const submittedVersion = 'v2'; + addCloseFacebookPopupHandler(true); + + Kinvey.User.loginWithMIC(invalidUrl, Kinvey.AuthorizationGrant.AuthorizationCodeLoginPage, { version: submittedVersion }) + .then(() => done(new Error(shouldNotBeInvokedMessage))) + .catch(() => { + expect(actualHref).to.equal(getExpectedInitialUrl(externalConfig.appKey, submittedVersion, invalidUrl)); + done(); + }).catch(done); + }); + + it('should throw an error if the user cancels the login', (done) => { + addCloseFacebookPopupHandler(); + + Kinvey.User.loginWithMIC(redirectUrl) + .then(() => done(new Error(shouldNotBeInvokedMessage))) + .catch((err) => { + expect(err.message).to.equal(cancelledLoginMessage); + done(); + }).catch(done); + }); + + it('should throw an error if the authorization grant is invalid', (done) => { + addLoginFacebookHandler(); + Kinvey.User.loginWithMIC(redirectUrl, 'InvalidAuthorizationGrant', { micId: authServiceId }) + .then(() => done(new Error(shouldNotBeInvokedMessage))) + .catch((err) => { + expect(err.message).to.contain('Please use a supported authorization grant.'); + done(); + }).catch(done); + }); + + it('should throw an error if an active user already exists', (done) => { + addLoginFacebookHandler(); + Kinvey.User.signup() + .then(() => { + return Kinvey.User.loginWithMIC(redirectUrl) + }) + .then(() => done(new Error(shouldNotBeInvokedMessage))) + .catch((err) => { + expect(err.message).to.contain('An active user already exists.'); + done(); + }).catch(done); + }); + }); +} + +runner.run(testFunc); diff --git a/test/integration/clean-up-user-collection.js b/test/integration/clean-up-user-collection.js new file mode 100644 index 000000000..9836f3ba2 --- /dev/null +++ b/test/integration/clean-up-user-collection.js @@ -0,0 +1,38 @@ +const request = require('request'); + +const cleanUpUserCollection = (config) => { + + // Set the headers + const headers = { + 'Authorization': `Basic ${Buffer.from(`${config.appKey}:${config.masterSecret}`).toString('base64')}`, + 'Content-Type': 'application/json', + 'X-Kinvey-Delete-Entire-Collection': true, + 'X-Kinvey-Retain-collection-Metadata': true + } + + const body = { collectionName: 'user' }; + + /// Configure the request + const options = { + url: `https://baas.kinvey.com/rpc/${config.appKey}/remove-collection`, + method: 'POST', + headers: headers, + json: true, + body: body + } + + // Start the request + return new Promise((resolve, reject) => { + request(options, (error, response) => { + if (!error && response.statusCode == 200) { + resolve(); + } + else { + reject('User collection cleanup failed!'); + } + }); + }); +}; + + +module.exports = cleanUpUserCollection; diff --git a/test/integration/configs/tests-config.js b/test/integration/configs/tests-config.js index d4762b507..bb556896e 100644 --- a/test/integration/configs/tests-config.js +++ b/test/integration/configs/tests-config.js @@ -1,31 +1,39 @@ const testsConfig = { - collectionName: 'Books' + collectionName: 'Books', + fbEmail: process.env.FACEBOOK_EMAIL, + fbPassword: process.env.FACEBOOK_PASSWORD, + authServiceId: 'decad9197f0f4680a46d902327c5c131' }; const appCredentials = { html5: { - appKey: 'kid_H1fs4gFsZ', - appSecret: 'aa42a6d47d0049129c985bfb37821877' + appKey: process.env.HTML5_APP_KEY, + appSecret: process.env.HTML5_APP_SECRET, + masterSecret: process.env.HTML5_MASTER_SECRET }, nativescript: { android: { - appKey: 'kid_ryEW0UQrM', - appSecret: '5a006e23a102442884dc9de5d2d2abc9', + appKey: process.env.NS_ANDROID_APP_KEY, + appSecret: process.env.NS_ANDROID_APP_SECRET, + masterSecret: process.env.NS_ANDROID_MASTER_SECRET }, ios: { - appKey: 'kid_By6pBd7BG', - appSecret: '7568820462ce40eeb1f56ec01a4cf2ae', + appKey: process.env.NS_IOS_APP_KEY, + appSecret: process.env.NS_IOS_APP_SECRET, + masterSecret: process.env.NS_IOS_MASTER_SECRET } }, phonegap: { android: { - appKey: 'kid_SyP_lFXBz', - appSecret: '997be7e8aae142fb89796352dc57b5be', + appKey: process.env.PG_ANDROID_APP_KEY, + appSecret: process.env.PG_ANDROID_APP_SECRET, + masterSecret: process.env.PG_ANDROID_MASTER_SECRET }, ios: { - appKey: 'kid_SyqplFXrz', - appSecret: 'c00090630ace4eb3956367a44319afcf', + appKey: process.env.PG_IOS_APP_KEY, + appSecret: process.env.PG_IOS_APP_SECRET, + masterSecret: process.env.PG_IOS_MASTER_SECRET } } }; diff --git a/test/integration/create-platform-specific-config.js b/test/integration/create-platform-specific-config.js index f1b14dc2c..95e5ace51 100644 --- a/test/integration/create-platform-specific-config.js +++ b/test/integration/create-platform-specific-config.js @@ -16,7 +16,8 @@ const getCredentialsByEnvironment = (appConfig, platform, os) => { const app = appConfig[platform][os] || appConfig[platform]; return { appKey: app.appKey, - appSecret: app.appSecret + appSecret: app.appSecret, + masterSecret: app.masterSecret }; }; @@ -27,6 +28,7 @@ const createPlatformSpecificConfig = (platform, os) => { const configFileContents = compiled({ appConfig: JSON.stringify(testsConfig, null, 2) }); fs.writeFileSync(resultConfigFilePath, configFileContents, 'utf8'); + return testsConfig; }; module.exports = createPlatformSpecificConfig; diff --git a/test/integration/run-tests.js b/test/integration/run-tests.js index 7110c90e9..d40f959bb 100644 --- a/test/integration/run-tests.js +++ b/test/integration/run-tests.js @@ -1,6 +1,7 @@ const path = require('path'); const program = require('commander'); const createPlatformSpecificConfig = require('./create-platform-specific-config.js'); +const cleanUpUserCollection = require('./clean-up-user-collection.js'); program .option('--platform [type]', 'Add Platform [html5/phonegap/nativescript]') @@ -9,10 +10,13 @@ program const runnerConfigFilePath = path.join(__dirname, '../../', 'packages', `kinvey-${program.platform}-sdk`, 'runner-config'); -createPlatformSpecificConfig(program.platform, program.os); +const config = createPlatformSpecificConfig(program.platform, program.os); -const runPipeline = require(runnerConfigFilePath); -runPipeline(program.os) +cleanUpUserCollection(config) + .then(() => { + const runPipeline = require(runnerConfigFilePath); + return runPipeline(program.os) + }) .then(() => { console.log('The tests passed successfully!'); if (program.platform === 'html5') { diff --git a/test/integration/tests/sync.test.js b/test/integration/tests/sync.test.js index fddee71c9..7194e1dde 100644 --- a/test/integration/tests/sync.test.js +++ b/test/integration/tests/sync.test.js @@ -273,7 +273,7 @@ function testFunc() { syncStore.clearSync() .then(() => storeToTest.pendingSyncEntities()) .then((entities) => { - expect(entities).to.be.an.empty.array; + expect(entities).to.be.an('array').that.is.empty; done(); }) .catch(done);