diff --git a/build-ios.sh b/build-ios.sh index e0e0ad0425..1d833f6be1 100755 --- a/build-ios.sh +++ b/build-ios.sh @@ -80,6 +80,7 @@ ionic cordova plugin add https://github.com/Sunbird-Ed/jjdltc-cordova-plugin-zip ionic cordova plugin add cordova-plugin-sign-in-with-apple ionic cordova plugin rm cordova-plugin-inappupdatemanager ionic cordova plugin add https://github.com/subranil/cordova-plugin-inappupdatemanager.git +ionic cordova plugin add https://github.com/521dimensions/cordova-plugin-screen-orientation ionic cordova plugin add ionic-plugin-deeplinks --variable URL_SCHEME="${URL_SCHEME}" --variable DEEPLINK_SCHEME=https --variable DEEPLINK_HOST="${DEEPLINK_HOST}" #Temporary Workaround to generate build as webpack was complaining of Heap Space #need to inspect on webpack dependdencies at the earliest diff --git a/build_config b/build_config index 884aa3df74..1326614a5b 100644 --- a/build_config +++ b/build_config @@ -13,7 +13,7 @@ cordova-plugin=cordova-plugin-network-information cordova-plugin=cordova-plugin-statusbar cordova-plugin=cordova-plugin-webview-checker cordova-plugin=https://github.com/adriano-di-giovanni/cordova-plugin-shared-preferences.git -cordova-plugin=https://github.com/katzer/cordova-plugin-local-notifications.git +cordova-plugin=https://github.com/fquirin/cordova-plugin-local-notifications.git cordova-plugin=https://github.com/Sunbird-Ed/sb-cordova-plugin-fcm.git#release-5.0.2 cordova-plugin=cordova-plugin-advanced-http cordova-plugin=cordova-plugin-android-permissions @@ -40,7 +40,7 @@ cordova-plugin=cordova-plugin-media cordova-plugin=cordova.plugins.diagnostic sunbird-cordova-plugin=https://github.com/project-sunbird/cordova-plugin-openrap.git sunbird-cordova-plugin=https://github.com/project-sunbird/cordova-plugin-qr-scanner.git#release-5.0.2 -sunbird-cordova-plugin=https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#release-5.0.2 +sunbird-cordova-plugin=https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#release-5.1.1 sunbird-cordova-plugin=https://github.com/project-sunbird/sb-cordova-plugin-sync.git sunbird-cordova-plugin=https://github.com/subranil/cordova-plugin-inappupdatemanager.git#release-3.7.0 cordova-plugin=uk.co.workingedge.phonegap.plugin.istablet diff --git a/config.xml b/config.xml index 49d4f79498..f50b745744 100644 --- a/config.xml +++ b/config.xml @@ -18,12 +18,14 @@ + + @@ -176,7 +178,7 @@ - + diff --git a/package-lock.json b/package-lock.json index 9c6e98b1b2..45d865a010 100644 --- a/package-lock.json +++ b/package-lock.json @@ -515,9 +515,9 @@ } }, "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -1372,27 +1372,27 @@ } }, "@babel/compat-data": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", - "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", "dev": true }, "@babel/core": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", - "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", + "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.3", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.3", - "@babel/types": "^7.19.3", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1401,12 +1401,12 @@ }, "dependencies": { "@babel/generator": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", - "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", + "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", "dev": true, "requires": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.2", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" } @@ -1479,21 +1479,21 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", - "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.19.3", + "@babel/compat-data": "^7.20.0", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.21.3", "semver": "^6.3.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", - "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz", + "integrity": "sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", @@ -1501,7 +1501,7 @@ "@babel/helper-function-name": "^7.19.0", "@babel/helper-member-expression-to-functions": "^7.18.9", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6" } }, @@ -1612,19 +1612,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" }, "dependencies": { "@babel/template": { @@ -1650,9 +1650,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", - "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true }, "@babel/helper-remap-async-to-generator": { @@ -1681,21 +1681,21 @@ } }, "@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "requires": { - "@babel/types": "^7.19.4" + "@babel/types": "^7.20.2" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" } }, "@babel/helper-split-export-declaration": { @@ -1751,14 +1751,14 @@ } }, "@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", "dev": true, "requires": { "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" }, "dependencies": { "@babel/template": { @@ -1838,9 +1838,9 @@ } }, "@babel/parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", - "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -1864,9 +1864,9 @@ } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz", - "integrity": "sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", @@ -1897,13 +1897,13 @@ } }, "@babel/plugin-proposal-decorators": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.19.3.tgz", - "integrity": "sha512-MbgXtNXqo7RTKYIXVchVJGPvaVufQH3pxvQyfbGvNw1DObIhph+PesYXJTcd8J4DdWibvf6Z2eanOyItX8WnJg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.2.tgz", + "integrity": "sha512-nkBH96IBmgKnbHQ5gXFrcmez+Z9S2EIDKDQGp005ROqBigc88Tky4rzCnlP/lnlj245dCEQl4/YyV0V1kYh5dw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-create-class-features-plugin": "^7.20.2", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/plugin-syntax-decorators": "^7.19.0" @@ -1970,16 +1970,16 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", - "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.19.4", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-plugin-utils": "^7.19.0", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.1" } }, "@babel/plugin-proposal-optional-catch-binding": { @@ -2099,12 +2099,12 @@ } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-syntax-import-meta": { @@ -2198,12 +2198,12 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-arrow-functions": { @@ -2236,27 +2236,27 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz", - "integrity": "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz", + "integrity": "sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-classes": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", - "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-compilation-targets": "^7.20.0", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" } @@ -2271,12 +2271,12 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz", - "integrity": "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-dotall-regex": { @@ -2347,39 +2347,36 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", - "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-module-transforms": "^7.19.6", "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-validator-identifier": "^7.19.1" } }, "@babel/plugin-transform-modules-umd": { @@ -2422,12 +2419,12 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz", + "integrity": "sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-property-literals": { @@ -2459,9 +2456,9 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz", - "integrity": "sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.18.6", @@ -2519,14 +2516,14 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz", - "integrity": "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz", + "integrity": "sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-typescript": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.20.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" } }, "@babel/plugin-transform-unicode-escapes": { @@ -2549,18 +2546,18 @@ } }, "@babel/preset-env": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz", - "integrity": "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dev": true, "requires": { - "@babel/compat-data": "^7.19.4", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-plugin-utils": "^7.19.0", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.19.1", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -2569,7 +2566,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.19.4", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", @@ -2580,7 +2577,7 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -2593,10 +2590,10 @@ "@babel/plugin-transform-arrow-functions": "^7.18.6", "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.19.4", - "@babel/plugin-transform-classes": "^7.19.0", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.19.4", + "@babel/plugin-transform-destructuring": "^7.20.2", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -2604,14 +2601,14 @@ "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.0", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", "@babel/plugin-transform-modules-umd": "^7.18.6", "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-parameters": "^7.20.1", "@babel/plugin-transform-property-literals": "^7.18.6", "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", @@ -2623,7 +2620,7 @@ "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.2", "babel-plugin-polyfill-corejs2": "^0.3.3", "babel-plugin-polyfill-corejs3": "^0.6.0", "babel-plugin-polyfill-regenerator": "^0.4.1", @@ -2656,11 +2653,11 @@ } }, "@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" } }, "@babel/template": { @@ -2675,30 +2672,30 @@ } }, "@babel/traverse": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", - "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.4", + "@babel/generator": "^7.20.1", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.4", - "@babel/types": "^7.19.4", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" }, "dependencies": { "@babel/generator": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", - "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", + "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", "dev": true, "requires": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.2", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" } @@ -2721,9 +2718,9 @@ } }, "@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.19.4", @@ -3029,9 +3026,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" } } }, @@ -3555,9 +3552,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -3750,17 +3747,19 @@ } }, "@project-sunbird/client-services": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@project-sunbird/client-services/-/client-services-5.0.0.tgz", - "integrity": "sha512-O7gWLhbZUevkIVbVhmUmFoo0GgKOuotVJcgt0H2otpXfGFNA45bGFolQHmBI4xzXcfdBQ+6XRjgMua3/7AoS0w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@project-sunbird/client-services/-/client-services-5.0.1.tgz", + "integrity": "sha512-iGP12/cu1PJxFabt/rbouhdwT6hHJDZGc3g3fnIKoMNJqaubwAcxppUq6dj7Vx2+dY5bbO6cMfwRuEzNCKCJ9g==", "requires": { "@project-sunbird/telemetry-sdk": "0.0.26", + "dayjs": "^1.8.20", "inversify": "^5.0.1", "jsonld": "^5.2.0", "jsonld-signatures": "^6.0.0", "jszip": "^3.7.1", + "moment": "^2.29.4", "node-fetch": "2.6.5", - "qs": "^6.9.4", + "qs": "^6.9.7", "reflect-metadata": "^0.1.13", "whatwg-fetch": "^3.1.0" }, @@ -3773,13 +3772,18 @@ "grunt-karma": "^0.12.2", "karma": "^3.0.0" } + }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" } } }, "@project-sunbird/common-consumption-v9": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@project-sunbird/common-consumption-v9/-/common-consumption-v9-5.0.0.tgz", - "integrity": "sha512-0OZHkYQ/QOq3G+M13gqEv+UTwbrTlhwJuPT3bgzH8HiMTKBHaXZezMpLAgcAfDGK74u5aDE5jqmHwiLLMECXvA==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@project-sunbird/common-consumption-v9/-/common-consumption-v9-5.0.1.tgz", + "integrity": "sha512-+su+ihlx0EGLca6RWqt8wHH9Jen80Y9NSVpSMZMRjbhzp8k/o7NcsH1tB7b9rqr2n0+1niMBEGjZvR3Anu8DEg==" }, "@project-sunbird/common-form-elements-v9": { "version": "5.0.3", @@ -3859,9 +3863,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" } } } @@ -3881,9 +3885,9 @@ "integrity": "sha512-61jigtq4ertmh9XhnSDQlfZDSn1a8TXr7PvMxmJVWlzp4uoXHudZRzPoIi4IpvKD4AEk8nFRjIMc5eSsM5UJhw==" }, "@project-sunbird/sb-themes": { - "version": "0.0.82", - "resolved": "https://registry.npmjs.org/@project-sunbird/sb-themes/-/sb-themes-0.0.82.tgz", - "integrity": "sha512-4joheQxIKVgR4dNL1X+vmHaFL2ynjfdTsq5jG+g/t9qBcfspx5GLkv/yAG+oKoLkWP+F2oH3Ev78y5ErQclJ5g==", + "version": "0.0.86", + "resolved": "https://registry.npmjs.org/@project-sunbird/sb-themes/-/sb-themes-0.0.86.tgz", + "integrity": "sha512-+vlnoIbrgzAA+xicdebGDfyczRHpdJ3Zch7krm5ebaXkNOFiBimoRjxi2pkYe8NxoXNloMwICMJxi9pdFjJjsg==", "dev": true }, "@project-sunbird/sunbird-epub-player-v9": { @@ -3929,29 +3933,49 @@ } }, "@project-sunbird/sunbird-sdk": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-sdk/-/sunbird-sdk-5.0.0.tgz", - "integrity": "sha512-cr6O5prywk1vsmn22otFSv6lQ5Hr0jWKPY6khzSNQBJlfSIFk8pISf4FlA+kEu4J1ZwITtHa8+3LiiAJIGP1zQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-sdk/-/sunbird-sdk-5.1.1.tgz", + "integrity": "sha512-2EHbBSZ9bz7GUvFDQ0hWFiPb0h2jTWzsaBIP7TkSzXcZ7pFquxX/h2Tcz2zaNUuFGzKigraTscKziMnqvSZ/5w==", "requires": { "crypto-js": "3.1.9-1", "dayjs": "^1.8.20", - "inversify": "^5.0.1", + "inversify": "^5.1.1", "jsonwebtoken": "^8.5.1", - "node-fetch": "2.6.5", + "node-fetch": "2.6.7", "pako": "^1.0.11", - "qs": "^6.9.1", + "qs": "^6.9.7", "reflect-metadata": "^0.1.13", "typescript-collections": "^1.3.3", "uuid": "^3.4.0", - "whatwg-fetch": "^3.0.0" + "whatwg-fetch": "^3.6.2" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + } } }, "@project-sunbird/sunbird-video-player-v9": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-video-player-v9/-/sunbird-video-player-v9-5.0.4.tgz", - "integrity": "sha512-0OhQi8Ahjo1uHlPgx73a+qFIKgpGmn/8pOXTHtbsZ7N0yY/yd9PLnLUbJbWFPMa3gfQQJlei2dCUwrYN26n8kA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-video-player-v9/-/sunbird-video-player-v9-5.1.3.tgz", + "integrity": "sha512-4aruDiyXNiEG4dZLDxlXtCSDYlq/VDNMeaGdH/0WcBCfBfH78XrsFAcvd+UPR1A4vm8mWOZSDKSXZZ092frCwQ==", "requires": { - "@project-sunbird/sunbird-player-sdk-v9": "4.6.4" + "@project-sunbird/sunbird-player-sdk-v9": "5.1.0" + }, + "dependencies": { + "@project-sunbird/sunbird-player-sdk-v9": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-player-sdk-v9/-/sunbird-player-sdk-v9-5.1.0.tgz", + "integrity": "sha512-Vg3MXOkBC7c1px9pTX58T0wqkseLAw0kWLflA8CpttIUQYFJBxxu5wEB8z8Y5zb6zDFrykxWRbPEcbdYdfOKRw==", + "requires": { + "ally.js": "1.4.1" + } + } } }, "@project-sunbird/telemetry-sdk": { @@ -4009,18 +4033,18 @@ "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" }, "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz", + "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@stencil/core": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.18.1.tgz", - "integrity": "sha512-/fXkh1lwZ+X9JQCw50mPjhBogzEHOBvVC5pLoDLZqodVYK0DGWILM2YLV4dcIUBNEK8/HMDpO/Rq81/rS3mNOw==" + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.19.3.tgz", + "integrity": "sha512-rb6pBWTfD5xDg5M/uEJUeclatE/tqBE3zCCNrEB47AtdkNCzC9fOikdzCYbpdAEpU6GvC60REFr0bd8QFUjn3Q==" }, "@tootallnate/once": { "version": "1.1.2", @@ -4029,9 +4053,9 @@ "dev": true }, "@types/babel__core": { - "version": "7.1.19", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", - "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", + "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -4475,9 +4499,9 @@ } }, "@xmldom/xmldom": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.6.tgz", - "integrity": "sha512-HHXP9hskkFQHy8QxxUXkS7946FFIhYVfGqsk0WLwllmexN9x/+R4UBLvurHEuyXRfVEObVR8APuQehykLviwSQ==" + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.9.tgz", + "integrity": "sha512-yceMpm/xd4W2a85iqZyO09gTnHvXF6pyiWjD2jcOJs7hRoZtNNOO1eJlhHj1ixA+xip2hOyGn+LgcvLCMo5zXA==" }, "@xtuc/ieee754": { "version": "1.2.0", @@ -4655,9 +4679,9 @@ "dev": true }, "android-versions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.8.0.tgz", - "integrity": "sha512-2diLBcg3J4sGesUCl/3wkcHhTUOPDXptlXjj/m48yEC0TuVNaEfzgrXQYpqnqj1b5bId9HsAXvR9HaO/mHDcCw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.8.1.tgz", + "integrity": "sha512-5a0YyylAk6pPM2Ezi0vWaPPNbS6tSNRs+micbgk5NpHEN5YW1ez+T94G5orysfwBEBDMHoxm5GNc5ZDUPgRrhw==", "requires": { "semver": "^5.7.1" }, @@ -4917,13 +4941,13 @@ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" }, "array.prototype.reduce": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", - "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-array-method-boxes-properly": "^1.0.0", "is-string": "^1.0.7" } @@ -5013,9 +5037,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true } } @@ -5226,9 +5250,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -5238,15 +5262,6 @@ } } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, "babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -6016,9 +6031,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001420", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001420.tgz", - "integrity": "sha512-OnyeJ9ascFA9roEj72ok2Ikp7PHJTKubtEJIQ/VK3fdsS50q4KWy+Z5X0A1/GswEItKX0ctAp8n4SYDE7wTu6A==", + "version": "1.0.30001431", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", + "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true }, "canonical-path": { @@ -6049,9 +6064,9 @@ }, "dependencies": { "core-js": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz", - "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==", + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==", "optional": true } } @@ -6989,9 +7004,9 @@ } }, "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -7070,9 +7085,9 @@ } }, "cordova-common": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-4.0.2.tgz", - "integrity": "sha512-od7aNShyuBajzPY83mUEO8tERwwWdFklXETHiXP5Ft87CWeo/tSuwNPFztyTy8XYc74yXdogXKPTJeUHuVzB8Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-4.1.0.tgz", + "integrity": "sha512-sYfOSfpYGQOmUDlsARUbpT/EvVKT/E+GI3zwTXt+C6DjZ7xs6ZQVHs3umHKSidjf9yVM2LLmvGFpGrGX7aGxug==", "requires": { "@netflix/nerror": "^1.1.3", "ansi": "^0.3.1", @@ -7132,8 +7147,7 @@ }, "cordova-plugin-app-version": { "version": "0.1.14", - "resolved": "https://registry.npmjs.org/cordova-plugin-app-version/-/cordova-plugin-app-version-0.1.14.tgz", - "integrity": "sha512-HN6Yz6IIdRO+iMvCHN/qMe8/O4miOpHH/pDtWNjIYTjV3MzP+XdzFJoFnq2zxlNNXFz0Zn8REGQhFY77vV4AWQ==", + "resolved": "git+https://github.com/sampart/cordova-plugin-app-version.git#cac2431c96f52501dc5fe7b90479e9fe5f409525", "dev": true }, "cordova-plugin-appavailability": { @@ -7310,8 +7324,8 @@ "integrity": "sha512-og7UmXbaWoSrOmo/mZu/c7vKDdUMu2eVrdRMvIJY6qqZ6Fv2BrJvOXm8prVt0xjWqWOMJpQs3DAajX8+N39Cqw==" }, "cordova-plugin-sunbirdsplash": { - "version": "git+https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#4d4aaeb3029a3e3c684e305780dac463b329c27c", - "from": "git+https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#release-5.0.2", + "version": "git+https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#1cf29b4f169896f9ac95bb8a9af7af6e639bb770", + "from": "git+https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#release-5.1.1", "dev": true }, "cordova-plugin-telerik-imagepicker": { @@ -7376,9 +7390,9 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-compat": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz", - "integrity": "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==", + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz", + "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", "dev": true, "requires": { "browserslist": "^4.21.4" @@ -7585,9 +7599,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -8537,9 +8551,9 @@ } }, "dompurify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz", - "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz", + "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==", "optional": true }, "domutils": { @@ -8836,25 +8850,25 @@ "dev": true }, "epubjs": { - "version": "0.3.88", - "resolved": "https://registry.npmjs.org/epubjs/-/epubjs-0.3.88.tgz", - "integrity": "sha512-VRumULpUELYmYwzypyfbDwoSIqDp2LXOXCtY3o55o3YDW5Zm32UjtZuX/xaWFGqyZORNNMWWQ8VlMaY1djnDYg==", + "version": "0.3.89", + "resolved": "https://registry.npmjs.org/epubjs/-/epubjs-0.3.89.tgz", + "integrity": "sha512-QQQ7f8vS/Wcabt0VU0mxR2AG+R1r+Kxr1aYmLjjHx8bgjxEm1aM3Zfr3WNPUiG6ZrhjpaFO547PODRSKC8lecA==", "requires": { "@types/localforage": "0.0.34", + "@xmldom/xmldom": "^0.7.5", "core-js": "^3.6.5", "event-emitter": "^0.3.5", "jszip": "^3.4.0", "localforage": "^1.7.3", "lodash": "^4.17.15", "marks-pane": "^1.0.9", - "path-webpack": "0.0.3", - "xmldom": "^0.3.0" + "path-webpack": "0.0.3" }, "dependencies": { "core-js": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz", - "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==" + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==" }, "localforage": { "version": "1.10.0", @@ -8863,11 +8877,6 @@ "requires": { "lie": "3.1.1" } - }, - "xmldom": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz", - "integrity": "sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==" } } }, @@ -11309,9 +11318,9 @@ } }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "requires": { "has": "^1.0.3" } @@ -12380,9 +12389,9 @@ } }, "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true }, "jest-regex-util": { @@ -13097,9 +13106,9 @@ }, "dependencies": { "core-js": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz", - "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==", + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==", "optional": true } } @@ -13315,9 +13324,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -13742,9 +13751,9 @@ } }, "loglevel": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", - "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", + "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", "dev": true }, "lolex": { @@ -14069,9 +14078,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -14517,9 +14526,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" } } }, @@ -14571,9 +14580,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" } } }, @@ -15031,14 +15040,14 @@ } }, "object.getownpropertydescriptors": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", - "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", "requires": { - "array.prototype.reduce": "^1.0.4", + "array.prototype.reduce": "^1.0.5", "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.20.1" + "es-abstract": "^1.20.4" } }, "object.pick": { @@ -15050,14 +15059,14 @@ } }, "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "objectorarray": { @@ -15954,9 +15963,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -16435,9 +16444,9 @@ "dev": true }, "pouchdb": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb/-/pouchdb-7.3.0.tgz", - "integrity": "sha512-OwsIQGXsfx3TrU1pLruj6PGSwFH+h5k4hGNxFkZ76Um7/ZI8F5TzUHFrpldVVIhfXYi2vP31q0q7ot1FSLFYOw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb/-/pouchdb-7.3.1.tgz", + "integrity": "sha512-oanSnM3SD9lPRuVRwEZWVbtWKYluw0q5phT5BXWi2b9Zqd5mJUPWKbKWJu03cDPM9wySmKKd7yfl9O9/eIQ5fg==", "requires": { "abort-controller": "3.0.0", "argsarray": "0.0.1", @@ -16532,18 +16541,18 @@ } }, "pouchdb-abstract-mapreduce": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-7.3.0.tgz", - "integrity": "sha512-+2fVt3SDh7D776lIGbYZOsKX5js1aUyUw7iJaTGitxSdQ2ObWSTrr3SUrj5Qo1CkgPXwRM3Tdoq/53JYAa2qCA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-7.3.1.tgz", + "integrity": "sha512-0zKXVFBvrfc1KnN0ggrB762JDmZnUpePHywo9Bq3Jy+L1FnoG7fXM5luFfvv5/T0gEw+ZTIwoocZECMnESBI9w==", "requires": { - "pouchdb-binary-utils": "7.3.0", - "pouchdb-collate": "7.3.0", - "pouchdb-collections": "7.3.0", - "pouchdb-errors": "7.3.0", - "pouchdb-fetch": "7.3.0", - "pouchdb-mapreduce-utils": "7.3.0", - "pouchdb-md5": "7.3.0", - "pouchdb-utils": "7.3.0" + "pouchdb-binary-utils": "7.3.1", + "pouchdb-collate": "7.3.1", + "pouchdb-collections": "7.3.1", + "pouchdb-errors": "7.3.1", + "pouchdb-fetch": "7.3.1", + "pouchdb-mapreduce-utils": "7.3.1", + "pouchdb-md5": "7.3.1", + "pouchdb-utils": "7.3.1" }, "dependencies": { "immediate": { @@ -16552,47 +16561,47 @@ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" }, "pouchdb-binary-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.0.tgz", - "integrity": "sha512-xvBH/XGHGcou2vkEzszJxkCc7YElfRUrkLUg51Jbdmh1mogLDUO0bU3Tj6TOIIJfRkQrU/HV+dDkMAhsil0amQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.1.tgz", + "integrity": "sha512-crZJNfAEOnUoRk977Qtmk4cxEv6sNKllQ6vDDKgQrQLFjMUXma35EHzNyIJr1s76J77Q4sqKQAmxz9Y40yHGtw==", "requires": { "buffer-from": "1.1.2" } }, "pouchdb-collections": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.0.tgz", - "integrity": "sha512-Xr54m2+fErShXn+qAT4xwqJ+8NwddNPeTMJT4z4k1sZsrwfHmZsWbsKAyGPMF04eQaaU+7DDRMciu2VzaBUXyg==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.1.tgz", + "integrity": "sha512-yUyDqR+OJmtwgExOSJegpBJXDLAEC84TWnbAYycyh+DZoA51Yw0+XVQF5Vh8Ii90/Ut2xo88fmrmp0t6kqom8w==" }, "pouchdb-errors": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.0.tgz", - "integrity": "sha512-dTBbIC1BbCy6J9W/Csg5xROgb3wJN3HpbgAJHHSEtAkb8oA45KZmU3ZwEpNhf0AfPuQm4XgW1936PvlDlGgJiw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.1.tgz", + "integrity": "sha512-Zktz4gnXEUcZcty8FmyvtYUYsHskoST05m6H5/E2gg/0mCfEXq/XeyyLkZHaZmqD0ZPS9yNmASB1VaFWEKEaDw==", "requires": { "inherits": "2.0.4" } }, "pouchdb-md5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.0.tgz", - "integrity": "sha512-wL04QgoKyd/L/TV5gxgcvlEyCJiZoXCOEFJklTzkdza/kBQNJGPH7i0ZhKa7Sb+AvZYoWZHddf1Zgv7rBScHkA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.1.tgz", + "integrity": "sha512-aDV8ui/mprnL3xmt0gT/81DFtTtJiKyn+OxIAbwKPMfz/rDFdPYvF0BmDC9QxMMzGfkV+JJUjU6at0PPs2mRLg==", "requires": { - "pouchdb-binary-utils": "7.3.0", + "pouchdb-binary-utils": "7.3.1", "spark-md5": "3.0.2" } }, "pouchdb-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.0.tgz", - "integrity": "sha512-HH+5IXXWn/ZgVCSnrlydBMYn6MabT7RS7SNoo9w8qVH9efpZSp3eLchw6yMQNLw8LQefWmbbskiHV9VgJmSVWQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.1.tgz", + "integrity": "sha512-R3hHBo1zTdTu/NFs3iqkcaQAPwhIH0gMIdfVKd5lbDYlmP26rCG5pdS+v7NuoSSFLJ4xxnaGV+Gjf4duYsJ8wQ==", "requires": { "argsarray": "0.0.1", "clone-buffer": "1.0.0", "immediate": "3.3.0", "inherits": "2.0.4", - "pouchdb-collections": "7.3.0", - "pouchdb-errors": "7.3.0", - "pouchdb-md5": "7.3.0", + "pouchdb-collections": "7.3.1", + "pouchdb-errors": "7.3.1", + "pouchdb-md5": "7.3.1", "uuid": "8.3.2" } }, @@ -16654,9 +16663,9 @@ } }, "pouchdb-collate": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-collate/-/pouchdb-collate-7.3.0.tgz", - "integrity": "sha512-ys7rXKtEr6cfghgUjknwFJiOkITebV6JmeTybJKCzMV0r2luXu0OoPQsKVpE/wbM/3F5LxfpbFKGFpPcfGMvTA==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-collate/-/pouchdb-collate-7.3.1.tgz", + "integrity": "sha512-o4gyGqDMLMSNzf6EDTr3eHaH/JRMoqRhdc+eV+oA8u00nTBtr9wD+jypVe2LbgKLJ4NWqx2qVkXiTiQdUFtsLQ==" }, "pouchdb-collections": { "version": "7.0.0", @@ -16679,9 +16688,9 @@ } }, "pouchdb-fetch": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-fetch/-/pouchdb-fetch-7.3.0.tgz", - "integrity": "sha512-8/lcg8iMDG+GVs1dHNXA4ktJSEpH71dHU3xesMJ25tNQOqfAaaWrkfz9j71ZYDDkveLYE6UjUzl/sDacu2hSjw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-fetch/-/pouchdb-fetch-7.3.1.tgz", + "integrity": "sha512-205xAtvdHRPQ4fp1h9+RmT9oQabo9gafuPmWsS9aEl3ER54WbY8Vaj1JHZGbU4KtMTYvW7H5088zLS7Nrusuag==", "requires": { "abort-controller": "3.0.0", "fetch-cookie": "0.11.0", @@ -16699,17 +16708,17 @@ } }, "pouchdb-find": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-find/-/pouchdb-find-7.3.0.tgz", - "integrity": "sha512-EwhnfyxCAkKf8PG4tfndTTygEmtuz+o1LiZkxfPrflfXA3m1jo1ithib0hwBYtEwEYWuZxH6B8pRZutbLoQCGA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-find/-/pouchdb-find-7.3.1.tgz", + "integrity": "sha512-AeqUfAVY1c7IFaY36BRT0vIz9r4VTKq/YOWTmiqndOZUQ/pDGxyO2fNFal6NN3PyYww0JijlD377cPvhnrhJVA==", "requires": { - "pouchdb-abstract-mapreduce": "7.3.0", - "pouchdb-collate": "7.3.0", - "pouchdb-errors": "7.3.0", - "pouchdb-fetch": "7.3.0", - "pouchdb-md5": "7.3.0", - "pouchdb-selector-core": "7.3.0", - "pouchdb-utils": "7.3.0" + "pouchdb-abstract-mapreduce": "7.3.1", + "pouchdb-collate": "7.3.1", + "pouchdb-errors": "7.3.1", + "pouchdb-fetch": "7.3.1", + "pouchdb-md5": "7.3.1", + "pouchdb-selector-core": "7.3.1", + "pouchdb-utils": "7.3.1" }, "dependencies": { "immediate": { @@ -16718,47 +16727,47 @@ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" }, "pouchdb-binary-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.0.tgz", - "integrity": "sha512-xvBH/XGHGcou2vkEzszJxkCc7YElfRUrkLUg51Jbdmh1mogLDUO0bU3Tj6TOIIJfRkQrU/HV+dDkMAhsil0amQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.1.tgz", + "integrity": "sha512-crZJNfAEOnUoRk977Qtmk4cxEv6sNKllQ6vDDKgQrQLFjMUXma35EHzNyIJr1s76J77Q4sqKQAmxz9Y40yHGtw==", "requires": { "buffer-from": "1.1.2" } }, "pouchdb-collections": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.0.tgz", - "integrity": "sha512-Xr54m2+fErShXn+qAT4xwqJ+8NwddNPeTMJT4z4k1sZsrwfHmZsWbsKAyGPMF04eQaaU+7DDRMciu2VzaBUXyg==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.1.tgz", + "integrity": "sha512-yUyDqR+OJmtwgExOSJegpBJXDLAEC84TWnbAYycyh+DZoA51Yw0+XVQF5Vh8Ii90/Ut2xo88fmrmp0t6kqom8w==" }, "pouchdb-errors": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.0.tgz", - "integrity": "sha512-dTBbIC1BbCy6J9W/Csg5xROgb3wJN3HpbgAJHHSEtAkb8oA45KZmU3ZwEpNhf0AfPuQm4XgW1936PvlDlGgJiw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.1.tgz", + "integrity": "sha512-Zktz4gnXEUcZcty8FmyvtYUYsHskoST05m6H5/E2gg/0mCfEXq/XeyyLkZHaZmqD0ZPS9yNmASB1VaFWEKEaDw==", "requires": { "inherits": "2.0.4" } }, "pouchdb-md5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.0.tgz", - "integrity": "sha512-wL04QgoKyd/L/TV5gxgcvlEyCJiZoXCOEFJklTzkdza/kBQNJGPH7i0ZhKa7Sb+AvZYoWZHddf1Zgv7rBScHkA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.1.tgz", + "integrity": "sha512-aDV8ui/mprnL3xmt0gT/81DFtTtJiKyn+OxIAbwKPMfz/rDFdPYvF0BmDC9QxMMzGfkV+JJUjU6at0PPs2mRLg==", "requires": { - "pouchdb-binary-utils": "7.3.0", + "pouchdb-binary-utils": "7.3.1", "spark-md5": "3.0.2" } }, "pouchdb-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.0.tgz", - "integrity": "sha512-HH+5IXXWn/ZgVCSnrlydBMYn6MabT7RS7SNoo9w8qVH9efpZSp3eLchw6yMQNLw8LQefWmbbskiHV9VgJmSVWQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.1.tgz", + "integrity": "sha512-R3hHBo1zTdTu/NFs3iqkcaQAPwhIH0gMIdfVKd5lbDYlmP26rCG5pdS+v7NuoSSFLJ4xxnaGV+Gjf4duYsJ8wQ==", "requires": { "argsarray": "0.0.1", "clone-buffer": "1.0.0", "immediate": "3.3.0", "inherits": "2.0.4", - "pouchdb-collections": "7.3.0", - "pouchdb-errors": "7.3.0", - "pouchdb-md5": "7.3.0", + "pouchdb-collections": "7.3.1", + "pouchdb-errors": "7.3.1", + "pouchdb-md5": "7.3.1", "uuid": "8.3.2" } }, @@ -16778,14 +16787,14 @@ } }, "pouchdb-mapreduce-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-7.3.0.tgz", - "integrity": "sha512-KDVSd+H2r+XWTrQfKWV71SknDDYRjYXoeWs0ZQl3xITHCcTl+fIgqyagg/XN+Zy/U9LeLPGMe2JdgPx9H8lJgw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-7.3.1.tgz", + "integrity": "sha512-oUMcq82+4pTGQ6dtrhgORHOVHZSr6w/5tFIUGlv7RABIDvJarL4snMawADjlpiEwPdiQ/ESG8Fqt8cxqvqsIgg==", "requires": { "argsarray": "0.0.1", "inherits": "2.0.4", - "pouchdb-collections": "7.3.0", - "pouchdb-utils": "7.3.0" + "pouchdb-collections": "7.3.1", + "pouchdb-utils": "7.3.1" }, "dependencies": { "immediate": { @@ -16794,47 +16803,47 @@ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" }, "pouchdb-binary-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.0.tgz", - "integrity": "sha512-xvBH/XGHGcou2vkEzszJxkCc7YElfRUrkLUg51Jbdmh1mogLDUO0bU3Tj6TOIIJfRkQrU/HV+dDkMAhsil0amQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.1.tgz", + "integrity": "sha512-crZJNfAEOnUoRk977Qtmk4cxEv6sNKllQ6vDDKgQrQLFjMUXma35EHzNyIJr1s76J77Q4sqKQAmxz9Y40yHGtw==", "requires": { "buffer-from": "1.1.2" } }, "pouchdb-collections": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.0.tgz", - "integrity": "sha512-Xr54m2+fErShXn+qAT4xwqJ+8NwddNPeTMJT4z4k1sZsrwfHmZsWbsKAyGPMF04eQaaU+7DDRMciu2VzaBUXyg==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.1.tgz", + "integrity": "sha512-yUyDqR+OJmtwgExOSJegpBJXDLAEC84TWnbAYycyh+DZoA51Yw0+XVQF5Vh8Ii90/Ut2xo88fmrmp0t6kqom8w==" }, "pouchdb-errors": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.0.tgz", - "integrity": "sha512-dTBbIC1BbCy6J9W/Csg5xROgb3wJN3HpbgAJHHSEtAkb8oA45KZmU3ZwEpNhf0AfPuQm4XgW1936PvlDlGgJiw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.1.tgz", + "integrity": "sha512-Zktz4gnXEUcZcty8FmyvtYUYsHskoST05m6H5/E2gg/0mCfEXq/XeyyLkZHaZmqD0ZPS9yNmASB1VaFWEKEaDw==", "requires": { "inherits": "2.0.4" } }, "pouchdb-md5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.0.tgz", - "integrity": "sha512-wL04QgoKyd/L/TV5gxgcvlEyCJiZoXCOEFJklTzkdza/kBQNJGPH7i0ZhKa7Sb+AvZYoWZHddf1Zgv7rBScHkA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.1.tgz", + "integrity": "sha512-aDV8ui/mprnL3xmt0gT/81DFtTtJiKyn+OxIAbwKPMfz/rDFdPYvF0BmDC9QxMMzGfkV+JJUjU6at0PPs2mRLg==", "requires": { - "pouchdb-binary-utils": "7.3.0", + "pouchdb-binary-utils": "7.3.1", "spark-md5": "3.0.2" } }, "pouchdb-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.0.tgz", - "integrity": "sha512-HH+5IXXWn/ZgVCSnrlydBMYn6MabT7RS7SNoo9w8qVH9efpZSp3eLchw6yMQNLw8LQefWmbbskiHV9VgJmSVWQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.1.tgz", + "integrity": "sha512-R3hHBo1zTdTu/NFs3iqkcaQAPwhIH0gMIdfVKd5lbDYlmP26rCG5pdS+v7NuoSSFLJ4xxnaGV+Gjf4duYsJ8wQ==", "requires": { "argsarray": "0.0.1", "clone-buffer": "1.0.0", "immediate": "3.3.0", "inherits": "2.0.4", - "pouchdb-collections": "7.3.0", - "pouchdb-errors": "7.3.0", - "pouchdb-md5": "7.3.0", + "pouchdb-collections": "7.3.1", + "pouchdb-errors": "7.3.1", + "pouchdb-md5": "7.3.1", "uuid": "8.3.2" } }, @@ -16867,12 +16876,12 @@ "integrity": "sha512-tci5u6NpznQhGcPv4ho1h0miky9rs+ds/T9zQ9meQeDZbUojXNaX1Jxsb0uYEQQ+HMqdcQs3Akdl0/u0mgwPGg==" }, "pouchdb-selector-core": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-selector-core/-/pouchdb-selector-core-7.3.0.tgz", - "integrity": "sha512-sK/cCrIGeL9ImcMhKGcwa54+bzX7Wv4hhVV+oUW3T1Nasaoxh+Muem1GuA+x1+SbTCE8y37rUg8i6DIOhX51ew==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-selector-core/-/pouchdb-selector-core-7.3.1.tgz", + "integrity": "sha512-HBX+nNGXcaL9z0uNpwSMRq2GNZd3EZXW+fe9rJHS0hvJohjZL7aRJLoaXfEdHPRTNW+CpjM3Rny60eGekQdI/w==", "requires": { - "pouchdb-collate": "7.3.0", - "pouchdb-utils": "7.3.0" + "pouchdb-collate": "7.3.1", + "pouchdb-utils": "7.3.1" }, "dependencies": { "immediate": { @@ -16881,47 +16890,47 @@ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" }, "pouchdb-binary-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.0.tgz", - "integrity": "sha512-xvBH/XGHGcou2vkEzszJxkCc7YElfRUrkLUg51Jbdmh1mogLDUO0bU3Tj6TOIIJfRkQrU/HV+dDkMAhsil0amQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.3.1.tgz", + "integrity": "sha512-crZJNfAEOnUoRk977Qtmk4cxEv6sNKllQ6vDDKgQrQLFjMUXma35EHzNyIJr1s76J77Q4sqKQAmxz9Y40yHGtw==", "requires": { "buffer-from": "1.1.2" } }, "pouchdb-collections": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.0.tgz", - "integrity": "sha512-Xr54m2+fErShXn+qAT4xwqJ+8NwddNPeTMJT4z4k1sZsrwfHmZsWbsKAyGPMF04eQaaU+7DDRMciu2VzaBUXyg==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.3.1.tgz", + "integrity": "sha512-yUyDqR+OJmtwgExOSJegpBJXDLAEC84TWnbAYycyh+DZoA51Yw0+XVQF5Vh8Ii90/Ut2xo88fmrmp0t6kqom8w==" }, "pouchdb-errors": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.0.tgz", - "integrity": "sha512-dTBbIC1BbCy6J9W/Csg5xROgb3wJN3HpbgAJHHSEtAkb8oA45KZmU3ZwEpNhf0AfPuQm4XgW1936PvlDlGgJiw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.3.1.tgz", + "integrity": "sha512-Zktz4gnXEUcZcty8FmyvtYUYsHskoST05m6H5/E2gg/0mCfEXq/XeyyLkZHaZmqD0ZPS9yNmASB1VaFWEKEaDw==", "requires": { "inherits": "2.0.4" } }, "pouchdb-md5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.0.tgz", - "integrity": "sha512-wL04QgoKyd/L/TV5gxgcvlEyCJiZoXCOEFJklTzkdza/kBQNJGPH7i0ZhKa7Sb+AvZYoWZHddf1Zgv7rBScHkA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.3.1.tgz", + "integrity": "sha512-aDV8ui/mprnL3xmt0gT/81DFtTtJiKyn+OxIAbwKPMfz/rDFdPYvF0BmDC9QxMMzGfkV+JJUjU6at0PPs2mRLg==", "requires": { - "pouchdb-binary-utils": "7.3.0", + "pouchdb-binary-utils": "7.3.1", "spark-md5": "3.0.2" } }, "pouchdb-utils": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.0.tgz", - "integrity": "sha512-HH+5IXXWn/ZgVCSnrlydBMYn6MabT7RS7SNoo9w8qVH9efpZSp3eLchw6yMQNLw8LQefWmbbskiHV9VgJmSVWQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.3.1.tgz", + "integrity": "sha512-R3hHBo1zTdTu/NFs3iqkcaQAPwhIH0gMIdfVKd5lbDYlmP26rCG5pdS+v7NuoSSFLJ4xxnaGV+Gjf4duYsJ8wQ==", "requires": { "argsarray": "0.0.1", "clone-buffer": "1.0.0", "immediate": "3.3.0", "inherits": "2.0.4", - "pouchdb-collections": "7.3.0", - "pouchdb-errors": "7.3.0", - "pouchdb-md5": "7.3.0", + "pouchdb-collections": "7.3.1", + "pouchdb-errors": "7.3.1", + "pouchdb-md5": "7.3.1", "uuid": "8.3.2" } }, @@ -17369,9 +17378,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -17576,21 +17585,11 @@ "dev": true }, "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "requires": { - "minimatch": "3.0.4" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } + "minimatch": "^3.0.5" } }, "reflect-metadata": { @@ -17614,14 +17613,14 @@ } }, "regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" @@ -17647,9 +17646,9 @@ } }, "regexpu-core": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", - "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", "dev": true, "requires": { "regenerate": "^1.4.2", @@ -17657,7 +17656,7 @@ "regjsgen": "^0.7.1", "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" } }, "regjsgen": { @@ -18105,9 +18104,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -19048,9 +19047,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -19521,23 +19520,23 @@ "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" }, "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" } }, "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" } }, "string_decoder": { @@ -19615,9 +19614,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -19705,9 +19704,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -20141,9 +20140,9 @@ } }, "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -20631,9 +20630,9 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true }, "unicode-property-aliases-ecmascript": { @@ -21302,9 +21301,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -21984,9 +21983,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -22121,11 +22120,18 @@ } }, "x2js": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/x2js/-/x2js-3.4.3.tgz", - "integrity": "sha512-+65+WHCaQ9E0Gb2FDz/tYRSRBGGvFsSMiWDGn8KbgJOLkJhZBMCU1lxuRMLyTcx/54IopT0rDQWCUz2f7FTsyQ==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/x2js/-/x2js-3.4.4.tgz", + "integrity": "sha512-yG/ThaBCgnsa3aoMPAe7QwDpcyU4D70hjXC4Y1lZSfD/Tgd0MpE19FnZZRAjekryw0c8cffpOt9zsPEiqktO6Q==", "requires": { - "@xmldom/xmldom": "^0.7.4" + "@xmldom/xmldom": "^0.8.3" + }, + "dependencies": { + "@xmldom/xmldom": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz", + "integrity": "sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==" + } } }, "xcode": { diff --git a/package.json b/package.json index 18eff9c73f..e6689292ea 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "@ionic/storage": "2.2.0", "@ngx-translate/core": "^11.0.1", "@ngx-translate/http-loader": "^4.0.0", - "@project-sunbird/client-services": "5.0.0", - "@project-sunbird/common-consumption-v9": "5.0.0", + "@project-sunbird/client-services": "5.1.1", + "@project-sunbird/common-consumption-v9": "5.1.0", "@project-sunbird/common-form-elements-v9": "5.0.3", "@project-sunbird/content-player": "5.1.0", "@project-sunbird/discussions-ui-v8": "2.6.0-beta.2", @@ -74,8 +74,8 @@ "@project-sunbird/sunbird-epub-player-v9": "4.8.1", "@project-sunbird/sunbird-pdf-player-v9": "4.8.1", "@project-sunbird/sunbird-quml-player-v9": "5.0.2", - "@project-sunbird/sunbird-sdk": "5.0.0", - "@project-sunbird/sunbird-video-player-v9": "5.0.4", + "@project-sunbird/sunbird-sdk": "5.1.1", + "@project-sunbird/sunbird-video-player-v9": "5.1.3", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", "chartjs-plugin-stacked100": "^0.7.1", @@ -95,8 +95,9 @@ "cordova-plugin-device": "^2.0.3", "cordova-plugin-dialogs": "^2.0.2", "cordova-plugin-document-viewer": "^0.9.13", - "cordova-plugin-file-opener2": "^2.2.1", "cordova-plugin-file-transfer": "git+https://github.com/apache/cordova-plugin-file-transfer.git", + "cordova-plugin-file": "^7.0.0", + "cordova-plugin-file-opener2": "^2.2.1", "cordova-plugin-filechooser": "^1.2.0", "cordova-plugin-filepath": "^1.5.8", "cordova-plugin-filepicker": "^1.1.6", @@ -120,7 +121,7 @@ "datatables.net-fixedcolumns": "^3.3.3", "dayjs": "1.9.8", "dom-to-image": "^2.6.0", - "epubjs": "0.3.88", + "epubjs": "^0.3.89", "es6-promise-plugin": "^4.2.2", "grapheme-splitter": "^1.0.4", "hammerjs": "^2.0.8", @@ -131,7 +132,7 @@ "katex": "^0.12.0", "lodash-es": "^4.17.20", "material-design-icons": "^3.0.1", - "moment": "^2.29.1", + "moment": "^2.29.4", "ng-recaptcha": "^5.0.0", "ng2-charts": "^2.4.2", "ng2-material-dropdown": "0.11.0", @@ -180,7 +181,7 @@ "@babel/preset-typescript": "^7.9.0", "@ionic/angular-toolkit": "^2.3.1", "@ionic/v4-migration-tslint": "^1.7.1", - "@project-sunbird/sb-themes": "0.0.82", + "@project-sunbird/sb-themes": "0.0.86", "@project-sunbird/telemetry-sdk": "0.0.29", "@types/jest": "^25.2.1", "@types/node": "12.11.5", @@ -194,11 +195,11 @@ "cordova-plugin-fcm-with-dependecy-updated": "git+https://github.com/Sunbird-Ed/sb-cordova-plugin-fcm.git#release-5.0.2", "cordova-plugin-file": "^7.0.0", "cordova-plugin-inappupdatemanager": "git+https://github.com/subranil/cordova-plugin-inappupdatemanager.git#release-3.7.0", - "cordova-plugin-local-notification": "git+https://github.com/katzer/cordova-plugin-local-notifications.git", + "cordova-plugin-local-notification": "git+https://github.com/fquirin/cordova-plugin-local-notifications.git", "cordova-plugin-openrap": "git+https://github.com/project-sunbird/cordova-plugin-openrap.git", "cordova-plugin-proguard": "git+https://github.com/greybax/cordova-plugin-proguard.git", "cordova-plugin-qr-scanner": "git+https://github.com/project-sunbird/cordova-plugin-qr-scanner.git#release-5.0.2", - "cordova-plugin-sunbirdsplash": "git+https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#release-5.0.2", + "cordova-plugin-sunbirdsplash": "git+https://github.com/project-sunbird/cordova-plugin-sunbirdsplash.git#release-5.1.1", "cordova-plugin-telerik-imagepicker": "^2.3.3", "cordova-zip-plugin": "git+https://github.com/Sunbird-Ed/jjdltc-cordova-plugin-zip.git", "fs-extra": "^8.1.0", @@ -291,7 +292,8 @@ "cordova-plugin-sunbirdsplash": {} }, "platforms": [ - "android" + "android", + "ios" ] }, "jest": { @@ -358,4 +360,4 @@ "/src/app/manage-learn/" ] } -} \ No newline at end of file +} diff --git a/scripts/android/android-10-migration-fix.js b/scripts/android/android-10-migration-fix.js index b0e4ef242c..afb4fa1b82 100644 --- a/scripts/android/android-10-migration-fix.js +++ b/scripts/android/android-10-migration-fix.js @@ -8,9 +8,14 @@ var pathList = [ "platforms/android/cordova-plugin-local-notification/app-localnotification.gradle", ] for (let i = 0; i < pathList.length; i++) { - if(process.platform == "darwin") { - shell(`sed -i "" "s!compile!implementation!g" ${pathList[i]}`) - } else { - shell(`sed -i "s!compile!implementation!g" ${pathList[i]}`) + try { + if(process.platform == "darwin") { + shell(`sed -i "" "s!compile!implementation!g" ${pathList[i]}`) + } else { + shell(`sed -i "s!compile!implementation!g" ${pathList[i]}`) + } + } + catch(err) { + console.log('error on script plugins ', pathList[i] + ' - ' + err); } } \ No newline at end of file diff --git a/scripts/deleteUnUsableIosIcon.js b/scripts/deleteUnUsableIosIcon.js index c474b199f0..618d4049d6 100644 --- a/scripts/deleteUnUsableIosIcon.js +++ b/scripts/deleteUnUsableIosIcon.js @@ -133,7 +133,8 @@ function deleteUnUsableIosIcon(ionicIconFilePath) { "sync-circle.svg", "arrow-up-circle-outline.svg", "arrow-down-circle-outline.svg", - "caret-forward-circle.svg" + "caret-forward-circle.svg", + "ribbon.svg" ].includes(file))) { fs.unlinkSync(ionicIconFilePath + '/' + file); } diff --git a/src/app.scss b/src/app.scss index 5a78cfca80..b6d634d161 100644 --- a/src/app.scss +++ b/src/app.scss @@ -805,6 +805,42 @@ ion-toast.red-toast{ --background: #{map-get($colors, vivid_red)}; } +html[data-theme="JOYFUL"][data-mode="DARKMODE"].ios { + .loading-wrapper, + ion-toast { + --background: #{map-get($colors, granite_gray)}; + } +} + +html[data-theme="JOYFUL"][data-mode="DARKMODE"] { + sb-library-card sb-library-card-v4 .sb--card.sb--card--theme2 { + .sb--card__info{ + .subject-info, + .sb--card__meta{ + color: white !important; + } + } + } + + sb-course-curiculum-card .sb--card.sb--card--course--curiculum .sb--card__main-area{ + .sb--card__info .sb--card__title { + background: transparent !important; + } + } + + lib-lib-entry { + &.main_discuss_lib { + .ng2-tag-input__text-input { + width: 100%; + color: white !important; + } + .discussion-start-modal .discussion-start-modal-content .discussion-start-form .start-form-content .start-form-label { + color: white !important; + } + } + } +} + .btn-block { margin: 0 !important; position: relative; @@ -2099,7 +2135,7 @@ html[data-theme="JOYFUL"][data-mode="DARKMODE"] { } .background-theme-color{ background-color: var(--app-secondary-background) !important; -} + } .message,.projects-labels, .clr-primary { color: $white; } @@ -2120,13 +2156,13 @@ html[data-theme="JOYFUL"][data-mode="DARKMODE"] { } .button-clr{ color: map-get($colors, black) !important; -} -.clr-downloaded-btn{ - color:green !important; -} -ion-icon.mg-popup-btn-icon.md.hydrated{ - filter: invert(0) !important; -} + } + .clr-downloaded-btn{ + color:green !important; + } + ion-icon.mg-popup-btn-icon.md.hydrated{ + filter: invert(0) !important; + } .loading-spinner.sc-ion-loading-md, .loading-wrapper.ion-overlay-wrapper.sc-ion-loading-md{ filter: invert(0) !important; @@ -2146,7 +2182,7 @@ ion-icon.mg-popup-btn-icon.md.hydrated{ .action-btn{ color: #000 !important; --background: #FFD954 !important; - } + } } .attachments-list{ @@ -2154,8 +2190,11 @@ ion-icon.mg-popup-btn-icon.md.hydrated{ color: #000 !important; } } - .segment-button-has-label-only.segment-button-layout-icon-top.ion-activatable.ion-activatable-instant.ion-focusable.hydrated.segment-button-checked ion-label { - color: white !important; + .segment-button-has-label-only.segment-button-layout-icon-top.ion-activatable.ion-activatable-instant.ion-focusable.hydrated.segment-button-checked { + ion-label { + color: balck !important; + } + --ion-background-color: transparent } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fa0aa3b79f..f83b3f3eb0 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -785,7 +785,9 @@ export class AppComponent implements OnInit, AfterViewInit { .then(result => { if (result) { setTimeout(() => { - this.events.publish('force_optional_upgrade', result); + if(!this.platform.is('ios')) { + this.events.publish('force_optional_upgrade', result); + } }, 5000); } }) diff --git a/src/app/category-list/category-list-page.html b/src/app/category-list/category-list-page.html index 98c4042550..c7476c9b48 100755 --- a/src/app/category-list/category-list-page.html +++ b/src/app/category-list/category-list-page.html @@ -83,7 +83,8 @@ [viewMoreButtonText]="'VIEW_MORE' | translate" [maxCardCount]="3" [layoutConfig]="layoutConfiguration" (viewMoreClick)="navigateToViewMorePage(section?.contents, section.name, section?.totalCount)" - (cardClick)="navigateToDetailPage($event, section.name)"> + (cardClick)="navigateToDetailPage($event, section.name)" + [defaultImage]="defaultImage"> diff --git a/src/app/category-list/category-list-page.spec.ts b/src/app/category-list/category-list-page.spec.ts index 0cf996fa58..365a807d71 100644 --- a/src/app/category-list/category-list-page.spec.ts +++ b/src/app/category-list/category-list-page.spec.ts @@ -7,19 +7,18 @@ import { NavigationService } from '../../services/navigation-handler.service'; import { ContentService, CourseService, FormService, ProfileService } from '@project-sunbird/sunbird-sdk'; import { ScrollToService } from '../../services/scroll-to.service'; import { - Environment, FormAndFrameworkUtilService, ImpressionType, InteractSubtype, InteractType, PageId, SearchFilterService, + Environment, InteractSubtype, InteractType, PageId, SearchFilterService, TelemetryGeneratorService } from '../../services'; import { ContentUtil } from '@app/util/content-util'; import { RouterLinks } from '@app/app/app.constant'; import { ModalController } from '@ionic/angular'; -import { promises } from 'dns'; -import { hostname } from 'os'; describe('CategoryListPage', () => { let categoryListPage: CategoryListPage; const mockCommonUtilService: Partial = { - translateMessage: jest.fn() + translateMessage: jest.fn(), + convertFileToBase64: jest.fn(() => of(fn => fn())) as any }; const mockProfileService: Partial = { getActiveSessionProfile: jest.fn(() => of({ @@ -186,7 +185,6 @@ describe('CategoryListPage', () => { const mockCourseService: Partial = {}; const mockScrollService: Partial = {}; const mockTelemetryGeneratorService: Partial = {}; - const mockFormAndFrameworkUtilService: Partial = {}; const mockModalController: Partial = {}; beforeAll(() => { @@ -213,24 +211,22 @@ describe('CategoryListPage', () => { }); describe('ionViewWillEnter', () => { - // it('should get appName from commonUtilService and also check if supportedFacets is available', (done) => { - // // arrange - // mockCommonUtilService.getAppName = jest.fn(() => Promise.resolve('Sunbird')); - // mockHeaderService.showHeaderWithBackButton = jest.fn(); - // mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([])); - // mockContentService.buildContentAggregator = jest.fn(() => ({ - // aggregate: data - // })) as any; - // // act - // categoryListPage.ionViewWillEnter(); - // // assert - // setTimeout(() => { - // expect(mockCommonUtilService.getAppName).toHaveBeenCalled(); - // expect(mockHeaderService.showHeaderWithBackButton).toHaveBeenCalled(); - // expect(data).toHaveBeenCalled(); - // done(); - // }, 0); - // }); + it('should get appName from commonUtilService and also check if supportedFacets is available', (done) => { + // arrange + mockCommonUtilService.getAppName = jest.fn(() => Promise.resolve('Sunbird')); + mockHeaderService.showHeaderWithBackButton = jest.fn(); + mockContentService.buildContentAggregator = jest.fn(() => ({ + aggregate: data + })) as any; + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn() + // act + categoryListPage.ionViewWillEnter(); + // assert + setTimeout(() => { + expect(mockHeaderService.showHeaderWithBackButton).toHaveBeenCalled(); + done(); + }, 0); + }); it('should generate impression telemetry', (done) => { //arrange const corRelationList = [{ @@ -273,13 +269,25 @@ describe('CategoryListPage', () => { ]; const onSelectedFilter = [{ name: "accountancy", count: 124, apply: false }]; const isInitialCall = false; + jest.fn(categoryListPage, 'fetchAndSortData').mockImplementation({}, true) //act categoryListPage.ngOnInit(); //assert setTimeout(() => { expect(mockCommonUtilService.getAppName).toHaveBeenCalled(); expect(mockSearchFilterService.fetchFacetFilterFormConfig).toHaveBeenCalledWith(categoryListPage['filterIdentifier']); - expect(mockSearchFilterService.reformFilterValues).toHaveBeenCalledWith(categoryListPage['filterCriteria'].facetFilters, categoryListPage['formAPIFacets']) + expect(mockSearchFilterService.reformFilterValues).toHaveBeenCalledWith([ + { + "name": "sample_string", + "values": [ + { + "apply": true, + "count": 2, + "name": "sample_string", + } + ] + } + ], categoryListPage['formAPIFacets']) expect(categoryListPage['formAPIFacets']).toBeTruthy(); expect(categoryListPage['supportedFacets']).toBeTruthy(); done(); @@ -401,6 +409,16 @@ describe('CategoryListPage', () => { aggregate: { groupBy: "subject", groupSortBy: [{ name: { order: "asc" } }] }, filterPillBy: null }; + categoryListPage['filterCriteria'] = { + facets: ["se_mediums", "subject", "primaryCategory", "audience"], + primaryCategories: ["Course"], + limit: 100, + mode: "soft", + offset: 0 + } + mockProfileService.getActiveSessionProfile = jest.fn(() => of({ + profileType: 'Student', subject: ['subject 1'] + } as any)) categoryListPage['preFetchedFilterCriteria'] = null; //act categoryListPage.deduceFilterCriteria(); @@ -408,6 +426,28 @@ describe('CategoryListPage', () => { }) }); describe('onPrimaryFacetFilterSelect', () => { + it('should check name values for facetFilterValue and toApply are equal on else case ', (done) => { + //arrange + const primaryFacetFilter = { code: "subject", values: [], name: "Subject", index: 2 }; + const toApply = [{ name: "audience", count: 124, apply: false }]; + categoryListPage.deduceFilterCriteria = jest.fn(() => { + return { + query: 'a query', + facetFilters: [ + { name: 'subject1', code: 'code1', values: [{ name: 'audience' }] }, + { name: 'course', code: 'code2', values: [{ name: 'maths' }] } + ] + } + }); + const refreshPillFilter = true; + const onSelectedFilter = []; + //act + categoryListPage.onPrimaryFacetFilterSelect(primaryFacetFilter, toApply); + //assert + setTimeout(() => { + done(); + }, 0) + }); it('should check name values for facetFilterValue and toApply are equal', (done) => { //arrange const primaryFacetFilter = { code: "subject", values: [], name: "Subject", index: 2 }; @@ -466,7 +506,7 @@ describe('CategoryListPage', () => { // act categoryListPage.navigateToViewMorePage({ identifier: 'sample_id', isAvailableLocally: true, contentData: { pkgVersion: 1 } - }, 'Mathematics'); + }, 'Mathematics',1); // assert expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( InteractType.TOUCH, @@ -475,39 +515,39 @@ describe('CategoryListPage', () => { PageId.LIBRARY, telemetryObject ); - expect(mockRouter.navigate).toHaveBeenCalledWith([RouterLinks.TEXTBOOK_VIEW_MORE], { - state: { - contentList: { - identifier: 'sample_id', isAvailableLocally: true, contentData: { pkgVersion: 1 } - }, - subjectName: 'Mathematics', - corRelation: [ - { id: 'Mathematics', type: 'Section' }, - { id: 'browse_by_audience', type: 'RootSection' }, - { id: { - searchCriteria: { - "subjects": ["maths"], - }, - facet: 'Course', - aggregate: { - "groupBy": "subject", - "groupSortBy": [{ - "name": { - "order": "asc", - "preference": [ - "audience", - ["subject 1", "subject 2"], - ['accountancy'], - ["subject 1", "subject 2"], - ], - }, - }]}, - filterPillBy: null - }, type: 'Content'}], - supportedFacets: ["se_mediums", "subject", "primaryCategory", "audience"], - totalCount: undefined - } - }); + // expect(mockRouter.navigate).toHaveBeenCalledWith([RouterLinks.TEXTBOOK_VIEW_MORE], { + // state: { + // contentList: { + // identifier: 'sample_id', isAvailableLocally: true, contentData: { pkgVersion: 1 } + // }, + // subjectName: 'Mathematics', + // corRelation: [ + // { id: 'Mathematics', type: 'Section' }, + // { id: 'browse_by_audience', type: 'RootSection' }, + // { id: { + // searchCriteria: { + // "subjects": ["maths"], + // }, + // facet: 'Course', + // aggregate: { + // "groupBy": "subject", + // "groupSortBy": [{ + // "name": { + // "order": "asc", + // "preference": [ + // "audience", + // ["subject 1", "subject 2"], + // ['accountancy'], + // ["subject 1", "subject 2"], + // ], + // }, + // }]}, + // filterPillBy: null + // }, type: 'Content'}], + // supportedFacets: ["se_mediums", "subject", "primaryCategory", "audience"], + // totalCount: 1 + // } + // }); }); it('should generate interact telemetry and if network is not available and showToast', () => { // arrange @@ -522,7 +562,7 @@ describe('CategoryListPage', () => { // act categoryListPage.navigateToViewMorePage({ identifier: 'sample_id', isAvailableLocally: false, contentData: { pkgVersion: 1 } - }, 'Mathematics'); + }, 'Mathematics',1); // assert expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( InteractType.TOUCH, @@ -579,43 +619,6 @@ describe('CategoryListPage', () => { expect(mockCommonUtilService.presentToastForOffline).toHaveBeenCalled(); }); }); - - // it('should navigate to formFilter page', (done) => { - // // arrange - // mockModalController.create = jest.fn(() => (Promise.resolve( - // { - // present: jest.fn(() => Promise.resolve({})), - // onDidDismiss: jest.fn(() => Promise.resolve({ - // data: { - // appliedFilterCriteria: { - // facetFilters: [ - // { - // name: 'sample_string', - // code: 'sample_code', - // values: [{ - // name: 'audience', - // apply: true - // }] - // } - // ] - // } - - // }, - // })), - // } as any - // ))); - // mockContentService.buildContentAggregator = jest.fn(() => ({ - // aggregate: data - // })) as any; - // // act - // categoryListPage.navigateToFilterFormPage(); - // // assert - // setTimeout(() => { - // expect(mockModalController.create).toHaveBeenCalled(); - // done(); - // }); - // }); - describe('navigateToFilterFormPage', () => { it('should navigate to filter form page', (done) => { //arrange @@ -649,6 +652,30 @@ describe('CategoryListPage', () => { done(); }); }) + it('should navigate to filter form page else on dismiss', (done) => { + //arrange + const isDataEmpty = true; + const openFiltersPage = (mockModalController.create = jest.fn(() => { + return Promise.resolve({ + present: jest.fn(() => Promise.resolve({})), + onDidDismiss: jest.fn(() => Promise.resolve({ + data: "" + })), + }) as any; + })); + categoryListPage.displayFacetFilters = { + name1: [{ name: 'na 1', apply: false }, { name: 'na 2', apply: false }], + name2: [{ name: 'na 1', apply: false }, { name: 'na 2', apply: false }] + }; + jest.spyOn(categoryListPage, 'deduceFilterCriteria').mockImplementation(); + //act + categoryListPage.navigateToFilterFormPage(); + //assert + setTimeout(() => { + expect(mockModalController.create).toHaveBeenCalled(); + done(); + }); + }) }); it('should call scrollService() to id', () => { // arrange @@ -704,12 +731,125 @@ describe('CategoryListPage', () => { filterPillBy: "subject" }; categoryListPage['preFetchedFilterCriteria'] = null; + categoryListPage['supportedUserTypesConfig'] = [{code: 'audience'}]; + categoryListPage['primaryFacetFiltersFormGroup'] = { + value: { + children: { + persona: { + type: { type: 'sample-type' }, + code: 'sample-code', + subPersona: 'sample-subpersona' + } + }, + name: 'sample name', + persona: { + type: { type: 'sample-type' }, + code: 'sample-code', + subPersona: [{}] + } + }, + patchValue: jest.fn() + } as any; categoryListPage.deduceFilterCriteria = jest.fn(() => { return { query: 'a query', facetFilters: [ - { name: 'subject', code: 'code1', values: [{ name: 'audience' }] }, - { name: 'course', code: 'code2', values: [{ name: 'maths' }] } + { name: 'subject', code: 'code1', values: [] }, + { name: 'course', code: 'code2', values: [] } + ] + } + }); + const refreshPillFilter = true; + //act + categoryListPage.pillFilterHandler(pill); + //assert + setTimeout(() => { + done(); + }) + }) + it('should check facetFilter if it return true, if no filter values', (done) => { + //arrange + const pill = { name: "course", count: 3034, apply: true }; + categoryListPage['filterPillList'] = [{ name: "course", count: 3016, apply: true }]; + categoryListPage['formField'] = { + searchCriteria: { subjects: ['maths'] }, + facet: 'Course', + aggregate: { groupBy: "subject", groupSortBy: [{ name: { order: "asc" } }] }, + filterPillBy: "subject" + }; + categoryListPage['preFetchedFilterCriteria'] = null; + categoryListPage['supportedUserTypesConfig'] = [{code: 'audience'}]; + categoryListPage['primaryFacetFiltersFormGroup'] = { + value: { + children: { + persona: { + type: { type: 'sample-type' }, + code: 'sample-code', + subPersona: 'sample-subpersona' + } + }, + name: 'sample name', + persona: { + type: { type: 'sample-type' }, + code: 'sample-code', + subPersona: [{}] + } + }, + patchValue: jest.fn() + } as any; + categoryListPage.deduceFilterCriteria = jest.fn(() => { + return { + query: 'a query', + facetFilters: [ + // { name: 'subject', code: 'code1', values: [{ name: 'audience', apply: true }] }, + // { name: 'course', code: 'code2', values: [{ name: 'maths', apply: false }] } + ] + } + }); + const refreshPillFilter = true; + //act + categoryListPage.pillFilterHandler(pill); + //assert + setTimeout(() => { + done(); + }) + }) + it('should check facetFilter if it return true on else case', (done) => { + //arrange + const pill = { name: "course", count: 3034, apply: true }; + categoryListPage['filterPillList'] = [{ name: "course", count: 3016, apply: true }]; + categoryListPage['formField'] = { + searchCriteria: { subjects: ['maths'] }, + facet: 'Course', + aggregate: { groupBy: "subject", groupSortBy: [{ name: { order: "asc" } }] }, + filterPillBy: "subject1" + }; + categoryListPage['preFetchedFilterCriteria'] = null; + categoryListPage['supportedUserTypesConfig'] = [{code: 'audience'}]; + categoryListPage['primaryFacetFiltersFormGroup'] = { + value: { + children: { + persona: { + type: { type: 'sample-type' }, + code: 'sample-code', + subPersona: 'sample-subpersona' + } + }, + name: 'sample name', + persona: { + type: { type: 'sample-type' }, + code: 'sample-code', + subPersona: [{}] + } + }, + patchValue: jest.fn() + } as any; + categoryListPage.deduceFilterCriteria = jest.fn(() => { + return { + query: 'a query', + facetFilters: [ + { name: 'subject', code: 'code1', values: [{ name: 'audience', apply: true }] }, + { name: 'audience', code: 'code2', values: [{ name: 'maths', apply: false }] } ] } }); @@ -731,8 +871,8 @@ describe('CategoryListPage', () => { index: 0, filterPillBy: "primaryCategory", primaryFacetFilters: [ - { code: "subject", name: "Subject", index: 2 }, - { code: "audience", name: "Role", index: 4 } + { code: "subject", name: "Subject", index: 2, patchValue: jest.fn() }, + { code: "audience", name: "Role", index: 4 }, ] } //act @@ -740,5 +880,28 @@ describe('CategoryListPage', () => { //assert expect(formFields.filterPillBy).toBeTruthy(); }) + + it('should check whether formFields.filterPillBy is true or not and else on primaryFacetFilters', () => { + //arrange + const existingSearchFilters = {}; + const formFields = { + facet: "Digital Textbook", + index: 0, + filterPillBy: "primaryCategory", + primaryFacetFilters: '' + } + //act + categoryListPage.getExistingFilters(formFields); + //assert + expect(formFields.filterPillBy).toBeTruthy(); + }) + + it('should check else case if no formfields', () => { + //arrange + const formFields = '' + //act + categoryListPage.getExistingFilters(formFields); + //assert + }) }); }); \ No newline at end of file diff --git a/src/app/category-list/category-list-page.ts b/src/app/category-list/category-list-page.ts index 1e131a11e5..1c72f175eb 100755 --- a/src/app/category-list/category-list-page.ts +++ b/src/app/category-list/category-list-page.ts @@ -37,7 +37,6 @@ import { Subscription } from 'rxjs'; import { PillBorder, PillsColorTheme } from '@project-sunbird/common-consumption'; import { ObjectUtil } from '@app/util/object.util'; - @Component({ selector: 'app-category-list-page', templateUrl: './category-list-page.html', @@ -82,6 +81,7 @@ export class CategoryListPage implements OnInit, OnDestroy { fromLibrary = false; sectionCode = ''; primaryFacetFiltersFormGroup: FormGroup; + filterFields: {[k: string]: any} = {}; private readonly searchCriteria: ContentSearchCriteria; private readonly filterCriteria: ContentSearchCriteria; @@ -92,6 +92,7 @@ export class CategoryListPage implements OnInit, OnDestroy { layoutConfiguration = { layout: 'v4' }; + defaultImage = ''; appName = ''; categoryDescription = ''; categoryTitle = ''; @@ -182,6 +183,9 @@ export class CategoryListPage implements OnInit, OnDestroy { facets: this.supportedFacets, searchType: SearchType.SEARCH, }, true); + (await this.commonUtilService.convertFileToBase64('assets/imgs/ic_launcher.png')).subscribe((img) => { + this.defaultImage = img; + }); } async ionViewWillEnter() { @@ -199,7 +203,7 @@ export class CategoryListPage implements OnInit, OnDestroy { ); } - private async fetchAndSortData(searchCriteria, isInitialCall: boolean, refreshPillFilter = true, onSelectedFilter?: any) { + private async fetchAndSortData(searchCriteria, isInitialCall: boolean, refreshPillFilter = true, onSelectedFilter?: any, filterKey?) { this.showSheenAnimation = true; this.profile = await this.profileService.getActiveSessionProfile({ requiredFields: ProfileConstants.REQUIRED_FIELDS }).toPromise(); if (onSelectedFilter) { @@ -207,14 +211,24 @@ export class CategoryListPage implements OnInit, OnDestroy { onSelectedFilter.forEach((selectedFilter) => { selectedData.push(selectedFilter.name); }); + if (filterKey) { + this.filterFields = this.filterFields ? this.filterFields : {}; + this.filterFields[filterKey] = selectedData; + } if (this.formField.aggregate && this.formField.aggregate.groupSortBy && this.formField.aggregate.groupSortBy.length) { this.formField.aggregate.groupSortBy.forEach((data) => { + let applyFilters = []; + Object.keys(this.filterFields).forEach((e) => { + if (this.filterFields[e].length) { + applyFilters = applyFilters.concat(this.filterFields[e]); + } + }); if (data.name && data.name.preference && data.name.preference.length) { data.name.preference.push(selectedData); } else { data.name.preference = selectedData; } - }); + }); } } @@ -496,11 +510,11 @@ export class CategoryListPage implements OnInit, OnDestroy { } }); - await this.applyFilter(appliedFilterCriteria, true, toApply); + await this.applyFilter(appliedFilterCriteria, true, toApply, primaryFacetFilter.code); } } - private async applyFilter(appliedFilterCriteria: ContentSearchCriteria, refreshPillFilter = true, onSelectedFilter?) { + private async applyFilter(appliedFilterCriteria: ContentSearchCriteria, refreshPillFilter = true, onSelectedFilter?, filterKey?) { const tempSearchCriteria: ContentSearchCriteria = { ...appliedFilterCriteria, mode: 'hard', @@ -514,7 +528,7 @@ export class CategoryListPage implements OnInit, OnDestroy { } } }); - await this.fetchAndSortData(tempSearchCriteria, false, refreshPillFilter, onSelectedFilter); + await this.fetchAndSortData(tempSearchCriteria, false, refreshPillFilter, onSelectedFilter, filterKey); } async pillFilterHandler(pill){ diff --git a/src/app/components/access-discussion/access-discussion.component.spec.ts b/src/app/components/access-discussion/access-discussion.component.spec.ts index 0902918953..2280fbba04 100644 --- a/src/app/components/access-discussion/access-discussion.component.spec.ts +++ b/src/app/components/access-discussion/access-discussion.component.spec.ts @@ -57,6 +57,17 @@ describe('GroupDetailsPage', () => { expect(mockDiscussionService.getForumIds).toHaveBeenCalled(); expect(accessDiscussionComponent.isForumEnabled).toBe(true); }); + + it('should check for forumIds, not enabled', () => { + // arrange + mockAppGlobalService.isForumEnabled = false; + mockDiscussionService.getForumIds = jest.fn(() => throwError({ error: 'error' })) as any; + // act + accessDiscussionComponent.ngOnInit() + // assert + expect(mockDiscussionService.getForumIds).toHaveBeenCalled(); + expect(accessDiscussionComponent.isForumEnabled).toBe(true); + }); }) describe('checkAccess', () => { diff --git a/src/app/components/confirm-alert/confirm-alert.component.spec.ts b/src/app/components/confirm-alert/confirm-alert.component.spec.ts index 9ae7a66771..6825942baa 100644 --- a/src/app/components/confirm-alert/confirm-alert.component.spec.ts +++ b/src/app/components/confirm-alert/confirm-alert.component.spec.ts @@ -71,7 +71,7 @@ describe('ConfirmAlertComponent', () => { it('should select can download false and dimiss popover with passing data', () => { mockPopoverController.dismiss = jest.fn(); - confirmAlertComponent.selectOption(false); + confirmAlertComponent.selectOption(); expect(mockPopoverController.dismiss).toHaveBeenCalledWith(false); }); @@ -83,4 +83,21 @@ describe('ConfirmAlertComponent', () => { expect(mockPopoverController.dismiss).toHaveBeenCalled(); }); + + it('should unsubscribe on ngOnDestroy', () => { + confirmAlertComponent.backButtonFunc = { + unsubscribe: jest.fn() + } + + confirmAlertComponent.ngOnDestroy(); + + expect(confirmAlertComponent.backButtonFunc.unsubscribe).toHaveBeenCalled(); + }); + + it('should return if not subscribed', () => { + confirmAlertComponent.backButtonFunc = undefined; + + confirmAlertComponent.ngOnDestroy(); + + }); }); diff --git a/src/app/components/popups/certificate-verification/certificate-verification-popup.component.html b/src/app/components/popups/certificate-verification/certificate-verification-popup.component.html index 39f5ac6bd4..8748d179ec 100644 --- a/src/app/components/popups/certificate-verification/certificate-verification-popup.component.html +++ b/src/app/components/popups/certificate-verification/certificate-verification-popup.component.html @@ -17,7 +17,7 @@
{{'CERTIFICATE_ISSUED_TO' | translate}}
{{certificateData.issuedTo}}
-
{{'SUCCESSFULLY_COMPLETING_COURSE' | translate}}
+
{{content | translate}}
{{certificateData.trainingName}}
{{'ON_APPNAME_ON' | translate: {'appName': appName} }} diff --git a/src/app/components/popups/certificate-verification/certificate-verification-popup.component.ts b/src/app/components/popups/certificate-verification/certificate-verification-popup.component.ts index e31c4afc70..4a8fcbee55 100644 --- a/src/app/components/popups/certificate-verification/certificate-verification-popup.component.ts +++ b/src/app/components/popups/certificate-verification/certificate-verification-popup.component.ts @@ -14,9 +14,10 @@ export class CertificateVerificationPopoverComponent implements OnInit, OnDestro @Input() actionsButtons: any; @Input() certificateData: any; @Input() showHeader = true; + @Input() isProject :boolean; backButtonFunc: Subscription; appName: string; - + content ='SUCCESSFULLY_COMPLETING_COURSE'; constructor( private commonUtilService: CommonUtilService, public popoverCtrl: PopoverController, @@ -24,6 +25,7 @@ export class CertificateVerificationPopoverComponent implements OnInit, OnDestro private events: Events) { } ngOnInit() { + this.content = this.isProject ? 'SUCCESSFULLY_COMPLETING_PROJECT' :'SUCCESSFULLY_COMPLETING_COURSE'; this.backButtonFunc = this.platform.backButton.subscribeWithPriority(11, () => { this.popoverCtrl.dismiss({ isLeftButtonClicked: null }); this.backButtonFunc.unsubscribe(); diff --git a/src/app/components/popups/sb-generic-popover/sb-generic-popover.component.spec.ts b/src/app/components/popups/sb-generic-popover/sb-generic-popover.component.spec.ts index 0ad1e25832..8c30edc8d2 100644 --- a/src/app/components/popups/sb-generic-popover/sb-generic-popover.component.spec.ts +++ b/src/app/components/popups/sb-generic-popover/sb-generic-popover.component.spec.ts @@ -45,7 +45,10 @@ describe('SbGenericPopoverComponent', () => { sbGenericPopoverComponent.backButtonFunc = { unsubscribe: unsubscribeFn, } as any; - + mockEvents.subscribe = jest.fn((topic, fn) => { + if(topic == 'selectedContents:changed') { + fn({selectedContents: {}}) + }}); // act sbGenericPopoverComponent.ngOnInit(); // assert @@ -83,4 +86,12 @@ describe('SbGenericPopoverComponent', () => { expect(mockPopOverController.dismiss).toHaveBeenCalledWith({ isLeftButtonClicked: false }); }); + it('should dismiss the popup on deleteContent, buttonIndex default', () => { + // arrange + // act + sbGenericPopoverComponent.deleteContent(); + // assert + expect(mockPopOverController.dismiss).toHaveBeenCalledWith({ isLeftButtonClicked: true }); + }); + }); diff --git a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.html b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.html index d85536e873..3f6793e62b 100644 --- a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.html +++ b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.html @@ -12,29 +12,28 @@ Certificate of completion
+
{{'FRMELEMNTS_LBL_PROFTITLE' | translate}}:
-
+
{{ profile?.firstName | titlecase }} {{ profile?.lastName | titlecase }} edit-name
-
{{'FRMELEMNTS_MSG_NAMEONCERT' | categoryKeyTranslate: content }}
+
{{'FRMELEMNTS_MSG_NAMEONCERT' | categoryKeyTranslate: content }}
+
{{'FRMELEMNTS_MSG_CERTIFICATION_NAME_DISPLAY' | translate }}
{{'FRMELEMNTS_MSG_EDITCERTNAME' | translate: {'%appName': appName} }}
- +

{{'FRMELEMNTS_CKBX_CERTMSG' | translate}}

-
-
- \ No newline at end of file diff --git a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.scss b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.scss index 3f15621936..ac3facaae4 100644 --- a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.scss +++ b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.scss @@ -25,4 +25,10 @@ ion-footer { .sb-popover-footer { padding: 16px; } +} +.word-wrap-name{ + word-break: break-all; + overflow-x: auto; + max-height: 8rem; + scroll-behavior: auto; } \ No newline at end of file diff --git a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.spec.ts b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.spec.ts index 79460ddcb8..bf31a2733d 100644 --- a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.spec.ts +++ b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.spec.ts @@ -2,7 +2,7 @@ import { PopoverController } from '@ionic/angular'; import { ProfileService, SharedPreferences } from '@project-sunbird/sunbird-sdk'; import { of } from 'rxjs'; import { AppGlobalService, CommonUtilService, NavigationService, } from '@app/services'; -import { mockProfileData } from '../../../profile/profile.page.spec.data'; +import { mockProfileData, paylod } from '../../../profile/profile.page.spec.data'; import { ProfileNameConfirmationPopoverComponent } from './sb-profile-name-confirmation-popup.component'; import { PageId } from '../../../../services/telemetry-constants'; import { PreferenceKey } from '../../../app.constant'; @@ -86,16 +86,31 @@ describe('ProfileNameConfirmationPopoverComponent', () => { }); describe('onProfilePageClick test-suites', () => { - it('should generate telemetry and navigate to district mapping if network is available', () => { + it('should generate telemetry and navigate to district mapping if network is available, if no project content', () => { // arrange // act + profileNameConfirmationPopoverComponent.projectContent = "Project content"; profileNameConfirmationPopoverComponent.onProfilePageClick(); // assert expect(mockNavService.navigateToEditPersonalDetails).toHaveBeenCalledWith( mockProfileData, - PageId.PROFILE_NAME_CONFIRMATION_POPUP + PageId.PROFILE_NAME_CONFIRMATION_POPUP, + paylod + ); + expect(mockPopoverCtrl.dismiss).toHaveBeenCalledWith({ editProfileClicked: true }); + }); + it('should generate telemetry and navigate to district mapping if network is available, if no project content', () => { + // arrange + // act + profileNameConfirmationPopoverComponent.projectContent = ""; + profileNameConfirmationPopoverComponent.onProfilePageClick(); + // assert + expect(mockNavService.navigateToEditPersonalDetails).toHaveBeenCalledWith( + mockProfileData, + PageId.PROFILE_NAME_CONFIRMATION_POPUP, + '' ); expect(mockPopoverCtrl.dismiss).toHaveBeenCalledWith({ editProfileClicked: true }); }); }); -}); +}); \ No newline at end of file diff --git a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.ts b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.ts index ad204cd849..54d9107335 100644 --- a/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.ts +++ b/src/app/components/popups/sb-profile-name-confirmation-popup/sb-profile-name-confirmation-popup.component.ts @@ -16,9 +16,11 @@ import { NavigationService } from '@app/services/navigation-handler.service'; }) export class ProfileNameConfirmationPopoverComponent { @Input() content; + @Input() projectContent; appName; profile; doNotShowAgain = false; + buttonLabel ="START_LEARNING"; constructor( @Inject('PROFILE_SERVICE') private profileService: ProfileService, @@ -30,6 +32,7 @@ export class ProfileNameConfirmationPopoverComponent { ) { } async ionViewWillEnter() { + this.buttonLabel = this.projectContent ? "FRMELEMNTS_LBL_START_IMPROVEMENT" : "START_LEARNING"; this.commonUtilService.getAppName().then((res) => { this.appName = res; }); const userId = await this.appGlobalService.getActiveProfileUid(); @@ -59,7 +62,8 @@ export class ProfileNameConfirmationPopoverComponent { } onProfilePageClick() { - this.navService.navigateToEditPersonalDetails(this.profile, PageId.PROFILE_NAME_CONFIRMATION_POPUP); + let payload = this.projectContent ? {code:'name',children:[]} : '' + this.navService.navigateToEditPersonalDetails(this.profile, PageId.PROFILE_NAME_CONFIRMATION_POPUP,payload); this.closePopover({ editProfileClicked: true }); } diff --git a/src/app/components/popups/sb-subject-list-popup/sb-subject-list-popup.component.spec.ts b/src/app/components/popups/sb-subject-list-popup/sb-subject-list-popup.component.spec.ts index cf3feb36f6..afacd6028d 100644 --- a/src/app/components/popups/sb-subject-list-popup/sb-subject-list-popup.component.spec.ts +++ b/src/app/components/popups/sb-subject-list-popup/sb-subject-list-popup.component.spec.ts @@ -1,9 +1,8 @@ import { SbSubjectListPopupComponent } from './sb-subject-list-popup.component' -import { - TelemetryGeneratorService -} from '@app/services'; +import {CorReleationDataType, Environment, ImpressionType, PageId, TelemetryGeneratorService} from '@app/services'; import {ModalController, Platform} from '@ionic/angular'; import { of } from 'rxjs'; +import {CorrelationData} from 'sunbird-sdk'; describe('SbSubjectListPopupComponent', () => { let sbSubjectListPopupComponent: SbSubjectListPopupComponent; @@ -52,17 +51,36 @@ describe('SbSubjectListPopupComponent', () => { }) - it('if no value ', () =>{ - //arrange - const event = { - data : { - length : '' - } + it('if no value ', () =>{ + //arrange + const event = { + data : { + length : '' } - // act - const data = sbSubjectListPopupComponent.handlePillSelect(event); - // assert - expect( data ).toBeUndefined(); - }) + } + // act + const data = sbSubjectListPopupComponent.handlePillSelect(event); + // assert + expect( data ).toBeUndefined(); + }) }); - }); \ No newline at end of file + + describe('ngOnInit', () => { + it('should generate telemtry on ngOnInit', () => { + // arrange + const corRelationList: Array = []; + corRelationList.push({id: sbSubjectListPopupComponent.subjectList.toString(), type: CorReleationDataType.SUBJECT_LIST}); + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn() + // act + sbSubjectListPopupComponent.ngOnInit() + // assert + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith(ImpressionType.POP_UP_CATEGORY, + '', + Environment.HOME, + PageId.HOME, + undefined, undefined, undefined, undefined, + corRelationList) + }) + + }) +}); \ No newline at end of file diff --git a/src/app/components/popups/upgrade-popover/upgrade-popover.component.spec.ts b/src/app/components/popups/upgrade-popover/upgrade-popover.component.spec.ts index e9a8cebe82..55f19779b8 100644 --- a/src/app/components/popups/upgrade-popover/upgrade-popover.component.spec.ts +++ b/src/app/components/popups/upgrade-popover/upgrade-popover.component.spec.ts @@ -10,7 +10,7 @@ describe('UpgradePopoverComponent', () => { window.cordova.plugins = { InAppUpdateManager: { - checkForImmediateUpdate: jest.fn() + checkForImmediateUpdate: jest.fn((fn, fn1) => {fn(); fn1()}) } }; diff --git a/src/app/components/show-certificate-component/show-certificate-component.component.html b/src/app/components/show-certificate-component/show-certificate-component.component.html index d9199ee597..9906f7a0bf 100644 --- a/src/app/components/show-certificate-component/show-certificate-component.component.html +++ b/src/app/components/show-certificate-component/show-certificate-component.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/components/show-certificate-component/show-certificate-component.component.ts b/src/app/components/show-certificate-component/show-certificate-component.component.ts index d6037cab7c..6fcf40c78d 100644 --- a/src/app/components/show-certificate-component/show-certificate-component.component.ts +++ b/src/app/components/show-certificate-component/show-certificate-component.component.ts @@ -21,18 +21,26 @@ export class ShowCertificateComponent implements OnInit { showCompletionCertificate = false; showMeritCertificate = false; meritCertPercent : any ; + criteria : any; constructor( private telemetryGeneratorService: TelemetryGeneratorService, private commonUtilService: CommonUtilService ) { } ngOnInit() { + this.objId = this.content.identifier; this.objType = this.content.contentType; this.objVer = this.content.pkgVersion; for(let key in this.certificateDetails) { const certCriteria = this.certificateDetails[key]['criteria']; - if (certCriteria) { + if(typeof(certCriteria)== 'string') { + this.criteria = JSON.parse(certCriteria); + this.showCompletionCertificate = this.criteria.enrollment && this.criteria.enrollment.status === 2 ? true : false; + this.showMeritCertificate = this.criteria.assessment && this.criteria.assessment.score ? true : false; + this.meritCertPercent = this.criteria.assessment && this.criteria.assessment.score['>=']; + } + else if (certCriteria) { this.showCompletionCertificate = certCriteria.enrollment && certCriteria.enrollment.status === 2 ? true : false; this.showMeritCertificate = certCriteria.assessment && certCriteria.assessment.score ? true : false; this.meritCertPercent = certCriteria.assessment && certCriteria.assessment.score['>=']; diff --git a/src/app/content-details/content-details.page.html b/src/app/content-details/content-details.page.html index 96fcaac23f..41bdcae3e6 100644 --- a/src/app/content-details/content-details.page.html +++ b/src/app/content-details/content-details.page.html @@ -1,5 +1,5 @@ - +
@@ -167,7 +167,7 @@
- +
+
+
+ {{'CERTIFICATE' | translate}} +
+ {{message | translate}} +
+
+
this.activeUserId = activeUserId); - this.pageData = this.router.getCurrentNavigation().extras.state.request; + let paramData = this.router.getCurrentNavigation().extras.state.request; + if( paramData.type == 'project'){ + this.projectData = paramData; + let keys = Object.keys(this.projectData.certificate); + if( this.projectData.certificate && this.projectData.certificate.eligible && this.projectData.certificate.osid){ + this.getProjectCertificate(); + }else{ + if((this.projectData.certificate && (keys[this.projectData.certificate.eligible] && !this.projectData.certificate.eligible) ) || (this.projectData.certificate && this.projectData.certificate.eligible && !this.projectData.certificate.osid)){ + this.message = 'FRMELEMNTS_MSG_PROJECT_SUBMITTED_CERTIFICATE_SOON' + }else + if((this.projectData.certificate && !keys[this.projectData.certificate.eligible] )){ + this.message = 'FRMELEMNTS_MSG_NOT_MET_CERTIFCATE' + } + } + }else{ + this.pageData =paramData; + this.loadCertificate(); + } this.appHeaderService.showHeaderWithBackButton(); } - ngAfterViewInit() { - this.loadCertificate(); + ngAfterViewInit() {} + getProjectCertificate(){ + const config ={ + url : urlConstants.API_URLS.PROJECT_CERTIFICATE_DOWNLOAD + this.projectData.certificate.osid, + headers:{ + template :this.projectData.templateUrl, + accept:this.acceptType + } + } + this.apiService.get(config).pipe( + tap(this.initCertificateTemplate.bind(this)), + ).toPromise(); } - ngOnDestroy() { } @@ -181,6 +212,10 @@ export class CertificateViewPage implements OnInit, AfterViewInit, OnDestroy { ref.insertCSS({ code: "body{height: 100%;}" }); }; setTimeout(funcExecute, 1000); + let that = this; + ref.addEventListener('exit', function (event) { + that.location.back(); + }); } else { this.certificateContainer.nativeElement.innerHTML = template; } @@ -194,7 +229,8 @@ export class CertificateViewPage implements OnInit, AfterViewInit, OnDestroy { try { const downloadRequest = await (async () => { - const baseFileName = `${this.pageData.certificate.name}_${this.pageData.courseId}_${this.activeUserId}`; + const baseFileName = this.pageData ? + `${this.pageData.certificate.name}_${this.pageData.courseId}_${this.activeUserId}` : `${this.projectData.name}_${this.projectData.project}_${this.activeUserId}` switch (option.label) { case 'PDF': { this.generateDownloadTypeTelemetry('pdf'); diff --git a/src/app/profile/profile.page.html b/src/app/profile/profile.page.html index 696ef7baa1..0f99dadf6e 100644 --- a/src/app/profile/profile.page.html +++ b/src/app/profile/profile.page.html @@ -211,7 +211,7 @@
-
+
{{'FRMELEMNTS_LBL_MY_LEARNINGS' | translate}} ({{mappedTrainingCertificates?.length}})‎ @@ -259,6 +259,58 @@
+ + +
+
+ {{'FRMELEMNTS_LBL_IMP_CERTIFICATE' | translate}} ({{projects?.length}})‎ + +
+
+
+
+
+ {{training.title}} +
+
+ {{training.completedDate | date :'dd/MM/yyyy'}} +
+
+ {{training?.status | titlecase}} +
+
+ {{ training.label | translate }} + {{training.dateTime | date:'dd/MM/yyyy'}} +
+
+
+ +
+
+
+ + {{'FRMELEMNTS_BTN_LOAD_MORE' | translate}} + + + + {{'SHOW_LESS' | translate }} + + +
+
+
{{'FRMELEMNTS_LBL_LEARNER_PASSBOOK' | translate}} ({{learnerPassbookCount}})‎ diff --git a/src/app/profile/profile.page.scss b/src/app/profile/profile.page.scss index 85d6ac4418..cb1ec9f90d 100644 --- a/src/app/profile/profile.page.scss +++ b/src/app/profile/profile.page.scss @@ -442,4 +442,7 @@ padding-top: 12px; } +} +.project-submitted{ + color : var(--app-green) !important; } \ No newline at end of file diff --git a/src/app/profile/profile.page.spec.data.ts b/src/app/profile/profile.page.spec.data.ts index a391cd56ce..3032a4d856 100644 --- a/src/app/profile/profile.page.spec.data.ts +++ b/src/app/profile/profile.page.spec.data.ts @@ -68,6 +68,10 @@ export const mockProfileData = { } }; +export const paylod ={ + code:'name', + children:[] +} export const mockFormData = [ { code: 'tenant', diff --git a/src/app/profile/profile.page.ts b/src/app/profile/profile.page.ts index 0ca4994033..f50284ddef 100644 --- a/src/app/profile/profile.page.ts +++ b/src/app/profile/profile.page.ts @@ -81,6 +81,9 @@ import { SegmentationTagService, TagPrefixConstants } from '@app/services/segmen import { OrganizationSearchCriteria } from '@project-sunbird/sunbird-sdk'; import { FrameworkCategory } from '@project-sunbird/client-services/models/channel'; import { LocationHandler } from '@app/services/location-handler'; +import { urlConstants } from '../manage-learn/core/constants/urlConstants'; +import { UnnatiDataService } from '../manage-learn/core/services/unnati-data.service'; +import { statusType } from '../manage-learn/core'; @Component({ selector: 'app-profile', templateUrl: './profile.page.html', @@ -115,9 +118,11 @@ export class ProfilePage implements OnInit { readonly DEFAULT_PAGINATION_LIMIT = 3; readonly DEFAULT_ENROLLED_COURSE_LIMIT = 3; + readonly DEFAULT_PROJECTS_LIMIT = 1; rolesLimit = 2; badgesLimit = 2; myLearningLimit = this.DEFAULT_ENROLLED_COURSE_LIMIT; + myImprovementsLimit = this.DEFAULT_PROJECTS_LIMIT; learnerPassbookLimit = this.DEFAULT_ENROLLED_COURSE_LIMIT; startLimit = 0; custodianOrgId: string; @@ -153,6 +158,9 @@ export class ProfilePage implements OnInit { learnerPassbookCount: any; enrolledCourseList = []; categories: any; + projects=[]; + projectsCount =0; + projectStatus =statusType; constructor( @Inject('PROFILE_SERVICE') private profileService: ProfileService, @Inject('AUTH_SERVICE') private authService: AuthService, @@ -182,7 +190,8 @@ export class ProfilePage implements OnInit { private profileHandler: ProfileHandler, private segmentationTagService: SegmentationTagService, private platform: Platform, - private locationHandler: LocationHandler + private locationHandler: LocationHandler, + private unnatiDataService : UnnatiDataService ) { const extrasState = this.router.getCurrentNavigation().extras.state; if (extrasState) { @@ -269,6 +278,7 @@ export class ProfilePage implements OnInit { this.getEnrolledCourses(refresher); this.searchContent(); this.getSelfDeclaredDetails(); + this.getProjectsCertificate(); }); }) .catch(async error => { @@ -432,6 +442,9 @@ export class ProfilePage implements OnInit { await this.getLearnerPassbook(); this.learnerPassbookLimit = this.learnerPassbook.length; break; + case 'myImprovements': + this.myImprovementsLimit = this.projects.length; + break; } this.telemetryGeneratorService.generateInteractTelemetry( InteractType.TOUCH, @@ -574,6 +587,33 @@ export class ProfilePage implements OnInit { } } + downloadCertificate(data,type?){ + if(type && type == 'project'){ + this.projectCertificateDownload(data); + }else{ + this.downloadTrainingCertificate(data) + } + } + async projectCertificateDownload(project){ + if (!this.commonUtilService.networkInfo.isNetworkAvailable) { + this.commonUtilService.showToast('OFFLINE_CERTIFICATE_MESSAGE', false, '', 3000, 'top'); + return; + } + await this.checkForPermissions().then(async (result) => { + if (result) { + const request = { type:'project',name:project.title, project: project._id, certificate: project.certificate, templateUrl : project.certificate.templateUrl }; + if (this.platform.is('ios')) { + (window as any).cordova.InAppBrowser.open(request.certificate['templateUrl'], '_blank', "toolbarposition=top"); + } else { + this.router.navigate([`/${RouterLinks.PROFILE}/${RouterLinks.CERTIFICATE_VIEW}`], { + state: { request } + }); + } + } else { + this.commonUtilService.showSettingsPageToast('FILE_MANAGER_PERMISSION_DESCRIPTION', this.appName, PageId.PROFILE, true); + } + }); + } async downloadTrainingCertificate(course: { courseName: string, dateTime: string, @@ -604,13 +644,9 @@ export class ProfilePage implements OnInit { return; } } - if (this.platform.is('ios')) { - (window as any).cordova.InAppBrowser.open(request.certificate['templateUrl'], '_blank', "toolbarposition=top"); - } else { - this.router.navigate([`/${RouterLinks.PROFILE}/${RouterLinks.CERTIFICATE_VIEW}`], { - state: { request } - }); - } + this.router.navigate([`/${RouterLinks.PROFILE}/${RouterLinks.CERTIFICATE_VIEW}`], { + state: { request } + }); } else { if (!this.commonUtilService.networkInfo.isNetworkAvailable) { this.commonUtilService.showToast('OFFLINE_CERTIFICATE_MESSAGE', false, '', 3000, 'top'); @@ -1242,4 +1278,13 @@ export class ProfilePage implements OnInit { this.categories = categories.supportedFrameworkConfig; }); } + + getProjectsCertificate(){ + const config ={ + url : urlConstants.API_URLS.PROJECT_CERTIFICATES + } + this.unnatiDataService.get(config).subscribe(resp =>{ + this.projects = resp.result.data; + }) + } } \ No newline at end of file diff --git a/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.page.ts b/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.page.ts index b07bb183e3..634a67df01 100644 --- a/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.page.ts +++ b/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.page.ts @@ -367,7 +367,7 @@ export class SelfDeclaredTeacherEditPage { this.updateConsent(userDetails, declarations[0].orgId); } else if (this.editType === 'edit' && this.isTenantChanged) { this.generateTncAudit(); - this.commonUtilService.showToast('THANK_YOU_FOR_SUBMITTING_YOUR_DETAILS'); + this.commonUtilService.showToast(this.commonUtilService.translateMessage('FRMELEMNTS_MSG_UPDATED_SUCCESSFULLY')); this.updateConsent(userDetails, declarations[1].orgId, this.previousOrgId); } else { this.commonUtilService.showToast(this.commonUtilService.translateMessage('FRMELEMNTS_MSG_UPDATED_SUCCESSFULLY')); diff --git a/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.spec.ts b/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.spec.ts index 427ca71e02..a8e3ff85f3 100644 --- a/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.spec.ts +++ b/src/app/profile/self-declared-teacher-edit/self-declared-teacher-edit.spec.ts @@ -496,7 +496,7 @@ describe('SelfDeclaredTeacherEditPage', () => { selfDeclaredTeacherEditPage.submit().then(() => { // assert expect(mockProfileService.updateServerProfileDeclarations).toHaveBeenCalled(); - expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('THANK_YOU_FOR_SUBMITTING_YOUR_DETAILS'); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('sample_translation'); }); }); diff --git a/src/app/resources/resource.component.spec.ts b/src/app/resources/resource.component.spec.ts index 2fa956c19a..69ba53dc29 100644 --- a/src/app/resources/resource.component.spec.ts +++ b/src/app/resources/resource.component.spec.ts @@ -41,13 +41,15 @@ import {TranslateService} from '@ngx-translate/core'; import {Router} from '@angular/router'; import {SplaschreenDeeplinkActionHandlerDelegate} from '@app/services/sunbird-splashscreen/splaschreen-deeplink-action-handler-delegate'; import {mockContentData} from '@app/app/content-details/content-details.page.spec.data'; -import {NEVER, of, Subscription} from 'rxjs'; +import {NEVER, of, Subscription, throwError} from 'rxjs'; import {ContentFilterConfig, EventTopics, PreferenceKey, PrimaryCategory, RouterLinks, ViewMore} from '../app.constant'; import {CorReleationDataType, ImpressionType} from '../../services/telemetry-constants'; import {NavigationService} from '../../services/navigation-handler.service'; import {FrameworkSelectionDelegateService} from '../profile/framework-selection/framework-selection.page'; -import { CorrelationData, FormService } from '@project-sunbird/sunbird-sdk'; +import { ContentAggregatorRequest, CorrelationData, FormService } from '@project-sunbird/sunbird-sdk'; import { ContentAggregatorHandler } from '../../services/content/content-aggregator-handler.service'; +import { Orientation } from '../../services/content/content-aggregator-namespaces'; +import { fn } from 'moment'; describe('ResourcesComponent', () => { let resourcesComponent: ResourcesComponent; @@ -124,12 +126,12 @@ describe('ResourcesComponent', () => { onFrameworkSelectionSubmit: jest.fn() } }; - const mockFormService: Partial = {}; const mockContentAggregatorHandler: Partial = {}; const mockProfileHandler: Partial = { getAudience: jest.fn(() => Promise.resolve(['Student'])) }; + window.console.warn = jest.fn(); const constructComponent = () => { resourcesComponent = new ResourcesComponent( mockProfileService as ProfileServiceImpl, @@ -168,6 +170,7 @@ describe('ResourcesComponent', () => { beforeEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); + mockSharedPreference.getString = jest.fn(() => of('')) }); it('should create instance of ResourceComponent', () => { @@ -335,7 +338,7 @@ describe('ResourcesComponent', () => { mockAppGlobalService.getNameForCodeInFramework = jest.fn(); // act resourcesComponent.getChannelId(); - resourcesComponent.getPopularContent(false); + resourcesComponent.getPopularContent(); // assert setTimeout(() => { expect(resourcesComponent.getGroupByPage).toHaveBeenCalled(); @@ -354,6 +357,28 @@ describe('ResourcesComponent', () => { medium: ['English', 'Bengali'], grade: ['Mathematics', 'Science'] }; + const contentSearchCriteria = {board: ['Tripura', 'Assam', 'CBSE'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science', 'Class 12'], + } + const requestAggregate: ContentAggregatorRequest = { + userPreferences: { + board: ['Tripura', 'Assam', 'CBSE'], + medium: ['English', 'Bengali'], + gradeLevel: ['Mathematics', 'Science', 'Class 12'], + subject: ['Physics', 'Mathematics'], + }, + applyFirstAvailableCombination: { + medium: ['English', 'Bengali'], + gradeLevel: ['Mathematics', 'Science', 'Class 12'] + }, + interceptSearchCriteria: jest.fn(fn => fn({ + contentSearchCriteria: {board: ['Tripura', 'Assam', 'CBSE'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science', 'Class 12'], + } + })) as any + } resourcesComponent.profile = { uid: 'sample_uid', handle: 'Guest', @@ -371,6 +396,10 @@ describe('ResourcesComponent', () => { mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); jest.spyOn(resourcesComponent, 'generateExtraInfoTelemetry').mockImplementation(); jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({syllabus: ['']})) mockContentService.buildContentAggregator = jest.fn(() => ({ handle: jest.fn(() => of({ title: JSON.stringify({en: 'TV Programs'}), @@ -397,7 +426,8 @@ describe('ResourcesComponent', () => { mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve([{ title: JSON.stringify({en: 'TV Programs'}), orientation: 'horizontal', - section: { + theme: {orientation: Orientation.VERTICAL}, + data: { sections: [ { contents: [ @@ -426,7 +456,6 @@ describe('ResourcesComponent', () => { expect(mockAppGlobalService.setSelectedBoardMediumGrade).toHaveBeenCalled(); expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); expect(mockNgZone.run).toHaveBeenCalled(); - expect(mockContentAggregatorHandler.aggregate).toHaveBeenCalled(); done(); }, 0); }); @@ -437,25 +466,20 @@ describe('ResourcesComponent', () => { searchType: SearchType.SEARCH, mode: 'hard', board: ['Tripura', 'Assam'], - medium: ['English', 'Bengali'], - grade: ['Mathematics', 'Science'] + medium: '', + grade: '' }; - resourcesComponent.profile = { - uid: 'sample_uid', - handle: 'Guest', - profileType: ProfileType.TEACHER, - board: ['CBSE'], - grade: ['Class 12'], - medium: ['English', 'Bengali'], - source: ProfileSource.LOCAL, - createdAt: '08.01.2020', - subject: ['Physics', 'Mathematics'] - } + resourcesComponent.profile = '' mockAppGlobalService.setSelectedBoardMediumGrade = jest.fn(); mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); jest.spyOn(resourcesComponent, 'generateExtraInfoTelemetry').mockImplementation(); jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({syllabus: ['']})) + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve([{ title: JSON.stringify({en: 'Digital Books'}), orientation: 'vertical', @@ -510,8 +534,8 @@ describe('ResourcesComponent', () => { handle: 'Guest', profileType: ProfileType.TEACHER, board: ['CBSE'], - grade: ['Class 12'], - medium: ['English', 'Bengali'], + grade: [], + medium: [], source: ProfileSource.LOCAL, createdAt: '08.01.2020', subject: ['Physics', 'Mathematics'] @@ -520,11 +544,15 @@ describe('ResourcesComponent', () => { mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({syllabus: ['']})) jest.spyOn(resourcesComponent, 'generateExtraInfoTelemetry').mockImplementation(); mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve([{ title: JSON.stringify({en: 'Digital Books'}), - orientation: 'vertical', - section: { + theme:{orientation: 'vertical'}, + data: { sections: [ { contents: [ @@ -542,6 +570,7 @@ describe('ResourcesComponent', () => { ] } }] as ContentsGroupedByPageSection)); + jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); mockCommonUtilService.networkInfo.isNetworkAvailable = false; mockNgZone.run = jest.fn((fn) => fn()); // act @@ -556,7 +585,7 @@ describe('ResourcesComponent', () => { }, 0); }); - it('should handle catchPart when getGroupByPageSection() returns an error', (done) => { + it('should handle response is empty on aggregate when getGroupByPageSection(), name is object has selecetd language', (done) => { // arrange const request: ContentSearchCriteria = { searchType: SearchType.SEARCH, @@ -576,9 +605,17 @@ describe('ResourcesComponent', () => { createdAt: '08.01.2020', subject: ['Physics', 'Mathematics'] } + resourcesComponent.searchGroupingContents = { + sections: [{name: {ur: ''}}] + } mockAppGlobalService.setSelectedBoardMediumGrade = jest.fn(); mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); - mockContentAggregatorHandler.aggregate = jest.fn(() => undefined); + jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({syllabus: ['']})) + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve('')); mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); mockNgZone.run = jest.fn((fn) => fn()); mockCommonUtilService.showToast = jest.fn(() => { @@ -596,232 +633,16 @@ describe('ResourcesComponent', () => { }, 0); }); - }); - - it('should call relevant methods inside when ngOnInit() called at the beginning', (done) => { - // arrange - mockSharedPreference.getBoolean = jest.fn(() => of(false)); - mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); - jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); - jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); - jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); - const data = jest.fn((fn) => fn()); - mockCommonUtilService.networkAvailability$ = { - subscribe: data - } as any; - mockAppGlobalService.generateConfigInteractEvent = jest.fn(); - mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); - mockEvents.subscribe = jest.fn((topic, fn) => { - if (topic === EventTopics.TAB_CHANGE) { - fn('LIBRARY'); - } - }); - resourcesComponent.storyAndWorksheets = [{ - contents: [{ - name: 'sunbird', - appIcon: 'http:appIcon' - }] - }]; - mockCommonUtilService.networkInfo.isNetworkAvailable = true; - mockChangeRef.detectChanges = jest.fn(); - // act - resourcesComponent.ngOnInit(); - // assert - setTimeout(() => { - expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); - expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith( - ImpressionType.PAGE_REQUEST, '', - PageId.LIBRARY, - Environment.ONBOARDING - ); - expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); - expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); - expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); - expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); - expect(mockEvents.subscribe).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should appIcon is not avilable', (done) => { - // arrange - mockSharedPreference.getBoolean = jest.fn(() => of(true)); - jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); - jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); - jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); - const data = jest.fn((fn) => fn()); - mockCommonUtilService.networkAvailability$ = { - subscribe: data - } as any; - mockAppGlobalService.generateConfigInteractEvent = jest.fn(); - mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); - mockEvents.subscribe = jest.fn((topic, fn) => { - if (topic === EventTopics.TAB_CHANGE) { - fn('LIBRARY'); - } - }); - resourcesComponent.storyAndWorksheets = [{ - contents: [{ - name: 'sunbird', - }] - }]; - mockCommonUtilService.networkInfo.isNetworkAvailable = true; - mockChangeRef.detectChanges = jest.fn(); - mockCommonUtilService.convertFileSrc = jest.fn(() => 'http://sample.png'); - // act - resourcesComponent.ngOnInit(); - // assert - setTimeout(() => { - expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); - expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); - expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); - expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); - expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); - expect(mockEvents.subscribe).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should appIcon is not avilable', (done) => { - // arrange - mockSharedPreference.getBoolean = jest.fn(() => of(true)); - jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); - jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); - jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); - const data = jest.fn((fn) => fn()); - mockCommonUtilService.networkAvailability$ = { - subscribe: data - } as any; - mockAppGlobalService.generateConfigInteractEvent = jest.fn(); - mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); - mockEvents.subscribe = jest.fn((topic, fn) => { - if (topic === EventTopics.TAB_CHANGE) { - fn('LIBRARY'); - } - }); - resourcesComponent.storyAndWorksheets = [{ - contents: [{ - name: 'sunbird', - }] - }]; - mockCommonUtilService.networkInfo.isNetworkAvailable = true; - mockChangeRef.detectChanges = jest.fn(); - const fileSrcData = [undefined, 'sample']; - mockCommonUtilService.convertFileSrc = jest.fn(() => fileSrcData.shift()); - // act - resourcesComponent.ngOnInit(); - // assert - setTimeout(() => { - expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); - expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); - expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); - expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); - expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); - expect(mockEvents.subscribe).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should call qrScanner else if part when subscribeMethod returns emptyString', (done) => { - // arrange - mockSharedPreference.getBoolean = jest.fn(() => of(true)); - jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); - jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); - mockAppGlobalService.generateConfigInteractEvent = jest.fn(); - mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); - jest.spyOn(mockAppGlobalService, 'getPageIdForTelemetry').mockReturnValue(PageId.LIBRARY); - mockQRScanner.startScanner = jest.fn(); - mockEvents.subscribe = jest.fn((topic, fn) => { - if (topic === EventTopics.TAB_CHANGE) { - fn(''); - } - }); - // act - resourcesComponent.ngOnInit(); - // assert - setTimeout(() => { - expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); - expect(mockEvents.subscribe).toHaveBeenCalled(); - expect(mockQRScanner.startScanner).toHaveBeenCalledWith(PageId.LIBRARY); - expect(mockAppGlobalService.getPageIdForTelemetry).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should subscribe onBoardingCard completed event when ngAfterViewInit called', (done) => { - // arrange - mockEvents.subscribe = jest.fn((topic, fn) => { - if (topic === 'onboarding-card:completed') { - fn(true); - } - }); - // act - resourcesComponent.ngAfterViewInit(); - // assert - setTimeout(() => { - expect(mockEvents.subscribe).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should check for subscription and unsubscribe all those events when ionViewWillLeave()', (done) => { - // arrange - resourcesComponent.refresher = { disabled: false }; - mockHeaderService.showHeaderWithHomeButton = jest.fn(); - jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); - jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); - jest.spyOn(resourcesComponent, 'getChannelId').mockImplementation(); - jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); - const mockHeaderEventsSubscription = { unsubscribe: jest.fn() } as Partial; - const mockEventsBusSubscription = { unsubscribe: jest.fn() } as Partial; - mockEventBusService.events = () => ({ - subscribe: jest.fn(() => mockEventsBusSubscription) - }); - mockHeaderService.headerEventEmitted$ = { - subscribe: jest.fn(() => mockHeaderEventsSubscription) - }; - mockEvents.unsubscribe = jest.fn(); - resourcesComponent.coachTimeout = { clearTimeout: jest.fn() }; - mockSharedPreference.getBoolean = jest.fn(() => of(false)); - mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); - // act - resourcesComponent.ionViewWillEnter().then(() => { - resourcesComponent.ionViewWillLeave(); - - // assert - expect(mockEventsBusSubscription.unsubscribe).toHaveBeenCalled(); - expect(mockHeaderEventsSubscription.unsubscribe).toHaveBeenCalled(); - expect(mockEvents.unsubscribe).toHaveBeenCalled(); - expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); - expect(mockTelemetryGeneratorService.generatePageLoadedTelemetry).toHaveBeenCalledWith( - PageId.LIBRARY, - Environment.ONBOARDING - ); - done(); - }); - }); - - describe('ionViewDidLeave', () => { - it('should clear timeout on view did leave', () => { - // arrange - // act - resourcesComponent.ionViewDidLeave() - // assert - expect(resourcesComponent.coachTimeout.clearTimeout).toHaveBeenCalled(); - }) - it('should hanlde else case', () => { - // arrange - resourcesComponent.coachTimeout = false; - // act - resourcesComponent.ionViewDidLeave() - // assert - }) - }); - - describe('getCurrentUser profile checks ', () => { - it('should check for guest user based on the that profile will be set to teacher', () => { + it('should handle response is empty on aggregate when getGroupByPageSection(), name is has value', (done) => { // arrange - const mockGuestProfile: Profile = { + const request: ContentSearchCriteria = { + searchType: SearchType.SEARCH, + mode: 'hard', + board: ['Tripura', 'Assam'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science'] + }; + resourcesComponent.profile = { uid: 'sample_uid', handle: 'Guest', profileType: ProfileType.TEACHER, @@ -831,42 +652,749 @@ describe('ResourcesComponent', () => { source: ProfileSource.LOCAL, createdAt: '08.01.2020', subject: ['Physics', 'Mathematics'] - }; - resourcesComponent.guestUser = true; - const profileType = jest.spyOn(mockAppGlobalService, 'getGuestUserType').mockReturnValue(ProfileType.TEACHER); - mockCommonUtilService.isAccessibleForNonStudentRole = jest.fn(() => true); - jest.spyOn(resourcesComponent, 'getLocalContent').mockImplementation(); - jest.spyOn(mockAppGlobalService, 'getCurrentUser').mockReturnValue(mockGuestProfile); + } + resourcesComponent.searchGroupingContents = { + sections: [{name: 'ur'}] + } + mockAppGlobalService.setSelectedBoardMediumGrade = jest.fn(); + mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); + jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({syllabus: ['']})) + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve('')); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockNgZone.run = jest.fn((fn) => fn()); + mockCommonUtilService.showToast = jest.fn(() => { + return 'ERROR_FETCHING_DATA'; + }); + // act - resourcesComponent.getCurrentUser(); - // assert - expect(resourcesComponent.getLocalContent).toHaveBeenCalled(); - expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalledWith(); + resourcesComponent.getGroupByPage(false, false); + setTimeout(() => { + // assert + expect(mockAppGlobalService.setSelectedBoardMediumGrade).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + expect(mockContentAggregatorHandler.aggregate).toHaveBeenCalled(); + done(); + }, 0); }); - it('should check for guest user profileType.STUDENT', () => { + it('should handle response is empty on aggregate when getGroupByPageSection(), if no name in sections', (done) => { // arrange - const mockGuestProfile: Profile = { + const request: ContentSearchCriteria = { + searchType: SearchType.SEARCH, + mode: 'hard', + board: ['Tripura', 'Assam'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science'] + }; + resourcesComponent.profile = { uid: 'sample_uid', handle: 'Guest', - profileType: ProfileType.STUDENT, + profileType: ProfileType.TEACHER, board: ['CBSE'], grade: ['Class 12'], medium: ['English', 'Bengali'], source: ProfileSource.LOCAL, createdAt: '08.01.2020', - subject: ['Physics', 'Mathematics'] - }; - resourcesComponent.guestUser = true; - const profileType = jest.spyOn(mockAppGlobalService, 'getGuestUserType').mockReturnValue(ProfileType.STUDENT); - mockCommonUtilService.isAccessibleForNonStudentRole = jest.fn(() => false); - jest.spyOn(resourcesComponent, 'getLocalContent').mockImplementation(); - jest.spyOn(mockAppGlobalService, 'getCurrentUser').mockReturnValue(mockGuestProfile); + subject: [] + } + resourcesComponent.searchGroupingContents = { + sections: [{name: ''}] + } + mockAppGlobalService.setSelectedBoardMediumGrade = jest.fn(); + mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); + jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({syllabus: ['']})) + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve('')); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockNgZone.run = jest.fn((fn) => fn()); + mockCommonUtilService.showToast = jest.fn(() => { + return 'ERROR_FETCHING_DATA'; + }); + // act - resourcesComponent.getCurrentUser(); - // assert - expect(resourcesComponent.getLocalContent).toHaveBeenCalled(); - expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalledWith(); + resourcesComponent.getGroupByPage(false, false); + setTimeout(() => { + // assert + expect(mockAppGlobalService.setSelectedBoardMediumGrade).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + expect(mockContentAggregatorHandler.aggregate).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should handle catchPart when getGroupByPageSection() returns an error', (done) => { + // arrange + const request: ContentSearchCriteria = { + searchType: SearchType.SEARCH, + mode: 'hard', + board: ['Tripura', 'Assam'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science'] + }; + resourcesComponent.profile = { + uid: 'sample_uid', + handle: 'Guest', + profileType: ProfileType.TEACHER, + board: ['CBSE'], + grade: ['Class 12'], + medium: ['English', 'Bengali'], + source: ProfileSource.LOCAL, + createdAt: '08.01.2020', + subject: ['Physics', 'Mathematics'] + } + mockAppGlobalService.setSelectedBoardMediumGrade = jest.fn(); + mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.reject('SERVER_ERROR')); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockNgZone.run = jest.fn((fn) => fn()); + mockCommonUtilService.showToast = jest.fn(() => { + return 'ERROR_FETCHING_DATA'; + }); + + // act + resourcesComponent.getGroupByPage(false, false); + setTimeout(() => { + // assert + expect(mockAppGlobalService.setSelectedBoardMediumGrade).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + expect(mockContentAggregatorHandler.aggregate).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should handle catchPart when getGroupByPageSection() returns an error', (done) => { + // arrange + const request: ContentSearchCriteria = { + searchType: SearchType.SEARCH, + mode: 'hard', + board: ['Tripura', 'Assam'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science'] + }; + resourcesComponent.profile = { + uid: 'sample_uid', + handle: 'Guest', + profileType: ProfileType.TEACHER, + board: ['CBSE'], + grade: ['Class 12'], + medium: ['English', 'Bengali'], + source: ProfileSource.LOCAL, + createdAt: '08.01.2020', + subject: ['Physics', 'Mathematics'] + } + mockAppGlobalService.setSelectedBoardMediumGrade = jest.fn(); + mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.reject('SERVER_ERROR')); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockNgZone.run = jest.fn((fn) => fn()); + mockCommonUtilService.showToast = jest.fn(() => { + return 'ERROR_FETCHING_DATA'; + }); + + // act + resourcesComponent.getGroupByPage(true); + setTimeout(() => { + // assert + expect(mockAppGlobalService.setSelectedBoardMediumGrade).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + expect(mockContentAggregatorHandler.aggregate).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should handle catchPart when getGroupByPageSection() returns an error', (done) => { + // arrange + const request: ContentSearchCriteria = { + searchType: SearchType.SEARCH, + mode: 'hard', + board: ['Tripura', 'Assam'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science'] + }; + const requestAggregate: ContentAggregatorRequest = { + userPreferences: { + board: ['Tripura', 'Assam', 'CBSE'], + medium: ['English', 'Bengali'], + gradeLevel: ['Mathematics', 'Science', 'Class 12'], + subject: ['Physics', 'Mathematics'], + }, + applyFirstAvailableCombination: { + medium: ['English', 'Bengali'], + gradeLevel: ['Mathematics', 'Science', 'Class 12'] + }, + interceptSearchCriteria: jest.fn(fn => fn({ + contentSearchCriteria: {board: ['Tripura', 'Assam', 'CBSE'], + medium: ['English', 'Bengali'], + grade: ['Mathematics', 'Science', 'Class 12'], + } + })) as any + } + resourcesComponent.profile = { + uid: 'sample_uid', + handle: 'Guest', + profileType: ProfileType.TEACHER, + board: ['CBSE'], + grade: ['Class 12'], + medium: ['English', 'Bengali'], + source: ProfileSource.LOCAL, + createdAt: '08.01.2020', + subject: ['Physics', 'Mathematics'] + } + mockAppGlobalService.setSelectedBoardMediumGrade = jest.fn(); + mockTelemetryGeneratorService.generateEndSheenAnimationTelemetry = jest.fn(); + mockContentAggregatorHandler.aggregate = jest.fn((requestAggregate) => Promise.resolve({})); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockNgZone.run = jest.fn((fn) => fn()); + mockCommonUtilService.showToast = jest.fn(() => { + return 'ERROR_FETCHING_DATA'; + }); + + // act + resourcesComponent.getGroupByPage(); + setTimeout(() => { + // assert + expect(mockAppGlobalService.setSelectedBoardMediumGrade).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + expect(mockContentAggregatorHandler.aggregate).toHaveBeenCalled(); + done(); + }, 0); + }); + + }); + + describe('ngOnInit', () => { + it('should call relevant methods inside when ngOnInit() called at the beginning', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(false)); + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn(false)); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + appIcon: 'http:appIcon' + }] + }]; + mockCommonUtilService.networkInfo.isNetworkAvailable = true; + mockChangeRef.detectChanges = jest.fn(); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith( + ImpressionType.PAGE_REQUEST, '', + PageId.LIBRARY, + Environment.ONBOARDING + ); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should call relevant methods inside when ngOnInit() called at the beginning', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(false)); + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn(false)); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + appIcon: '' + }] + }]; + mockCommonUtilService.networkInfo.isNetworkAvailable = true; + mockChangeRef.detectChanges = jest.fn(); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith( + ImpressionType.PAGE_REQUEST, '', + PageId.LIBRARY, + Environment.ONBOARDING + ); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should appIcon is not avilable, with basepath', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn(false)); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + appIcon: 'appIcon', + basePath: 'path' + }] + }]; + mockCommonUtilService.networkInfo.isNetworkAvailable = true; + mockChangeRef.detectChanges = jest.fn(); + mockCommonUtilService.convertFileSrc = jest.fn(() => 'http://sample.png'); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should appIcon is not avilable, storyAndWorksheets length is 0', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn(false)); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = []; + mockCommonUtilService.networkInfo.isNetworkAvailable = true; + mockChangeRef.detectChanges = jest.fn(); + mockCommonUtilService.convertFileSrc = jest.fn(() => 'http://sample.png'); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should appIcon is not avilable, no basepath', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn()); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + appIcon: 'appIcon', + basePath: '', + isAvailableLocally: false + }] + }]; + mockCommonUtilService.networkInfo.isNetworkAvailable = true; + mockChangeRef.detectChanges = jest.fn(); + mockCommonUtilService.convertFileSrc = jest.fn(() => 'http://sample.png'); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should appIcon is not avilable, no appicon', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn(true)); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + appIcon: '', + basePath: '', + isAvailableLocally: false, + identifier: 'id-123' + }] + }]; + mockCommonUtilService.networkInfo.isNetworkAvailable = false; + mockChangeRef.detectChanges = jest.fn(); + mockCommonUtilService.convertFileSrc = jest.fn(() => 'http://sample.png'); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should appIcon is not avilable, if netwrok false', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn()); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + appIcon: 'https:appIcon', + identifier: 'id-123', + basePath: 'path', + isAvailableLocally: false + }] + }]; + mockCommonUtilService.networkInfo.isNetworkAvailable = false; + mockChangeRef.detectChanges = jest.fn(); + const fileSrcData = [undefined, 'sample']; + mockCommonUtilService.convertFileSrc = jest.fn(() => false); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should appIcon is not avilable, if netwrok and networkAvailability are false, ', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const data = jest.fn((fn) => fn(false)); + mockCommonUtilService.networkAvailability$ = { + subscribe: data + } as any; + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn('LIBRARY'); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + appIcon: 'https:appIcon', + identifier: 'id-123', + basePath: 'path', + isAvailableLocally: false + }] + }]; + mockCommonUtilService.networkInfo.isNetworkAvailable = false; + mockChangeRef.detectChanges = jest.fn(); + const fileSrcData = [undefined, 'sample']; + mockCommonUtilService.convertFileSrc = jest.fn(() => false); + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); + expect(resourcesComponent.scrollToTop).toHaveBeenCalled(); + expect(mockAppGlobalService.generateConfigInteractEvent).toHaveBeenCalled(); + expect(mockAppNotificationService.handleNotification).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should call qrScanner else if part when subscribeMethod returns emptyString', (done) => { + // arrange + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'scrollToTop').mockImplementation(); + mockAppGlobalService.generateConfigInteractEvent = jest.fn(); + mockAppNotificationService.handleNotification = jest.fn(() => Promise.resolve()); + jest.spyOn(mockAppGlobalService, 'getPageIdForTelemetry').mockReturnValue(PageId.LIBRARY); + mockQRScanner.startScanner = jest.fn(); + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === EventTopics.TAB_CHANGE) { + fn(''); + } + }); + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + name: 'sunbird', + isAvailableLocally: false + }] + }]; + // act + resourcesComponent.ngOnInit(); + // assert + setTimeout(() => { + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(mockEvents.subscribe).toHaveBeenCalled(); + expect(mockQRScanner.startScanner).toHaveBeenCalledWith(PageId.LIBRARY); + expect(mockAppGlobalService.getPageIdForTelemetry).toHaveBeenCalled(); + done(); + }, 0); + }); + }) + + it('should subscribe onBoardingCard completed event when ngAfterViewInit called', (done) => { + // arrange + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === 'onboarding-card:completed') { + fn(true); + } + }); + // act + resourcesComponent.ngAfterViewInit(); + // assert + setTimeout(() => { + expect(mockEvents.subscribe).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should check for subscription and unsubscribe all those events when ionViewWillLeave()', (done) => { + // arrange + resourcesComponent.refresher = { disabled: false }; + mockHeaderService.showHeaderWithHomeButton = jest.fn(); + jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'getChannelId').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + const mockHeaderEventsSubscription = { unsubscribe: jest.fn() } as Partial; + const mockEventsBusSubscription = { unsubscribe: jest.fn() } as Partial; + mockEventBusService.events = () => ({ + subscribe: jest.fn((fn) => mockEventsBusSubscription) + }); + mockHeaderService.headerEventEmitted$ = { + subscribe: jest.fn((fn) => mockHeaderEventsSubscription) + }; + mockEvents.unsubscribe = jest.fn(); + resourcesComponent.coachTimeout = { clearTimeout: jest.fn() }; + mockSharedPreference.getBoolean = jest.fn(() => of(false)); + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); + // act + resourcesComponent.ionViewWillEnter().then(() => { + resourcesComponent.ionViewWillLeave(); + + // assert + expect(mockEventsBusSubscription.unsubscribe).toHaveBeenCalled(); + expect(mockHeaderEventsSubscription.unsubscribe).toHaveBeenCalled(); + expect(mockEvents.unsubscribe).toHaveBeenCalled(); + expect(mockSharedPreference.getBoolean).toHaveBeenCalledWith(PreferenceKey.COACH_MARK_SEEN); + expect(mockTelemetryGeneratorService.generatePageLoadedTelemetry).toHaveBeenCalledWith( + PageId.LIBRARY, + Environment.ONBOARDING + ); + done(); + }); + }); + + it('should check subscription before view will leave ', () => { + // arrange + resourcesComponent.eventSubscription = undefined; + resourcesComponent.headerObservable = undefined; + // act + resourcesComponent.ionViewWillLeave(); + }) + + describe('ionViewDidLeave', () => { + it('should clear timeout on view did leave', () => { + // arrange + // act + resourcesComponent.ionViewDidLeave() + // assert + expect(resourcesComponent.coachTimeout.clearTimeout).toHaveBeenCalled(); + }) + it('should hanlde else case', () => { + // arrange + resourcesComponent.coachTimeout = false; + // act + resourcesComponent.ionViewDidLeave() + // assert + }) + }); + + describe('getCurrentUser profile checks ', () => { + it('should check for guest user based on the that profile will be set to teacher', () => { + // arrange + const mockGuestProfile: Profile = { + uid: 'sample_uid', + handle: 'Guest', + profileType: ProfileType.TEACHER, + board: ['CBSE'], + grade: ['Class 12'], + medium: ['English', 'Bengali'], + source: ProfileSource.LOCAL, + createdAt: '08.01.2020', + subject: ['Physics', 'Mathematics'] + }; + resourcesComponent.guestUser = true; + const profileType = jest.spyOn(mockAppGlobalService, 'getGuestUserType').mockReturnValue(ProfileType.TEACHER); + mockCommonUtilService.isAccessibleForNonStudentRole = jest.fn(() => true); + jest.spyOn(resourcesComponent, 'getLocalContent').mockImplementation(); + jest.spyOn(mockAppGlobalService, 'getCurrentUser').mockReturnValue(mockGuestProfile); + // act + resourcesComponent.getCurrentUser(); + // assert + expect(resourcesComponent.getLocalContent).toHaveBeenCalled(); + expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalledWith(); + }); + + it('should check for guest user profileType.STUDENT', () => { + // arrange + const mockGuestProfile: Profile = { + uid: 'sample_uid', + handle: 'Guest', + profileType: ProfileType.STUDENT, + board: ['CBSE'], + grade: ['Class 12'], + medium: ['English', 'Bengali'], + source: ProfileSource.LOCAL, + createdAt: '08.01.2020', + subject: ['Physics', 'Mathematics'] + }; + resourcesComponent.guestUser = true; + const profileType = jest.spyOn(mockAppGlobalService, 'getGuestUserType').mockReturnValue(ProfileType.STUDENT); + mockCommonUtilService.isAccessibleForNonStudentRole = jest.fn(() => false); + jest.spyOn(resourcesComponent, 'getLocalContent').mockImplementation(); + jest.spyOn(mockAppGlobalService, 'getCurrentUser').mockReturnValue(mockGuestProfile); + // act + resourcesComponent.getCurrentUser(); + // assert + expect(resourcesComponent.getLocalContent).toHaveBeenCalled(); + expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalledWith(); + }); + + + it('should check for guest user profileType.STUDENT', () => { + // arrange + const mockGuestProfile: Profile = { + uid: 'sample_uid', + handle: 'Guest', + profileType: ProfileType.TEACHER, + board: ['CBSE'], + grade: ['Class 12'], + medium: ['English', 'Bengali'], + source: ProfileSource.LOCAL, + createdAt: '08.01.2020', + subject: ['Physics', 'Mathematics'] + }; + resourcesComponent.guestUser = true; + const profileType = jest.spyOn(mockAppGlobalService, 'getGuestUserType').mockReturnValue(ProfileType.NONE); + mockCommonUtilService.isAccessibleForNonStudentRole = jest.fn(() => false); + jest.spyOn(resourcesComponent, 'getLocalContent').mockImplementation(); + jest.spyOn(mockAppGlobalService, 'getCurrentUser').mockReturnValue(mockGuestProfile); + // act + resourcesComponent.getCurrentUser(); + // assert + expect(resourcesComponent.getLocalContent).toHaveBeenCalled(); + expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalledWith(); }); it('should assign audiance filter as loggedIn if current user is loggedIn', () => { @@ -883,6 +1411,17 @@ describe('ResourcesComponent', () => { }); }); + it('should return if Textbook is not locally Available', () => { + // arrange + resourcesComponent.locallyDownloadResources = ''; + resourcesComponent.storyAndWorksheets = '' + // act + resourcesComponent.markLocallyAvailableTextBook(); + // assert + expect(resourcesComponent.locallyDownloadResources).toEqual(resourcesComponent.locallyDownloadResources); + expect(resourcesComponent.storyAndWorksheets).toEqual(resourcesComponent.storyAndWorksheets); + }); + it('should check for Textbook which is locally Available', () => { // arrange resourcesComponent.locallyDownloadResources = [ @@ -912,6 +1451,35 @@ describe('ResourcesComponent', () => { }); + it('should check for Textbook which is not locally Available, handle else', () => { + // arrange + resourcesComponent.locallyDownloadResources = [ + { + identifier: 'sample_identifier', + contentData: {}, + isAvailableLocally: true + } + ]; + resourcesComponent.storyAndWorksheets = [ + { + contents: [{ + identifier: '', + contentData: {}, + mimeType: 'sample_mimeType', + basePath: 'sampleBasePath', + contentType: 'sample_contentType', + isAvailableLocally: false + }] + } + ]; + // act + resourcesComponent.markLocallyAvailableTextBook(); + // assert + expect(resourcesComponent.locallyDownloadResources).toEqual(resourcesComponent.locallyDownloadResources); + expect(resourcesComponent.storyAndWorksheets).toEqual(resourcesComponent.storyAndWorksheets); + + }); + it('should check for Textbook, if not Available locally return', () => { // arrange resourcesComponent.locallyDownloadResources = [ @@ -951,12 +1519,68 @@ describe('ResourcesComponent', () => { expect(mockTelemetryGeneratorService.generateExtraInfoTelemetry).toHaveBeenCalled(); }); - it('should subscribe events and other methods when ionViewWillEnter()', (done) => { + it('should generateExtra info telemetry when generateExtraInfo Telemetry() called', () => { + // arrange + const mockSectionsCount = [ + { + contents: [{ + identifier: 'sample_identifier', + contentData: {}, + mimeType: 'sample_mimeType', + basePath: 'sampleBasePath', + contentType: 'sample_contentType', + isAvailableLocally: true + }] + } + ]; + mockTelemetryGeneratorService.generateExtraInfoTelemetry = jest.fn(); + mockCommonUtilService.networkInfo.isNetworkAvailable = true; + // act + resourcesComponent.generateExtraInfoTelemetry(mockSectionsCount.length); + // assert + expect(mockTelemetryGeneratorService.generateExtraInfoTelemetry).toHaveBeenCalled(); + }); + + it('should subscribe events and other methods when ionViewWillEnter()', (done) => { + // arrange + resourcesComponent.refresher = { disabled: false }; + resourcesComponent.pageLoadedSuccess = false; + mockHeaderService.showHeaderWithHomeButton = jest.fn(); + mockHeaderService.headerEventEmitted$ = NEVER; + mockEvents.subscribe = jest.fn((topic, fn) => { + if (topic === 'update_header') { + fn(); + } + }); + jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getPopularContent').mockImplementation(); + jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); + jest.spyOn(resourcesComponent, 'getChannelId').mockImplementation(); + jest.spyOn(resourcesComponent, 'subscribeSdkEvent').mockImplementation(); + mockSharedPreference.getBoolean = jest.fn(() => of(true)); + // act + resourcesComponent.ionViewWillEnter().then(() => { + // assert + expect(mockHeaderService.showHeaderWithHomeButton).toHaveBeenCalled(); + expect(mockSplashScreenDeeplinkActionHandlerDelegate.isDelegateReady).toEqual(true); + expect(mockSharedPreference.getBoolean).toHaveBeenCalled(); + done(); + }); + }); + + it('should subscribe events and other methods when ionViewWillEnter(), when pageloaded successfully', (done) => { // arrange resourcesComponent.refresher = { disabled: false }; - resourcesComponent.pageLoadedSuccess = false; + resourcesComponent.pageLoadedSuccess = true; + resourcesComponent.isFirstTimeOnboarding = false; mockHeaderService.showHeaderWithHomeButton = jest.fn(); - mockHeaderService.headerEventEmitted$ = NEVER; + const mockConfig = { + subscribe: jest.fn(() => { }) + }; + mockHeaderService.headerEventEmitted$ = of(mockConfig); + jest.spyOn(resourcesComponent, 'handleHeaderEvents').mockImplementation(() => { + return; + }); mockEvents.subscribe = jest.fn((topic, fn) => { if (topic === 'update_header') { fn(); @@ -1019,15 +1643,37 @@ describe('ResourcesComponent', () => { resourcesComponent.categoryGradeLevels = [{ selected: ' ' }]; resourcesComponent.categoryGradeLevelsArray[0] = 'sample'; document.getElementById = jest.fn(() => (false)) as any; - setTimeout(() => { - document.getElementById = jest.fn(() => ( - {scrollIntoView: jest.fn(() => {})} - )) as any; - }, 0); + window.setTimeout = jest.fn((fn) => { + fn(() => { + document.getElementById = jest.fn(() => ( + {scrollIntoView: jest.fn(() => {})} + )) as any; + }); + }, 1000) as any; + // act + resourcesComponent.classClickHandler(undefined, false); + // assert + expect(resourcesComponent.currentGrade).toBe(undefined); + }); + + it('should handle else part when index does not match and classClicked is false ', () => { + // arrange + resourcesComponent.currentGrade = undefined; + resourcesComponent.categoryGradeLevels = [{ selected: ' ' }]; + resourcesComponent.categoryGradeLevelsArray[0] = 'sample'; + document.getElementById = jest.fn(() => (false)) as any; + window.setTimeout = jest.fn((fn) => { + fn(() => { + document.getElementById = jest.fn(() => ( + {scrollIntoView: jest.fn(() => {})} + )) as any; + }); + }) as any; // act resourcesComponent.classClickHandler(undefined, false); // assert expect(resourcesComponent.currentGrade).toBe(undefined); + expect(document.getElementById).toHaveBeenCalledTimes(2); }); }) @@ -1062,6 +1708,19 @@ describe('ResourcesComponent', () => { resourcesComponent.searchGroupingContents = { combination: { gradeLevel: 'class 1' + }, + sections:{ + contents: [ + { + appIcon: 'https:', + } + ], + name: 'mathematics', + display: { + name: { + en: 'Mathematics' + } + }, } }; resourcesComponent.categoryGradeLevelsArray = ['class 1', 'class 2']; @@ -1091,6 +1750,19 @@ describe('ResourcesComponent', () => { resourcesComponent.searchGroupingContents = { combination: { gradeLevel: undefined + }, + sections:{ + contents: [ + { + appIcon: 'https:', + } + ], + name: 'mathematics', + display: { + name: { + en: 'Mathematics' + } + }, } }; resourcesComponent.getGroupByPageReq = { @@ -1164,7 +1836,9 @@ describe('ResourcesComponent', () => { resourcesComponent.networkSubscription = { unsubscribe: jest.fn() }; - + resourcesComponent.headerObservable = { + unsubscribe: jest.fn() + } // act resourcesComponent.ngOnDestroy(); @@ -1172,7 +1846,16 @@ describe('ResourcesComponent', () => { expect(resourcesComponent.networkSubscription.unsubscribe).toHaveBeenCalled(); }); - it('should subscribe events and check for payload', (done) => { + it('should check for subscription ngOnDestroy()', () => { + // arrange + resourcesComponent.networkSubscription = false; + resourcesComponent.headerObservable = undefined + // act + resourcesComponent.ngOnDestroy(); + // assert + }); + + it('should subscribe events and check for payload', () => { // arrange mockEventBusService.events = jest.fn(() => of({ payload: { currentCount: 1, totalCount: 10 }, @@ -1195,12 +1878,37 @@ describe('ResourcesComponent', () => { resourcesComponent.subscribeSdkEvent(); // assert setTimeout(() => { - done(); + }, 0); + }); + + it('should subscribe events and check for payload, if not import_completed', () => { + // arrange + mockEventBusService.events = jest.fn(() => of({ + payload: { currentCount: 1, totalCount: 10 }, + type: ContentEventType.UPDATE, + + })); + resourcesComponent.profile = { + uid: 'sample_uid', + handle: 'Guest', + profileType: ProfileType.TEACHER, + board: ['CBSE'], + grade: ['Class 12'], + medium: ['English', 'Bengali'], + source: ProfileSource.LOCAL, + createdAt: '08.01.2020', + subject: ['Physics', 'Mathematics'] + } + jest.spyOn(resourcesComponent, 'getLocalContent').mockImplementation(); + // act + resourcesComponent.subscribeSdkEvent(); + // assert + setTimeout(() => { }, 0); }); describe('swipeDownToRefresh', () => { - it('calls getCurrentUser and getCategoryData when called upon', (done) => { + it('calls getCurrentUser and getCategoryData when called upon', () => { // arrange const refresher = { target: { complete: jest.fn() } }; jest.spyOn(resourcesComponent, 'getCurrentUser').mockImplementation(); @@ -1212,10 +1920,9 @@ describe('ResourcesComponent', () => { setTimeout(() => { expect(resourcesComponent.getCurrentUser).toHaveBeenCalled(); expect(resourcesComponent.getGroupByPage).toHaveBeenCalled(); - done(); }, 0); }); - it('should call getPopular content if refresh is undefined', (done) => { + it('should call getPopular content if refresh is undefined', () => { // arrange const refresher = undefined; jest.spyOn(resourcesComponent, 'getCategoryData').mockImplementation(); @@ -1225,12 +1932,11 @@ describe('ResourcesComponent', () => { resourcesComponent.swipeDownToRefresh(refresher); // assert setTimeout(() => { - done(); }, 0); }); }); - it('should generate interact telemetry and call QR scanner service when called upon', (done) => { + it('should generate interact telemetry and call QR scanner service when called upon', () => { // arrange mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); mockQRScanner.startScanner = jest.fn(() => Promise.resolve('qr_scanner called')); @@ -1245,11 +1951,10 @@ describe('ResourcesComponent', () => { PageId.LIBRARY ); expect(mockQRScanner.startScanner).toHaveBeenCalledWith(PageId.LIBRARY); - done(); }, 0); }); - it('should navigate, getFilteredConfig and navigate to search page', (done) => { + it('should navigate, getFilteredConfig and navigate to search page', () => { // arrange mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); mockFormAndFrameworkUtilService.getSupportedContentFilterConfig = jest.fn(() => Promise.resolve('supported_config')); @@ -1260,14 +1965,71 @@ describe('ResourcesComponent', () => { setTimeout(() => { expect(mockFormAndFrameworkUtilService.getSupportedContentFilterConfig) .toHaveBeenCalledWith(ContentFilterConfig.NAME_LIBRARY); - expect(mockRouter.navigate).toHaveBeenCalled(); - done(); + // expect(mockRouter.navigate).toHaveBeenCalled(); }, 0); }); it('should fetch current user data and call board, medium and grade methods ', () => { // arrange mockAppGlobalService.getCurrentUser = jest.fn(() => ['sample_syllabus']); + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve([{ + title: JSON.stringify({en: 'TV Programs'}), + orientation: 'horizontal', + theme: {orientation: Orientation.VERTICAL}, + data: { + sections: [ + { + contents: [ + { + appIcon: 'https:', + } + ], + name: 'mathematics', + display: { + name: { + en: 'Mathematics' + } + }, + }, + ] + } + }])); + jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); + jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); + // act + resourcesComponent.getCategoryData(); + // assert + expect(resourcesComponent.getMediumData).toHaveBeenCalled(); + expect(resourcesComponent.getGradeLevelData).toHaveBeenCalled(); + expect(resourcesComponent.getSubjectData).toHaveBeenCalled(); + }); + + it('should fetch current user data length > 0 and call board, medium and grade methods ', () => { + // arrange + mockAppGlobalService.getCurrentUser = jest.fn(() => ({syllabus:['sample_syllabus']})); + mockContentAggregatorHandler.aggregate = jest.fn(() => Promise.resolve([{ + title: JSON.stringify({en: 'TV Programs'}), + orientation: 'horizontal', + theme: {orientation: Orientation.VERTICAL}, + data: { + sections: [ + { + contents: [ + { + appIcon: 'https:', + } + ], + name: 'mathematics', + display: { + name: { + en: 'Mathematics' + } + }, + }, + ] + } + }])); jest.spyOn(resourcesComponent, 'getMediumData').mockImplementation(); jest.spyOn(resourcesComponent, 'getGradeLevelData').mockImplementation(); jest.spyOn(resourcesComponent, 'getSubjectData').mockImplementation(); @@ -1288,7 +2050,7 @@ describe('ResourcesComponent', () => { expect(mockFrameworkUtilService.getFrameworkCategoryTerms).toHaveBeenCalled(); }); - it('should fetch medium data from framework category ', (done) => { + it('should fetch medium data from framework category ', () => { // arrange mockFrameworkUtilService.getFrameworkCategoryTerms = jest.fn(() => of(['sample_data'])); jest.spyOn(resourcesComponent, 'arrangeMediumsByUserData').mockImplementation(); @@ -1297,8 +2059,7 @@ describe('ResourcesComponent', () => { // assert setTimeout(() => { expect(mockFrameworkUtilService.getFrameworkCategoryTerms).toHaveBeenCalled(); - expect(resourcesComponent.arrangeMediumsByUserData).toHaveBeenCalled(); - done(); + // expect(resourcesComponent.arrangeMediumsByUserData).toHaveBeenCalled(); }, 0); }); @@ -1342,15 +2103,15 @@ describe('ResourcesComponent', () => { mockRouter.navigate = jest.fn(); // act resourcesComponent.navigateToDetailPage({ - data: { subject: 'mathematics part 1', isAvailableLocally: true }, + data: { subject: 'mathematics part 1', isAvailableLocally: true, content: {contentId: 'id', identifier: 'id-123'}}, index: 0 - }, 'mathematics'); + }, ''); // assert expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( InteractType.TOUCH, InteractSubtype.CONTENT_CLICKED, - Environment.HOME, PageId.LIBRARY, { id: undefined, type: undefined, version: '' }, - { positionClicked: 0, sectionName: 'mathematics' }, { l1: undefined }, [{ id: 'mathematics', type: 'Section' }]); + Environment.HOME, PageId.LIBRARY, { id: 'id-123', type: undefined, version: '' }, + { positionClicked: 0, sectionName: '' }, { l1: 'id' }, [{ id: '', type: 'Section' }]); expect(mockCommonUtilService.networkInfo.isNetworkAvailable).toBe(true); expect(mockNavService.navigateToDetailPage).toHaveBeenCalled(); }); @@ -1522,6 +2283,18 @@ describe('ResourcesComponent', () => { it('should fetch localContent when called upon', () => { // arrange mockContentService.getContents = jest.fn((data) => of(data)); + resourcesComponent.profile = ''; + mockNgZone.run = jest.fn((fn) => fn()); + // act + resourcesComponent.getLocalContent(); + // assert + expect(mockContentService.getContents).toHaveBeenCalled(); + }); + + it('should fetch localContent when called upon, with profile uid', () => { + // arrange + resourcesComponent.profile = { uid: 'id'} + mockContentService.getContents = jest.fn((data) => of(data)); mockNgZone.run = jest.fn((fn) => fn()); // act resourcesComponent.getLocalContent(); @@ -1530,6 +2303,29 @@ describe('ResourcesComponent', () => { }); describe('arrangeMediumsByUserData', () => { + it('should return slected medium if data is present, if no medium length', () => { + // arrange + const categoryMediumsParam = ['english', 'hindi']; + mockAppGlobalService.getCurrentUser = jest.fn(() => ({ + ProfileType: "Teacher", + name: 'sample-name', + board: ['cbsc'], + medium: [], + grade: ['class 1', 'class 2'] + })); + resourcesComponent.categoryMediumNamesArray = ['kannada', 'english', 'hindi']; + resourcesComponent.searchGroupingContents = { + combination: { medium: 'english' } + }; + jest.spyOn(resourcesComponent, 'mediumClickHandler').mockImplementation(() => { + return 0; + }); + // act + resourcesComponent.arrangeMediumsByUserData(categoryMediumsParam); + // assert + expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalled(); + }); + it('should return slected medium if data is present', () => { // arrange const categoryMediumsParam = ['english', 'hindi']; @@ -1553,6 +2349,29 @@ describe('ResourcesComponent', () => { expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalled(); }); + it('should return slected medium if data is present', () => { + // arrange + const categoryMediumsParam = ['english', 'hindi']; + mockAppGlobalService.getCurrentUser = jest.fn(() => ({ + ProfileType: "Teacher", + name: 'sample-name', + board: ['cbsc'], + medium: ['english', 'hindi'], + grade: ['class 1', 'class 2'] + })); + resourcesComponent.categoryMediumNamesArray = ['hindi', 'kannada', 'english']; + resourcesComponent.searchGroupingContents = { + combination: { medium: 'english' } + }; + jest.spyOn(resourcesComponent, 'mediumClickHandler').mockImplementation(() => { + return 0; + }); + // act + resourcesComponent.arrangeMediumsByUserData(categoryMediumsParam); + // assert + expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalled(); + }); + it('should return slected medium if data is not present', () => { // arrange const categoryMediumsParam = ['english', 'hindi']; @@ -1585,6 +2404,7 @@ describe('ResourcesComponent', () => { // arrange resourcesComponent.refresher = { disabled: false }; mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([{code: 'experienceSwitchPopupConfig', config:{isEnabled: true}}])) + window.setTimeout = jest.fn((fn) => {fn()}, 2000) as any; resourcesComponent.coachTimeout = jest.fn((fn) => fn( mockAppGlobalService.showNewTabsSwitchPopup = jest.fn() )); @@ -1596,6 +2416,18 @@ describe('ResourcesComponent', () => { }, 2000); }); + it('should not call setTimeout for ionViewDidEnter, if no code experienceSwitchPopupConfig', (done) => { + // arrange + resourcesComponent.refresher = { disabled: false }; + mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([{code: 'experienceSwitchPopupConfig', config:{isEnabled: false}}])) + // act + resourcesComponent.ionViewDidEnter(); + // assert + setTimeout(() => { + done(); + }, 2000); + }); + it('should navigate To ViewMoreContentsPage for horizontal section', () => { const request = { searchCriteria: undefined, @@ -1706,6 +2538,82 @@ describe('ResourcesComponent', () => { }); }); + it('should prepare the delegate navigation method for Frameworkdetails page when internet is available, if no children', (done) => { + // act + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + }; + const formOutput = { + board: [{ + name: 'State (Karnataka)', + code: 'ka_k-12_1' + }], + medium: { + name: 'English', + code: 'english', + frameworkCode: 'ka_k-12_1' + }, + grade: { + name: 'Class 9', + code: 'class9', + frameworkCode: 'ka_k-12_1' + }, + subject: 'other', + contenttype: 'digitextbbok', + children: '' + }; + mockRouter.navigate = jest.fn(); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + // act + resourcesComponent.onFrameworkSelectionSubmit({}, formOutput, mockRouter, mockCommonUtilService, + mockTelemetryGeneratorService, []).then(() => { + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + expect(mockRouter.navigate).toHaveBeenCalled(); + done(); + }); + }); + + it('should prepare the delegate navigation method for Frameworkdetails page when internet is available, if childern other is empty', (done) => { + // act + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + }; + const formOutput = { + board: { + name: 'State (Karnataka)', + code: 'ka_k-12_1' + }, + medium: { + name: 'English', + code: 'english', + frameworkCode: 'ka_k-12_1' + }, + grade: { + name: 'Class 9', + code: 'class9', + frameworkCode: 'ka_k-12_1' + }, + subject: 'other', + contenttype: 'digitextbbok', + children: { + subject: { + other: '' + } + } + }; + mockRouter.navigate = jest.fn(); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + // act + resourcesComponent.onFrameworkSelectionSubmit({}, formOutput, mockRouter, mockCommonUtilService, + mockTelemetryGeneratorService, []).then(() => { + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + expect(mockRouter.navigate).toHaveBeenCalled(); + done(); + }); + }); + it('should show the offline toast when internet is now available', (done) => { // act mockCommonUtilService.networkInfo = { @@ -1828,4 +2736,84 @@ describe('ResourcesComponent', () => { expect(mockHeaderService.showHeaderWithHomeButton).toHaveBeenCalledWith(['search', 'download', 'notification']); }) }) + + describe('appTutorialScreen', () => { + it('should handle appTutorialScreen', () => { + // arrnge + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + const present = jest.fn(() => Promise.resolve()); + mockPopoverCtrl.create = jest.fn(() => Promise.resolve({ + present: present + })) as any + // act + resourcesComponent.appTutorialScreen() + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith(InteractType.TOUCH, + InteractSubtype.INFORMATION_ICON_CLICKED, + Environment.HOME, + PageId.LIBRARY); + }) + }); + describe('exploreOtherContents', () => { + it('should exploreOtherContents', () => { + // arrange + const navigationExtras = { + state: { + subjects: [''], + categoryGradeLevels: resourcesComponent.categoryGradeLevels, + storyAndWorksheets: resourcesComponent.storyAndWorksheets, + primaryCategories: PrimaryCategory.FOR_LIBRARY_TAB, + selectedGrade: [''], + selectedMedium: ['hindi'] + } + }; + resourcesComponent.profile = { + board: [''] + } + resourcesComponent.currentGrade = '' + resourcesComponent.currentMedium = '' + mockRouter.navigate = jest.fn(); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn() + // act + resourcesComponent.exploreOtherContents(); + // assert + expect(mockRouter.navigate).toHaveBeenCalledWith([RouterLinks.EXPLORE_BOOK], navigationExtras); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + }); + it('should exploreOtherContents', () => { + // arrange + resourcesComponent.storyAndWorksheets = [{ + contents: [{ + identifier: '', + contentData: {}, + mimeType: 'sample_mimeType', + basePath: 'sampleBasePath', + contentType: 'sample_contentType', + isAvailableLocally: false + }] + }]; + const navigationExtras = { + state: { + subjects: [''], + categoryGradeLevels: resourcesComponent.categoryGradeLevels, + storyAndWorksheets: resourcesComponent.storyAndWorksheets, + primaryCategories: PrimaryCategory.FOR_LIBRARY_TAB, + selectedGrade: [''], + selectedMedium: ['hindi'] + } + }; + resourcesComponent.profile = { + board: '' + } + resourcesComponent.currentGrade = 'class 1'; + resourcesComponent.currentMedium = 'hindi'; + mockRouter.navigate = jest.fn(); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn() + // act + resourcesComponent.exploreOtherContents(); + // assert + expect(mockRouter.navigate).toHaveBeenCalledWith([RouterLinks.EXPLORE_BOOK], navigationExtras); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled(); + }) + }) }); \ No newline at end of file diff --git a/src/app/search-filter/search-filter.page.scss b/src/app/search-filter/search-filter.page.scss index 34d67ecd8f..556b2bb67b 100644 --- a/src/app/search-filter/search-filter.page.scss +++ b/src/app/search-filter/search-filter.page.scss @@ -35,7 +35,6 @@ --sbt-box-shadow-black: rgba(0, 0, 0, 0.1); --sbt-box-shadow-3px: 0.1875rem 0.1875rem 0.125rem 0 var(--sbt-box-shadow-black); --sbt-theme-purple-selectbox: #6841B3; - --sbt-theme-purple-selectbox-lbg: #F2EEFF; --sbt-bradius: 0.125rem; --sbt-bradius-24: calc(var(--sbt-bradius) * 12); @@ -51,7 +50,7 @@ .cfe-multiselect-label { margin: .5rem 0; display: flex; - color: var(--gray-800); + color: var(--app-black); font-size: var(--font-size-normal); } diff --git a/src/app/search-filter/search-filter.page.spec.ts b/src/app/search-filter/search-filter.page.spec.ts index 4b417e607d..a865a9026e 100644 --- a/src/app/search-filter/search-filter.page.spec.ts +++ b/src/app/search-filter/search-filter.page.spec.ts @@ -41,6 +41,7 @@ describe('SearchFilterPage', () => { }; const mockTelemetryGeneratorService: Partial = {}; const mockFilterFormConfigMapper: Partial = {}; + window.console.error = jest.fn() JSON.parse = jest.fn().mockImplementationOnce(() => { return FilterCriteriaData; @@ -138,6 +139,74 @@ describe('SearchFilterPage', () => { done(); }, 0); }); + + it('should handle else case if filterFormTemplateConfig is present', (done) => { + // arrange + searchFilterPage['isPageLoadedFirstTime'] = true; + searchFilterPage['initialFilterCriteria'] = { + facetFilters: [ + { + name: 'se_mediums', values: [ + { + name: 'english', count: 30408, apply: false, values: [{ + name: 'english' + }] + }, + { + name: 'hindi', count: 2107, apply: false, values: [{ + name: 'hindi' + }] + } + ] + }, + { + name: 'se_gradeLevels', values: [ + { + name: 'class 10', count: 6446, apply: false, values: [{ + name: 'class 10' + }] + }, + { + name: 'class 1', count: 23017, apply: false, values: [{ + name: 'class 1' + }] + } + ] + } + ], + facets: ['se_mediums', 'se_gradeLevels'] + }; + mockFilterFormConfigMapper.map = jest.fn(() => Promise.resolve({ + config: [{ + facet: 'board', + type: 'dropdown', + }], + defaults: { + values: ['english', 'hindi'] + } + })) + mockFormAndFrameworkUtilService.changeChannelIdToName = jest.fn(() => Promise.resolve(searchFilterPage['initialFilterCriteria'])); + JSON.parse = jest.fn().mockImplementationOnce(() => { + return searchFilterPage['initialFilterCriteria']; + }); + mockSearchFilterService.getFacetFormAPIConfig = jest.fn(() => Promise.resolve('string' as any)); + // act + searchFilterPage.ngOnInit(); + // assert + setTimeout(() => { + expect(mockFormAndFrameworkUtilService.changeChannelIdToName).toHaveBeenCalled(); + expect(searchFilterPage.baseSearchFilter).toEqual({ + values: ['english', 'hindi'] + }); + expect(searchFilterPage.filterFormTemplateConfig).toEqual([ + { + facet: 'board', + type: 'dropdown', + } + ]); + done(); + }, 0); + }); }); describe('resetFilter', () => { @@ -151,6 +220,14 @@ describe('SearchFilterPage', () => { // assert expect(searchFilterPage.searchFilterComponent.resetFilter).toHaveBeenCalled(); }); + + it('should delegate form reset to SbSearchFacetFilterComponent', () => { + // arrange + searchFilterPage.searchFilterComponent = undefined; + // act + searchFilterPage.resetFilter(); + // assert + }); }); describe('when a selection is made', () => { @@ -284,6 +361,71 @@ describe('SearchFilterPage', () => { done() }, 0); }); + it('should call refresh form and selection might not be of type string, if no facet filters', (done) => { + //arrange + const event = { + name: 'se_mediums', + values: 'se_gradeLevels' + }; + JSON.parse = jest.fn().mockImplementationOnce(() => { + return searchFilterPage['initialFilterCriteria'] = { + facetFilters: [ + { + name: 'se_mediums', values: [ + { + name: 'english', count: 30408, apply: false, values: [{ + name: 'english' + }] + }, + { + name: 'hindi', count: 2107, apply: false, values: [{ + name: 'hindi' + }] + } + ] + }, + { + name: 'se_gradeLevels', values: [ + { + name: 'class 10', count: 6446, apply: false, values: [{ + name: 'class 10' + }] + }, + { + name: 'class 1', count: 23017, apply: false, values: [{ + name: 'class 1' + }] + } + ] + } + ], + facets: ['se_mediums', 'se_gradeLevels'] + }; + }); + const sampleFilterCriteria = {}; + searchFilterPage['initialFilterCriteria'] = sampleFilterCriteria; + searchFilterPage['isPageLoadedFirstTime'] = false; + mockContentService.searchContent = jest.fn(() => of({ filterCriteria: sampleFilterCriteria })); + mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve({ + present: jest.fn(), + dismiss: jest.fn(() => Promise.resolve()) + })); + mockFormAndFrameworkUtilService.changeChannelIdToName = jest.fn(() => Promise.reject('error message')); + mockSearchFilterService.reformFilterValues = jest.fn(() => Promise.resolve([ + { name: 'board', values: [Array] }, + { name: 'medium', values: [Array] } + ])) + //act + searchFilterPage.valueChanged(event); + //assert + setTimeout(() => { + expect(mockFormAndFrameworkUtilService.changeChannelIdToName).toHaveBeenCalled(); + expect(searchFilterPage['isPageLoadedFirstTime']).toBe(false); + expect(mockContentService.searchContent).toHaveBeenCalled(); + expect(mockCommonUtilService.getLoader).toHaveBeenCalled(); + done() + }, 0); + }); }); describe('when form is applied', () => { diff --git a/src/app/search/search.page.scss b/src/app/search/search.page.scss index 08b3f14a60..b875efd8c6 100644 --- a/src/app/search/search.page.scss +++ b/src/app/search/search.page.scss @@ -448,7 +448,7 @@ top: 13rem; height: 3rem; div { - background-color: white; + background-color: var(--app-white); border-radius: 1rem; width: 94%; margin: 0 0.625rem; diff --git a/src/app/search/search.page.spec.ts b/src/app/search/search.page.spec.ts index 960c021523..0f46c1b86e 100644 --- a/src/app/search/search.page.spec.ts +++ b/src/app/search/search.page.spec.ts @@ -28,17 +28,18 @@ import { Location } from '@angular/common'; import { ImpressionType, PageId, Environment, InteractSubtype, InteractType, LogLevel, Mode } from '@app/services/telemetry-constants'; import { of, throwError } from 'rxjs'; import { NgZone, ChangeDetectorRef } from '@angular/core'; -import { FormAndFrameworkUtilService, AuditType, ImpressionSubtype, GroupHandlerService, OnboardingConfigurationService } from '../../services'; +import { FormAndFrameworkUtilService, AuditType, ImpressionSubtype, GroupHandlerService, OnboardingConfigurationService, CorReleationDataType } from '../../services'; import { SbProgressLoader } from '@app/services/sb-progress-loader.service'; import { CsGroupAddableBloc } from '@project-sunbird/client-services/blocs'; import { NavigationService } from '../../services/navigation-handler.service'; import { ProfileHandler } from '@app/services/profile-handler'; import { mockSupportedUserTypeConfig } from '../../services/profile-handler.spec.data'; -import { Search } from '../app.constant'; -import { ContentEventType, DownloadEventType, DownloadProgress, NetworkError } from '@project-sunbird/sunbird-sdk'; +import { Search, SwitchableTabsConfig } from '../app.constant'; +import { ContentEventType, CorrelationData, DownloadEventType, DownloadProgress, NetworkError } from '@project-sunbird/sunbird-sdk'; import { mockOnboardingConfigData } from '../components/discover/discover.page.spec.data'; describe('SearchPage', () => { let searchPage: SearchPage; + window.console.warn = jest.fn() const mockAppGlobalService: Partial = { generateSaveClickedTelemetry: jest.fn(), isUserLoggedIn: jest.fn(() => true), @@ -109,14 +110,15 @@ describe('SearchPage', () => { } }; const mockRouter: Partial = { - getCurrentNavigation: jest.fn(() => mockRouterExtras as any), + getCurrentNavigation: jest.fn(() => ({extras: {state: {}}})) as any, navigate: jest.fn(() => Promise.resolve(true)) }; const mockTelemetryGeneratorService: Partial = { generateInteractTelemetry: jest.fn(), generateImpressionTelemetry: jest.fn(), generateBackClickedTelemetry: jest.fn(), - generateExtraInfoTelemetry: jest.fn() + generateExtraInfoTelemetry: jest.fn(), + generatePageLoadedTelemetry: jest.fn() }; const mockTranslate: Partial = { currentLang: 'en' @@ -221,6 +223,22 @@ describe('SearchPage', () => { mockProfileHandler as ProfileHandler, mockOnboardingConfigurationService as OnboardingConfigurationService ); + const mockRouterExtras = { + extras: { + state: { + primaryCategories: 'primaryCategories', + corRelationList: 'corRelationList', + source: PageId.GROUP_DETAIL, + enrolledCourses: 'enrolledCourses' as any, + userId: 'userId', + shouldGenerateEndTelemetry: false, + preAppliedFilter: { + query: '' + }, + }, + } + }; + mockRouter.getCurrentNavigation = jest.fn(() => mockRouterExtras) as any; }); beforeEach(() => { @@ -230,7 +248,7 @@ describe('SearchPage', () => { it('should create a instance of searchPage', () => { expect(searchPage).toBeTruthy(); - expect(searchPage.primaryCategories).toEqual('primaryCategories'); + expect(searchPage.primaryCategories).toEqual(undefined); }); // arrange @@ -239,6 +257,22 @@ describe('SearchPage', () => { // describe('ngOnInit', () => { it('should fetch app name on ngOnInit', (done) => { // arrange + const mockRouterExtras = { + extras: { + state: { + primaryCategories: 'primaryCategories', + corRelationList: 'corRelationList', + source: PageId.GROUP_DETAIL, + enrolledCourses: 'enrolledCourses' as any, + userId: 'userId', + shouldGenerateEndTelemetry: false, + preAppliedFilter: { + query: '' + }, + }, + } + }; + mockRouter.getCurrentNavigation = jest.fn(() => mockRouterExtras) as any; // act searchPage.ngOnInit(); // assert @@ -269,6 +303,28 @@ describe('SearchPage', () => { }, 200); }); + it('should focus the search bar, on else case without dialcode and has refresher', (done) => { + // arrange + searchPage.isFirstLaunch = true; + searchPage.source = "source"; + searchPage.searchBar = { + setFocus: jest.fn() + }; + searchPage.dialCode = "abc"; + searchPage.refresher = {disabled: true} as any; + jest.spyOn(searchPage, 'checkUserSession').mockImplementation(); + mockSbProgressLoader.hide = jest.fn(); + // act + searchPage.ionViewDidEnter(); + // assert + expect(searchPage.checkUserSession).toHaveBeenCalled(); + setTimeout(() => { + expect(searchPage.isFirstLaunch).toBeTruthy(); + expect(mockSbProgressLoader.hide).toHaveBeenCalled(); + done(); + }, 200); + }); + it('should set current FrameworkId', (done) => { // arrange // act @@ -281,6 +337,18 @@ describe('SearchPage', () => { }, 0); }); + it('should set current FrameworkId', (done) => { + // arrange + mockSharedPreferences.getString = jest.fn(() => throwError({error:''})) + // act + searchPage.getFrameworkId(); + // assert + expect(mockSharedPreferences.getString).toHaveBeenCalled(); + setTimeout(() => { + done(); + }, 0); + }); + describe('onSearchHistoryTap', () => { it('onSearchHistoryTap', () => { // arrange @@ -413,6 +481,7 @@ describe('SearchPage', () => { identifier: 'identifier', contentType: 'collection' }; + mockAppGlobalService.isOnBoardingCompleted = false; mockTelemetryGeneratorService.isCollection = jest.fn(() => true); // act searchPage.openCollection(collection); @@ -420,7 +489,7 @@ describe('SearchPage', () => { expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( InteractType.TOUCH, InteractSubtype.CONTENT_CLICKED, - Environment.HOME, + Environment.ONBOARDING, PageId.DIAL_SEARCH, { id: 'identifier', type: 'collection', version: '' }, { root: true }, @@ -502,7 +571,7 @@ describe('SearchPage', () => { }; mockTelemetryGeneratorService.isCollection = jest.fn(() => true); searchPage.isDialCodeSearch = true; - mockAppGlobalService.isOnBoardingCompleted = jest.fn(() => false); + mockAppGlobalService.isOnBoardingCompleted = jest.fn(() => true); mockAppGlobalService.getProfileSettingsStatus = jest.fn(() => Promise.resolve(true)); searchPage.guestUser = false; // act @@ -564,6 +633,14 @@ describe('SearchPage', () => { // assert expect(searchPage.profile.grade.length).toEqual(1); }); + it('should reset grade, if no grade', () => { + // arrange + searchPage.profile = {} as any; + // act + searchPage.setGrade(true, ['']); + // assert + expect(searchPage.profile.grade.length).toEqual(0); + }); it('should set grade', () => { // arrange searchPage.profile = { @@ -582,6 +659,14 @@ describe('SearchPage', () => { // assert expect(searchPage.profile.medium.length).toEqual(1); }); + it('should reset medium, if no medium', () => { + // arrange + searchPage.profile = {} as any; + // act + searchPage.setMedium(true, ['']); + // assert + expect(searchPage.profile.medium.length).toEqual(0); + }); it('should set medium', () => { // arrange searchPage.profile = { @@ -612,7 +697,7 @@ describe('SearchPage', () => { describe('setCurrentProfile', () => { it('should set current profile', () => { // arrange - searchPage.profile = {}; + searchPage.profile = {medium: []}; const data = { framework: 'framework', board: 'board', @@ -633,7 +718,7 @@ describe('SearchPage', () => { }); it('should set current profile', () => { // arrange - searchPage.profile = {}; + searchPage.profile = {medium: ['medium1']}; const data = { framework: 'framework', board: 'board', @@ -688,31 +773,24 @@ describe('SearchPage', () => { }); }); describe('checkProfileData', () => { - it('should handle error on get active channel ', () => { + it('should return if no data framework', () => { // arrange - const data = { - framework: 'framework1' - }; - const profile = { - syllabus: ['framework1'] - }; - mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => throwError(new NetworkError('No_Internet'))); + const data = {}; + const profile = {syllabus: ['framework1']}; // act searchPage.checkProfileData(data, profile); // assert - setTimeout(() => { - expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_OFFLINE_MODE') - }, 0); }) it('should set profile data accordingly', () => { // arrange const data = { - framework: 'framework1' + framework: 'framework1', + board: ['board1'] }; const profile = { syllabus: ['framework1'] }; - mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of({ identifier: 'fm', name: 'fm' })); + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of([{ identifier: 'fm', name: 'fm' }])); // act searchPage.checkProfileData(data, profile); // assert @@ -723,27 +801,25 @@ describe('SearchPage', () => { } ); }); - it('should set profile data accordingly', (done) => { + it('should set profile data accordingly, else case if framework or board are different', () => { // arrange const data = { - framework: 'framework1', - contentType: 'Resource' + framework: 'framework', + board: '' }; const profile = { syllabus: ['framework1'] }; - const getActiveChannelSuggestedFrameworkListResp = [{ identifier: 'framework1', name: 'framework1' }]; - mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of(getActiveChannelSuggestedFrameworkListResp)); - mockFrameworkService.getFrameworkDetails = jest.fn(() => throwError(new NetworkError('No_Internet'))); + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of([{ identifier: 'framework', name: 'board1' }])); // act searchPage.checkProfileData(data, profile); // assert - setTimeout(() => { - expect(searchPage.isProfileUpdated).toEqual(true); - expect(mockFrameworkService.getFrameworkDetails).toHaveBeenCalled(); - expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_OFFLINE_MODE') - done(); - }, 0); + expect(mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList).toHaveBeenCalledWith( + { + language: 'en', + requiredCategories: FrameworkCategoryCodesGroup.DEFAULT_FRAMEWORK_CATEGORIES + } + ); }); it('should set profile data accordingly', (done) => { // arrange @@ -895,13 +971,14 @@ describe('SearchPage', () => { done(); }, 0); }); + it('should set profile data accordingly', (done) => { // arrange const data = { framework: 'framework1', - board: 'board', - medium: ['medium1'], - gradeLevel: ['grade1'], + board: '', + medium: '', + gradeLevel: '', contentType: 'Resource' }; const profile = { @@ -946,108 +1023,336 @@ describe('SearchPage', () => { }, 10); }); - }); - describe('editProfile', () => { - it('should edit Profile', (done) => { + it('should set profile data accordingly', (done) => { // arrange - searchPage.gradeList = [{ code: 'grade1', name: 'grade1' }]; - searchPage.profile = { - grade: ['grade1'], - gradeValue: { - grade1: 'grade1' - } + const data = { + framework: 'framework1', + board: 'board', + medium: ['medium1'], + gradeLevel: ['grade1'], + contentType: 'Resource' }; - mockProfileService.updateProfile = jest.fn(() => of({ syllabus: 'sylabus' })); - mockCommonUtilService.handleToTopicBasedNotification = jest.fn(); - // act - searchPage.editProfile(); - // assert - setTimeout(() => { - expect(mockCommonUtilService.handleToTopicBasedNotification).toHaveBeenCalled(); - done(); - }, 0); - }); - it('should edit Profile and publish event for on boarding', (done) => { - // arrange - searchPage.gradeList = [{ code: 'grade1', name: 'grade1' }]; - searchPage.profile = { - grade: ['grade1'], - gradeValue: { - grade1: 'grade1' - } + const profile = { + syllabus: ['framework1'], + board: ['boardcode'], + medium: ['medium1'], + grade: ['grade1'] }; - mockProfileService.updateProfile = jest.fn(() => of({ syllabus: ['sylabus1', 'sylabus2'], board: ['board1', 'board2'], grade:['grade1', 'grade2'], medium: ['english', 'hindi']})); - mockEvents.publish = jest.fn(); - mockAppGlobalService.setOnBoardingCompleted = jest.fn() + const getActiveChannelSuggestedFrameworkListResp = [{ identifier: 'framework1', name: 'framework1' }]; + const getFrameworkDetailsResp = { + categories: [ + { + code: 'board', + terms: [ + { code: 'boardcode', name: 'board' } + ] + }, + { + code: 'medium', + terms: [ + { code: 'medium1', name: 'medium1' } + ] + }, + { + code: 'gradeLevel', + terms: [ + { code: 'grade1', name: 'grade1' } + ] + } + ] + }; + jest.spyOn(searchPage, 'setCurrentProfile').mockImplementation(); + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of(getActiveChannelSuggestedFrameworkListResp)); + mockFrameworkService.getFrameworkDetails = jest.fn(() => of(getFrameworkDetailsResp)); // act - searchPage.editProfile(); + searchPage.checkProfileData(data, profile); // assert setTimeout(() => { - expect(mockEvents.publish).toHaveBeenCalledWith('user-profile-changed'); - expect(mockEvents.publish).toHaveBeenCalledWith('refresh:profile'); - expect(mockAppGlobalService.setOnBoardingCompleted).toHaveBeenCalled(); + expect(searchPage.isProfileUpdated).toEqual(true); + expect(searchPage.boardList).toEqual(getFrameworkDetailsResp.categories[0].terms); done(); - }, 0); + }, 10); }); - }); - describe('openContent', () => { - it('should open content', () => { - // arrange - jest.spyOn(searchPage, 'generateInteractEvent').mockImplementation(); - jest.spyOn(searchPage, 'checkParent').mockImplementation(); - const contentMock = { - identifier: 'id1', - contentType: 'type1', - pkgVersion: '1' - }; - // act - searchPage.openContent('collection', contentMock); - // assert - expect(searchPage.generateInteractEvent).toHaveBeenCalledWith( - contentMock.identifier, - contentMock.contentType, - contentMock.pkgVersion, - 0 - ); - expect(searchPage.parentContent).toEqual('collection'); - expect(searchPage.checkParent).toHaveBeenCalled(); - }); - it('should navigate to batch list popup', (done) => { + it('should set profile data accordingly', (done) => { // arrange - jest.spyOn(searchPage, 'generateInteractEvent').mockImplementation(); - jest.spyOn(searchPage, 'navigateToBatchListPopup').mockImplementation(); - mockAppGlobalService.setEnrolledCourseList = jest.fn(); - const kgetEnrolledCoursesResp = [ - { - contentId: 'id1', - cProgress: 80, - batch: { - status: 2 + const data = { + framework: 'framework1', + board: 'board', + medium: '', + gradeLevel: '', + contentType: 'Resource' + }; + const profile = { + syllabus: ['framework1'], + board: ['boardcode'], + medium: ['medium1'], + grade: ['grade1'] + }; + const getActiveChannelSuggestedFrameworkListResp = [{ identifier: 'framework1', name: 'framework1' }]; + const getFrameworkDetailsResp = { + categories: [ + { + code: 'board', + terms: [ + { code: 'boardcode', name: 'board' } + ] + }, + { + code: 'medium', + terms: [ + { code: 'medium1', name: 'medium1' } + ] + }, + { + code: 'gradeLevel', + terms: [ + { code: 'grade1', name: 'grade1' } + ] } - } - ]; - searchPage.enrolledCourses = kgetEnrolledCoursesResp; - mockCourseService.getEnrolledCourses = jest.fn(() => of(kgetEnrolledCoursesResp)); - const contentMock = { - identifier: 'id1', - contentType: 'type1', - pkgVersion: '1' + ] }; + jest.spyOn(searchPage, 'setCurrentProfile').mockImplementation(); + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of(getActiveChannelSuggestedFrameworkListResp)); + mockFrameworkService.getFrameworkDetails = jest.fn(() => of(getFrameworkDetailsResp)); // act - searchPage.openContent(undefined, contentMock, 0, undefined); + searchPage.checkProfileData(data, profile); // assert - expect(searchPage.generateInteractEvent).toHaveBeenCalled(); setTimeout(() => { - expect(mockCourseService.getEnrolledCourses).toHaveBeenCalledWith({"returnFreshCourses": true, "userId": "userId"}); + expect(searchPage.isProfileUpdated).toEqual(true); + expect(searchPage.boardList).toEqual(getFrameworkDetailsResp.categories[0].terms); done(); - }, 0); + }, 10); }); - it('should show content details', (done) => { + + + it('should set profile data accordingly', (done) => { // arrange - jest.spyOn(searchPage, 'generateInteractEvent').mockImplementation(); - jest.spyOn(searchPage, 'navigateToBatchListPopup').mockImplementation(); - mockAppGlobalService.setEnrolledCourseList = jest.fn(); + const data = { + framework: 'framework1', + board: 'board', + medium: ['medium1'], + gradeLevel: '', + contentType: 'Resource' + }; + const profile = { + syllabus: ['framework1'], + board: ['boardcode'], + medium: ['medium1'], + grade: ['grade1'] + }; + const getActiveChannelSuggestedFrameworkListResp = [{ identifier: 'framework1', name: 'framework1' }]; + const getFrameworkDetailsResp = { + categories: [ + { + code: 'board', + terms: [ + { code: 'boardcode', name: 'board' } + ] + }, + { + code: 'medium', + terms: [ + { code: 'medium1', name: 'medium1' } + ] + }, + { + code: 'gradeLevel', + terms: [ + { code: 'grade1', name: 'grade1' } + ] + } + ] + }; + jest.spyOn(searchPage, 'setCurrentProfile').mockImplementation(); + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of(getActiveChannelSuggestedFrameworkListResp)); + mockFrameworkService.getFrameworkDetails = jest.fn(() => of(getFrameworkDetailsResp)); + // act + searchPage.checkProfileData(data, profile); + // assert + setTimeout(() => { + expect(searchPage.isProfileUpdated).toEqual(true); + expect(searchPage.boardList).toEqual(getFrameworkDetailsResp.categories[0].terms); + done(); + }, 10); + }); + it('should set profile data accordingly', (done) => { + // arrange + const data = { + framework: 'framework1', + contentType: 'Resource' + }; + const profile = { + syllabus: ['framework1'] + }; + const getActiveChannelSuggestedFrameworkListResp = [{ identifier: 'framework1', name: 'framework1' }]; + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of(getActiveChannelSuggestedFrameworkListResp)); + mockFrameworkService.getFrameworkDetails = jest.fn(() => throwError(new NetworkError('No_Internet'))); + // act + searchPage.checkProfileData(data, profile); + // assert + setTimeout(() => { + expect(searchPage.isProfileUpdated).toEqual(true); + done(); + }, 0); + }); + it('should set profile data accordingly', (done) => { + // arrange + const data = { + framework: 'framework1', + contentType: 'Resource' + }; + const profile = { + syllabus: ['framework1'] + }; + const getActiveChannelSuggestedFrameworkListResp = [{ identifier: 'framework1', name: 'framework1' }]; + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => of(getActiveChannelSuggestedFrameworkListResp)); + mockFrameworkService.getFrameworkDetails = jest.fn(() => throwError({error: 'No_Internet'})); + // act + searchPage.checkProfileData(data, profile); + // assert + setTimeout(() => { + expect(searchPage.isProfileUpdated).toEqual(true); + done(); + }, 0); + }); + it('should handle error on get active channel ', () => { + // arrange + const data = { + framework: 'framework1' + }; + const profile = { + syllabus: ['framework1'] + }; + mockFrameworkUtilService.getActiveChannelSuggestedFrameworkList = jest.fn(() => throwError(new NetworkError('No_Internet'))); + // act + searchPage.checkProfileData(data, profile); + // assert + setTimeout(() => { + // expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_OFFLINE_MODE') + }, 0); + }) + }); + describe('editProfile', () => { + it('should edit Profile', (done) => { + // arrange + searchPage.gradeList = [{ code: 'grade1', name: 'grade1' }]; + searchPage.profile = { + grade: ['grade1'], + gradeValue: { + grade1: 'grade1' + } + }; + mockProfileService.updateProfile = jest.fn(() => of({ syllabus: 'sylabus' })); + mockCommonUtilService.handleToTopicBasedNotification = jest.fn(); + // act + searchPage.editProfile(); + // assert + setTimeout(() => { + expect(mockCommonUtilService.handleToTopicBasedNotification).toHaveBeenCalled(); + done(); + }, 0); + }); + it('should edit Profile, handle else if no grades length', (done) => { + // arrange + searchPage.gradeList = [{ code: 'grade1', name: 'grade1' }]; + searchPage.profile = { + grade: [], + gradeValue: { + grade1: 'grade1' + } + }; + mockProfileService.updateProfile = jest.fn(() => of({ syllabus: 'sylabus' })); + mockCommonUtilService.handleToTopicBasedNotification = jest.fn(); + // act + searchPage.editProfile(); + // assert + setTimeout(() => { + expect(mockCommonUtilService.handleToTopicBasedNotification).toHaveBeenCalled(); + done(); + }, 0); + }); + it('should edit Profile and publish event for on boarding', (done) => { + // arrange + searchPage.gradeList = [{ code: 'grade', name: 'grade1' }]; + searchPage.profile = { + grade: ['grade1'], + gradeValue: { + grade1: 'grade1' + } + }; + mockProfileService.updateProfile = jest.fn(() => of({ syllabus: ['sylabus1', 'sylabus2'], board: ['board1', 'board2'], grade:['grade1', 'grade2'], medium: ['english', 'hindi']})); + mockEvents.publish = jest.fn(); + mockAppGlobalService.setOnBoardingCompleted = jest.fn() + // act + searchPage.editProfile(); + // assert + setTimeout(() => { + expect(mockEvents.publish).toHaveBeenCalledWith('user-profile-changed'); + expect(mockEvents.publish).toHaveBeenCalledWith('refresh:profile'); + expect(mockAppGlobalService.setOnBoardingCompleted).toHaveBeenCalled(); + done(); + }, 0); + }); + }); + describe('openContent', () => { + it('should open content', () => { + // arrange + jest.spyOn(searchPage, 'generateInteractEvent').mockImplementation(); + jest.spyOn(searchPage, 'checkParent').mockImplementation(); + const contentMock = { + identifier: 'id1', + contentType: 'type1', + pkgVersion: '1' + }; + // act + searchPage.openContent('collection', contentMock); + // assert + expect(searchPage.generateInteractEvent).toHaveBeenCalledWith( + contentMock.identifier, + contentMock.contentType, + contentMock.pkgVersion, + 0 + ); + expect(searchPage.parentContent).toEqual('collection'); + expect(searchPage.checkParent).toHaveBeenCalled(); + + }); + it('should navigate to batch list popup', (done) => { + // arrange + jest.spyOn(searchPage, 'generateInteractEvent').mockImplementation(); + jest.spyOn(searchPage, 'navigateToBatchListPopup').mockImplementation(); + mockAppGlobalService.setEnrolledCourseList = jest.fn(); + const kgetEnrolledCoursesResp = [ + { + contentId: 'id1', + cProgress: 80, + batch: { + status: 2 + } + } + ]; + searchPage.enrolledCourses = kgetEnrolledCoursesResp; + mockCourseService.getEnrolledCourses = jest.fn(() => of(kgetEnrolledCoursesResp)); + const contentMock = { + identifier: 'id1', + contentType: 'type1', + pkgVersion: '1' + }; + // act + searchPage.openContent(undefined, contentMock, 0, undefined); + // assert + expect(searchPage.generateInteractEvent).toHaveBeenCalled(); + setTimeout(() => { + expect(mockCourseService.getEnrolledCourses).toHaveBeenCalledWith({"returnFreshCourses": true, "userId": undefined}); + done(); + }, 0); + }); + it('should show content details', (done) => { + // arrange + jest.spyOn(searchPage, 'generateInteractEvent').mockImplementation(); + jest.spyOn(searchPage, 'navigateToBatchListPopup').mockImplementation(); + mockAppGlobalService.setEnrolledCourseList = jest.fn(); const kgetEnrolledCoursesResp = [ { contentId: 'id1', @@ -1070,7 +1375,7 @@ describe('SearchPage', () => { // assert expect(searchPage.generateInteractEvent).toHaveBeenCalled(); setTimeout(() => { - expect(mockCourseService.getEnrolledCourses).toHaveBeenCalledWith({"returnFreshCourses": true, "userId": "userId"}); + expect(mockCourseService.getEnrolledCourses).toHaveBeenCalledWith({"returnFreshCourses": true, "userId": undefined}); done(); }, 0); }); @@ -1085,7 +1390,7 @@ describe('SearchPage', () => { pkgVersion: '1' }; // act - searchPage.openContent(undefined, contentMock, 0, undefined, true); + searchPage.openContent(undefined, contentMock, 1, undefined, true); // assert setTimeout(() => { done(); @@ -1124,6 +1429,38 @@ describe('SearchPage', () => { done(); }, 0); }); + + it('should goto filter page on showFilter, handle else cndtn', (done) => { + // arrange + searchPage.source = ''; + searchPage.searchFilterConfig = [{code: 'name', name:'name', translation: 'msg'}] + searchPage.initialFilterCriteria = { + facetFilters: [{ name: 'name2' }] + }; + searchPage.responseData = { + filterCriteria: { + facetFilters: [{ name: 'name1' }] + } + }; + const getLibraryFilterConfigResp = [ + { name: 'name', code: 'code' } + ]; + mockCommonUtilService.getTranslatedValue = jest.fn(() => 'translation'); + mockFormAndFrameworkUtilService.getLibraryFilterConfig = jest.fn(() => Promise.resolve(getLibraryFilterConfigResp)); + // act + searchPage.showFilter(); + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( + InteractType.TOUCH, + InteractSubtype.FILTER_BUTTON_CLICKED, + Environment.HOME, + 'search' + ); + setTimeout(() => { + expect(mockRouter.navigate).toHaveBeenCalled(); + done(); + }, 0); + }); }); describe('applyFilter and handleCancel', () => { it('should catch error on searchcontent', () => { @@ -1145,6 +1482,27 @@ describe('SearchPage', () => { // act searchPage.applyFilter(); }) + it('should updateFilterIcon on applyFilter, if no audience name for facet filter', (done) => { + // arrange + const offset = 10; + const searchContentResp = '' + searchPage.responseData = { + filterCriteria: { + facetFilters: [{ name: 'audience1', values: ['val1'] }], + offset: offset + } + }; + jest.spyOn(searchPage, 'updateFilterIcon').mockImplementation(); + jest.spyOn(searchPage, 'fetchPrimaryCategoryFilters').mockImplementation(); + mockContentService.searchContent = jest.fn(() => of(searchContentResp)); + searchPage.isDialCodeSearch = false; + // act + searchPage.applyFilter(offset); + // assert + setTimeout(() => { + done(); + }, 0); + }); it('should updateFilterIcon on applyFilter', (done) => { // arrange const offset = 10; @@ -1235,6 +1593,7 @@ describe('SearchPage', () => { expect(searchPage.isEmptyResult).toEqual(false); }); }); + describe('handleSearch', () => { it('should return without doing anything', () => { // arrange @@ -1262,7 +1621,7 @@ describe('SearchPage', () => { mockContentService.searchContent = jest.fn(() => of(undefined)); searchPage.isEmptyResult = true; // act - searchPage.handleSearch(false, 100); + searchPage.handleSearch(true, 100); // assert }); it('should handle success search scenario', (done) => { @@ -1568,40 +1927,97 @@ describe('SearchPage', () => { done(); }, 0); }); + it('should call processdialcoderesult, handle else case if no length for secctions and profile board', (done) => { + // arrange + searchPage.dialCode = 'abcdef'; + jest.spyOn(searchPage, 'processDialCodeResult').mockImplementation(); + const getSupportedContentFilterConfigResp = ['textbook', 'resource']; + mockFormAndFrameworkUtilService.getSupportedContentFilterConfig = jest.fn( + () => Promise.resolve(getSupportedContentFilterConfigResp)); + const dialAssembleResp = { + sections: [] + }; + mockpageService.getPageAssemble = jest.fn(() => of(dialAssembleResp)); + searchPage.profile = { + board: [] + }; + // act + searchPage.getContentForDialCode(); + // assert + setTimeout(() => { + expect(searchPage.isDialCodeSearch).toBe(true); + expect(searchPage.primaryCategories).toEqual(getSupportedContentFilterConfigResp); + done(); + }, 0); + }); it('should handle failure case of dial pageassemble API', (done) => { // arrange searchPage.dialCode = 'abcdef'; mockCommonUtilService.networkInfo = { - isNetworkAvailable: true + isNetworkAvailable: true + }; + const getSupportedContentFilterConfigResp = ['textbook', 'resource']; + mockFormAndFrameworkUtilService.getSupportedContentFilterConfig = jest.fn( + () => Promise.resolve(getSupportedContentFilterConfigResp)); + mockpageService.getPageAssemble = jest.fn(() => throwError({})); + // act + searchPage.getContentForDialCode(); + // assert + setTimeout(() => { + expect(searchPage.isDialCodeSearch).toBe(true); + expect(searchPage.primaryCategories).toEqual(getSupportedContentFilterConfigResp); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('SOMETHING_WENT_WRONG'); + expect(mockLocation.back).toHaveBeenCalled(); + done(); + }, 0); + }); + it('should handle failure case of dial pageassemble API when network is not there', (done) => { + // arrange + searchPage.dialCode = 'abcdef'; + mockCommonUtilService.networkInfo = { + isNetworkAvailable: false }; const getSupportedContentFilterConfigResp = ['textbook', 'resource']; mockFormAndFrameworkUtilService.getSupportedContentFilterConfig = jest.fn( () => Promise.resolve(getSupportedContentFilterConfigResp)); mockpageService.getPageAssemble = jest.fn(() => throwError({})); + searchPage.source = PageId.ONBOARDING_PROFILE_PREFERENCES; + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); // act searchPage.getContentForDialCode(); // assert setTimeout(() => { expect(searchPage.isDialCodeSearch).toBe(true); expect(searchPage.primaryCategories).toEqual(getSupportedContentFilterConfigResp); - expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('SOMETHING_WENT_WRONG'); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_OFFLINE_MODE'); expect(mockLocation.back).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith( + AuditType.TOAST_SEEN, + ImpressionSubtype.OFFLINE_MODE, + PageId.SCAN_OR_MANUAL, + Environment.ONBOARDING, + undefined, + undefined, + undefined, + undefined, + [{ id: 'abcdef', type: 'QR' }] + ); done(); }, 0); }); - it('should handle failure case of dial pageassemble API when network is not there', (done) => { + it('should handle failure case of dial pageassemble API when network is not there, handle for source home', (done) => { // arrange searchPage.dialCode = 'abcdef'; mockCommonUtilService.networkInfo = { isNetworkAvailable: false }; + searchPage.source = PageId.HOME; const getSupportedContentFilterConfigResp = ['textbook', 'resource']; mockFormAndFrameworkUtilService.getSupportedContentFilterConfig = jest.fn( () => Promise.resolve(getSupportedContentFilterConfigResp)); mockpageService.getPageAssemble = jest.fn(() => throwError({})); - searchPage.source = PageId.ONBOARDING_PROFILE_PREFERENCES; + searchPage.source = PageId.HOME; mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); - searchPage.source = PageId.ONBOARDING_PROFILE_PREFERENCES; // act searchPage.getContentForDialCode(); // assert @@ -1614,7 +2030,7 @@ describe('SearchPage', () => { AuditType.TOAST_SEEN, ImpressionSubtype.OFFLINE_MODE, PageId.SCAN_OR_MANUAL, - Environment.ONBOARDING, + Environment.HOME, undefined, undefined, undefined, @@ -1644,6 +2060,48 @@ describe('SearchPage', () => { expect.anything(), ); }); + it('should generate interact event', () => { + // arrange + searchPage.isDialCodeSearch = false; + mockAppGlobalService.isOnBoardingCompleted = jest.fn(() => true); + searchPage['corRelationList'] = [] + mockAppGlobalService.isOnBoardingCompleted = false; + // act + searchPage.generateInteractEvent('identifier', 'collection', 'ver', 1); + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( + InteractType.TOUCH, + InteractSubtype.CONTENT_CLICKED, + Environment.ONBOARDING, + PageId.HOME, + expect.anything(), + expect.anything(), + expect.anything(), + expect.anything(), + ); + }); + it('should generate interact event', () => { + // arrange + searchPage.isDialCodeSearch = false; + searchPage.source = ""; + mockAppGlobalService.isOnBoardingCompleted = jest.fn(() => true); + searchPage['corRelationList'] = [{id: 'id1', type: 'section'}] + mockAppGlobalService.isOnBoardingCompleted = false; + // act + searchPage.generateInteractEvent('identifier', 'collection', 'ver', 1); + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( + InteractType.TOUCH, + InteractSubtype.CONTENT_CLICKED, + Environment.ONBOARDING, + PageId.SEARCH, + expect.anything(), + expect.anything(), + expect.anything(), + expect.anything(), + ); + }); + it('should generate qrsession end event', () => { // arrange mockTelemetryGeneratorService.generateEndTelemetry = jest.fn(); @@ -1660,11 +2118,18 @@ describe('SearchPage', () => { expect.anything() ); }); + it('should generate qrsession end event, if page id is undefined', () => { + // arrange + // act + searchPage.generateQRSessionEndEvent(undefined, 'qrData'); + // assert + }); it('should generate QRScanSuccess Interact Event online', () => { // arrange mockCommonUtilService.networkInfo = { isNetworkAvailable: true }; + searchPage.source = 'home' // act searchPage.generateQRScanSuccessInteractEvent(2, 'ABCDEF'); // assert @@ -1682,13 +2147,14 @@ describe('SearchPage', () => { mockCommonUtilService.networkInfo = { isNetworkAvailable: false }; + searchPage.source = ""; // act searchPage.generateQRScanSuccessInteractEvent(2, 'ABCDEF'); // assert expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( InteractType.OTHER, InteractSubtype.DIAL_SEARCH_RESULT_FOUND, - expect.anything(), + PageId.SEARCH, PageId.SEARCH, undefined, { count: 2, networkAvailable: 'N', scannedData: 'ABCDEF' } @@ -1839,21 +2305,69 @@ describe('SearchPage', () => { done(); }, 0); }); + it('checkParent on error, if not an instance of network', (done) => { + // arrange + mockContentService.getContentDetails = jest.fn(() => throwError({error:'no-internet'})); + // act + searchPage.checkParent('parent', 'child'); + // assert + setTimeout(() => { + done(); + }, 0); + }); }); describe('processDialCodeResult', () => { + it('processDialCodeResult, content and collections are empty array', () => { + // arrange + const dialResult = [{collections: [], contents: [], display: false}]; + mockCommonUtilService.getTranslatedValue = jest.fn(); + searchPage.displayDialCodeResult = []; + searchPage.isSingleContent = false; + jest.spyOn(searchPage, 'generateImpressionEvent').mockImplementation() + // act + searchPage.processDialCodeResult(dialResult); + + }); + + it('processDialCodeResult', () => { + // arrange + const dialResult = [{collections: [{childNodes: 'id1'}], contents: [{identifier: 'id1', contentType: 'Course'}, {identifier: 'id2'}], display: false}]; + mockCommonUtilService.getTranslatedValue = jest.fn(); + searchPage.displayDialCodeResult = []; + searchPage.isSingleContent = false; + jest.spyOn(searchPage, 'generateImpressionEvent').mockImplementation() + // act + searchPage.processDialCodeResult(dialResult); + + }); + + it('processDialCodeResult', () => { + // arrange + const dialResult = [{collections: [{childNodes: 'id2', content:[]}], contents: [{identifier: 'id1', contentType: 'Course'}, {identifier: 'id2'}], display: false}]; + mockCommonUtilService.getTranslatedValue = jest.fn(); + searchPage.displayDialCodeResult = []; + searchPage.isSingleContent = false; + jest.spyOn(searchPage, 'generateImpressionEvent').mockImplementation() + // act + searchPage.processDialCodeResult(dialResult); + + }); + it('processDialCodeResult', () => { // arrange - const dialResult = [{collections: [{childNodes: 'id1'}], contents: [{identifier: 'id1', contentType: 'Course'}, {identifier: 'id2'}], display: true}]; + const dialResult = [{collections: [{childNodes: 'id2', content:[{identifier:''}]}], contents: [{identifier: 'id1', contentType: 'Course2'}, {identifier: 'id2'}], display: true}]; mockCommonUtilService.getTranslatedValue = jest.fn(); searchPage.displayDialCodeResult = []; + searchPage.isSingleContent = false; jest.spyOn(searchPage, 'generateImpressionEvent').mockImplementation() // act searchPage.processDialCodeResult(dialResult); }); + it('processDialCodeResult for sibling content and navigate back', (done) => { // arrange - const dialResult = [{collections: [{childNodes: 'id1'}], contents: [{identifier: 'id1'}], display: false}]; + const dialResult = [{collections: [], contents: '', display: false}]; mockCommonUtilService.getTranslatedValue = jest.fn(); searchPage.isSingleContent = false jest.spyOn(searchPage, 'generateImpressionEvent').mockImplementation() @@ -1871,8 +2385,26 @@ describe('SearchPage', () => { }); }); describe('downloadParentContent', () => { + it('should handle else case if no import content data', (done) => { + // arrange + mockPlatform.is = jest.fn(platform => platform == 'ios') + const importContentResp = []; + mockContentService.importContent = jest.fn(() => of(importContentResp)); + const parent = { identifier: 'id' }; + // act + searchPage.downloadParentContent(parent); + // assert + expect(searchPage.downloadProgress).toEqual(0); + expect(searchPage.isDownloadStarted).toEqual(true); + setTimeout(() => { + expect(searchPage.queuedIdentifiers).toEqual([]); + done(); + }, 0); + }); + it('should push not downloaded identifiers in to queue', (done) => { // arrange + mockPlatform.is = jest.fn(platform => platform == 'ios') const importContentResp = [ { status: ContentImportStatus.ENQUEUED_FOR_DOWNLOAD, identifier: 'id1' }]; mockContentService.importContent = jest.fn(() => of(importContentResp)); @@ -1898,8 +2430,38 @@ describe('SearchPage', () => { }, 0); }); + it('should push not downloaded identifiers in to queue', (done) => { + // arrange + mockPlatform.is = jest.fn(platform => platform == 'ios') + const importContentResp = [ + { status: ContentImportStatus.ENQUEUED_FOR_DOWNLOAD, identifier: 'id1' }]; + mockContentService.importContent = jest.fn(() => of(importContentResp)); + const parent = { identifier: 'id' }; + searchPage.source = PageId.USER_TYPE_SELECTION; + // act + searchPage.downloadParentContent(parent); + // assert + expect(searchPage.downloadProgress).toEqual(0); + expect(searchPage.isDownloadStarted).toEqual(true); + setTimeout(() => { + expect(searchPage.queuedIdentifiers).toEqual(['id1', 'id1']); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( + InteractType.OTHER, + InteractSubtype.LOADING_SPINE, + Environment.ONBOARDING, + PageId.DIAL_SEARCH, + undefined, + undefined, + undefined, + expect.anything() + ); + done(); + }, 0); + }); + it('should push not downloaded identifiers not found', (done) => { // arrange + mockPlatform.is = jest.fn(platform => platform == 'android') const importContentResp = [ { status: ContentImportStatus.NOT_FOUND, identifier: 'id1' }]; mockContentService.importContent = jest.fn(() => of(importContentResp)); @@ -1945,6 +2507,7 @@ describe('SearchPage', () => { mockContentService.importContent = jest.fn(() => throwError(new NetworkError('no_internet'))); const parent = { identifier: 'id' }; mockCommonUtilService.showToast = jest.fn() + searchPage['corRelationList'] = undefined as any; // act searchPage.downloadParentContent(parent); // assert @@ -1954,6 +2517,19 @@ describe('SearchPage', () => { }, 0); }) + it('should catch error on import content else case', (done) => { + // arrange + mockContentService.importContent = jest.fn(() => throwError({error:'no_internet'})); + const parent = { identifier: 'id' }; + searchPage['corRelationList'] = undefined as any; + // act + searchPage.downloadParentContent(parent); + // assert + setTimeout(() => { + done(); + }, 0); + }) + describe('handleDeviceBackButton', () => { it('should handle Device BackButton for dialcode', () => { // arrange @@ -1977,6 +2553,29 @@ describe('SearchPage', () => { ); }); + it('should handle Device BackButton for dialcode', () => { + // arrange + const subscribeWithPriorityData = jest.fn((_, fn) => fn()); + mockPlatform.backButton = { + subscribeWithPriority: subscribeWithPriorityData + } as any; + jest.spyOn(searchPage, 'navigateToPreviousPage').mockImplementation(() => { + return Promise.resolve(); + }); + searchPage.source = PageId.ONBOARDING; + searchPage.displayDialCodeResult = [{ dialCodeResult: ['result-1', 'result-2'] }]; + mockTelemetryGeneratorService.generateBackClickedNewTelemetry = jest.fn(); + // act + searchPage.handleDeviceBackButton(); + // assert + expect(searchPage.displayDialCodeResult[0].dialCodeResult.length).toBeGreaterThan(0); + expect(mockTelemetryGeneratorService.generateBackClickedNewTelemetry).toHaveBeenCalledWith( + true, + Environment.ONBOARDING, + PageId.QR_BOOK_RESULT + ); + }); + it('should handle Device BackButton', () => { // arrange const subscribeWithPriorityData = jest.fn((_, fn) => fn()); @@ -1995,13 +2594,7 @@ describe('SearchPage', () => { expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( ImpressionType.SEARCH, Environment.HOME, false, undefined, - [ - { id: '', type: 'API' }, - { id: '', type: 'API' }, - { id: '', type: 'API' }, - { id: 'SearchResult', type: 'Section' }, - { id: 'filter', type: 'DiscoveryType' }, - ] + undefined ); }); }); @@ -2023,10 +2616,32 @@ describe('SearchPage', () => { PageId.QR_BOOK_RESULT ); }); + it('should generate beck telemetry for qrCode', () => { + searchPage.displayDialCodeResult = [{ + dialCodeResult: ['result-1'] + }]; + mockTelemetryGeneratorService.generateBackClickedNewTelemetry = jest.fn(); + searchPage.source = PageId.HOME; + // act + searchPage.goBack(); + // assert + expect(mockTelemetryGeneratorService.generateBackClickedNewTelemetry).toHaveBeenCalledWith( + false, + Environment.HOME, + PageId.QR_BOOK_RESULT + ); + }); it('should generate search telemetry for qrCode', (done) => { searchPage.displayDialCodeResult = [{ dialCodeResult: [] }]; + searchPage['corRelationList'] = [ + { id: '', type: 'API' }, + { id: '', type: 'API' }, + { id: '', type: 'API' }, + { id: 'SearchResult', type: 'Section' }, + { id: 'filter', type: 'DiscoveryType' }, + ] mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); // act searchPage.goBack(); @@ -2058,6 +2673,30 @@ describe('SearchPage', () => { searchPage.getContentCount(displayDialCodeResult) // assert }) + + it('should check dail code result and return an count, if no content length', () => { + // arrange + let totalCount; + const displayDialCodeResult = [{ + dialCodeResult: [{content:[]}], + dialCodeContentResult: ['Result-2'] + }]; + // act + searchPage.getContentCount(displayDialCodeResult) + // assert + }) + + it('should check dail code result and return an count, if no dialcode result length', () => { + // arrange + let totalCount; + const displayDialCodeResult = [{ + dialCodeResult: [], + dialCodeContentResult: [] + }]; + // act + searchPage.getContentCount(displayDialCodeResult) + // assert + }) }); describe('cancelDownload', () => { @@ -2079,6 +2718,24 @@ describe('SearchPage', () => { done(); }, 0); }); + + it('should cancel download, else case if isSingleContent false', (done) => { + // arrange + searchPage.parentContent = {identifier: "result"} + mockContentService.cancelDownload = jest.fn(() => of()) + mockLocation.back = jest.fn(); + mockZone.run = jest.fn((fn) => fn()) + searchPage.showLoading = false + searchPage.isSingleContent = false + // act + searchPage.cancelDownload(); + // assert + setTimeout(() => { + expect(mockContentService.cancelDownload).toHaveBeenCalledWith(searchPage.parentContent.identifier); + expect(mockZone.run).toHaveBeenCalled(); + done(); + }, 0); + }); it('should catch a error on cancel download', (done) => { // arrange searchPage.parentContent = {identifier: ""} @@ -2097,6 +2754,23 @@ describe('SearchPage', () => { done(); }, 0); }); + it('should catch a error on cancel download, on singlecontent false', (done) => { + // arrange + searchPage.parentContent = {identifier: ""} + mockContentService.cancelDownload = jest.fn(() => throwError({})) + mockLocation.back = jest.fn(); + mockZone.run = jest.fn((fn) => fn( + )) + searchPage.showLoading = false + searchPage.isSingleContent = false + // act + searchPage.cancelDownload(); + // assert + setTimeout(() => { + expect(mockContentService.cancelDownload).toHaveBeenCalledWith(searchPage.parentContent.identifier); + done(); + }, 0); + }); }) describe('add activity', () => { @@ -2128,13 +2802,28 @@ describe('SearchPage', () => { { identifier: 'id1', selected: true, contentType: 'contentType' } ]; searchPage.activityList = [{ - id: 'id1' + id: 'id1' + }]; + // act + searchPage.addActivityToGroup(); + // assert + expect(mockCommonUtilService.showToast).toHaveBeenCalled(); + }); + + it('should not addActivityToGroup', () => { + // arrange + searchPage.searchContentResult = [ + { identifier: 'id1', selected: true, contentType: 'contentType' } + ]; + searchPage.activityTypeData = {}; + searchPage.activityList = [{ + id: 'id2' }]; // act searchPage.addActivityToGroup(); // assert - expect(mockCommonUtilService.showToast).toHaveBeenCalled(); }); + it('should open content', () => { // arrange const result = [ @@ -2153,6 +2842,24 @@ describe('SearchPage', () => { false ); }); + it('should open content, handle if not selected', () => { + // arrange + const result = [ + { identifier: 'some_id', selected: false, contentType: 'contentType' } + ]; + searchPage.searchContentResult = result; + jest.spyOn(searchPage, 'openContent').mockImplementation(); + // act + searchPage.openSelectedContent(); + // assert + expect(searchPage.openContent).toHaveBeenCalledWith( + undefined, + undefined, + 0, + undefined, + false + ); + }); }); describe('searchOnFocus()', ()=> { @@ -2191,23 +2898,30 @@ describe('SearchPage', () => { mockHeaderService.showHeaderWithHomeButton = jest.fn(); searchPage.isFromGroupFlow = false; searchPage.enableSearch = false; + searchPage['selectedSwitchableTab'] = SwitchableTabsConfig.HOME_DISCOVER_TABS_CONFIG; mockSharedPreferences.getString = jest.fn(() => Promise.resolve("HOME_DISCOVER_TABS_CONFIG")) // act searchPage.handleHeaderEvents({ name: 'back' }); // assert - expect(mockLocation.back).toHaveBeenCalled(); }); it('should navigate back for other case', () => { // arrange mockHeaderService.showHeaderWithHomeButton = jest.fn(); searchPage.isFromGroupFlow = false; searchPage.enableSearch = false; + searchPage['selectedSwitchableTab'] = SwitchableTabsConfig.RESOURCE_COURSE_TABS_CONFIG; mockSharedPreferences.getString = jest.fn(() => Promise.resolve("somedata")) // act searchPage.handleHeaderEvents({ name: 'back' }); // assert expect(mockLocation.back).toHaveBeenCalled(); }); + it('should handle default event', () => { + // arrange + // act + searchPage.handleHeaderEvents({ name: 'default' }); + // assert + }); }); describe('fetchPrimaryCategoryFilters()', () => { @@ -2258,6 +2972,39 @@ describe('SearchPage', () => { // assert }); + it('should handle else case, if the name of facetfilters is not primary category', () => { + // arrange + const facetFilters = [ + { + name: 'board', + value: [ + { + name: 'board1' + }, + { + name: 'board2' + } + ] + }, + { + name: 'primaryCategory2', + value: [ + { + name: 'category1' + }, + { + name: 'category2' + } + ] + } + ] + // act + searchPage.fetchPrimaryCategoryFilters(facetFilters); + // assert + setTimeout(() => { + }); + }); + it('should assign value to primaryCategoryFilters if the value is not assigned and primary category is present in facetfilters', () => { // arrange const facetFilters = [ @@ -2304,6 +3051,26 @@ describe('SearchPage', () => { expect(searchPage.applyFilter).not.toHaveBeenCalled(); }); + it('should handle else if no initialfiltercrietria', () => { + // arrange + const event = { + data: [ + { + name: 'facet 1', + value: { + name: 'value1' + } + } + ] + }; + searchPage.initialFilterCriteria = '' + searchPage.applyFilter = jest.fn(); + // act + searchPage.handleFilterSelect(event); + // assert + expect(searchPage.applyFilter).not.toHaveBeenCalled(); + }); + it('should terminate the flow if the initialFilterCriteria does not have the facet primaryCategory', () => { // arrange const event = { @@ -2509,6 +3276,7 @@ describe('SearchPage', () => { searchPage.preAppliedFilter = { query: '' } + searchPage.dialCode = ''; jest.spyOn(searchPage, 'enableHeaderEvents').mockImplementation(); mockHeaderService.headerEventEmitted$ = { subscribe: jest.fn((fn => fn({ name: '' })))} @@ -2528,7 +3296,6 @@ describe('SearchPage', () => { describe('ionViewWillLeave', () => { it('should unsubscribe event ', () => { // arrange - // searchPage.backButtonFunc = true; searchPage.backButtonFunc = { unsubscribe: jest.fn() } @@ -2547,6 +3314,22 @@ describe('SearchPage', () => { expect(searchPage.eventSubscription.unsubscribe).toHaveBeenCalled(); expect(searchPage.headerObservable).toBeUndefined(); }) + + it('should unsubscribe event ', () => { + // arrange + searchPage.backButtonFunc = undefined + searchPage.eventSubscription = undefined + searchPage.headerObservable = { + unsubscribe: jest.fn() + } + searchPage.refresher = { + disabled: true + } + // act + searchPage.ionViewWillLeave(); + // assert + expect(searchPage.headerObservable).toBeUndefined(); + }) }); describe('redirectToNotifications', () => { it('should call telemetry and redirect to notification', () => { @@ -2588,6 +3371,18 @@ describe('SearchPage', () => { expect(searchPage.eventSubscription.unsubscribe).toHaveBeenCalled(); expect(searchPage.headerObservable).toBeUndefined(); }) + + it('should return if events not subscribed ', () => { + // arrange + searchPage.eventSubscription = undefined; + searchPage.headerObservable = { + unsubscribe: jest.fn() + } + // act + searchPage.ngOnDestroy(); + // assert + expect(searchPage.headerObservable).toBeUndefined(); + }) }) describe('scrollToTop', () => { @@ -2622,6 +3417,9 @@ describe('SearchPage', () => { mockHeaderService.headerEventEmitted$ = { subscribe: jest.fn((fn => fn({ name: '' })))} mockHeaderService.showHeaderWithBackButton = jest.fn() + searchPage.headerObservable = { + unsubscribe: jest.fn() + } as any // act searchPage.tabViewWillEnter(); // assert @@ -2676,6 +3474,25 @@ describe('SearchPage', () => { expect(mockEventsBusService.events).toHaveBeenCalled(); }, 0); }); + it('should subscribe sdk events, if progress is not 100 ', () => { + // arrange + const event = {type: DownloadEventType.PROGRESS, payload: { + downloadId: 'sample_id', + identifier: 'sample', + progress: -1, + status: 8, + bytesDownloaded: 3242, + totalSizeInBytes: 234 + }}; + mockEventsBusService.events = jest.fn(() => of(event)) + mockEvents.subscribe = jest.fn(); + // act + searchPage.subscribeSdkEvent() + // assert + setTimeout(() => { + expect(mockEventsBusService.events).toHaveBeenCalled(); + }, 0); + }); it('should subscribe sdk events for import progress ', () => { // arrange const event = {type: ContentEventType.IMPORT_PROGRESS, payload: { @@ -2699,6 +3516,62 @@ describe('SearchPage', () => { clearInterval() }, 1000); }) + it('should subscribe sdk events for import progress, current count and total count are same ', () => { + // arrange + const event = {type: ContentEventType.IMPORT_PROGRESS, payload: { + downloadId: 'sample_id', + identifier: 'sample', + progress: 2, + status: 8, + totalCount: 2, + currentCount: 2, + bytesDownloaded: 3242, + totalSizeInBytes: 234 + }} + mockEventsBusService.events = jest.fn(() => of(event)) + mockEvents.subscribe = jest.fn(); + mockCommonUtilService.translateMessage = jest.fn(); + let timer = 2; + window.setInterval = jest.fn((fn) => fn({ + searchPage:{['loadingDisplayText'] : `Getting things ready in ${timer--} seconds`} + + }), 1000) as any + window.clearInterval = jest.fn(); + // act + searchPage.subscribeSdkEvent() + // assert + setTimeout(() => { + expect(mockEventsBusService.events).toHaveBeenCalled(); + clearInterval() + }, 1000); + }) + it('should subscribe sdk events for import progress, current count and total count are same ', () => { + // arrange + const event = {type: ContentEventType.IMPORT_PROGRESS, payload: { + downloadId: 'sample_id', + identifier: 'sample', + progress: 2, + status: 8, + totalCount: 2, + currentCount: 2, + bytesDownloaded: 3242, + totalSizeInBytes: 234 + }} + mockEventsBusService.events = jest.fn(() => of(event)) + mockEvents.subscribe = jest.fn(); + mockCommonUtilService.translateMessage = jest.fn(); + window.setInterval = jest.fn((fn) => fn({ + + }), 1000) as any + window.clearInterval = jest.fn(); + // act + searchPage.subscribeSdkEvent() + // assert + setTimeout(() => { + expect(mockEventsBusService.events).toHaveBeenCalled(); + clearInterval() + }, 1000); + }) it('should subscribe sdk events for import completed ', () => { // arrange const event = {type: ContentEventType.IMPORT_COMPLETED, payload: { @@ -2712,6 +3585,7 @@ describe('SearchPage', () => { bytesDownloaded: 3242, totalSizeInBytes: 234 }} + searchPage.source = PageId.USER_TYPE_SELECTION; mockEventsBusService.events = jest.fn(() => of(event)) mockEvents.subscribe = jest.fn(); mockEvents.publish = jest.fn(); @@ -2728,6 +3602,68 @@ describe('SearchPage', () => { }) }, 0); }) + + it('should subscribe sdk events for import completed, if source is not USER_TYPE_SELECTION ', () => { + // arrange + const event = {type: ContentEventType.IMPORT_COMPLETED, payload: { + downloadId: 'sample_id', + identifier: 'sample', + progress: 2, + contentId: 'id', + status: 8, + totalCount: 2, + currentCount: 1, + bytesDownloaded: 3242, + totalSizeInBytes: 234 + }} + searchPage.source = PageId.HOME; + mockEventsBusService.events = jest.fn(() => of(event)) + mockEvents.subscribe = jest.fn(); + mockEvents.publish = jest.fn(); + searchPage.queuedIdentifiers = ['id', 'id1']; + searchPage.isDownloadStarted = true; + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn() + // act + searchPage.subscribeSdkEvent() + // assert + setTimeout(() => { + expect(mockEventsBusService.events).toHaveBeenCalled(); + expect(mockEvents.publish).toHaveBeenCalledWith('savedResources:update', { + update: true + }) + }, 0); + }) + + it('should subscribe sdk events for import completed, if queued identifier does not include contentid ', () => { + // arrange + const event = {type: ContentEventType.IMPORT_COMPLETED, payload: { + downloadId: 'sample_id', + identifier: 'sample', + progress: 2, + contentId: 'id', + status: 8, + totalCount: 2, + currentCount: 1, + bytesDownloaded: 3242, + totalSizeInBytes: 234 + }} + searchPage.source = PageId.USER_TYPE_SELECTION; + mockEventsBusService.events = jest.fn(() => of(event)) + mockEvents.subscribe = jest.fn(); + mockEvents.publish = jest.fn(); + searchPage.queuedIdentifiers = ['id2', 'id1']; + searchPage.isDownloadStarted = true; + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn() + // act + searchPage.subscribeSdkEvent() + // assert + setTimeout(() => { + expect(mockEventsBusService.events).toHaveBeenCalled(); + expect(mockEvents.publish).toHaveBeenCalledWith('savedResources:update', { + update: true + }) + }, 0); + }) it('should subscribe sdk events for import completed with different length', () => { // arrange const event = {type: ContentEventType.IMPORT_COMPLETED, payload: { @@ -2741,6 +3677,7 @@ describe('SearchPage', () => { bytesDownloaded: 3242, totalSizeInBytes: 234 }} + searchPage.queuedIdentifiers = []; mockEventsBusService.events = jest.fn(() => of(event)) mockEvents.subscribe = jest.fn(); mockEvents.publish = jest.fn(); @@ -2756,12 +3693,119 @@ describe('SearchPage', () => { }) }) - // describe('ngAfterViewInit', () => { - // it('should ngAfterViewInit ', () => { - // // arrange - // // act - // searchPage.ngAfterViewInit(); - // // assert - // }) - // }) + describe('ngAfterViewInit', () => { + it('should handle searchbar and search history on ngAfterViewInit ', () => { + // arrange + const event: CustomEvent = {target:['value']} + searchPage.searchBar = { + ionChange: { + pipe: jest.fn(() => ({ + rxjsMap: jest.fn((fn) => fn(event)), + share:jest.fn(), + startWith: jest.fn(), + debounceTime: jest.fn(), + switchMap: jest.fn((fn) => fn('')), + tap: jest.fn((fn1) => fn1()) + })) + } + }; + window.setTimeout = jest.fn((fn) => fn({}), ) as any; + searchPage['searchHistoryService'] = { + getEntries: jest.fn() + } + // act + searchPage.ngAfterViewInit(); + // assert + }) + }); + + describe('init', () => { + beforeEach(() => { + const mockRouterExtras = { + extras: { + state: { + dialCode: [{}], + primaryCategories: 'primaryCategories', + corRelationList: 'corRelationList', + source: PageId.HOME, + enrolledCourses: 'enrolledCourses' as any, + userId: 'userId', + shouldGenerateEndTelemetry: false, + preAppliedFilter: '' + }, + } + }; + mockRouter.getCurrentNavigation = jest.fn(() => mockRouterExtras as any); + }) + it('should call init and generatepage loaded telemtry', () => { + // arrange + const mockRouterExtras = { + extras: { + state: { + dialCode: [{}], + primaryCategories: 'primaryCategories', + corRelationList: 'corRelationList', + source: PageId.HOME, + enrolledCourses: 'enrolledCourses' as any, + userId: 'userId', + shouldGenerateEndTelemetry: false, + preAppliedFilter: '' + }, + } + }; + searchPage.responseData = { + filterCriteria: { + facetFilters: [{ name: 'audience1', values: ['val1'] }] + } + }; + mockRouter.getCurrentNavigation = jest.fn(() => mockRouterExtras as any), + mockEvent.subscribe = jest.fn((_, fn) => fn({ + facetFilters: [{ name: 'audience1', values: ['val1'] }] + })); + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn() + const corRelationList: Array = []; + corRelationList.push({ id: searchPage.dialCode, type: CorReleationDataType.QR }); + corRelationList.push({ id: '1', type: CorReleationDataType.COUNT_BOOK }); + // act + searchPage.init() + // assert + setTimeout(() => { + }, 0); + }) + it('should call init and generatepage loaded telemtry', () => { + // arrange + const mockRouterExtras = { + extras: { + state: { + dialCode: [], + primaryCategories: 'primaryCategories', + corRelationList: 'corRelationList', + source: PageId.HOME, + enrolledCourses: 'enrolledCourses' as any, + userId: 'userId', + shouldGenerateEndTelemetry: false, + preAppliedFilter: '' + }, + } + }; + searchPage.responseData = { + filterCriteria: { + facetFilters: [{ name: 'audience1', values: ['val1'] }] + } + }; + mockRouter.getCurrentNavigation = jest.fn(() => mockRouterExtras as any), + mockEvent.subscribe = jest.fn((_, fn) => fn( { + facetFilters: [{ name: 'audience1', values: ['val1'] }] + })); + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn() + const corRelationList: Array = []; + corRelationList.push({ id: searchPage.dialCode, type: CorReleationDataType.QR }); + corRelationList.push({ id: '1', type: CorReleationDataType.COUNT_BOOK }); + // act + searchPage.init() + // assert + setTimeout(() => { + }, 0); + }) + }) }); diff --git a/src/app/settings/about-us/about-us.component.spec.ts b/src/app/settings/about-us/about-us.component.spec.ts index 0c922c0af3..9f4b2ca2d0 100644 --- a/src/app/settings/about-us/about-us.component.spec.ts +++ b/src/app/settings/about-us/about-us.component.spec.ts @@ -13,12 +13,13 @@ import {Router} from '@angular/router'; import {AppVersion} from '@ionic-native/app-version/ngx'; import {AppHeaderService, UtilityService} from '../../../services'; import {ContentService, DeviceInfo, ProfileService, SharedPreferences} from '@project-sunbird/sunbird-sdk'; -import {of, Subscription, throwError} from 'rxjs'; +import {of, Subscription} from 'rxjs'; window['sbutility'] = { removeFile: jest.fn(), shareSunbirdConfigurations: jest.fn((_, __, fn) => fn()) }; +window.console.error = jest.fn() describe('AboutUsComponent', () => { let aboutUsComponent: AboutUsComponent; @@ -176,6 +177,7 @@ describe('AboutUsComponent', () => { describe('ionViewDidLeave', () => { it('should remove sub utility file ', (done) => { // arrange + window['sbutility'].removeFile = jest.fn((fn) => fn()) // act aboutUsComponent.ionViewDidLeave(); // asert @@ -187,6 +189,9 @@ describe('AboutUsComponent', () => { }) it('should catch error on remove sub utility file ', (done) => { // arrange + window['sbutility'].removeFile = jest.fn((success, error) => { + error({}) + }) // act aboutUsComponent.ionViewDidLeave(); // asert @@ -236,7 +241,7 @@ describe('AboutUsComponent', () => { dismiss} )); mockSharedPreferences.putString = jest.fn(() => of()) - mockSharedPreferences.getString = jest.fn(() => of('false')) + mockSharedPreferences.getString = jest.fn(() => of(false)) as any // act aboutUsComponent.shareInformation() // assert @@ -259,6 +264,31 @@ describe('AboutUsComponent', () => { {present, dismiss} )); + window['sbutility'].shareSunbirdConfigurations = jest.fn((_, _1, success, error) => { + error({}) + }) + // act + aboutUsComponent.shareInformation() + // assert + setTimeout(() => { + expect(mockProfileService.getAllProfiles).toHaveBeenCalled(); + expect(mockContentService.getContents).toHaveBeenCalled() + expect(window['sbutility'].shareSunbirdConfigurations).toThrowError(); + done() + }, 0); + }) + + it('should return without sharing information, if config file path is false, if no loader', (done) => { + // arrange + const present = jest.fn(() => Promise.resolve()); + const dismiss = jest.fn(() => Promise.resolve()); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn() + mockProfileService.getAllProfiles = jest.fn(()=> of([])) + mockContentService.getContents = jest.fn(() => of([])) + mockCommonUtilService.getLoader = jest.fn(() => undefined); + window['sbutility'].shareSunbirdConfigurations = jest.fn((_, _1, success, error) => { + error({}) + }) // act aboutUsComponent.shareInformation() // assert @@ -284,6 +314,9 @@ describe('AboutUsComponent', () => { mockSharedPreferences.putString = jest.fn(() => of()) mockSharedPreferences.getString = jest.fn(() => of('true')) mockSocialSharing.share = jest.fn(() => Promise.reject()) + window['sbutility'].shareSunbirdConfigurations = jest.fn((_, _1, success, error) => { + success({}) + }) // act aboutUsComponent.shareInformation() // assert diff --git a/src/app/sign-in/sign-in.page.html b/src/app/sign-in/sign-in.page.html index 41ca7ccd70..b3ded2fb6d 100644 --- a/src/app/sign-in/sign-in.page.html +++ b/src/app/sign-in/sign-in.page.html @@ -40,4 +40,4 @@

Welcome To {{appName}}

- + \ No newline at end of file diff --git a/src/app/sign-in/sign-in.page.scss b/src/app/sign-in/sign-in.page.scss index 1b77ebc53a..81f528b158 100644 --- a/src/app/sign-in/sign-in.page.scss +++ b/src/app/sign-in/sign-in.page.scss @@ -95,8 +95,9 @@ .account-info { width: 85%; p { + font-size: 11px; color: map-get($colors , medium_light_shade_gray ); - font-family: "Noto Sans", sans-serif; + font-family: "Noto Sans"; font-size: 12px; letter-spacing: 0; line-height: 17px; @@ -138,7 +139,7 @@ span { color: map-get($colors , medium_light_shade_gray ); - font-family: "Noto Sans", sans-serif; + font-family: "Noto Sans"; font-size: 14px; letter-spacing: 0; line-height: 19px; diff --git a/src/app/sign-in/sign-in.page.spec.ts b/src/app/sign-in/sign-in.page.spec.ts index b2ba714ca3..b3b3e418da 100644 --- a/src/app/sign-in/sign-in.page.spec.ts +++ b/src/app/sign-in/sign-in.page.spec.ts @@ -508,4 +508,4 @@ describe('SignInPage', () => { }); }); }); -}); +}); \ No newline at end of file diff --git a/src/app/sign-in/sign-in.page.ts b/src/app/sign-in/sign-in.page.ts index e72d8c7d98..5886b60698 100644 --- a/src/app/sign-in/sign-in.page.ts +++ b/src/app/sign-in/sign-in.page.ts @@ -287,4 +287,4 @@ export class SignInPage implements OnInit { this.commonUtilService.showToast('ERROR_WHILE_LOGIN'); }); } -} +} \ No newline at end of file diff --git a/src/app/signup/otp/otp.page.spec.ts b/src/app/signup/otp/otp.page.spec.ts index 8addb6477c..3d971941ed 100644 --- a/src/app/signup/otp/otp.page.spec.ts +++ b/src/app/signup/otp/otp.page.spec.ts @@ -46,6 +46,7 @@ describe('OtpPage', () => { })) as any } const mockSharedPreferences: Partial = {} + window.console.error = jest.fn(); beforeAll(() => { otpPage = new OtpPage( mockProfileService as ProfileService, @@ -95,9 +96,74 @@ describe('OtpPage', () => { isNetworkAvailable: true } const presentFn = jest.fn(() => Promise.resolve()); + const dismissFn = jest.fn(() => Promise.resolve()); mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve({ present: presentFn, - dismiss: jest.fn(() => Promise.resolve()) + dismiss: dismissFn + })); + otpPage.userData = { + contactInfo: { + type: 'phone', + phone: '9876586432' + }, + userId:'some_id', + location: [{ + type: '324', + code: '234' + }], + profileUserTypes: [{ + type: 'admin' + }] + } + otpPage.otpInfoForm = {value: {otp: '23223'}} as FormGroup + const req = { + key: otpPage.userData.contactInfo.phone, + type: ProfileConstants.CONTACT_TYPE_PHONE, + otp: '23423', + ...(otpPage.userData.contactInfo.phone && + otpPage.userData.contactInfo.phone.match(/(([a-z]|[A-Z])+[*]+([a-z]*[A-Z]*[0-9]*)*@)|([0-9]+[*]+[0-9]*)+/g) && + { userId: otpPage.userData.userId }) + }; + const profileReq = { + userId: otpPage.userData.userId, + profileLocation: { + type: '234', + code: '213' + }, + firstName: otpPage.userData.name, + lastName: '', + dob: otpPage.userData.dob, + profileUserTypes: otpPage.userData.profileUserTypes + }; + mockProfileService.verifyOTP = jest.fn(() => of()) + mockProfileService.updateServerProfile = jest.fn(() => of({})) as any + const categoriesProfileData = { + hasFilledLocation: true, + showOnlyMandatoryFields: true, + }; + mockRouter.navigate = jest.fn() + // act + otpPage.continue() + // assert + setTimeout(() => { + expect(mockProfileService.verifyOTP).toHaveBeenCalledWith(req); + expect(mockProfileService.updateServerProfile).toHaveBeenCalledWith(profileReq); + expect(mockRouter.navigate).toHaveBeenCalledWith([`/${RouterLinks.PROFILE}/${RouterLinks.CATEGORIES_EDIT}`],{ + state: categoriesProfileData + }) + }, 0); + }) + + it('should verify otp through phone number and continue', () => { + // arrange + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + } + const presentFn = jest.fn(() => Promise.resolve()); + const dismissFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve({ + present: presentFn, + dismiss: dismissFn })); otpPage.userData = { contactInfo: { @@ -108,7 +174,10 @@ describe('OtpPage', () => { location: { type: '324', code: '234' - } + }, + profileUserTypes: [{ + type: '' + }] } otpPage.otpInfoForm = {value: {otp: '23223'}} as FormGroup const req = { @@ -256,6 +325,52 @@ describe('OtpPage', () => { }, 0); }) + it('should catch error on update server profile, error else case', () => { + // arrange + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + } + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve({ + present: presentFn, + dismiss: jest.fn(() => Promise.resolve()) + })); + otpPage.otpInfoForm = {value: {otp: '23223'}} as FormGroup + + otpPage.userData = { + contactInfo: { + type: 'email', + email: 'asda@add.dew' + }, + userId:'some_id', + location: { + type: '324', + code: '234' + } + } + const req = { + key: otpPage.userData.contactInfo.email, + type: ProfileConstants.CONTACT_TYPE_EMAIL, + otp: '324', + ...(otpPage.userData.contactInfo && + otpPage.userData.contactInfo.email.match(/(([a-z]|[A-Z])+[*]+([a-z]*[A-Z]*[0-9]*)*@)|([0-9]+[*]+[0-9]*)+/g) && + { userId: otpPage.userData.userId }) + }; + mockProfileService.verifyOTP = jest.fn(() => of()); + mockProfileService.updateServerProfile = jest.fn(() => throwError({response: {body: { params: {err:'UOS_USRUPD0063'}}}})) as any + mockCommonUtilService.showToast = jest.fn() + mockCommonUtilService.translateMessage = jest.fn() + // act + otpPage.continue() + // assert + setTimeout(() => { + expect(mockProfileService.verifyOTP).toHaveBeenCalledWith(req); + expect(mockProfileService.updateServerProfile).toHaveBeenCalled() + expect(mockCommonUtilService.translateMessage).toHaveBeenCalledWith('SOMETHING_WENT_WRONG') + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith() + }, 0); + }) + it('should catch error on verify otp profile', () => { // arrange mockCommonUtilService.networkInfo = { @@ -295,6 +410,84 @@ describe('OtpPage', () => { }, 0); }) + it('should catch error on verify otp profile, else if response code is not 400', () => { + // arrange + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + } + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve({ + present: presentFn, + dismiss: jest.fn(() => Promise.resolve()) + })); + otpPage.otpInfoForm = {value: {otp: '23223'}} as FormGroup + const locationCodes = []; + otpPage.userData = { + contactInfo: { + type: 'email', + email: 'asda@add.dew' + }, + userId:'some_id', + location: [{ + type: '324', + code: '234' + }] + } + let response = new Response(); + response = {responseCode: 402, body: { params: {err:'UOS_OTPVERFY0063'}, result: {remainingAttempt: 1}}}; + const error: HttpClientError = new HttpClientError('Error', response); + mockProfileService.verifyOTP = jest.fn(() => throwError(error)) as any + mockCommonUtilService.showToast = jest.fn() + mockCommonUtilService.translateMessage = jest.fn() + // act + otpPage.continue() + // assert + setTimeout(() => { + expect(mockProfileService.verifyOTP).toHaveBeenCalledWith(); + expect(mockCommonUtilService.translateMessage).toHaveBeenCalledWith('SOMETHING_WENT_WRONG') + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith() + }, 0); + }) + + it('should catch error on verify otp profile, else if response body is not object', () => { + // arrange + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + } + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve({ + present: presentFn, + dismiss: jest.fn(() => Promise.resolve()) + })); + otpPage.otpInfoForm = {value: {otp: '23223'}} as FormGroup + const locationCodes = []; + otpPage.userData = { + contactInfo: { + type: 'email', + email: 'asda@add.dew' + }, + userId:'some_id', + location: [{ + type: '324', + code: '234' + }] + } + let response = new Response(); + response = {responseCode: 400, body: "", result: {remainingAttempt: 1}}; + const error: HttpClientError = new HttpClientError('Error', response); + mockProfileService.verifyOTP = jest.fn(() => throwError(error)) as any + mockCommonUtilService.showToast = jest.fn() + mockCommonUtilService.translateMessage = jest.fn() + // act + otpPage.continue() + // assert + setTimeout(() => { + expect(mockProfileService.verifyOTP).toHaveBeenCalledWith(); + expect(mockCommonUtilService.translateMessage).toHaveBeenCalledWith('SOMETHING_WENT_WRONG') + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith() + }, 0); + }) + it('should catch error on verify otp profile and no remainingAttempt', () => { // arrange mockCommonUtilService.networkInfo = { @@ -371,7 +564,7 @@ describe('OtpPage', () => { type: ProfileConstants.CONTACT_TYPE_PHONE, ...(otpPage.userData.contactInfo && otpPage.userData.contactInfo.phone.match(/(([a-z]|[A-Z])+[*]+([a-z]*[A-Z]*[0-9]*)*@)|([0-9]+[*]+[0-9]*)+/g) && - { userId: otpPage.userData.userId, templateId: OTPTemplates.EDIT_CONTACT_OTP_TEMPLATE }) + { userId: 'some_id', templateId: OTPTemplates.EDIT_CONTACT_OTP_TEMPLATE }) } const presentFn = jest.fn(() => Promise.resolve()); @@ -410,7 +603,7 @@ describe('OtpPage', () => { type: ProfileConstants.CONTACT_TYPE_EMAIL, ...(otpPage.userData.contactInfo.email && otpPage.userData.contactInfo.email.match(/(([a-z]|[A-Z])+[*]+([a-z]*[A-Z]*[0-9]*)*@)|([0-9]+[*]+[0-9]*)+/g) && - { userId: otpPage.userData.userId, templateId: OTPTemplates.EDIT_CONTACT_OTP_TEMPLATE }) + { userId: 'some_id', templateId: OTPTemplates.EDIT_CONTACT_OTP_TEMPLATE }) }; const presentFn = jest.fn(() => Promise.resolve()); mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve({ @@ -458,6 +651,35 @@ describe('OtpPage', () => { }, 0); }) + it('should catch error on generateotp for contact type email, loader undefined', () => { + // arrange + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + } + otpPage.userData = { + contactInfo: { + type: 'email', + email: 'asda@add.dew' + }, + userId:'some_id', + templateId: '', + location: { + type: '324', + code: '234' + } + } + // const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => Promise.resolve(undefined)); + mockProfileService.generateOTP = jest.fn(() => throwError({})) + // act + otpPage.resendOTP() + // assert + setTimeout(() => { + + expect(mockProfileService.generateOTP).toHaveBeenCalled(); + }, 0); + }) + it('should show toast if no network available', () => { // arrange mockCommonUtilService.networkInfo = { diff --git a/src/app/user-type-selection/user-type-selection.html b/src/app/user-type-selection/user-type-selection.html index 509385878f..87385399ff 100644 --- a/src/app/user-type-selection/user-type-selection.html +++ b/src/app/user-type-selection/user-type-selection.html @@ -1,5 +1,5 @@ -
+
{ const mockEvents: Partial = {}; const mockHeaderService: Partial = {}; const mockProfileService: Partial = { - updateProfile: jest.fn(() => of({})) + updateProfile: jest.fn(() => of({})), + setActiveSessionForProfile: jest.fn(() => of({})), + getActiveSessionProfile: jest.fn(() => Promise.resolve({})) }; const mockRouterExtras = { extras: { @@ -96,6 +98,7 @@ describe('UserTypeSelectionPage', () => { const mockLoginHandlerService: Partial = {}; const mockOnboardingConfigurationService: Partial = {}; const mockExternalIdVerificationService: Partial = {}; + window.console.error = jest.fn() beforeAll(() => { userTypeSelectionPage = new UserTypeSelectionPage( @@ -135,8 +138,16 @@ describe('UserTypeSelectionPage', () => { // arrange userTypeSelectionPage['profile'] = { uid: 'sample_uid' }; jest.useFakeTimers(); + window.setTimeout = jest.fn((fn) => { + fn(); + }, 30) as any + mockProfileService.updateProfile = jest.fn(() => of({})); + mockProfileService.setActiveSessionForProfile = jest.fn(() => of(true)); + mockProfileService.getActiveSessionProfile = jest.fn(() => of({ + uid: 'sample-uid', + handle: 'USER' + })); mockNgZone.run = jest.fn((fn) => fn()); - jest.advanceTimersByTime(200); mockSharedPreferences.putString = jest.fn(() => of(undefined)); // act userTypeSelectionPage.selectUserTypeCard('USER_TYPE_1', ProfileType.TEACHER, true); @@ -166,6 +177,98 @@ describe('UserTypeSelectionPage', () => { jest.useRealTimers(); jest.clearAllTimers(); }); + + it('should update the selectedUserType , if onboarding complted', () => { + // arrange + userTypeSelectionPage['profile'] = { uid: 'sample_uid' }; + jest.useFakeTimers(); + window.setTimeout = jest.fn((fn) => { + fn(); + }, 30) as any + mockProfileService.updateProfile = jest.fn(() => of({})); + mockProfileService.setActiveSessionForProfile = jest.fn(() => of(true)); + mockProfileService.getActiveSessionProfile = jest.fn(() => of({ + uid: 'sample-uid', + handle: 'USER' + })); + mockAppGlobalService.isOnBoardingCompleted = true; + mockNgZone.run = jest.fn((fn) => fn()); + mockSharedPreferences.putString = jest.fn(() => of(undefined)); + // act + userTypeSelectionPage.selectUserTypeCard('USER_TYPE_1', ProfileType.TEACHER, true); + // assert + expect(userTypeSelectionPage.selectedUserType).toEqual(ProfileType.TEACHER); + expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'USER_TYPE_1'); + expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'CONTINUE_AS_ROLE', undefined); + expect(mockSharedPreferences.putString).toHaveBeenCalledWith(PreferenceKey.SELECTED_USER_TYPE, ProfileType.TEACHER); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(1, + InteractType.TOUCH, + InteractSubtype.USER_TYPE_SELECTED, + Environment.HOME, + PageId.USER_TYPE_SELECTION, + undefined, + { userType: 'TEACHER' } + ); + + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(2, + InteractType.SELECT_USERTYPE, '', + 'onboarding', + PageId.USER_TYPE, + undefined, + undefined, + undefined, + [{ id: 'teacher', type: CorReleationDataType.USERTYPE }] + ); + jest.useRealTimers(); + jest.clearAllTimers(); + }); + + it('should update the selectedUserType , if onboarding complted', () => { + // arrange + userTypeSelectionPage.categoriesProfileData = {status: true, showOnlyMandatoryFields: false}; + userTypeSelectionPage['profile'] = { uid: 'sample_uid' }; + jest.useFakeTimers(); + window.setTimeout = jest.fn((fn) => { + fn(); + }, 30) as any + mockAppGlobalService.isOnBoardingCompleted = true; + mockNgZone.run = jest.fn((fn) => fn()); + mockSharedPreferences.putString = jest.fn(() => of(undefined)); + // act + userTypeSelectionPage.selectUserTypeCard('USER_TYPE_1', ProfileType.TEACHER, true); + // assert + expect(userTypeSelectionPage.selectedUserType).toEqual(ProfileType.TEACHER); + expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'USER_TYPE_1'); + expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'CONTINUE_AS_ROLE', undefined); + expect(mockSharedPreferences.putString).toHaveBeenCalledWith(PreferenceKey.SELECTED_USER_TYPE, ProfileType.TEACHER); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(1, + InteractType.TOUCH, + InteractSubtype.USER_TYPE_SELECTED, + Environment.HOME, + PageId.USER_TYPE_SELECTION, + undefined, + { userType: 'TEACHER' } + ); + + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(2, + InteractType.SELECT_USERTYPE, '', + 'onboarding', + PageId.USER_TYPE, + undefined, + undefined, + undefined, + [{ id: 'teacher', type: CorReleationDataType.USERTYPE }] + ); + jest.useRealTimers(); + jest.clearAllTimers(); + }); + + it('should update the selectedUserType , return if isActive false else case', () => { + // arrange + // act + userTypeSelectionPage.selectUserTypeCard('USER_TYPE_1', ProfileType.TEACHER, false); + // assert + }); }); it('should return categories ProfileData', () => { @@ -184,6 +287,19 @@ describe('UserTypeSelectionPage', () => { expect(mockSplashScreenService.handleSunbirdSplashScreenActions).toHaveBeenCalled(); }); + it('should not invok onboarding Splash screen, if guardActivated', () => { + userTypeSelectionPage.frameworkGuard.guardActivated = true; + mockSplashScreenService.handleSunbirdSplashScreenActions = jest.fn(() => Promise.resolve(undefined)); + userTypeSelectionPage.ionViewDidEnter(); + }); + + it('should invoked onboarding Splash screen, handle else case if not forward migrated', () => { + userTypeSelectionPage.frameworkGuard.guardActivated = true; + userTypeSelectionPage['navParams'] = { categoriesProfileData: {}, forwardMigration: false }; + mockSplashScreenService.handleSunbirdSplashScreenActions = jest.fn(() => Promise.resolve(undefined)); + userTypeSelectionPage.ionViewDidEnter(); + }); + describe('handleBackButton', () => { it('should not navigate to language settings page for onboarding completed', () => { // arrange @@ -249,6 +365,31 @@ describe('UserTypeSelectionPage', () => { true); }); + it('should invoked backButton', () => { + // arrange + const event = { name: 'back' }; + mockAppGlobalService.isOnBoardingCompleted = true; + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + jest.spyOn(userTypeSelectionPage, 'handleBackButton').mockImplementation(() => { + return; + }); + // act + userTypeSelectionPage.handleHeaderEvents(event); + // assert + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( + PageId.USER_TYPE_SELECTION, + Environment.HOME, + true); + }); + + it('should invoked backButton, if event name is not back', () => { + // arrange + const event = { name: 'exit' }; + // act + userTypeSelectionPage.handleHeaderEvents(event); + // assert + }); + describe('setUserTypeForNewUser', () => { it('should update userType for new user', (done) => { // arrange @@ -285,17 +426,81 @@ describe('UserTypeSelectionPage', () => { }); describe('ionViewWillEnter', () => { - it('should initialized all user-type', (done) => { + it('should initialized all user-type', () => { // arrange mockAppGlobalService.isUserLoggedIn = jest.fn(() => false); mockProfileHandler.getSupportedUserTypes = jest.fn(() => Promise.resolve([])); (mockRouter as any).url = `/${RouterLinks.USER_TYPE_SELECTION}`; - jest.useFakeTimers(); + window.setTimeout = jest.fn((fn) => { + fn({}); + }, 350) as any + mockAppGlobalService.isOnBoardingCompleted = true; mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); - jest.spyOn(userTypeSelectionPage, 'setUserTypeForNewUser').mockImplementation(() => { - return Promise.resolve(); + jest.spyOn(userTypeSelectionPage, 'getNavParams').mockImplementation(() => { + return; + }); + mockHeaderService.headerEventEmitted$ = of({ + subscribe: jest.fn((fn) => fn({ + unsubscribe: jest.fn(() => { }) + })) + }); + jest.spyOn(userTypeSelectionPage, 'handleHeaderEvents').mockImplementation(() => { + return; + }); + jest.spyOn(userTypeSelectionPage, 'handleBackButton').mockImplementation(() => { + return; + }); + mockCommonUtilService.getAppName = jest.fn(() => Promise.resolve('sunbird')); + mockCommonUtilService.showExitPopUp = jest.fn(); + mockHeaderService.hideHeader = jest.fn(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({ handle: 'sample-user' })); + const subscribeWithPriorityData = jest.fn((_, fn) => fn({ + unsubscribe: jest.fn() + })); + mockPlatform.backButton = { + subscribeWithPriority: subscribeWithPriorityData + } as any; + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + mockTelemetryGeneratorService.generateBackClickedNewTelemetry = jest.fn(); + jest.spyOn(userTypeSelectionPage, 'handleBackButton').mockImplementation(() => { + return; + }); + userTypeSelectionPage.backButtonFunc = { + unsubscribe: jest.fn() + } as any; + // act /assert + userTypeSelectionPage.ionViewWillEnter().then(() => { + expect(mockAppGlobalService.isUserLoggedIn).toHaveBeenCalled(); + expect(mockProfileHandler.getSupportedUserTypes).toHaveBeenCalled(); + expect(mockHeaderService.headerEventEmitted$).toBeTruthy(); + expect(mockCommonUtilService.getAppName).toHaveBeenCalled(); + expect(mockHeaderService.hideHeader).toHaveBeenCalled(); + expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalled(); + expect(subscribeWithPriorityData).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( + PageId.USER_TYPE_SELECTION, Environment.HOME, false + ); + + expect(mockTelemetryGeneratorService.generateBackClickedNewTelemetry).toHaveBeenCalledWith( + true, + Environment.HOME, + PageId.USER_TYPE + ); }); + }); + + it('should initialized all user-type, on boarding completed users', () => { + // arrange + mockAppGlobalService.isUserLoggedIn = jest.fn(() => false); + mockProfileHandler.getSupportedUserTypes = jest.fn(() => Promise.resolve([])); + (mockRouter as any).url = `/${RouterLinks.USER_TYPE_SELECTION}`; + window.setTimeout = jest.fn((fn) => { + fn({}); + }, 350) as any + mockAppGlobalService.isOnBoardingCompleted = false; + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); jest.spyOn(userTypeSelectionPage, 'getNavParams').mockImplementation(() => { return; }); @@ -310,8 +515,6 @@ describe('UserTypeSelectionPage', () => { jest.spyOn(userTypeSelectionPage, 'handleBackButton').mockImplementation(() => { return; }); - jest.advanceTimersByTime(450); - jest.runAllTimers(); mockCommonUtilService.getAppName = jest.fn(() => Promise.resolve('sunbird')); mockCommonUtilService.showExitPopUp = jest.fn(); mockHeaderService.hideHeader = jest.fn(); @@ -330,7 +533,7 @@ describe('UserTypeSelectionPage', () => { userTypeSelectionPage.backButtonFunc = { unsubscribe: jest.fn() } as any; - // act + // act /assert userTypeSelectionPage.ionViewWillEnter().then(() => { expect(mockAppGlobalService.isUserLoggedIn).toHaveBeenCalled(); expect(mockProfileHandler.getSupportedUserTypes).toHaveBeenCalled(); @@ -348,15 +551,14 @@ describe('UserTypeSelectionPage', () => { Environment.ONBOARDING, PageId.USER_TYPE ); - done(); }); - // assert - jest.useRealTimers(); - jest.clearAllTimers(); }); - it('should set user for logged-in user', (done) => { + it('should set user for logged-in user for platform ios show header', () => { // arrange + userTypeSelectionPage.categoriesProfileData = {status: true, showOnlyMandatoryFields: false}; + mockPlatform.is = jest.fn((platform) => platform === "ios"); + mockHeaderService.showHeaderWithHomeButton = jest.fn(); mockAppGlobalService.isUserLoggedIn = jest.fn(() => true); mockSharedPreferences.getString = jest.fn(() => of('teacher')); mockProfileHandler.getSupportedUserTypes = jest.fn(() => Promise.resolve([])); @@ -387,12 +589,15 @@ describe('UserTypeSelectionPage', () => { } as any; mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); mockTelemetryGeneratorService.generateBackClickedNewTelemetry = jest.fn(); + mockOnboardingConfigurationService.initialOnboardingScreenName = OnboardingScreenType.USER_TYPE_SELECTION; jest.spyOn(userTypeSelectionPage, 'handleBackButton').mockImplementation(() => { return; }); userTypeSelectionPage.backButtonFunc = { unsubscribe: jest.fn() } as any; + mockProfileService.setActiveSessionForProfile = jest.fn(() => of()); + mockProfileService.getActiveSessionProfile = jest.fn(() => Promise.resolve()); mockAppGlobalService.isOnBoardingCompleted = true; // act userTypeSelectionPage.ionViewWillEnter(); @@ -414,7 +619,74 @@ describe('UserTypeSelectionPage', () => { Environment.HOME, PageId.USER_TYPE ); - done(); + }, 0); + }); + + it('should set user for logged-in user for platform ios show header', () => { + // arrange + userTypeSelectionPage.categoriesProfileData = {status: true, showOnlyMandatoryFields: false}; + mockPlatform.is = jest.fn((platform) => platform === "android"); + mockCommonUtilService.showExitPopUp = jest.fn(); + mockAppGlobalService.isUserLoggedIn = jest.fn(() => true); + mockSharedPreferences.getString = jest.fn(() => of('teacher')); + mockProfileHandler.getSupportedUserTypes = jest.fn(() => Promise.resolve([])); + (mockRouter as any).url = `/${RouterLinks.ABOUT_US}`; + jest.spyOn(userTypeSelectionPage, 'getNavParams').mockImplementation(() => { + return; + }); + mockHeaderService.headerEventEmitted$ = of({ + subscribe: jest.fn((fn) => fn({ + unsubscribe: jest.fn(() => { }) + })) + }); + jest.spyOn(userTypeSelectionPage, 'handleHeaderEvents').mockImplementation(() => { + return; + }); + jest.spyOn(userTypeSelectionPage, 'handleBackButton').mockImplementation(() => { + return; + }); + mockCommonUtilService.getAppName = jest.fn(() => Promise.resolve('sunbird')); + mockCommonUtilService.showExitPopUp = jest.fn(); + mockHeaderService.hideHeader = jest.fn(); + mockAppGlobalService.getCurrentUser = jest.fn(() => ({ handle: 'sample-user' })); + const subscribeWithPriorityData = jest.fn((_, fn) => fn({ + unsubscribe: jest.fn() + })); + mockPlatform.backButton = { + subscribeWithPriority: subscribeWithPriorityData + } as any; + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + mockTelemetryGeneratorService.generateBackClickedNewTelemetry = jest.fn(); + mockOnboardingConfigurationService.initialOnboardingScreenName = OnboardingScreenType.USER_TYPE_SELECTION; + jest.spyOn(userTypeSelectionPage, 'handleBackButton').mockImplementation(() => { + return; + }); + userTypeSelectionPage.backButtonFunc = { + unsubscribe: jest.fn() + } as any; + mockProfileService.setActiveSessionForProfile = jest.fn(() => of()); + mockProfileService.getActiveSessionProfile = jest.fn(() => Promise.resolve()); + mockAppGlobalService.isOnBoardingCompleted = false; + // act + userTypeSelectionPage.ionViewWillEnter(); + setTimeout(() => { + expect(mockAppGlobalService.isUserLoggedIn).toHaveBeenCalled(); + expect(mockSharedPreferences.getString).toHaveBeenCalledWith(PreferenceKey.SELECTED_USER_TYPE); + expect(mockProfileHandler.getSupportedUserTypes).toHaveBeenCalled(); + expect(mockHeaderService.headerEventEmitted$).toBeTruthy(); + expect(mockCommonUtilService.getAppName).toHaveBeenCalled(); + expect(mockHeaderService.hideHeader).toHaveBeenCalled(); + expect(mockAppGlobalService.getCurrentUser).toHaveBeenCalled(); + expect(subscribeWithPriorityData).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( + PageId.USER_TYPE_SELECTION, Environment.HOME, false + ); + + expect(mockTelemetryGeneratorService.generateBackClickedNewTelemetry).toHaveBeenCalledWith( + true, + Environment.ONBOARDING, + PageId.USER_TYPE + ); }, 0); }); }); @@ -454,7 +726,7 @@ describe('UserTypeSelectionPage', () => { }); describe('navigateToTabsAsLogInUser', () => { - it('should return birthday popup', (done) => { + it('should return birthday popup', () => { // arrange userTypeSelectionPage.categoriesProfileData = { status: 'active', @@ -465,8 +737,11 @@ describe('UserTypeSelectionPage', () => { mockContainer.addTab = jest.fn(); mockTncUpdateHandlerService.isSSOUser = jest.fn(() => Promise.resolve(false)); mockAppGlobalService.showYearOfBirthPopup = jest.fn(() => Promise.resolve()); + mockTelemetryGeneratorService.generateAuditTelemetry = jest.fn(); + const correlationlist: Array = [{ id: PageId.USER_TYPE, type: CorReleationDataType.FROM_PAGE }]; + correlationlist.push({ id: 'sample-user-type', type: CorReleationDataType.USERTYPE }); mockRouter.navigate = jest.fn(() => Promise.resolve(true)); - mockExternalIdVerificationService.showExternalIdVerificationPopup = jest.fn(() => Promise.resolve()); + mockExternalIdVerificationService.showExternalIdVerificationPopup = jest.fn(() => Promise.resolve()); // act userTypeSelectionPage.navigateToTabsAsLogInUser(); // assert @@ -477,11 +752,78 @@ describe('UserTypeSelectionPage', () => { expect(mockAppGlobalService.showYearOfBirthPopup).toHaveBeenCalled(); expect(mockRouter.navigate).toHaveBeenCalledWith([RouterLinks.TABS]); expect(mockExternalIdVerificationService.showExternalIdVerificationPopup).toHaveBeenCalled(); - done(); }, 0); }); - it('should navigate to location page', (done) => { + it('should return birthday popup, isJoinTraningOnboardingFlow', () => { + // arrange + userTypeSelectionPage.categoriesProfileData = { + status: 'active', + showOnlyMandatoryFields: 'YES', + hasFilledLocation: true, + noOfStepsToCourseToc: 2 + }; + mockAppGlobalService.isJoinTraningOnboardingFlow = true; + mockContainer.removeAllTabs = jest.fn(); + mockContainer.addTab = jest.fn(); + mockTncUpdateHandlerService.isSSOUser = jest.fn(() => Promise.resolve(false)); + mockAppGlobalService.showYearOfBirthPopup = jest.fn(() => Promise.resolve()); + window.history = { + go: jest.fn() + } as any; + mockExternalIdVerificationService.showExternalIdVerificationPopup = jest.fn(() => Promise.resolve()); + // act + userTypeSelectionPage.navigateToTabsAsLogInUser(); + // assert + setTimeout(() => { + expect(mockContainer.removeAllTabs).toHaveBeenCalled(); + expect(mockContainer.addTab).toHaveBeenCalled(); + expect(mockTncUpdateHandlerService.isSSOUser).toHaveBeenCalled(); + expect(mockAppGlobalService.showYearOfBirthPopup).toHaveBeenCalled(); + expect(mockExternalIdVerificationService.showExternalIdVerificationPopup).toHaveBeenCalled(); + }, 0); + }); + + it('should return birthday popup, if ssouser', () => { + // arrange + userTypeSelectionPage.categoriesProfileData = { + status: 'active', + showOnlyMandatoryFields: 'YES', + hasFilledLocation: true, + noOfStepsToCourseToc: 2 + }; + mockAppGlobalService.isJoinTraningOnboardingFlow = true; + mockContainer.removeAllTabs = jest.fn(); + mockContainer.addTab = jest.fn(); + mockTncUpdateHandlerService.isSSOUser = jest.fn(() => Promise.resolve(true)); + mockRouter.navigate = jest.fn(() => Promise.resolve(true)); + mockExternalIdVerificationService.showExternalIdVerificationPopup = jest.fn(() => Promise.resolve()); + // act + userTypeSelectionPage.navigateToTabsAsLogInUser(); + // assert + setTimeout(() => { + expect(mockContainer.removeAllTabs).toHaveBeenCalled(); + expect(mockContainer.addTab).toHaveBeenCalled(); + expect(mockTncUpdateHandlerService.isSSOUser).toHaveBeenCalled(); + expect(mockExternalIdVerificationService.showExternalIdVerificationPopup).toHaveBeenCalled(); + }, 0); + }); + + it('should return birthday popup, return if no mandatory fileds', () => { + // arrange + userTypeSelectionPage.categoriesProfileData = { + status: 'active', + showOnlyMandatoryFields: '', + hasFilledLocation: true + }; + // act + userTypeSelectionPage.navigateToTabsAsLogInUser(); + // assert + setTimeout(() => { + }, 0); + }); + + it('should navigate to location page', () => { // arrange userTypeSelectionPage.categoriesProfileData = { status: 'active', @@ -506,11 +848,10 @@ describe('UserTypeSelectionPage', () => { expect(mockContainer.addTab).toHaveBeenCalled(); expect(mockTncUpdateHandlerService.isSSOUser).toHaveBeenCalled(); expect(mockRouter.navigate).toHaveBeenCalledWith([RouterLinks.DISTRICT_MAPPING], navigationExtras); - done(); }, 0); }); - it('should navigate to category edit page', (done) => { + it('should navigate to category edit page', () => { // arrange userTypeSelectionPage.categoriesProfileData = { status: false, @@ -523,13 +864,12 @@ describe('UserTypeSelectionPage', () => { // assert setTimeout(() => { expect(mockRouter.navigate).toHaveBeenCalled(); - done(); }, 0); }); }); describe('updateProfile', () => { - it('should navigate to tabs as guest', (done) => { + it('should navigate to tabs as guest', () => { // arrange userTypeSelectionPage.selectedUserType = 'sample-user'; mockProfileService.updateProfile = jest.fn(() => of({})); @@ -541,13 +881,12 @@ describe('UserTypeSelectionPage', () => { // assert setTimeout(() => { expect(mockProfileService.updateProfile).toHaveBeenCalled(); - expect(mockRouter.navigate).toHaveBeenCalledWith(['/tabs'], navigationExtras); + expect(mockRouter.navigate).toHaveBeenCalledWith(['sign-in']); expect(mockProfileService.updateServerProfile).toHaveBeenCalled(); - done(); }, 0); }); - it('should navigate To Tabs As LogInUser', (done) => { + it('should navigate To Tabs As LogInUser', () => { // arrange userTypeSelectionPage.selectedUserType = 'sample-user'; mockProfileService.updateProfile = jest.fn(() => of({})); @@ -562,11 +901,10 @@ describe('UserTypeSelectionPage', () => { setTimeout(() => { expect(mockProfileService.updateProfile).toHaveBeenCalled(); expect(mockProfileService.updateServerProfile).toHaveBeenCalled(); - done(); }, 0); }); - it('should navigate To signIn page', (done) => { + it('should navigate To signIn page', () => { // arrange userTypeSelectionPage.selectedUserType = ProfileType.ADMIN; mockProfileService.updateProfile = jest.fn(() => of({})); @@ -577,17 +915,16 @@ describe('UserTypeSelectionPage', () => { }); mockRouter.navigate = jest.fn(() => Promise.resolve(true)); // act - userTypeSelectionPage.updateProfile('sample-page', {}); + userTypeSelectionPage.updateProfile('sample-page'); // assert setTimeout(() => { expect(mockProfileService.updateProfile).toHaveBeenCalled(); expect(mockProfileService.updateServerProfile).toHaveBeenCalled(); expect(mockRouter.navigate).toHaveBeenCalledWith([RouterLinks.SIGN_IN]); - done(); }, 0); }); - it('should navigate To ProfileSettingsPage', (done) => { + it('should navigate To ProfileSettingsPage', () => { // arrange userTypeSelectionPage.selectedUserType = ProfileType.TEACHER; mockProfileService.updateProfile = jest.fn(() => of({})); @@ -606,11 +943,10 @@ describe('UserTypeSelectionPage', () => { expect(mockProfileService.updateServerProfile).toHaveBeenCalled(); expect(mockRouter.navigate).toHaveBeenCalled(); expect(mockNativePageTransitions.slide).toHaveBeenCalled(); - done(); }, 0); }); - it('should return error for update profile', (done) => { + it('should return error for update profile', () => { userTypeSelectionPage.selectedUserType = ProfileType.TEACHER; mockProfileService.updateProfile = jest.fn(() => throwError({error: {}})); mockProfileService.updateServerProfile = jest.fn(() => throwError({error: {}})); @@ -618,7 +954,6 @@ describe('UserTypeSelectionPage', () => { setTimeout(() => { expect(mockProfileService.updateProfile).toHaveBeenCalled(); expect(mockProfileService.updateServerProfile).toHaveBeenCalled(); - done(); }, 0); }); }); @@ -699,7 +1034,7 @@ describe('UserTypeSelectionPage', () => { mockNativePageTransitions.slide = jest.fn(() => Promise.resolve({})); mockRouter.navigate = jest.fn(() => Promise.resolve(true)); // act - userTypeSelectionPage.gotoNextPage(false); + userTypeSelectionPage.gotoNextPage(); // assert expect(mockEvents.publish).toHaveBeenCalledWith(AppGlobalService.USER_INFO_UPDATED); expect(mockCommonUtilService.isAccessibleForNonStudentRole).toHaveBeenCalled(); @@ -768,7 +1103,7 @@ describe('UserTypeSelectionPage', () => { expect(userTypeSelectionPage.profile.profileType).toBeTruthy(); }); - it('should set profile if profile is undefined and uid is not null', (done) => { + it('should set profile if profile is undefined and uid is not null', () => { // arrange userTypeSelectionPage.profile = { handle: undefined, @@ -787,9 +1122,6 @@ describe('UserTypeSelectionPage', () => { jest.spyOn(userTypeSelectionPage, 'gotoNextPage').mockImplementation(() => { return; }); - mockTelemetryGeneratorService.generateAuditTelemetry = jest.fn(); - const correlationlist: Array = [{ id: PageId.USER_TYPE, type: CorReleationDataType.FROM_PAGE }]; - correlationlist.push({ id: 'sample-user-type', type: CorReleationDataType.USERTYPE }); // act userTypeSelectionPage.continue(); // assert @@ -807,21 +1139,11 @@ describe('UserTypeSelectionPage', () => { PreferenceKey.GUEST_USER_ID_BEFORE_LOGIN, userTypeSelectionPage.profile.uid ); - expect(mockTelemetryGeneratorService.generateAuditTelemetry).toHaveBeenCalledWith( - Environment.ONBOARDING, - AuditState.AUDIT_UPDATED, - [AuditProps.PROFILE_TYPE], - AuditType.SELECT_USERTYPE, - undefined, - undefined, - undefined, - correlationlist - ); done(); }, 0); }); - it('should set profile if profile is undefined and uid is null', (done) => { + it('should set profile if profile is undefined and uid is null', () => { // arrange userTypeSelectionPage.profile = { handle: undefined, @@ -839,9 +1161,6 @@ describe('UserTypeSelectionPage', () => { jest.spyOn(userTypeSelectionPage, 'gotoNextPage').mockImplementation(() => { return; }); - mockTelemetryGeneratorService.generateAuditTelemetry = jest.fn(); - const correlationlist: Array = [{ id: PageId.USER_TYPE, type: CorReleationDataType.FROM_PAGE }]; - correlationlist.push({ id: 'sample-user-type', type: CorReleationDataType.USERTYPE }); // act userTypeSelectionPage.continue(); // assert @@ -855,21 +1174,11 @@ describe('UserTypeSelectionPage', () => { expect(mockProfileService.setActiveSessionForProfile).toHaveBeenCalledWith('sample-uid'); expect(mockProfileService.getActiveSessionProfile).toHaveBeenCalled(); expect(mockEvents.publish).toHaveBeenCalledWith(AppGlobalService.USER_INFO_UPDATED); - expect(mockTelemetryGeneratorService.generateAuditTelemetry).toHaveBeenCalledWith( - Environment.ONBOARDING, - AuditState.AUDIT_UPDATED, - [AuditProps.PROFILE_TYPE], - AuditType.SELECT_USERTYPE, - undefined, - undefined, - undefined, - correlationlist - ); done(); }, 0); }); - it('should return null if profile is undefined for catch part', (done) => { + it('should return null if profile is undefined for catch part', () => { // arrange userTypeSelectionPage.profile = { handle: undefined, @@ -894,7 +1203,6 @@ describe('UserTypeSelectionPage', () => { }); expect(mockProfileService.setActiveSessionForProfile).toHaveBeenCalledWith('sample-uid'); expect(mockProfileService.getActiveSessionProfile).toHaveBeenCalled(); - done(); }, 0); }); }); @@ -929,18 +1237,49 @@ describe('UserTypeSelectionPage', () => { ); }); + it('should generate interact telemetry', () => { + // arrange + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockAppGlobalService.isOnBoardingCompleted = false; + const values = new Map(); + values['userType'] = ('sample-user').toUpperCase(); + const correlationlist: Array = []; + correlationlist.push({ id: 'sample-user', type: CorReleationDataType.USERTYPE }); + mockAppGlobalService.isOnBoardingCompleted = true; + // act + userTypeSelectionPage.generateInteractEvent('sample-user'); + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(1, + InteractType.TOUCH, + InteractSubtype.USER_TYPE_SELECTED, + Environment.HOME, + PageId.USER_TYPE_SELECTION, + undefined, + values + ); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(2, + InteractType.SELECT_CONTINUE, '', + Environment.HOME, + PageId.USER_TYPE, + undefined, + values, + undefined, + correlationlist + ); + }); + it('should navigate to profile page', () => { mockRouter.navigate = jest.fn(() => Promise.resolve(true)); userTypeSelectionPage.navigateToProfilePage(); expect(mockRouter.navigate).toHaveBeenCalled(); }); - it('should return response for onSubmitAttempt', (done) => { - userTypeSelectionPage.onSubmitAttempt(); + it('should return response for onSubmitAttempt', () => { + window.setTimeout = jest.fn((fn) => { + fn() + }, 50) as any; jest.spyOn(userTypeSelectionPage, 'continue').mockImplementation(); - setTimeout(() => { - done(); - }, 50); + userTypeSelectionPage.onSubmitAttempt(); }); it('should unsubscribe back button', () => { diff --git a/src/app/user-type-selection/user-type-selection.ts b/src/app/user-type-selection/user-type-selection.ts index 82c6d30c3b..c40d00e6ce 100644 --- a/src/app/user-type-selection/user-type-selection.ts +++ b/src/app/user-type-selection/user-type-selection.ts @@ -271,18 +271,7 @@ export class UserTypeSelectionPage implements OnDestroy { } this.profile = success; this.gotoNextPage(); - const correlationlist: Array = [{ id: PageId.USER_TYPE, type: CorReleationDataType.FROM_PAGE }]; - correlationlist.push({ id: this.selectedUserType, type: CorReleationDataType.USERTYPE }); - this.telemetryGeneratorService.generateAuditTelemetry( - Environment.ONBOARDING, - AuditState.AUDIT_UPDATED, - [AuditProps.PROFILE_TYPE], - AuditType.SELECT_USERTYPE, - undefined, - undefined, - undefined, - correlationlist - ); + this.generateAuditEvents(); }).catch(() => { return 'null'; }); @@ -367,7 +356,7 @@ export class UserTypeSelectionPage implements OnDestroy { if (this.selectedUserType === ProfileType.ADMIN) { this.router.navigate([RouterLinks.SIGN_IN]); } else { - this.navigateToProfileSettingsPage(params); + this.navigateToProfileSettingsPage(params, true); } } }).catch(error => { @@ -421,7 +410,7 @@ export class UserTypeSelectionPage implements OnDestroy { this.router.navigate(['/tabs'], navigationExtras); } - async navigateToProfileSettingsPage(params) { + async navigateToProfileSettingsPage(params, isUpdateProfile? ) { const navigationExtras: NavigationExtras = { state: params }; const options: NativeTransitionOptions = { direction: 'left', @@ -432,6 +421,9 @@ export class UserTypeSelectionPage implements OnDestroy { fixedPixelsBottom: 0 }; this.nativePageTransitions.slide(options); + if(isUpdateProfile) { + this.generateAuditEvents(); + } this.router.navigate([`/${RouterLinks.PROFILE_SETTINGS}`], navigationExtras); } @@ -469,4 +461,19 @@ export class UserTypeSelectionPage implements OnDestroy { } this.isUserTypeSelected = this.selectedUserType !== 'none' ? true : false; } + + generateAuditEvents(){ + const correlationlist: Array = [{ id: PageId.USER_TYPE, type: CorReleationDataType.FROM_PAGE }]; + correlationlist.push({ id: this.selectedUserType, type: CorReleationDataType.USERTYPE }); + this.telemetryGeneratorService.generateAuditTelemetry( + Environment.ONBOARDING, + AuditState.AUDIT_UPDATED, + [AuditProps.PROFILE_TYPE], + AuditType.SELECT_USERTYPE, + undefined, + undefined, + undefined, + correlationlist + ); + } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index c01ce44441..e3a950b2a2 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1038,7 +1038,7 @@ "FRMELEMNTS_LBL_THIS_WEEK": "This Week", "FRMELEMNTS_LBL_FILTER": "Filters", "FRMELEMNTS_LBL_VIEW_PROJECT": "View project", - "FRMELEMNTS_BTN_LOAD_MORE": "Load More", + "FRMELEMNTS_BTN_LOAD_MORE": "Load more", "FRMELEMNTS_MSG_PROJECT_UPDATED_SUCCESS": "Your Project has been updated successfully", "FRMELEMNTS_LBL_PROJECT_CREATE": "Project Created", "FRMELEMNTS_MSG_PROJECT_CREATED_SUCCESS": "Your Project has been created successfully", @@ -1046,14 +1046,14 @@ "FRMELEMNTS_LBL_PAST": "Past", "FRMELEMNTS_LBL_PROJECTS": "Projects", "FRMELEMNTS_LBL_PROJECT_DESC": "Manage and track your school improvement easily, by creating tasks and planning project timelines.", - "FRMELEMNTS_LBL_CREATE_NEW_PROGRAM": "Create new Program", + "FRMELEMNTS_LBL_CREATE_NEW_PROGRAM": "Create new program", "FRMELEMNTS_BTN_IMPORT_PROJECT": "Import Project", "FRMELEMNTS_LBL_CREATE_PROGRAM_INPUT_PLACEHOLDER": "Program name", "FRMELEMNTS_LBL_CREATE_PROGRAM_INPUT_NAME": "Name", "FRMELEMNTS_LBL_CREATE_PROGRAM": "Create Program", "FRMELEMNTS_MSG_CREATE_PROGRAM_MESSAGE": "Enter your Program Name below and click 'save'", "FRMELEMNTS_LBL_MY_PROGRAMS": "My Programs", - "FRMELEMNTS_BTN_ADD_PROGRAM": "Add Program", + "FRMELEMNTS_BTN_ADD_PROGRAM": "Add program", "FRMELEMNTS_BTN_DELETE": "Delete", "FRMELEMNTS_LBL_FROM": "From", "FRMELEMNTS_LBL_TO": "To", @@ -1364,5 +1364,12 @@ "FRMELEMNTS_LBL_START_IMP_POPUP_MSG1":"You must click on the Start improvement button to access and edit the project details.", "FRMELEMNTS_LBL_START_IMP_POPUP_MSG2":"Click on the Start improvement to get complete access to the content", "FRMELEMNTS_LBL_TO_SUBMIT_PROJECT":"To finish submission, click on Submit button below", - "FRMELEMNTS_LBL_FILE_SIZE_EXCEEDED":"The file is too large and cannot be uploaded. The file you are tying to upload has exceeded the maximum file size (50 MB)" + "FRMELEMNTS_LBL_FILE_SIZE_EXCEEDED":"The file is too large and cannot be uploaded. The file you are tying to upload has exceeded the maximum file size (50 MB)", + "FRMELEMNTS_MSG_CERTIFICATION_NAME_DISPLAY":"This name will be printed on your improvement cerificate. Check if this is correct", + "SUCCESSFULLY_COMPLETING_PROJECT": "For successfully completing the project", + "FRMELEMNTS_MSG_NO_DATA_FOR_LOCAL_SEARCH":"Click on the Add {{entity}} button to add your {{entity}} to observe.", + "FRMELEMNTS_LBL_CERTIFICATION_CRITERIA":"Certificate criteria", + "FRMELEMNTS_LBL_IMP_CERTIFICATE":"Improvement certificate", + "FRMELEMNTS_MSG_PROJECT_SUBMITTED_CERTIFICATE_SOON":"Your project is submitted. You will receive a certificate soon if you meet the eligibility criteria.", + "FRMELEMNTS_MSG_NOT_MET_CERTIFCATE":" Sorry ! You have not met the certificate criteria for this Improvement Project." } \ No newline at end of file diff --git a/src/assets/styles/component/_sb-content-player.scss b/src/assets/styles/component/_sb-content-player.scss index 1d0c721bd8..b0e3c62ca8 100644 --- a/src/assets/styles/component/_sb-content-player.scss +++ b/src/assets/styles/component/_sb-content-player.scss @@ -103,4 +103,25 @@ border-radius: 50%; z-index: 100; } + +.sb-player-container-ios { + width: 100%; + background-color: black; + .sb-arrow-back + { + @include padding($base-block-space * 0.5); + color: $white; + } + .sb-circle + { + position: absolute; + top: 1rem; + left: 1rem; + height:($base-block-space * 3); + width:1.5rem; + // background-color: $white; + border-radius: 50%; + z-index: 100; + } +} // \ No newline at end of file diff --git a/src/assets/styles/component/_sb-popover.scss b/src/assets/styles/component/_sb-popover.scss index 55e0b0e7fb..21449604f9 100644 --- a/src/assets/styles/component/_sb-popover.scss +++ b/src/assets/styles/component/_sb-popover.scss @@ -78,6 +78,49 @@ } } + // ios + .sb-popover-header.ios { + border-radius: 4px 4px 0 0; + background-color: $info-color; + &::after { + background-image: none; + } + .sb-popover-toolbar { + min-height: 2.5rem; + max-height: 2.5rem; + background-color: $info-color; + border-radius: 4px 4px 0 0; + // @include padding(($base-block-space * 0.5), ($base-block-space * 1)); + .toolbar-background { + border-radius: 4px 4px 0 0; + background-color: $info-color; + } + .sb-popover-close-btn { + color: $white-color; + } + .sb-popover-title { + font-size: ($font-size-base); + font-weight: bold; + //@extend line-height: 19px; + text-align: center; + color: $white-color; + @include padding(0); + // @include padding(0, ($base-block-space * 5) ,0,0); + } + } + .sb-modal-close { + position: absolute; + right: 0.313rem; + width: 1.25rem; + height: 1.25rem; + display: flex; + top: -1.125rem; + text-align: center; + justify-content: center; + align-items: center; + } + } + .ios .sb-popover-header { margin-top: calc(constant(safe-area-inset-top)); //ios 11.2 margin-top: calc(env(safe-area-inset-top)); //ios 11.1 @@ -177,7 +220,7 @@ .sb-popover-header-ios { border-radius: 4px 4px 0 0; background-color: $info-color; - padding-top: 40px; + &::after { background-image: none; } @@ -210,7 +253,7 @@ width: 1.25rem; height: 1.25rem; display: flex; - top: -0.125rem; + top: -1.125rem; text-align: center; justify-content: center; align-items: center; diff --git a/src/assets/styles/themeable.scss b/src/assets/styles/themeable.scss index 3fedd57644..35ef7e50c8 100644 --- a/src/assets/styles/themeable.scss +++ b/src/assets/styles/themeable.scss @@ -38,6 +38,8 @@ html[data-theme="JOYFUL"]{ --button-sm-style-shadow: 3px 3px 2px 0 var(--app-shadow); --block-style-shadow: 0 2px 7px 0 var(--app-shadow); + --sbt-theme-purple-selectbox-lbg: #F2EEFF; + ion-toolbar { --background: var(--app-primary-header); border-bottom-left-radius: 20px; @@ -1112,6 +1114,41 @@ html[data-theme="JOYFUL"]{ } // } + .login-div { + sb-form { + sb-textbox { + input { + box-sizing: border-box; + width: 86vw; + height: 2.5rem; + border: 0.063rem solid #024F9D !important; + border-radius: 0.75rem !important; + background-color: #FFFFFF !important; + font-size: 0.75rem !important; + padding-left: 0.938rem !important; + } + input:focus { + border: 0.063rem solid #024F9D !important; + } + input::placeholder { + color: var(--app-lighter-gray); + margin-left: 0 !important; + font-size: 0.75rem !important; + font-weight: normal !important; + } + .sb-input { + label { + display: inline !important; + } + .fgt-pwsd-lbl { + color: #024F9D; + float: right; + } + } + } + } + } + // permission page app-permission{ .permission-container{ @@ -1822,11 +1859,12 @@ html[data-theme="JOYFUL"]{ &::after { content: ''; width: 100%; - height: 3.125rem; + height: 10.75rem; display: block; position: absolute; z-index: 0; top: 0; + left: 0; border-bottom-left-radius: 20px; background-color: var(--app-primary-header) !important; background-image: url(/assets/imgs/header-background.svg); diff --git a/src/global.scss b/src/global.scss index fea6bef264..65dcb96e5f 100644 --- a/src/global.scss +++ b/src/global.scss @@ -69,9 +69,9 @@ .df-cancel-btn{ color: $primary-color !important; } - .open-btn{ - color: $gray-800 !important; - } + // .open-btn{ + // color: $gray-800 !important; + // } .topic-list-container{ height: calc(100vh - 175px) !important; } @@ -216,11 +216,14 @@ sunbird-pdf-player .sunbird-pdf-palyer-container pdf-viewer iframe{ } .certificate-tag{ background-color: var(--app-primary-header) !important; - padding: 2px 15px; + padding: 0px 5px; + padding-top: 2px; color: #000; text-transform: uppercase; font-size: 0.813rem !important; border-radius: 4px; font-weight: 500; align-self: flex-start; + margin: 5px 5px; + word-break: keep-all; } \ No newline at end of file diff --git a/src/services/app-global-service.service.spec.ts b/src/services/app-global-service.service.spec.ts index 945b776bf4..a2933d6768 100644 --- a/src/services/app-global-service.service.spec.ts +++ b/src/services/app-global-service.service.spec.ts @@ -5,11 +5,14 @@ import { Events } from '@app/util/events'; import { TelemetryGeneratorService } from './telemetry-generator.service'; import { UtilityService } from './utility-service'; import { of, throwError } from 'rxjs'; -import { PreferenceKey, EventTopics } from '../app/app.constant'; -import { InteractSubtype, Environment, PageId, InteractType, ImpressionType, ImpressionSubtype } from './telemetry-constants'; +import { PreferenceKey } from '../app/app.constant'; +import { InteractSubtype, Environment, PageId, InteractType, ID } from './telemetry-constants'; import { AppVersion } from '@ionic-native/app-version/ngx'; import { mockFrameworkData } from './app-global-service.service.spec.data'; import { UpgradePopoverComponent } from '@app/app/components/popups'; +import { YearOfBirthPopupComponent } from '@app/app/components/popups/year-of-birth-popup/year-of-birth-popup.component'; +import { NewExperiencePopupComponent } from '@app/app/components/popups/new-experience-popup/new-experience-popup.component'; +import { JoyfulThemePopupComponent } from '@app/app/components/popups/joyful-theme-popup/joyful-theme-popup.component'; describe('AppGlobalService', () => { let appGlobalService: AppGlobalService; @@ -22,7 +25,7 @@ describe('AppGlobalService', () => { }; const mockFrameworkService: Partial = {}; const mockEvent: Partial = { - subscribe: jest.fn(), + subscribe: jest.fn(() => of({skipSession: false})), publish: jest.fn(), unsubscribe: jest.fn() }; @@ -42,7 +45,7 @@ describe('AppGlobalService', () => { getBuildConfigValue: jest.fn(() => Promise.resolve('org.sunbird.app')) }; const mockAppVersion: Partial = {}; - + window.console.error = jest.fn(); beforeAll(() => { appGlobalService = new AppGlobalService( mockProfile as ProfileService, @@ -97,7 +100,6 @@ describe('AppGlobalService', () => { // arrange const key = 'media'; const value = true; - mockPreferences.getString = jest.fn(() => of(false)) mockPreferences.putString = jest.fn(() => of(undefined)); // act appGlobalService.setIsPermissionAsked(key, value); @@ -110,7 +112,7 @@ describe('AppGlobalService', () => { // arrange const key = 'media'; const value = true; - const data = mockPreferences.getString = jest.fn(() => of('{"key": "key_1"}')); + const data = mockPreferences.getString = jest.fn(() => of('')); mockPreferences.putString = jest.fn(() => of(undefined)); JSON.parse = jest.fn().mockImplementationOnce(() => { return data; @@ -180,6 +182,19 @@ describe('AppGlobalService', () => { }, 1); }); + + it('should handle else case if no signin Onboarding loader', (done) => { + // arrange + appGlobalService.signinOnboardingLoader = undefined; + // act + appGlobalService.closeSigninOnboardingLoader(); + // assert + setTimeout(() => { + // expect(appGlobalService.signinOnboardingLoader).toBeNull(); + done(); + }, 1); + }); + describe('getPageIdForTelemetry()', () => { it('should return expected pageId', () => { // arrange @@ -211,6 +226,12 @@ describe('AppGlobalService', () => { // act // assert expect(appGlobalService.getPageIdForTelemetry()).toEqual(PageId.LIBRARY); + + // arrange + appGlobalService.currentPageId = ''; + // act + // assert + expect(appGlobalService.getPageIdForTelemetry()).toEqual(PageId.LIBRARY); }); }); @@ -246,12 +267,7 @@ describe('AppGlobalService', () => { appGlobalService.isGuestUser = true; // act // assert - appGlobalService.getProfileSettingsStatus({ - syllabus: ['AP'], - board: ['AP'], - grade: ['class1'], - medium: ['English'] - }).then((response) => { + appGlobalService.getProfileSettingsStatus().then((response) => { expect(response).toBeTruthy(); done(); }); @@ -272,6 +288,24 @@ describe('AppGlobalService', () => { done(); }); }); + + it('should get current profile and set profile details', (done) => { + // arrange + appGlobalService.guestUserProfile = { + syllabus: ['AP'], + board: ['AP'], + grade: ['class1'], + medium: ['English'] + } as any; + appGlobalService.isGuestUser = true; + jest.spyOn(appGlobalService, 'getCurrentUser').mockReturnValue(appGlobalService.guestUserProfile) + // act + appGlobalService.getProfileSettingsStatus(appGlobalService.guestUserProfile).then((response) => { + // assert + expect(response).toBeTruthy(); + done(); + }); + }); }); describe('setBoardMediumGrade()', () => { @@ -412,6 +446,32 @@ describe('AppGlobalService', () => { undefined, paramsMap); }); + + it('should generate telemetry with GUEST_PROFILE page config for STUDENT profile type', () => { + // arrange + appGlobalService.guestProfileType = ProfileType.STUDENT; + appGlobalService.isGuestUser = true; + const paramsMap = new Map(); + paramsMap['isProfileSettingsCompleted'] = true; + paramsMap['isSignInCardConfigEnabled'] = false; + // act + appGlobalService.generateConfigInteractEvent(PageId.PROFILE, true); + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( + InteractType.OTHER, + InteractSubtype.INITIAL_CONFIG, + Environment.HOME, + PageId.LIBRARY, + undefined, + paramsMap); + }); + + it('should handle else if isguestuser is false', () => { + // arrange + appGlobalService.isGuestUser = false; + // act + appGlobalService.generateConfigInteractEvent('', false); + }) }); describe('generateAttributeChangeTelemetry()', () => { @@ -471,9 +531,20 @@ describe('AppGlobalService', () => { values); }); + it('should generate save clicked telemetry for else case', () => { + // arrange + appGlobalService.TRACK_USER_TELEMETRY = false; + const values = new Map(); + values['profile'] = profile; + values['validation'] = 'medium is required'; + // act + appGlobalService.generateSaveClickedTelemetry(profile, 'medium is required', PageId.LIBRARY, 'medium-clicked'); + // assert + }); + it('should generate attribute change telemetry when env is not given', () => { // arrange - appGlobalService.TRACK_USER_TELEMETRY = true; + appGlobalService.TRACK_USER_TELEMETRY = false; const values = new Map(); values['oldValue'] = ['Class 1']; values['newValue'] = ['Class 1', 'Class 2']; @@ -513,16 +584,30 @@ describe('AppGlobalService', () => { describe('getNameForCodeInFramework()', () => { it('should return the name of the provided code in the framework', () => { // arrange - appGlobalService['frameworkData'] = {gradeLevel: {terms: [{code: 'class1'}]}} - appGlobalService.getNameForCodeInFramework('gradeLevel', 'class1'); + appGlobalService['frameworkData'] = { + gradeLevel: {terms: [{code: 'class1'}]} + } as any // act + appGlobalService.getNameForCodeInFramework('gradeLevel', 'class1'); // assert }); - it('should not return if no framework data present', () => { + + it('should return the name handling with no matching terms', () => { // arrange - appGlobalService['frameworkData'] = {gradeLevel: {}} + appGlobalService['frameworkData'] = { + gradeLevel: {terms: [{code: 'class2'}]} + } as any + // act appGlobalService.getNameForCodeInFramework('gradeLevel', 'class1'); + // assert + }); + + it('should return the name handling else case if condition false', () => { + // arrange + appGlobalService['frameworkData'] = { + } as any // act + appGlobalService.getNameForCodeInFramework('gradeLevel', 'class1'); // assert }); }); @@ -795,7 +880,6 @@ describe('AppGlobalService', () => { done(); }); }); - it('should return profileType ADMIN', (done) => { // arrange mockPreferences.getString = jest.fn(() => of(ProfileType.ADMIN)); @@ -807,7 +891,6 @@ describe('AppGlobalService', () => { done(); }); }); - it('should return profileType PARENT', (done) => { // arrange mockPreferences.getString = jest.fn(() => of(ProfileType.PARENT)); @@ -820,6 +903,18 @@ describe('AppGlobalService', () => { }); }); + it('should return profileType new', (done) => { + // arrange + mockPreferences.getString = jest.fn(() => of('new')); + // act + // assert + appGlobalService.getGuestUserInfo().then((response) => { + expect(appGlobalService.isGuestUser).toBeTruthy(); + expect(response).toEqual('parent'); + done(); + }); + }); + it('should handle error scenario', (done) => { // arrange mockPreferences.getString = jest.fn(() => throwError({})); @@ -978,16 +1073,32 @@ describe('AppGlobalService', () => { it('should show force upgrade popup with shouldDismissAlert as true if type is optional', () => { // arrange + mockPopoverCtrl.create = jest.fn(() => (Promise.resolve({ + present: jest.fn(() => Promise.resolve({})), + onDidDismiss: jest.fn(() => Promise.resolve({ data: {}})) + } as any))); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); // act - appGlobalService.openPopover({ type: 'optional' }); + appGlobalService.openPopover({ type: 'optional', isOnboardingCompleted: true }); // assert expect(mockPopoverCtrl.create).toHaveBeenCalledWith({ component: UpgradePopoverComponent, - componentProps: { upgrade: { type: 'optional' } }, + componentProps: { upgrade: { isOnboardingCompleted: true, type: 'optional' } }, cssClass: 'upgradePopover', showBackdrop: true, backdropDismiss: true }); + setTimeout(() => { + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith(InteractType.BACKDROP_DISMISSED, + '', + Environment.HOME, + PageId.UPGRADE_POPUP, + undefined, + undefined, + undefined, + undefined, + ID.BACKDROP_CLICKED); + }, 0); }); }); @@ -1050,165 +1161,214 @@ describe('AppGlobalService', () => { }); }); - describe('setisDiscoverBackEnabled', () => { - it('should return the isDiscoverBackEnabled', () => { - // arrange - appGlobalService.isDiscoverBackEnabled = true; - // act - // assert - expect(appGlobalService.isDiscoverBackEnabled).toBeTruthy(); - }); + it('should be preSignInData flow', () => { + // arrange + appGlobalService.preSignInData = true; + // act + // assert + expect(appGlobalService.preSignInData).toBeTruthy(); }); - describe('setpreSignInData', () => { - it('should return the preSignInData', () => { - // arrange - appGlobalService.preSignInData = true; - // act - // assert - expect(appGlobalService.preSignInData).toBeTruthy(); - }); + it('should be generateCourseCompleteTelemetry flow', () => { + // arrange + appGlobalService.generateCourseCompleteTelemetry = true; + // act + // assert + expect(appGlobalService.generateCourseCompleteTelemetry).toBeTruthy(); }); - describe('setredirectUrlAfterLogin', () => { - it('should return the redirectUrlAfterLogin', () => { - // arrange - appGlobalService.redirectUrlAfterLogin = 'true'; - // act - // assert - expect(appGlobalService.redirectUrlAfterLogin).toBeTruthy(); - }); + it('should be generateCourseUnitCompleteTelemetry flow', () => { + // arrange + appGlobalService.generateCourseUnitCompleteTelemetry = true; + // act + // assert + expect(appGlobalService.generateCourseUnitCompleteTelemetry).toBeTruthy(); }); - describe('setselectedActivityCourseId', () => { - it('should return the selectedActivityCourseId', () => { - // arrange - appGlobalService.selectedActivityCourseId = 'true'; - // act - // assert - expect(appGlobalService.selectedActivityCourseId).toBeTruthy(); - }); + it('should be showCourseCompletePopup flow', () => { + // arrange + appGlobalService.showCourseCompletePopup = true; + // act + // assert + expect(appGlobalService.showCourseCompletePopup).toBeTruthy(); }); - describe('setformConfig', () => { - it('should return the formConfig', () => { - // arrange - appGlobalService.formConfig = 'true'; - // act - // assert - expect(appGlobalService.formConfig).toBeTruthy(); - }); + it('should be formConfig flow', () => { + // arrange + appGlobalService.formConfig = true; + // act + // assert + expect(appGlobalService.formConfig).toBeTruthy(); }); - describe('setshowCourseCompletePopup', () => { - it('should return the showCourseCompletePopup', () => { - // arrange - appGlobalService.showCourseCompletePopup = true; - // act - // assert - expect(appGlobalService.showCourseCompletePopup).toBeTruthy(); - }); + it('should be selectedActivityCourseId flow', () => { + // arrange + appGlobalService.selectedActivityCourseId = "true"; + // act + // assert + expect(appGlobalService.selectedActivityCourseId).toBeTruthy(); }); - describe('setgenerateCourseUnitCompleteTelemetry', () => { - it('should return the generateCourseUnitCompleteTelemetry', () => { - // arrange - appGlobalService.generateCourseUnitCompleteTelemetry = true; - // act - // assert - expect(appGlobalService.generateCourseUnitCompleteTelemetry).toBeTruthy(); - }); + it('should be redirectUrlAfterLogin flow', () => { + // arrange + appGlobalService.redirectUrlAfterLogin = "true"; + // act + // assert + expect(appGlobalService.redirectUrlAfterLogin).toBeTruthy(); }); - describe('setgenerateCourseCompleteTelemetry', () => { - it('should return the generateCourseCompleteTelemetry', () => { - // arrange - appGlobalService.generateCourseCompleteTelemetry = true; - // act - // assert - expect(appGlobalService.generateCourseCompleteTelemetry).toBeTruthy(); - }); + it('should be isDiscoverBackEnabled flow', () => { + // arrange + appGlobalService.isDiscoverBackEnabled = true; + // act + // assert + expect(appGlobalService.isDiscoverBackEnabled).toBeTruthy(); }); - describe('showJoyfulPopup ', () => { - it('should show showJoyfulPopup skipCoachScreenForDeeplink is true ', () => { + describe('showJoyfulPopup', () =>{ + it('should skip coach screen deep link to true', () => { // arrange - appGlobalService.skipCoachScreenForDeeplink = true + mockPreferences.getBoolean = jest.fn(() => of(true)) // act appGlobalService.showJoyfulPopup() // assert - setTimeout(() => { - }, 0); }) - - it('should show showJoyfulPopup skipCoachScreenForDeeplink is true and joyfull theme true', () => { + it('should skip coach screen deep link to false', () => { // arrange - appGlobalService.skipCoachScreenForDeeplink = false - mockPreferences.getBoolean = jest.fn(() => of(true)); + appGlobalService.skipCoachScreenForDeeplink = true; + mockPreferences.getBoolean = jest.fn(() => of(false)) // act appGlobalService.showJoyfulPopup() // assert - expect(mockPreferences.getBoolean).toHaveBeenCalledWith(PreferenceKey.IS_JOYFUL_THEME_POPUP_DISPLAYED); }) - - it('should show showJoyfulPopup skipCoachScreenForDeeplink is true', () => { + it('should skip coach screen deep link to false and joyfull display false and create popup', () => { // arrange - appGlobalService.skipCoachScreenForDeeplink = false - mockPreferences.getBoolean = jest.fn(() => of(false)); - mockAppVersion.getAppName = jest.fn(() => Promise.resolve('')) + mockPreferences.getBoolean = jest.fn(() => of(false)) // act appGlobalService.showJoyfulPopup() // assert - expect(mockPreferences.getBoolean).toHaveBeenCalledWith(PreferenceKey.IS_JOYFUL_THEME_POPUP_DISPLAYED); + setTimeout(() => { + expect(mockPopoverCtrl.create).toHaveBeenCalledWith({ + component: JoyfulThemePopupComponent, + componentProps: { appLabel: "appLabel" }, + backdropDismiss: false, + showBackdrop: true, + cssClass: 'sb-new-theme-popup' + }); + }, 0); }) }) - - describe('showNewTabsSwitchPopup ', () => { - it('should show showNewTabsSwitchPopup ', () => { + describe('showNewTabsSwitchPopup', () =>{ + it('should show new tab switch on popup display false', () => { + // arrange + mockPreferences.getString = jest.fn(() => of(false)) + // act + appGlobalService.showNewTabsSwitchPopup() + // assert + setTimeout(() => { + expect(mockPopoverCtrl.create).toHaveBeenCalledWith({ + component: NewExperiencePopupComponent, + componentProps: { appLabel: "appLabel" }, + backdropDismiss: false, + showBackdrop: true, + cssClass: 'sb-switch-new-experience-popup' + }); + }, 0); + }) + it('should show new tab switch on popup display true', () => { // arrange - mockPreferences.getString = jest.fn(() => of('')) - mockAppVersion.getAppName = jest.fn(() => Promise.resolve('')) + mockPreferences.getString = jest.fn(() => of(true)) // act appGlobalService.showNewTabsSwitchPopup() // assert }) }) - describe('getActiveProfileUid ', () => { - it('should show getActiveProfileUid ', () => { + describe('getActiveProfileUid', () =>{ + it('should get active profile uid', () => { // arrange - mockProfile.getActiveProfileSession = jest.fn(() => throwError({})); + mockProfile.getActiveProfileSession = jest.fn(() => of({uid: "some_id", managedSession: {uid: "some_id"}})) + // act + appGlobalService.getActiveProfileUid() + // assert + }); + it('should get active profile uid', () => { + // arrange + mockProfile.getActiveProfileSession = jest.fn(() => of({uid: "some_id", managedSession: ''})) // act appGlobalService.getActiveProfileUid() // assert }) - - it('should show getActiveProfileUid and get active session profile ', () => { + it('should get active profile uid on error return userid', () => { // arrange - mockProfile.getActiveProfileSession = jest.fn(() => of({uid: 'some_id', managedSession: {uid: 'id'}})) + mockProfile.getActiveProfileSession = jest.fn(() => throwError({})) // act appGlobalService.getActiveProfileUid() // assert }) }) + describe('showYearOfBirthPopup', () =>{ + it('should show year of birth popup', () => { + // arrange + const userProfile = {}; + // act + appGlobalService.showYearOfBirthPopup(userProfile) + // assert + expect(mockPopoverCtrl.create).toHaveBeenCalledWith({ + component: YearOfBirthPopupComponent, + componentProps: { }, + backdropDismiss: false, + showBackdrop: true, + cssClass: 'year-of-birth-popup' + }); + }) - describe('showYearOfBirthPopup ', () => { - it('should show showYearOfBirthPopup ', () => { + it('should show year of birth popup else case for user details', () => { // arrange + const userProfile = {dob: 1997, managedBy: 'some'}; // act - appGlobalService.showYearOfBirthPopup({}) + appGlobalService.showYearOfBirthPopup(userProfile) // assert }) }) + describe('setAccessibilityFocus', () =>{ + it('should set accssibility focus and set null if no ele focus', () => { + // arrange + const id = 123 + window.setTimeout = jest.fn((fn) => fn( + document.getElementById = jest.fn(() => null) + )) + // act + appGlobalService.setAccessibilityFocus(id) + // assert + setTimeout(() => { + + }, 0); + }) - describe('setAccessibilityFocus ', () => { - it('should show setAccessibilityFocus ', () => { + it('should set accssibility focus and handle ele focus', () => { // arrange + const id = 123 + window.setTimeout = jest.fn((fn) => fn( + document.getElementById = jest.fn(() => ({focus: jest.fn()})) as any + )) as any; // act - appGlobalService.setAccessibilityFocus('some_id') + appGlobalService.setAccessibilityFocus(id) + // assert setTimeout(() => { - // assert + + }, 0); }) }) -}); + + describe('setFramewokCategory', () => { + it('setFramewokCategory ', () => { + // arrange + // act + appGlobalService.setFramewokCategory({}); + // assert + expect(appGlobalService.getCachedFrameworkCategory()).toEqual({}); + }) + }) +}); \ No newline at end of file diff --git a/src/services/canvas-player.service.spec.ts b/src/services/canvas-player.service.spec.ts index 05427f60b5..afe5d92efe 100644 --- a/src/services/canvas-player.service.spec.ts +++ b/src/services/canvas-player.service.spec.ts @@ -8,6 +8,7 @@ import { CourseService, SharedPreferences } from '@project-sunbird/sunbird-sdk'; import { PreferenceKey } from '../app/app.constant'; import { LocalCourseService } from './local-course.service'; import { CommonUtilService } from './common-util.service'; +import { File } from '@ionic-native/file/ngx'; describe('CanvasPlayerService', () => { @@ -34,13 +35,15 @@ describe('CanvasPlayerService', () => { const mockHttp: Partial = {}; const mockEvents: Partial = {}; + const mockFile: Partial = {}; beforeAll(() => { canvasPlayerService = new CanvasPlayerService( mockHttp as HttpClient, mockEvents as Events, mockPreferences as SharedPreferences, mockLocalCourseService as LocalCourseService, - mockCommonUtilService as CommonUtilService + mockCommonUtilService as CommonUtilService, + mockFile as File ); }); @@ -88,29 +91,29 @@ describe('CanvasPlayerService', () => { 'ChildSecond Child' + ''; // arrange - mockHttp.get = jest.fn(() => of(mockXMLContent)); + mockFile.readAsText = jest.fn(() => Promise.resolve(mockXMLContent)); // act canvasPlayerService.xmlToJSon(mockXMLContent); // assert - expect(mockHttp.get).toHaveBeenCalled(); + expect(mockFile.readAsText).toHaveBeenCalled(); }); it('should readJSON if path is available and goes to catch part', () => { // arrange - mockHttp.get = jest.fn(() => Promise.reject('Unable to convert')); + mockFile.readAsText = jest.fn(() => Promise.reject('Unable to convert')); // act canvasPlayerService.xmlToJSon('sampleRandomPath'); // assert - expect(mockHttp.get).toHaveBeenCalled(); + expect(mockFile.readAsText).toHaveBeenCalled(); }); it('should not get inside if call, if path is undefined', () => { // arrange - mockHttp.get = jest.fn(() => of({})); + mockFile.readAsText = jest.fn(() => Promise.resolve('')); // act canvasPlayerService.xmlToJSon(''); // arrange - expect(mockHttp.get).not.toHaveBeenCalled(); + expect(mockFile.readAsText).not.toHaveBeenCalled(); }); }); diff --git a/src/services/canvas-player.service.ts b/src/services/canvas-player.service.ts index e084aebb3b..3e7cf331a7 100644 --- a/src/services/canvas-player.service.ts +++ b/src/services/canvas-player.service.ts @@ -6,6 +6,7 @@ import {MaxAttempt, PreferenceKey, ProfileConstants} from '@app/app/app.constant import { Events } from '@app/util/events'; import { LocalCourseService } from './local-course.service'; import { CommonUtilService } from './common-util.service'; +import { File } from '@ionic-native/file/ngx'; declare global { interface Window { @@ -21,6 +22,7 @@ export class CanvasPlayerService { @Inject('SHARED_PREFERENCES') private preferences: SharedPreferences, private localCourseService: LocalCourseService, private commonUtilService: CommonUtilService, + private file: File, ) { } /** @@ -109,15 +111,15 @@ export class CanvasPlayerService { * This will convert xml to JSON * @param {string} path Path to the xml file */ - xmlToJSon(path: string): Promise { + xmlToJSon(path: string, file): Promise { if (path.length) { const _headers = new HttpHeaders(); const headers = _headers.set('Content-Type', 'text/xml'); return new Promise((resolve, reject) => { try { - this._http.get(path, { headers: _headers, responseType: 'text' }).subscribe((data) => { + this.file.readAsText(path, file).then((response) => { const x2js = new X2JS(); - const json = x2js.xml2js(data); + const json = x2js.xml2js(response); resolve(json); }); } catch (error) { diff --git a/src/services/collection.service.spec.ts b/src/services/collection.service.spec.ts index 518e7818d1..21d993af30 100644 --- a/src/services/collection.service.spec.ts +++ b/src/services/collection.service.spec.ts @@ -11,6 +11,7 @@ describe('LocalCourseService', () => { const mockContentService: Partial = {}; const mockCommonUtilService: Partial = {}; + window.console.error = jest.fn() beforeAll(() => { collectionService = new CollectionService( diff --git a/src/services/common-util.service.spec.ts b/src/services/common-util.service.spec.ts index ba230e10ce..52efa65393 100644 --- a/src/services/common-util.service.spec.ts +++ b/src/services/common-util.service.spec.ts @@ -11,16 +11,18 @@ import { TelemetryGeneratorService } from '@app/services/telemetry-generator.ser import { InteractType, InteractSubtype, PageId, Environment } from '@app/services/telemetry-constants'; import { PreferenceKey } from '@app/app/app.constant'; import { SbGenericPopoverComponent } from '@app/app/components/popups/sb-generic-popover/sb-generic-popover.component'; -import { QRScannerAlert } from '@app/app/qrscanner-alert/qrscanner-alert.page'; +import { QRScannerAlert, QRAlertCallBack } from '@app/app/qrscanner-alert/qrscanner-alert.page'; import { TranslateService } from '@ngx-translate/core'; import { Network } from '@ionic-native/network/ngx'; import { NgZone } from '@angular/core'; import { WebView } from '@ionic-native/ionic-webview/ngx'; import { AppVersion } from '@ionic-native/app-version/ngx'; -import { of, throwError } from 'rxjs'; +import { of, Subject, throwError } from 'rxjs'; import { Router } from '@angular/router'; -import { AndroidPermissionsService, ComingSoonMessageService } from '.'; -import { TelemetryService } from '@project-sunbird/sunbird-sdk'; +import { AndroidPermissionsService, ComingSoonMessageService, ImpressionType, ObjectType } from '.'; +import { ProfileType, TelemetryService } from '@project-sunbird/sunbird-sdk'; +import { AndroidPermission } from './android-permissions/android-permission'; +import GraphemeSplitter from 'grapheme-splitter'; declare const FCMPlugin; @@ -62,12 +64,14 @@ describe('CommonUtilService', () => { } as any))) }; const mockNetwork: Partial = { - onChange: jest.fn(() => of([{ type: 'online' }])) + onChange: jest.fn(() => of([{ type: 'online' }, { type: 'offline' }])) }; const mockNgZone: Partial = { - run: jest.fn((fn) => fn()) + run: jest.fn((fn) => fn()) as any + }; + const mockPlatform: Partial = { + is: jest.fn(platform => platform == 'android') }; - const mockPlatform: Partial = {}; const mockTelemetryGeneratorService: Partial = { generateInteractTelemetry: jest.fn(), generateBackClickedTelemetry: jest.fn(), @@ -83,7 +87,6 @@ describe('CommonUtilService', () => { const mockPermissionService: Partial = {}; const mockComingSoonMessageService: Partial = {}; - beforeAll(() => { commonUtilService = new CommonUtilService( mockSharedPreferences as SharedPreferences, @@ -115,41 +118,37 @@ describe('CommonUtilService', () => { expect(commonUtilService).toBeTruthy(); }); - describe('addPopupAccessibility', ()=>{ + describe('showToast()', () => { - it('Should add the accessibilty to the toast popup', ()=>{ + it('should show Toast with provided configuration', () => { // arrange - commonUtilService['popupAccessibilityFocus'] = jest.fn(); - commonUtilService['getPlatformBasedActiveElement'] = jest.fn(); - const toast = { - present: jest.fn(), - addEventListener: jest.fn(), - onDidDismiss: jest.fn(()=>Promise.resolve()), - setAttribute: jest.fn() - } + jest.spyOn(commonUtilService, 'addPopupAccessibility').mockImplementation(()=>{ + return {present: presentFn} + }) // act - commonUtilService.addPopupAccessibility(toast, 'message'); + commonUtilService.showToast('CONTENT_COMING_SOON', false); // assert - expect(toast.setAttribute).toHaveBeenCalled(); + expect(mockToastController.create).toHaveBeenCalledWith({ + message: 'sample_translation', + duration: 3000, + position: 'bottom', + cssClass: '' + }); }); - }); - - describe('showToast()', () => { - it('should show Toast with provided configuration', () => { // arrange jest.spyOn(commonUtilService, 'addPopupAccessibility').mockImplementation(()=>{ return {present: presentFn} }) // act - commonUtilService.showToast('CONTENT_COMING_SOON', false); + commonUtilService.showToast('CONTENT_COMING_SOON', false, 'red-toast', 3000, 'bottom', {}); // assert expect(mockToastController.create).toHaveBeenCalledWith({ message: 'sample_translation', duration: 3000, position: 'bottom', - cssClass: '' + cssClass: 'red-toast' }); }); @@ -199,6 +198,13 @@ describe('CommonUtilService', () => { expect(commonUtilService.getTranslatedValue( '{\"en\": \"sample_translation\"}', 'en')).toEqual('sample_translation'); }); + + it('should return default if no translated value', () => { + // arrange + // act + // assert + expect(commonUtilService.getTranslatedValue('{\"sp\": \"sample_translation\"}', 'en')); + }); }); describe('getLoader()', () => { @@ -209,6 +215,14 @@ describe('CommonUtilService', () => { // assert expect(loader).toBeDefined(); }); + + it('should return loader instance, if it has duration and message passed', () => { + // arrange + // act + const loader: LoadingController = commonUtilService.getLoader('3000', 'some_msg'); + // assert + expect(loader).toBeDefined(); + }); }); describe('arrayToString()', () => { @@ -230,6 +244,23 @@ describe('CommonUtilService', () => { expect(mockSharedPreferences.putString).toHaveBeenCalledWith(PreferenceKey.SELECTED_LANGUAGE_CODE, 'en'); expect(mockSharedPreferences.putString).toHaveBeenCalledWith(PreferenceKey.SELECTED_LANGUAGE, 'English'); }); + + it('should handle else case if language is not found', () => { + // arrange + // act + commonUtilService.changeAppLanguage('other'); + // assert + }); + + it('should change the language to given language name, and if code is present', () => { + // arrange + // act + commonUtilService.changeAppLanguage('English', 'en'); + // assert + expect(mockTranslateService.use).toHaveBeenCalledWith('en'); + expect(mockSharedPreferences.putString).toHaveBeenCalledWith(PreferenceKey.SELECTED_LANGUAGE_CODE, 'en'); + expect(mockSharedPreferences.putString).toHaveBeenCalledWith(PreferenceKey.SELECTED_LANGUAGE, 'English'); + }); }); describe('afterOnBoardQRErrorAlert()', () => { @@ -245,6 +276,32 @@ describe('CommonUtilService', () => { // assert expect(mockPopoverController.create).toHaveBeenCalled(); }); + + it('should show Error alert popover, pass dialcode and source', () => { + // arrange + const createMock = jest.spyOn(mockPopoverController, 'create').mockResolvedValue({ + present: jest.fn(() => Promise.resolve({})), + onDidDismiss: jest.fn(() => Promise.resolve({ data: {isLeftButtonClicked: true} })) + } as any); + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + // act + commonUtilService.afterOnBoardQRErrorAlert('Invalid QR code', 'sample_message', PageId.ONBOARDING_PROFILE_PREFERENCES, ObjectType.QR); + // assert + expect(mockPopoverController.create).toHaveBeenCalled(); + }); + + it('should show Error alert popover, pass dialcode and source, on dismiss isLeftButtonClicked is not clicked', () => { + // arrange + const createMock = jest.spyOn(mockPopoverController, 'create').mockResolvedValue({ + present: jest.fn(() => Promise.resolve({})), + onDidDismiss: jest.fn(() => Promise.resolve({ data: {isLeftButtonClicked: false} })) + } as any); + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + // act + commonUtilService.afterOnBoardQRErrorAlert('Invalid QR code', 'sample_message', PageId.ONBOARDING_PROFILE_PREFERENCES, ObjectType.QR); + // assert + expect(mockPopoverController.create).toHaveBeenCalled(); + }); }); describe('showContentComingSoonAlert()', () => { @@ -269,6 +326,10 @@ describe('CommonUtilService', () => { it('should generate INTERACT telemetry with given source', (done) => { // arrange + const callback = { + tryAgain: jest.fn(), + cancel: jest.fn() + } as QRAlertCallBack const createMock = jest.spyOn(mockPopoverController, 'create').mockResolvedValue({ present: jest.fn(() => Promise.resolve({})), onDidDismiss: jest.fn(() => Promise.resolve({ data: undefined })), @@ -276,7 +337,7 @@ describe('CommonUtilService', () => { } as any); // const createMock = jest.spyOn(mockPopoverController, 'create'); // act - commonUtilService.showContentComingSoonAlert('permission', 'dial_code').then(() => { + commonUtilService.showContentComingSoonAlert(PageId.PERMISSION, 'dial_code', 'Qr').then(() => { // assert expect(mockPopoverController.create).toHaveBeenCalled(); expect(createMock.mock.calls[0][0]['component']).toEqual(QRScannerAlert); @@ -288,6 +349,30 @@ describe('CommonUtilService', () => { }); }); + it('should generate INTERACT telemetry with given source', (done) => { + // arrange + const createMock = jest.spyOn(mockPopoverController, 'create').mockResolvedValue({ + present: jest.fn(() => Promise.resolve({})), + onDidDismiss: jest.fn(() => Promise.resolve({ data: undefined })), + dismiss: jest.fn(() => Promise.resolve({})) + } as any); + const callback: QRAlertCallBack = { + tryAgain: jest.fn(), + cancel: jest.fn() + } + // act + commonUtilService.showContentComingSoonAlert(PageId.ONBOARDING_PROFILE_PREFERENCES, 'dial_code', 'Qr').then(() => { + // assert + expect(mockPopoverController.create).toHaveBeenCalled(); + expect(createMock.mock.calls[0][0]['component']).toEqual(SbGenericPopoverComponent); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith(InteractType.OTHER, + InteractSubtype.QR_CODE_COMINGSOON, + Environment.ONBOARDING, + 'profile-settings'); + done(); + }); + }); + it('should generate INTERACT telemetry if source is not provided', (done) => { // arrange const createMock = jest.spyOn(mockPopoverController, 'create').mockResolvedValue({ @@ -399,6 +484,36 @@ describe('CommonUtilService', () => { state: { type: 'state', name: 'Odisha' } }); }); + + it('should return default location if organisation has no location details', () => { + // arrange + const organisation = { + locations: '' + }; + // act + // assert + expect(commonUtilService.getOrgLocation(organisation)).toEqual( + { + block: '', + district: '', + state: '' + }); + }); + + it('should return default location if organisation has no location length', () => { + // arrange + const organisation = { + locations: [{}] + }; + // act + // assert + expect(commonUtilService.getOrgLocation(organisation)).toEqual( + { + block: '', + district: '', + state: '' + }); + }); }); describe('getUserLocation()', () => { @@ -418,6 +533,17 @@ describe('CommonUtilService', () => { state: { type: 'state', name: 'Odisha' } }); }); + + it('should return user location and handle if profile has no userlocation', () => { + // arrange + const profile = { + userLocations: [] + }; + // act + // assert + expect(commonUtilService.getUserLocation(profile)).toEqual( + {}); + }); }); describe('isUserLocationAvalable()', () => { @@ -452,7 +578,8 @@ describe('CommonUtilService', () => { const profile = { userLocations: [ { type: 'state', name: 'Odisha' }, - ] + ], + serverProfile: {} }; // act // assert @@ -538,6 +665,29 @@ describe('CommonUtilService', () => { done(); }, 0); }); + + it('should return true if IP location is available, if no data on get device location', (done) => { + // arrange + const profile = { + board: ['AP'], medium: ['English', 'Hindi', 'Bengali'], + grade: ['class 8', 'class9', 'class10'], profileType: 'teacher' + } as any; + mockProfileService.getActiveSessionProfile = jest.fn(() => of(profile)); + mockSharedPreferences.getString = jest.fn((arg) => of(undefined)); + FCMPlugin.unsubscribeFromTopic = jest.fn((_, resolve, reject) => resolve()); + FCMPlugin.subscribeToTopic = jest.fn((_, resolve, reject) => resolve()); + mockSharedPreferences.putString = jest.fn(() => of(undefined)); + // act + commonUtilService.handleToTopicBasedNotification(); + // assert + setTimeout(() => { + expect(mockProfileService.getActiveSessionProfile).toHaveBeenCalled(); + expect(mockSharedPreferences.getString).toHaveBeenNthCalledWith(1, PreferenceKey.DEVICE_LOCATION); + expect(mockSharedPreferences.getString).toHaveBeenNthCalledWith(2, PreferenceKey.SUBSCRIBE_TOPICS); + expect(mockSharedPreferences.putString).toHaveBeenCalled(); + done(); + }, 0); + }); }); describe('getFormattedDate', () => { @@ -597,6 +747,17 @@ describe('CommonUtilService', () => { }); }); + it('should return the state list is undefiend', (done) => { + // arrange + mockProfileService.searchLocation = jest.fn(() => of(undefined)); + // act + commonUtilService.getStateList().then((res) => { + // assert + expect(res).toEqual([]); + done(); + }); + }); + it('should return empty state list', (done) => { // arrange mockProfileService.searchLocation = jest.fn(() => throwError(new Error())); @@ -625,9 +786,9 @@ describe('CommonUtilService', () => { it('should return the district list with state code', (done) => { // arrange const code = 'state_code'; - mockProfileService.searchLocation = jest.fn(() => of([])); + mockProfileService.searchLocation = jest.fn(() => of('')); // act - commonUtilService.getDistrictList(code).then((res) => { + commonUtilService.getDistrictList('', code).then((res) => { // assert expect(res).toEqual([]); done(); @@ -709,6 +870,62 @@ describe('CommonUtilService', () => { done(); }, 0); }); + + it('should return yes-clicked telemetry', (done) => { + // arrange + commonUtilService = new CommonUtilService( + mockSharedPreferences as SharedPreferences, + mockProfileService as ProfileService, + mockTelemetryService as TelemetryService, + mockTranslateService as TranslateService, + mockLoadingController as LoadingController, + mockEvents as Events, + mockPopoverController as PopoverController, + mockNetwork as Network, + mockNgZone as NgZone, + mockPlatform as Platform, + mockTelemetryGeneratorService as TelemetryGeneratorService, + mockWebView as WebView, + mockAppversion as AppVersion, + mockRouter as Router, + mockToastController as ToastController, + mockPermissionService as AndroidPermissionsService, + mockComingSoonMessageService as ComingSoonMessageService + ); + mockPopoverController.create = jest.fn(() => Promise.resolve({ + present: jest.fn(() => Promise.resolve({})), + onDidDismiss: jest.fn(() => Promise.resolve({ data: {isLeftButtonClicked: true} })), + dismiss: jest.fn(() => Promise.resolve({})) + }) as any); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + mockNetwork.onChange = jest.fn(() => of([{ type: 'online' }])); + navigator['app'] = { + exitApp: jest.fn() + } as any + // act + commonUtilService.showExitPopUp('library', 'home', false); + // assert + setTimeout(() => { + expect(mockPopoverController.create).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith(InteractType.TOUCH, + InteractSubtype.YES_CLICKED, + 'home', + 'library'); + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should handle if u have alert and dismiss ', () => { + // arrange + commonUtilService['alert'] = { + dismiss: jest.fn() + } + // act + commonUtilService.showExitPopUp('library', 'home', false); + // assert + }) }); describe('handleAssessmentStatus()', () => { @@ -724,44 +941,58 @@ describe('CommonUtilService', () => { // assert expect(commonUtilService.showToast).toHaveBeenCalled(); }); - it('should show last attempt available popup and on click of continue return false', () => { // arrange const assessmentStatus = { isContentDisabled: false, isLastAttempt: true }; - commonUtilService.showAssessmentLastAttemptPopup = jest.fn(() => Promise.resolve({ - isLastAttempt: true, - limitExceeded: false, - isContentDisabled: false, - })); // act commonUtilService.handleAssessmentStatus(assessmentStatus); // assert - expect(commonUtilService.showAssessmentLastAttemptPopup).toHaveBeenCalled(); }); - + it('should return false if the assessment is available to play directly', () => { // arrange const assessmentStatus = { isContentDisabled: false, isLastAttempt: false }; - commonUtilService.showAssessmentLastAttemptPopup = jest.fn(() => Promise.resolve({ - isContentDisabled: false, - isLastAttempt: false, - limitExceeded: false, - })); commonUtilService.showToast = jest.fn(); // act commonUtilService.handleAssessmentStatus(assessmentStatus); // assert - expect(commonUtilService.showAssessmentLastAttemptPopup).not.toHaveBeenCalled(); expect(commonUtilService.showToast).not.toHaveBeenCalled(); - + }); + }); + describe('showAssessmentLastAttemptPopup', () => { + it('should show assessment popup', () => { + // arange + mockPopoverController.create = jest.fn(() => (Promise.resolve({ + present: jest.fn(() => Promise.resolve()), + onDidDismiss: jest.fn(() => Promise.resolve({canDelete: true})) + })))as any; + // act + commonUtilService.showAssessmentLastAttemptPopup({isCloseButtonClicked: false}); + // assert + setTimeout(() => { + }, 0); + }) + + it('should show assessment popup on dismiss delete is not allowed', () => { + // arange + mockPopoverController.create = jest.fn(() => (Promise.resolve({ + present: jest.fn(() => Promise.resolve()), + onDidDismiss: jest.fn(() => Promise.resolve({canDelete: false})) + })))as any; + // act + commonUtilService.showAssessmentLastAttemptPopup({isCloseButtonClicked: false}); + // assert + setTimeout(() => { + }, 0); + }) }); describe('fetchPrimaryCategory', () => { @@ -806,4 +1037,383 @@ describe('CommonUtilService', () => { }, 0); }); }); + + describe('convertFileToBase64', () => { + it('should convert file to base64 ', (done) => { + // arrange + fetch = jest.fn(() => { jest.fn(); }) as any + let file = "assets/imgs/ic_launcher.png" + const sub = new Subject(); + sub.next = jest.fn() + sub.complete = jest.fn() + sub.asObservable = jest.fn() + const reader = new FileReader(); + reader.onload = jest.fn(() => ({result: ''})) + reader.readAsDataURL = jest.fn() + // act + commonUtilService.convertFileToBase64(file); + // assert + done(); + }) + }); + + describe('openLink', () => { + it('should openLink ', () => { + // arrange + const url = ''; + // act + commonUtilService.openLink(url); + // assert + }) + }) + + describe('openUrlInBrowser', () => { + it('should openUrlInBrowser ', () => { + // arrange + const url = ''; + const options = 'hardwareback=yes,clearcache=no,zoom=no,toolbar=yes,disallowoverscroll=yes'; + window.cordova['InAppBrowser'].open = jest.fn(); + // act + commonUtilService.openUrlInBrowser(url); + // assert + expect(window.cordova['InAppBrowser'].open).toHaveBeenCalledWith(url, '_blank', options); + }) + }) + + describe('getAppDirection', () => { + it('should getAppDirection ', () => { + // arrange + mockPlatform['isRTL'] = jest.fn(() => true); + // act + commonUtilService.getAppDirection(); + // assert + }) + + it('should getAppDirection for isRTL false', () => { + // arrange + mockPlatform['isRTL'] = jest.fn(() => false); + // act + commonUtilService.getAppDirection(); + // assert + }) + }); + + describe('setGoogleCaptchaConfig', () => { + it('should set googlde captcha config ', () => { + // arrange + // act + commonUtilService.setGoogleCaptchaConfig('key', true); + // assert + }) + }) + + describe('getGoogleCaptchaConfig', () => { + it('shoul get google captchpa ', () => { + // arrange + // act + commonUtilService.getGoogleCaptchaConfig(); + // assert + }) + }) + + describe('isAccessibleForNonStudentRole', () => { + it('should handle accessible for non student role ', () => { + // arrange + // act + commonUtilService.isAccessibleForNonStudentRole(ProfileType.ADMIN); + // arrange + }) + + it('should handle accessible for non student role, handle for parent ', () => { + // arrange + // act + commonUtilService.isAccessibleForNonStudentRole(ProfileType.PARENT); + // arrange + }) + }) + + describe('getGivenPermissionStatus', () => { + it('should getGivenPermissionStatus', () => { + // arrange + mockPermissionService.checkPermissions = jest.fn(() => of([])); + // act + commonUtilService.getGivenPermissionStatus(AndroidPermission.CAMERA) + // assert + }) + }) + + describe('showSettingsPageToast', () => { + it('should showSettingsPageToast ', () => { + // arrange + const toastController = { + message: commonUtilService.translateMessage('description', 'sunbird'), + cssClass: 'permissionSettingToast', + buttons: [ + { + text: commonUtilService.translateMessage('SETTINGS'), + role: 'cancel', + handler: () => { } + } + ], + position: 'bottom', + duration: 3000 + } + mockToastController.create = jest.fn((toastController) => (Promise.resolve({ + present: jest.fn(() => Promise.resolve({})), + onWillDismiss: jest.fn(() => Promise.resolve({role: 'cancel'})) + } as any))) + mockRouter.navigate = jest.fn(); + // act + commonUtilService.showSettingsPageToast('description', 'sunbird', 'common-util', true); + // assert + }) + + it('should showSettingsPageToast if on boarding false', () => { + // arrange + mockToastController.create = jest.fn(() => (Promise.resolve({ + present: jest.fn(() => Promise.resolve({})), + onWillDismiss: jest.fn(() => Promise.resolve({role: 'cancel'})) + } as any))) + mockRouter.navigate = jest.fn(); + // act + commonUtilService.showSettingsPageToast('description', 'sunbird', 'common-util', false); + // assert + }) + + it('should showSettingsPageToast, if no role on dismiss', () => { + // arrange + mockToastController.create = jest.fn(() => (Promise.resolve({ + present: jest.fn(() => Promise.resolve({})), + onWillDismiss: jest.fn(() => Promise.resolve({role: ''})) + } as any))) + mockRouter.navigate = jest.fn(); + // act + commonUtilService.showSettingsPageToast('description', 'sunbird', 'common-util', false); + // assert + }) + }) + + describe('buildPermissionPopover', () => { + it('should buildPermissionPopover ', () => { + // arrange + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn() + // act + commonUtilService.buildPermissionPopover(()=> '', 'sunbird', 'Camera', 'allow', 'common-util', true); + // assert + setTimeout(() => { + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith(ImpressionType.CAMERA, + 'common-util', + PageId.PERMISSION_POPUP, + Environment.HOME); + }, 0); + }) + + it('should buildPermissionPopover, if permission is not camera and onboaromng is not completed', () => { + // arrange + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn() + // act + commonUtilService.buildPermissionPopover(()=> '', 'sunbird', 'file', 'allow', 'common-util', false); + // assert + setTimeout(() => { + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith(ImpressionType.FILE_MANAGEMENT, + 'common-util', + PageId.PERMISSION_POPUP, + Environment.ONBOARDING); + }, 0); + }) + }); + + describe('extractInitial', () => { + it('should extractInitial and return initial as empty string if no name', () => { + // arrange + // act + commonUtilService.extractInitial('') + // assert + }) + + it('should extractInitial from name and split ', () => { + // arrange + const name = "sample_name" + const splitter = new GraphemeSplitter(); + splitter.splitGraphemes = jest.fn(() => []) + + // act + commonUtilService.extractInitial(name) + // assert + }) + }); + + describe('populateGlobalCData', () => { + it('should populateGlobalCData', () => { + // arrange + // act + commonUtilService.populateGlobalCData(); + // assert + }) + }); + + describe('setRatingStarAriaLabel', () => { + it('should setRatingStarAriaLabel ', () => { + // arrange + const domTag = [ + {children: [ + {setAttribute: jest.fn(() => {})} + ]} + ]; + // act + commonUtilService.setRatingStarAriaLabel(domTag); + // assert + }) + + it('shopuld setRatingStarAriaLabel rating > 0', () => { + // arrange + const domTag = [ + {children: [ + {setAttribute: jest.fn(() => {})} + ]} + ]; + // act + commonUtilService.setRatingStarAriaLabel(domTag, 3); + // assert + }) + + it('should setRatingStarAriaLabel for inner children tags', () => { + // arrange + const domTag = [ + {children: [ + { + setAttribute: jest.fn(() => {}), + children:[ + {setAttribute: jest.fn(() => {}), + shadowRoot: { + querySelector: jest.fn(() => ({ + setAttribute: jest.fn(() => {}) + })) + }} + ]} + ]} + ] + // act + commonUtilService.setRatingStarAriaLabel(domTag); + // assert + }) + + it('should setRatingStarAriaLabel for inner children tags else case if no query selector button', () => { + // arrange + const domTag = [ + {children: [ + { + setAttribute: jest.fn(() => {}), + children:[ + {setAttribute: jest.fn(() => {}), + shadowRoot: { + querySelector: jest.fn() + }} + ]} + ]} + ] + // act + commonUtilService.setRatingStarAriaLabel(domTag); + // assert + }) + + it('shopuld handle setRatingStarAriaLabel, if no ratingDOMtag ', () => { + // arrange + // act + commonUtilService.setRatingStarAriaLabel([]); + // assert + }) + }); + + describe('getPlatformBasedActiveElement', () => { + it('shopuld getPlatformBasedActiveElement return active element', () => { + // arrange + window.document = { + getElementById: jest.fn(() => ({setAttribute: jest.fn(), focus: jest.fn()})) as any, + activeElement: { + shadowRoot: null + } + } as any + // act + commonUtilService.getPlatformBasedActiveElement(); + // assert + }) + + it('shopuld getPlatformBasedActiveElement check platfrom and return childe node of active element', () => { + // arrange + window.document = { + activeElement: { + shadowRoot: { + childNodes: [{}] + } + } + } as any + mockPlatform.is = jest.fn(platform => platform == "android"); + // act + commonUtilService.getPlatformBasedActiveElement(); + // assert + }) + }); + + describe('popupAccessibilityFocus', () => { + it('should popupAccessibilityFocus ', () => { + // arrange + const element = {setAttribute: jest.fn(), focus: jest.fn()} as any + window.setTimeout = jest.fn((fn) => fn({ + }), 0) as any; + // act + commonUtilService.popupAccessibilityFocus(element); + // assert + }) + }) + + describe('addPopupAccessibility', ()=>{ + it('Should add the accessibilty to the toast popup', ()=>{ + // arrange + commonUtilService['popupAccessibilityFocus'] = jest.fn(); + commonUtilService['getPlatformBasedActiveElement'] = jest.fn(() => {}) as any; + mockPlatform.is = jest.fn(platform => platform == "android"); + + const toast = { + present: jest.fn(), + addEventListener: jest.fn(), + onDidDismiss: jest.fn(()=>Promise.resolve()), + setAttribute: jest.fn() + } + window.document = { + getElementById: jest.fn(() => ({setAttribute: jest.fn(), focus: jest.fn()})) as any, + activeElement: { + shadowRoot: { + childNodes: [{}] + } + } + } as any + mockPlatform.is = jest.fn(platform => platform=="android"); + // act + commonUtilService.addPopupAccessibility(toast, 'message', 'sb-generic-toast'); + // assert + expect(toast.setAttribute).toHaveBeenCalled(); + }); + + it('Should add the accessibilty to the toast popup', ()=>{ + // arrange + commonUtilService['popupAccessibilityFocus'] = jest.fn(); + commonUtilService['getPlatformBasedActiveElement'] = jest.fn(); + mockPlatform.is = jest.fn(platform => platform == "android"); + + const toast = { + present: jest.fn(), + addEventListener: jest.fn((_, fn) => { + fn({setTimeout: jest.fn(fn => fn())}) + }), + onDidDismiss: jest.fn(()=>Promise.resolve()), + setAttribute: jest.fn() + } + // act + commonUtilService.addPopupAccessibility(toast, 'message'); + // assert + expect(toast.setAttribute).toHaveBeenCalled(); + }); + + }); }); diff --git a/src/services/common-util.service.ts b/src/services/common-util.service.ts index e1f5434958..d1211ee4a9 100644 --- a/src/services/common-util.service.ts +++ b/src/services/common-util.service.ts @@ -810,4 +810,18 @@ export class CommonUtilService { }); return guestProfile; } + + // Used to convert file to base png, updated function to handle default image in consumption library. + public async convertFileToBase64(file): Promise> { + let res = await fetch(file); + let blob = await res.blob(); + return new Observable(res => { + const reader = new FileReader(); + reader.onload = () => { + res.next(reader.result as string); + res.complete(); + } + reader.readAsDataURL(blob); + }); + } } diff --git a/src/services/content/content-aggregator-handler.service.spec.ts b/src/services/content/content-aggregator-handler.service.spec.ts index e0d8e4bf02..9081fc67d5 100644 --- a/src/services/content/content-aggregator-handler.service.spec.ts +++ b/src/services/content/content-aggregator-handler.service.spec.ts @@ -16,6 +16,7 @@ describe('ContentAggregatorHandler', () => { const mockcourseService: Partial = {}; const mockformService: Partial = {}; const mockprofileService: Partial = {}; + window.console.error = jest.fn() beforeAll(() => { contentAggregatorHandler = new ContentAggregatorHandler( @@ -491,5 +492,12 @@ describe('ContentAggregatorHandler', () => { contentAggregatorHandler.populateIcons([{dataSrc: {type:'Some_type'}, data: {sections: [{contents: [{cardImg: '', courseLogoUrl: "", appIcon: "", content: {appIcon: ""}}]}]}}]); // assert }) + + it('should populateIcons for data src type is some other if no content appicon', () => { + // arrange + // act + contentAggregatorHandler.populateIcons([{dataSrc: {type:'TRACKABLE_COLLECTIONS'}, data: {sections: [{contents: [{cardImg: "", appIcon: ""}]}]}}]); + // assert + }) }) }); diff --git a/src/services/content/content-share-handler.service.spec.ts b/src/services/content/content-share-handler.service.spec.ts index 99c3197581..26254e1e51 100644 --- a/src/services/content/content-share-handler.service.spec.ts +++ b/src/services/content/content-share-handler.service.spec.ts @@ -34,6 +34,7 @@ describe('ContentShareHandlerService', () => { const mockPlatform: Partial = { is: jest.fn(platform => platform === 'ios') }; + window.console.error = jest.fn() beforeAll(() => { contentShareHandlerService = new ContentShareHandlerService( @@ -664,6 +665,106 @@ describe('ContentShareHandlerService', () => { done(); }, 0); }); + + it('should save content file on device for ios platform exportContent', (done) => { + // arrange + mockPlatform.is = jest.fn((platform) => platform === "ios"); + let shareParams = { + byLink: false, + byFile: false, + saveFile: true + }; + const content: Partial = { + identifier: 'do_id', + contentType: 'contentType', + contentData: { + contentType: 'contentType', + } + }; + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, + })); + const contentExportResponse = { + exportedFilePath: 'samplepath' + }; + // shareParams.byFile = true; + mockContentService.exportContent = jest.fn(() => of(contentExportResponse)); + const values = new Map(); + values['ContentType'] = content.contentData.contentType; + mockCommonUtilService.showToast = jest.fn(); + + // act + contentShareHandlerService.shareContent(shareParams, content as Content, undefined, [], [], { l1: 'do_id' }, + PageId.CONTENT_DETAIL); + + // assert + setTimeout(() => { + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(1, + InteractType.TOUCH, InteractSubtype.SHARE_CONTENT_INITIATED, + Environment.HOME, PageId.CONTENT_DETAIL, + { + id: 'do_id', type: 'contentType', version: '' + }, + values, { l1: 'do_id' }, []); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(2, + InteractType.OTHER, InteractSubtype.SHARE_CONTENT_SUCCESS, + Environment.HOME, PageId.CONTENT_DETAIL, + { + id: 'do_id', type: 'contentType', version: '' + }, + values, { l1: 'do_id' }, []); + expect(presentFn).toHaveBeenCalled(); + expect(dismissFn).toHaveBeenCalled(); + expect(mockContentService.exportContent).toHaveBeenCalledWith( + { contentIds: ['do_id'], destinationFolder: 'undefinedDownload/', saveLocally: true, subContentIds: [] } + ); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('FILE_SAVED', '', 'green-toast'); + done(); + }, 0); + }); + + it('should save content file on device for ios platform exportContent', (done) => { + // arrange + mockPlatform.is = jest.fn((platform) => platform === "ios"); + const shareParams = { + byLink: false, + byFile: false, + saveFile: false + }; + const content: Partial = { + identifier: 'do_id', + contentType: 'contentType', + contentData: { + contentType: 'contentType', + } + }; + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, + })); + const contentExportResponse = { + exportedFilePath: 'samplepath' + }; + mockContentService.exportContent = jest.fn(() => of(contentExportResponse)); + const values = new Map(); + values['ContentType'] = content.contentData.contentType; + mockCommonUtilService.showToast = jest.fn(); + + // act + contentShareHandlerService.shareContent(shareParams, content as Content, undefined, [], [], { l1: 'do_id' }, + PageId.CONTENT_DETAIL); + // assert + setTimeout(() => { + done(); + }, 0); + }); }); }); diff --git a/src/services/content/player/content-player-handler.spec.ts b/src/services/content/player/content-player-handler.spec.ts index eefa0a5480..222953d8af 100644 --- a/src/services/content/player/content-player-handler.spec.ts +++ b/src/services/content/player/content-player-handler.spec.ts @@ -22,7 +22,8 @@ describe('ContentPlayerHandler', () => { readJSON: jest.fn(() => Promise.resolve({})) }; const mockFile: Partial = { - checkFile: jest.fn(() => Promise.resolve(true)) + checkFile: jest.fn(() => Promise.resolve(true)), + readAsText: jest.fn(() => Promise.resolve("")) }; const mockTelemetryGeneratorService: Partial = { generateInteractTelemetry: jest.fn() @@ -41,6 +42,7 @@ describe('ContentPlayerHandler', () => { const mockAppHeaderService: Partial = {}; const mockUtilityService: Partial = {}; + window.console.error = jest.fn() beforeAll(() => { contentPlayerHandler = new ContentPlayerHandler( @@ -81,7 +83,54 @@ describe('ContentPlayerHandler', () => { }); describe('launchContentPlayer()', () => { + it('should check compatibilityLevel and check for immediate update', () => { + // arrange + const mockContent = {identifier : 'do_212936404296335360119' , + contentData : { + ownershipType : [ + 'createdBy' + ], + totalQuestions: 10, + mimeType : 'application/vnd.sunbird.questionset', + gradeLevel : [ + 'Class 9' , + 'Class 10 ' + ], + version : 2, + streamingUrl : ' https://ntpstagingall.blob.core.windows.net/ntp-content-staging/content/ecml/do_212936404296335360119-latest' , + medium : [ + 'English' , + 'Hindi' + ], + resourceType : 'Teach', + compatibilityLevel: '7' + }, + isUpdateAvailable : false, + mimeType : 'application/vnd.ekstep.ecml-archive' , + basePath : '/_app_file_' , + contentType : 'resource' , + isAvailableLocally : false, + rollup : { + l1 : 'do_212936404296335360119' + } + } + cordova.plugins = { + InAppUpdateManager: { + checkForImmediateUpdate: jest.fn((fn, fn1) => { + fn(), + fn1() + }) + } + } + mockUtilityService.getBuildConfigValue = jest.fn(() => Promise.resolve('5')); + // act + contentPlayerHandler.launchContentPlayer(mockContent, true, true, { course: mockCourse , + correlationList: [{id: '123456789', type: 'API' }]} as ContentInfo, true).then(() => { + }); + // assert + }); + it('should disbale the user switcher if content is being played from course', () => { // arrange mockUtilityService.getBuildConfigValue = jest.fn(() => Promise.resolve('5')); @@ -138,6 +187,7 @@ describe('ContentPlayerHandler', () => { it('should launch the content player if is Streaming false and index.ecml is not available', (done) => { // arrange mockUtilityService.getBuildConfigValue = jest.fn(() => Promise.resolve('5')); + // mockPlayerConfigData.metadata.mimeType = 'video/mp4'; mockFile.checkFile = jest.fn(() => Promise.reject()); // act contentPlayerHandler.launchContentPlayer(mockContent, false, true, { course: mockCourse } as any, false); @@ -154,6 +204,7 @@ describe('ContentPlayerHandler', () => { mockFile.checkFile = jest.fn(() => Promise.reject()); mockUtilityService.getBuildConfigValue = jest.fn(() => Promise.resolve('5')); mockCanvasPlayerService.readJSON = jest.fn(() => Promise.reject()); + mockFile.readAsText = jest.fn(() => Promise.reject()); // act contentPlayerHandler.launchContentPlayer(mockContent, false, true, { course: mockCourse } as any, false); // assert @@ -178,6 +229,135 @@ describe('ContentPlayerHandler', () => { done(); }, 0); }); + + it('should launch the content player for mimetype video/mp4', (done) => { + // arrange + mockUtilityService.getBuildConfigValue = jest.fn(() => Promise.resolve('5')); + mockPlayerConfigData.metadata.mimeType = 'video/mp4'; + mockPlayerService.getPlayerConfig = jest.fn(() => of(mockPlayerConfigData)); + // act + contentPlayerHandler.launchContentPlayer(mockContent, true, true, {course: {courseId: 'do_1234'}} as any, true, true, false, { isLastAttempt: true, isContentDisabled: false, currentAttempt: 2, maxAttempts: 5 }, jest.fn()).then(() => { + }); + // assert + setTimeout(() => { + done(); + }, 0); + }); + + it('should launch the content player for mimetype video/webm', (done) => { + // arrange + mockUtilityService.getBuildConfigValue = jest.fn(() => Promise.resolve('5')); + mockPlayerConfigData.metadata.mimeType = 'video/webm'; + mockPlayerService.getPlayerConfig = jest.fn(() => of(mockPlayerConfigData)); + // act + contentPlayerHandler.launchContentPlayer(mockContent, true, true, {course: {courseId: 'do_1234'}} as any, true, true, false, { isLastAttempt: true, isContentDisabled: false, currentAttempt: 2, maxAttempts: 5 }, jest.fn()).then(() => { + }); + // assert + setTimeout(() => { + done(); + }, 0); + }); + + it('should disbale the user switcher if content is being played from course', () => { + // arrange + const mockContent = {identifier : 'do_212936404296335360119' , + contentData : { + ownershipType : [ + 'createdBy' + ], + mimeType : 'application/vnd.sunbird.questionset', + gradeLevel : [ + 'Class 9' , + 'Class 10 ' + ], + version : 2, + streamingUrl : ' https://ntpstagingall.blob.core.windows.net/ntp-content-staging/content/ecml/do_212936404296335360119-latest' , + medium : [ + 'English' , + 'Hindi' + ], + resourceType : 'Teach', + }, + isUpdateAvailable : false, + mimeType : 'application/vnd.sunbird.questionset' , + basePath : '/_app_file_' , + contentType : 'resource' , + isAvailableLocally : false, + rollup : { + l1 : 'do_212936404296335360119' + } + } + const mockPlayerConfigData = { + metadata : mockContent, + config : { + showEndPage : false, + endPage : [ + { + template : 'assessment' , + contentType : [ + 'SelfAssess' + ] + } + ], + splash : { + webLink : '' , + text : '' , + icon : '' , + bgImage : ' assets/icons/splacebackground_1.png' + }, + overlay : { + enableUserSwitcher : true, + showUser : false + }, + plugins : [ + { + id : 'org.sunbird.player.endpage' , + ver : '1.1' , + type : 'plugin' + } + ] + }, + context : { + did : 'ef37fc07aee31d87b386a408e0e4651e00486618' , + origin : 'https://staging.ntp.net.in' , + pdata : { + id : 'staging.sunbird.app' , + pid : 'sunbird.app' , + ver : '2.7.197staging-debug' + }, + objectRollup : { + l1 : 'do_212936404296335360119' + }, + sid : 'e33e1b95-e6e5-400f-b438-35df4b65ee73' , + actor : { + type : 'User' , + id : '7ebe9375-425e-4325-ba39-eac799871ed4' + }, + deeplinkBasePath : '' , + cdata : [ + { + id : ' 29c9c790-3845-11ea-8647-8b09062a2e3d' , + type : 'API' + } + ], + channel : '505c7c48ac6dc1edc9b08f21db5a571d' + }, + appContext : { + local : true, + server : false, + groupId : '' + }, + data : {}, + uid : '7ebe9375-425e-4325-ba39-eac799871ed4' + } + mockUtilityService.getBuildConfigValue = jest.fn(() => Promise.resolve('5')); + mockPlayerService.getPlayerConfig = jest.fn(() => of(mockPlayerConfigData)); + + // act + contentPlayerHandler.launchContentPlayer(mockContent, true, true, {course: {courseId: 'do_1234'}} as any, true, true, false, { isLastAttempt: true, isContentDisabled: false, currentAttempt: 2, maxAttempts: 5 }).then(() => { + }); + // assert + }); }); describe('playContent', () => { @@ -266,7 +446,7 @@ describe('ContentPlayerHandler', () => { corRelationList: [] }; mockAppHeaderService.hideHeader = jest.fn(); - jest.spyOn(ContentUtil, 'getTelemetryObject').mockReturnThis(); + jest.spyOn(ContentUtil, 'getTelemetryObject').mockReturnValue(undefined); jest.spyOn(ContentUtil, 'generateRollUp').mockReturnThis(); mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); contentPlayerHandler.launchContentPlayer = jest.fn(); diff --git a/src/services/content/player/content-player-handler.ts b/src/services/content/player/content-player-handler.ts index be476eb04b..5c4eb4a82d 100644 --- a/src/services/content/player/content-player-handler.ts +++ b/src/services/content/player/content-player-handler.ts @@ -104,7 +104,7 @@ export class ContentPlayerHandler { const filePath = this.commonUtilService.convertFileSrc(`${data.metadata.basePath}`); if (!isStreaming) { this.file.checkFile(`file://${data.metadata.basePath}/`, 'index.ecml').then((isAvailable) => { - this.canvasPlayerService.xmlToJSon(`${filePath}/index.ecml`).then((json) => { + this.canvasPlayerService.xmlToJSon(`file://${data.metadata.basePath}/`, 'index.ecml').then((json) => { data['data'] = JSON.stringify(json); this.router.navigate([RouterLinks.PLAYER], { state: { config: data, course : contentInfo.course, navigateBackToContentDetails, isCourse } }); @@ -114,15 +114,15 @@ export class ContentPlayerHandler { }); }).catch((err) => { console.error('err', err); - this.canvasPlayerService.readJSON(`${filePath}/index.json`).then((json) => { - data['data'] = json; + this.file.readAsText(`file://${data.metadata.basePath}/`, 'index.json').then((response)=> { + data['data'] = response; this.router.navigate([RouterLinks.PLAYER], { state: { config: data, course : contentInfo.course, navigateBackToContentDetails, corRelation: contentInfo.correlationList, isCourse } }); - }).catch((e) => { - console.error('readJSON error', e); - }); + console.error('readAsText error', e); + }) + }); } else { this.router.navigate([RouterLinks.PLAYER], diff --git a/src/services/formandframeworkutil.service.ts b/src/services/formandframeworkutil.service.ts index c34839360c..f9649ff929 100644 --- a/src/services/formandframeworkutil.service.ts +++ b/src/services/formandframeworkutil.service.ts @@ -756,15 +756,16 @@ export class FormAndFrameworkUtilService { } getFrameworkCategoryList(userType?: string): Promise { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { if (!userType) { - this.preferences.getString(PreferenceKey.SELECTED_USER_TYPE).toPromise().then((type) => { + await this.preferences.getString(PreferenceKey.SELECTED_USER_TYPE).toPromise().then((type) => { userType = type; }); } const framework = this.appGlobalService.getCachedFrameworkCategory(); console.log('................', framework); - if (Object.keys(framework).length === 0 || (Object.keys(framework).length > 0 && framework.userType !== userType)) { + if (Object.keys(framework).length === 0 || (Object.keys(framework).length > 0 && + (framework.userType !== userType || !userType))) { this.invokeFrameworkCategoriesFormApi(userType).then((res) => { resolve(res); }); diff --git a/src/services/group/group-handler.service.spec.ts b/src/services/group/group-handler.service.spec.ts index a0020d1882..34a2463779 100644 --- a/src/services/group/group-handler.service.spec.ts +++ b/src/services/group/group-handler.service.spec.ts @@ -13,7 +13,7 @@ describe('GroupHandlerService', () => { const mockCommonUtilService: Partial = {}; const mockTelemetryGeneratorService: Partial = {}; const mockLocation: Partial = {}; - + window.console.error = jest.fn(); beforeAll(() => { groupHandlerService = new GroupHandlerService( mockGroupService as GroupService, diff --git a/src/services/location-handler.ts b/src/services/location-handler.ts index e1a721297f..56cc968e16 100644 --- a/src/services/location-handler.ts +++ b/src/services/location-handler.ts @@ -215,7 +215,7 @@ export class LocationHandler { locations = schoolDetails; } else { const req: LocationSearchCriteria = { - from: CachedItemRequestSourceFrom.CACHE, + from: CachedItemRequestSourceFrom.SERVER, filters: locationFilter }; locations = await this.profileService.searchLocation(req).toPromise(); diff --git a/src/services/navigation-handler.service.ts b/src/services/navigation-handler.service.ts index f94439caac..1bca3f1f5a 100644 --- a/src/services/navigation-handler.service.ts +++ b/src/services/navigation-handler.service.ts @@ -80,7 +80,7 @@ export class NavigationService { }); } - navigateToEditPersonalDetails(profile, pageId) { + navigateToEditPersonalDetails(profile, pageId,payload?) { if (this.commonUtilService.networkInfo.isNetworkAvailable) { this.telemetryGeneratorService.generateInteractTelemetry( InteractType.TOUCH, @@ -92,7 +92,8 @@ export class NavigationService { state: { profile, isShowBackButton: true, - source: pageId + source: pageId, + payload } }; this.router.navigate([RouterLinks.DISTRICT_MAPPING], navigationExtras); diff --git a/src/services/print-pdf/print-pdf.service.spec.ts b/src/services/print-pdf/print-pdf.service.spec.ts index c378319a11..9fcfeec0fc 100644 --- a/src/services/print-pdf/print-pdf.service.spec.ts +++ b/src/services/print-pdf/print-pdf.service.spec.ts @@ -43,7 +43,7 @@ describe('PrintPdfService', () => { }); mockCommonUtilService.showToast = jest.fn(() => { }); mockTransfer.create = jest.fn(() => ({ - download: mockDownload + download: mockDownload })) as any; window.cordova.plugins.printer.canPrintItem = jest.fn((_, cb) => { cb(true); }); window.cordova.plugins.printer.print = jest.fn(); @@ -56,7 +56,7 @@ describe('PrintPdfService', () => { }, 0) }) - it('cannott print pdf', (done) => { + it('should show toast for canprint false on print pdf', (done) => { // arrange const url = 'downloadUrl'; const mockPresent = jest.fn(() => Promise.resolve()); @@ -72,24 +72,25 @@ describe('PrintPdfService', () => { }); mockCommonUtilService.showToast = jest.fn(() => { }); mockTransfer.create = jest.fn(() => ({ - download: mockDownload + download: mockDownload })) as any; window.cordova.plugins.printer.canPrintItem = jest.fn((_, cb) => { cb(false); }); + window.cordova.plugins.printer.print = jest.fn(); // act printPdfService.printPdf(url); setTimeout(() => { expect(mockTransfer.create).toHaveBeenCalled(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_COULD_NOT_OPEN_FILE'); done() }, 0) }) - it('should catch error on transfer create', (done) => { + it('should handle error on print pdf', (done) => { // arrange const url = 'downloadUrl'; const mockPresent = jest.fn(() => Promise.resolve()); const mockDismiss = jest.fn(() => Promise.resolve()); const mockDownload = jest.fn(() => Promise.reject({ - error: () => 'ERROR_COULD_NOT_OPEN_FILE' })); mockCommonUtilService.getLoader = jest.fn(() => { return Promise.resolve({ @@ -99,13 +100,14 @@ describe('PrintPdfService', () => { }); mockCommonUtilService.showToast = jest.fn(() => { }); mockTransfer.create = jest.fn(() => ({ - download: mockDownload + download: mockDownload })) as any; // act printPdfService.printPdf(url); setTimeout(() => { expect(mockTransfer.create).toHaveBeenCalled(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_COULD_NOT_OPEN_FILE'); done() }, 0) }) -}); +}); \ No newline at end of file diff --git a/src/services/qrscanresulthandler-service.spec.ts b/src/services/qrscanresulthandler-service.spec.ts index 0ec6a4ca43..99e965eb48 100644 --- a/src/services/qrscanresulthandler-service.spec.ts +++ b/src/services/qrscanresulthandler-service.spec.ts @@ -65,6 +65,7 @@ describe('QRScannerResultHandler', () => { }; const mockCertificateService: Partial = {} const mockPopoverController: Partial = {}; + window.console.error = jest.fn() beforeAll(() => { qRScannerResultHandler = new QRScannerResultHandler( @@ -97,6 +98,32 @@ describe('QRScannerResultHandler', () => { describe('parseDialCode()', () => { + it('should return parsed data from the link else without channel', (done) => { + // arrange + const url = 'https://www.sunbirded.org/get/dial/ABCDEF/?channel='; + mockFormAndFrameworkUtilService.getDialcodeRegexFormApi = jest.fn(() => + Promise.resolve('(\\/dial\\/(?[a-zA-Z0-9]+)|(\\/QR\\/\\?id=(?[a-zA-Z0-9]+)))')); + // act + qRScannerResultHandler.parseDialCode(url); + // assert + setTimeout(() => { + done(); + }, 600); + }); + + it('should return parsed data from the link else without channel on error', (done) => { + // arrange + const url = 'https://www.sunbirded.org/get/dial/ABCDEF/?channel=""'; + mockFormAndFrameworkUtilService.getDialcodeRegexFormApi = jest.fn(() => + Promise.resolve('(\\/dial\\/(?[a-zA-Z0-9]+)|(\\/QR\\/\\?id=(?[a-zA-Z0-9]+)))')); + // act + qRScannerResultHandler.parseDialCode(url); + // assert + setTimeout(() => { + done(); + }, 600); + }); + it('should return parsed data from the link', (done) => { // arrange const url = 'https://www.sunbirded.org/get/dial/ABCDEF/?channel=ChannelId%20'; @@ -217,6 +244,37 @@ describe('QRScannerResultHandler', () => { [{id: 'sample//ABCD', type: 'Source'}] ); }); + + it('should return interact telemetry event, if network not available', () => { + // arrange + mockCommonUtilService.networkInfo = { + isNetworkAvailable: false + }; + qRScannerResultHandler.scannedUrlMap = { + sunbird1: 'app' + }; + const scannedData = 'sample/dial/ABCD'; + const action = {type: 'te'}; + const dialCode = 'ABCD'; + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + // act + qRScannerResultHandler.generateQRScanSuccessInteractEvent(scannedData, action, dialCode, + {certificateId: 'cr-id', scannedFrom: 'genericApp'}); + // assert + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( + InteractType.OTHER, + InteractSubtype.QRCodeScanSuccess, + Environment.HOME, + PageId.QRCodeScanner, + { + id: 'cr-id', + type: 'certificate', + version: undefined, + }, new Map(), + undefined, + [{id: 'sample//ABCD', type: 'Source'}] + ); + }); }); describe('handleDialCode()', () => { @@ -256,6 +314,37 @@ describe('QRScannerResultHandler', () => { PageId.QRCodeScanner, { id: 'ABCDEF', type: 'qr', version: ' ' }, corRelationData); }); + + it('should navigate to Search page if the scanned data is a dialocde link for else case', () => { + // arrange + const scannData = 'https://sunbirded.org/get/dial/ABCDEF'; + mockTelemetryGeneratorService.generateUtmInfoTelemetry = jest.fn(); + const params = {channel: 'igot', role: 'other'}; + mockTelemetryService.updateCampaignParameters = jest.fn(); + jest.spyOn(qRScannerResultHandler, 'generateQRScanSuccessInteractEvent').mockImplementation(() => { + return; + }); + const corRelationData: CorrelationData[] = [{ + id: CorReleationDataType.SCAN, + type: CorReleationDataType.ACCESS_TYPE + }]; + // act + qRScannerResultHandler.handleDialCode('', scannData, 'ABCDEF'); + // assert + setTimeout(() => { + + expect(mockTelemetryService.updateCampaignParameters).toHaveBeenCalled(); + const values = new Map(); + values['networkAvailable'] = 'N'; + values['scannedData'] = 'https://sunbirded.org/get/dial/ABCDEF'; + values['action'] = 'SearchResult'; + + expect(mockTelemetryGeneratorService.generateUtmInfoTelemetry).toHaveBeenCalledWith( + params, + PageId.QRCodeScanner, { id: 'do_12345', type: 'Learning Resource', version: '' }, + corRelationData); + }, 0); + }); }); describe('handleContentId()', () => { @@ -712,12 +801,78 @@ describe('QRScannerResultHandler', () => { }, 0); }); - it('should not match all criteria for else part', (done) => { - const request = 'course'; + it('should match all criteria for else case', (done) => { + const request = 'The quick brown fox jumps over the lazy dog'; mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([{ pattern: '(?fox)', + code: 'guest' + }])); + mockNavController.navigateForward = jest.fn(() => Promise.resolve(true)); + // act + qRScannerResultHandler.navigateHandler(request); + // assert + setTimeout(() => { + expect(mockFormAndFrameworkUtilService.getFormFields).toHaveBeenCalled(); + expect(mockNavController.navigateForward).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should not match all criteria for else part and deeplink config match', (done) => { + const request = 'course'; + mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([{ + pattern: '(?fox)', + code: 'profile' + }])); + mockAppglobalService.isUserLoggedIn = jest.fn(() => false); + mockNavController.navigateForward = jest.fn(() => Promise.resolve(true)); + // act + qRScannerResultHandler.navigateHandler(request); + // assert + setTimeout(() => { + expect(mockFormAndFrameworkUtilService.getFormFields).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should not match all criteria for else part and content_id pattern', (done) => { + const request = 'The quick brown fox jumps over the lazy dog'; + mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([{ + pattern: '(?fox)', code: 'profile' }])); + mockAppglobalService.isUserLoggedIn = jest.fn(() => false); + mockNavController.navigateForward = jest.fn(() => Promise.resolve(true)); + // act + qRScannerResultHandler.navigateHandler(request); + // assert + setTimeout(() => { + expect(mockFormAndFrameworkUtilService.getFormFields).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should not match all criteria for else part for course id pattern', (done) => { + const request = 'The quick brown fox jumps over the lazy dog'; + mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([{ + pattern: '(?fox)', + code: 'guest' + }])); + mockAppglobalService.isUserLoggedIn = jest.fn(() => true); + mockNavController.navigateForward = jest.fn(() => Promise.resolve(true)); + // act + qRScannerResultHandler.navigateHandler(request); + // assert + setTimeout(() => { + expect(mockFormAndFrameworkUtilService.getFormFields).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should not match all criteria for else part for course id pattern for undefined url match groups', (done) => { + const request = 'The quick brown fox jumps over the lazy dog'; + mockFormAndFrameworkUtilService.getFormFields = jest.fn(() => Promise.resolve([{ + }])); mockAppglobalService.isUserLoggedIn = jest.fn(() => true); mockNavController.navigateForward = jest.fn(() => Promise.resolve(true)); // act diff --git a/src/services/storage-permission/storage-permission-handler.service.spec.ts b/src/services/storage-permission/storage-permission-handler.service.spec.ts index 2aa5840293..03601b3ad9 100644 --- a/src/services/storage-permission/storage-permission-handler.service.spec.ts +++ b/src/services/storage-permission/storage-permission-handler.service.spec.ts @@ -1,16 +1,7 @@ -import { of, throwError } from 'rxjs'; -import { SocialSharing } from '@ionic-native/social-sharing/ngx'; -import { - ContentService, StorageService, Content -} from 'sunbird-sdk'; +import { of } from 'rxjs'; import { CommonUtilService, TelemetryGeneratorService } from '../../services'; import { AppVersion } from '@ionic-native/app-version/ngx'; -import { - InteractType, InteractSubtype, - Environment, PageId, ImpressionSubtype -} from '../telemetry-constants'; -import { AppGlobalService } from '../app-global-service.service'; -import { StoragePermissionHandlerService } from './storage-permission-handler.service' +import {StoragePermissionHandlerService} from './storage-permission-handler.service' import { AndroidPermissionsService } from '../android-permissions/android-permissions.service'; import { Platform } from '@ionic/angular'; @@ -53,241 +44,150 @@ describe('ContentShareHandlerService', () => { }); describe('checkForPermissions', () => { - const PageName = 'some-page'; - it('should return true for ios', (done) => { - mockPlatform.is = jest.fn((key) => { - let isIos = false; - switch (key) { - case 'ios': - isIos = true; - break; - } - return isIos; - }); - storagePermissionHandlerService.checkForPermissions(PageName); - setTimeout(() => { - expect(mockPlatform.is).toHaveBeenCalled(); - done(); - }, 0); - }); + const PageName = 'some-page' + it('should return true if platform is ios', () => { + // arrange + mockPlatform.is = jest.fn((platform) => platform == 'ios'); + // act + storagePermissionHandlerService.checkForPermissions(PageName).then((res) => { + expect(res).toBe(true) + }) + }) it('should return true if permissions are already accepted', () => { // arrange - mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({ hasPermission: true })); + mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({hasPermission: true})) // act storagePermissionHandlerService.checkForPermissions(PageName).then((res) => { - expect(res).toBe(true); - }); - }); + expect(res).toBe(true) + }) + }) it('should return false if permissions are not accepted', () => { // arrange - mockCommonUtilService.showSettingsPageToast = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({ isPermissionAlwaysDenied: true })); + mockCommonUtilService.showSettingsPageToast = jest.fn(); + mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({isPermissionAlwaysDenied: true})) // act storagePermissionHandlerService.checkForPermissions(PageName).then((res) => { - expect(res).toBe(false); + expect(res).toBe(false) expect(mockCommonUtilService.showSettingsPageToast).toHaveBeenCalledWith( 'FILE_MANAGER_PERMISSION_DESCRIPTION', undefined, PageName, true - ); - }); - }); + ) + }) + }) it('should show settinngs toast when user doesnt give permission', (done) => { // arrange - mockCommonUtilService.showSettingsPageToast = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({ isPermissionAlwaysDenied: false })); - mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); - mockCommonUtilService.translateMessage = jest.fn((key) => { - let msg = ''; - switch (key) { - case 'NOT_NOW': - msg = 'Not Now'; - break; - } - return msg; - }); - mockCommonUtilService.buildPermissionPopover = jest.fn((callback) => { - callback('Not Now'); + mockCommonUtilService.showSettingsPageToast = jest.fn(); + mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({isPermissionAlwaysDenied: false})) + mockCommonUtilService.translateMessage = jest.fn(v => v); + mockCommonUtilService.buildPermissionPopover = jest.fn(async (callback) => { + await callback(mockCommonUtilService.translateMessage('NOT_NOW')); return { present: jest.fn(() => Promise.resolve()) }; - }) as any; + }); // act - storagePermissionHandlerService.checkForPermissions(PageName); + storagePermissionHandlerService.checkForPermissions(PageName) setTimeout(() => { expect(mockCommonUtilService.showSettingsPageToast).toHaveBeenCalledWith( 'FILE_MANAGER_PERMISSION_DESCRIPTION', undefined, PageName, true - ); - expect(mockCommonUtilService.getGivenPermissionStatus).toHaveBeenCalled(); - expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( - InteractType.TOUCH, InteractSubtype.NOT_NOW_CLICKED, Environment.SETTINGS, PageId.PERMISSION_POPUP - ); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'FILE_MANAGER'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'NOT_NOW'); - expect(mockCommonUtilService.buildPermissionPopover).toHaveBeenCalled(); - done(); - }, 0); - }); + ) + done() + }); + }) it('should return true if user gives permission', (done) => { // arrange mockCommonUtilService.showSettingsPageToast = jest.fn(); - mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({ isPermissionAlwaysDenied: false })); - mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); - mockCommonUtilService.translateMessage = jest.fn((key) => { - let msg = ''; - switch (key) { - case 'ALLOW': - msg = 'Allow'; - break; - } - return msg; - }); - mockCommonUtilService.buildPermissionPopover = jest.fn((callback) => { - callback('Allow'); + mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({isPermissionAlwaysDenied: false})) + mockCommonUtilService.translateMessage = jest.fn(v => v); + mockCommonUtilService.buildPermissionPopover = jest.fn(async (callback) => { + await callback(mockCommonUtilService.translateMessage('ALLOW')); return { present: jest.fn(() => Promise.resolve()) }; - }) as any; - mockPermissionService.requestPermission = jest.fn(() => of({ hasPermission: true })); + }); + mockPermissionService.requestPermission = jest.fn(() => of({hasPermission: true})) // act - storagePermissionHandlerService.checkForPermissions(PageName); + storagePermissionHandlerService.checkForPermissions(PageName) setTimeout(() => { - expect(mockCommonUtilService.getGivenPermissionStatus).toHaveBeenCalled(); - expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(1, - InteractType.TOUCH, InteractSubtype.ALLOW_CLICKED, Environment.SETTINGS, PageId.PERMISSION_POPUP - ); - expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(2, - InteractType.TOUCH, InteractSubtype.ALLOW_CLICKED, Environment.SETTINGS, PageId.APP_PERMISSION_POPUP - ); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'FILE_MANAGER'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'NOT_NOW'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(3, 'ALLOW'); - expect(mockCommonUtilService.buildPermissionPopover).toHaveBeenCalled(); - done(); - }, 0); - }); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled() + done() + }); + }) it('should show toast when permissions not set', (done) => { // arrange mockCommonUtilService.showSettingsPageToast = jest.fn(); - mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({ isPermissionAlwaysDenied: false })); - mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); - mockCommonUtilService.translateMessage = jest.fn((key) => { - let msg = ''; - switch (key) { - case 'ALLOW': - msg = 'Allow'; - break; - } - return msg; - }); - mockCommonUtilService.buildPermissionPopover = jest.fn((callback) => { - callback('Allow'); + mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({isPermissionAlwaysDenied: false})) + mockCommonUtilService.translateMessage = jest.fn(v => v); + mockCommonUtilService.buildPermissionPopover = jest.fn(async (callback) => { + await callback(mockCommonUtilService.translateMessage('ALLOW')); return { present: jest.fn(() => Promise.resolve()) }; - }) as any; - mockPermissionService.requestPermission = jest.fn(() => of({ isPermissionAlwaysDenied: true })); + }); + mockPermissionService.requestPermission = jest.fn(() => of({isPermissionAlwaysDenied: true})) // act - storagePermissionHandlerService.checkForPermissions(PageName); + storagePermissionHandlerService.checkForPermissions(PageName) setTimeout(() => { + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled() expect(mockCommonUtilService.showSettingsPageToast).toHaveBeenCalledWith( 'FILE_MANAGER_PERMISSION_DESCRIPTION', undefined, PageName, true - ); - expect(mockCommonUtilService.getGivenPermissionStatus).toHaveBeenCalled(); - expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(1, - InteractType.TOUCH, InteractSubtype.ALLOW_CLICKED, Environment.SETTINGS, PageId.PERMISSION_POPUP - ); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'FILE_MANAGER'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'NOT_NOW'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(3, 'ALLOW'); - expect(mockCommonUtilService.buildPermissionPopover).toHaveBeenCalled(); - done(); - }, 0); - }); + ) + done() + }); + }) - it('should show toast when permissions not set for else part', (done) => { + it('should show toast when request permission is undefined', (done) => { // arrange mockCommonUtilService.showSettingsPageToast = jest.fn(); - mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({ isPermissionAlwaysDenied: false })); - mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); - mockCommonUtilService.translateMessage = jest.fn((key) => { - let msg = ''; - switch (key) { - case 'ALLOW': - msg = 'Allow'; - break; - } - return msg; - }); - mockCommonUtilService.buildPermissionPopover = jest.fn((callback) => { - callback('Allow'); + mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({isPermissionAlwaysDenied: false})) + mockCommonUtilService.translateMessage = jest.fn(v => v); + mockCommonUtilService.buildPermissionPopover = jest.fn(async (callback) => { + await callback(mockCommonUtilService.translateMessage('ALLOW')); return { present: jest.fn(() => Promise.resolve()) }; - }) as any; - mockPermissionService.requestPermission = jest.fn(() => of({ isPermissionAlwaysDenied: false })); + }); + mockPermissionService.requestPermission = jest.fn(() => of({})) + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockCommonUtilService.showSettingsPageToast = jest.fn(); // act - storagePermissionHandlerService.checkForPermissions(PageName); + storagePermissionHandlerService.checkForPermissions(PageName) setTimeout(() => { + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalled() expect(mockCommonUtilService.showSettingsPageToast).toHaveBeenCalledWith( 'FILE_MANAGER_PERMISSION_DESCRIPTION', undefined, PageName, true - ); - expect(mockCommonUtilService.getGivenPermissionStatus).toHaveBeenCalled(); - expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(1, - InteractType.TOUCH, InteractSubtype.ALLOW_CLICKED, Environment.SETTINGS, PageId.PERMISSION_POPUP - ); - expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(2, - InteractType.TOUCH, InteractSubtype.DENY_CLICKED, Environment.SETTINGS, PageId.APP_PERMISSION_POPUP - ); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'FILE_MANAGER'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'NOT_NOW'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(3, 'ALLOW'); - expect(mockCommonUtilService.buildPermissionPopover).toHaveBeenCalled(); - done(); - }, 0); - }); + ) + done() + }); + }) - it('should not show toast for default value', (done) => { + it('should handle else case for selected button is undefined or empty', (done) => { // arrange mockCommonUtilService.showSettingsPageToast = jest.fn(); - mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({ isPermissionAlwaysDenied: false })); - mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); - mockCommonUtilService.translateMessage = jest.fn((key) => { - let msg = ''; - switch (key) { - case 'default': - msg = 'default'; - break; - } - return msg; - }); - mockCommonUtilService.buildPermissionPopover = jest.fn((callback) => { - callback('default'); + mockCommonUtilService.getGivenPermissionStatus = jest.fn(() => Promise.resolve({isPermissionAlwaysDenied: false})) + mockCommonUtilService.translateMessage = jest.fn(v => v); + mockCommonUtilService.buildPermissionPopover = jest.fn(async (callback) => { + await callback(mockCommonUtilService.translateMessage('')); return { present: jest.fn(() => Promise.resolve()) }; - }) as any; - mockPermissionService.requestPermission = jest.fn(() => of({ isPermissionAlwaysDenied: false })); + }); // act - storagePermissionHandlerService.checkForPermissions(PageName); + storagePermissionHandlerService.checkForPermissions(PageName) setTimeout(() => { - expect(mockCommonUtilService.getGivenPermissionStatus).toHaveBeenCalled(); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'FILE_MANAGER'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'NOT_NOW'); - expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(3, 'ALLOW'); - expect(mockCommonUtilService.buildPermissionPopover).toHaveBeenCalled(); - done(); - }, 0); - }); - }); + done() + }); + }) + }) }); diff --git a/src/services/sunbirdqrscanner.service.ts b/src/services/sunbirdqrscanner.service.ts index 3ef393ae7a..d441e42d86 100644 --- a/src/services/sunbirdqrscanner.service.ts +++ b/src/services/sunbirdqrscanner.service.ts @@ -20,6 +20,7 @@ import { Mode, PageId } from './telemetry-constants'; +import { ManageLearnCertificateService } from '@app/app/manage-learn/core/services/manage-learn-certificate.service'; declare const cordova; @@ -53,7 +54,8 @@ export class SunbirdQRScanner { private appVersion: AppVersion, private toastController: ToastController, private router: Router, - private modalCtrl: ModalController + private modalCtrl: ModalController, + private projectCert : ManageLearnCertificateService ) { const that = this; this.translate.get(this.QR_SCANNER_TEXT).subscribe((data) => { @@ -256,6 +258,8 @@ private getProfileSettingConfig() { } else if (this.qrScannerResultHandler.isContentId(scannedData)) { this.qrScannerResultHandler.handleContentId(source, scannedData); + } else if(scannedData.includes('ProjectCertificate')) { + this.projectCert.getProjectCertificate(scannedData); } else if(scannedData.includes('data=') || scannedData.includes('t=URL')) { this.qrScannerResultHandler.handleRcCertsQR(scannedData); } else if (scannedData.includes('/certs/')) { diff --git a/src/services/utility-service.spec.ts b/src/services/utility-service.spec.ts index 92177ebc8d..7a3fee696c 100644 --- a/src/services/utility-service.spec.ts +++ b/src/services/utility-service.spec.ts @@ -2,6 +2,7 @@ import { UtilityService } from './utility-service'; describe('UtilityService', () => { let utilityService: UtilityService; + window.console.error = jest.fn() beforeAll(() => { window['sbutility'] = { @@ -18,6 +19,10 @@ describe('UtilityService', () => { rm: jest.fn(() => { }), getApkSize: jest.fn(() => { }), getMetaData: jest.fn(() => { }), + verifyCaptcha: jest.fn(() => { }), + getAppAvailabilityStatus: jest.fn(() => { }), + startActivityForResult: jest.fn(() => { }), + openFileManager: jest.fn(() => { }) }; utilityService = new UtilityService(); }); @@ -774,12 +779,149 @@ describe('UtilityService', () => { jest.spyOn(utilityService, 'getBuildConfigValue').mockRejectedValue('0'); // act // assert - utilityService.getAppVersionCode().then((response) => { - expect(response).toEqual(1); - }).catch((err) => { + utilityService.getAppVersionCode().catch((err) => { expect(err).toEqual(0); }); }); }); + describe('verifyCaptcha()', () => { + it('should delegate to verifyCaptcha Method', (done) => { + // arrange + (window['sbutility']['verifyCaptcha'] as jest.Mock). + mockImplementation((SOME_KEY, successCallback, errorCallback) => { + setTimeout(() => { + successCallback(); + }); + }); + // act + // assert + utilityService.verifyCaptcha('key').then((response) => { + expect(window['sbutility']['verifyCaptcha']).toHaveBeenCalledWith('key', expect.any(Function), expect.any(Function)); + done(); + }); + }); + it('should reject to verifyCaptcha Method', (done) => { + // arrange + (window['sbutility']['verifyCaptcha'] as jest.Mock). + mockImplementation((SOME_PATH, successCallback, errorCallback) => { + setTimeout(() => { + errorCallback('error'); + }); + }); + // act + // assert + utilityService.verifyCaptcha('key').catch((err) => { + expect(window['sbutility']['verifyCaptcha']).toHaveBeenCalledWith('key', expect.any(Function), expect.any(Function)); + done(); + }); + }); + }); + + describe('checkAvailableAppList()', () => { + it('should delegate to checkAvailableAppList Method', (done) => { + // arrange + (window['sbutility']['getAppAvailabilityStatus'] as jest.Mock). + mockImplementation((SOME_PATH, successCallback, errorCallback) => { + setTimeout(() => { + successCallback({}); + }); + }); + // act + // assert + utilityService.checkAvailableAppList(['key']).then((response) => { + expect(response).toEqual({}); + expect(window['sbutility']['getAppAvailabilityStatus']).toHaveBeenCalledWith(['key'], expect.any(Function), expect.any(Function)); + done(); + }); + }); + it('should reject to checkAvailableAppList Method with 0', (done) => { + // arrange + (window['sbutility']['getAppAvailabilityStatus'] as jest.Mock). + mockImplementation((SOME_VALUE, successCallback, errorCallback) => { + setTimeout(() => { + errorCallback('error'); + }); + }); + // act + // assert + utilityService.checkAvailableAppList(['key']).catch((err) => { + expect(err).toEqual('error'); + expect(window['sbutility']['getAppAvailabilityStatus']).toHaveBeenCalledWith(['key'], expect.any(Function), expect.any(Function)); + done() + }); + }); + }); + + describe('startActivityForResult()', () => { + it('should delegate to startActivityForResult Method with 1', (done) => { + // arrange + (window['sbutility']['startActivityForResult'] as jest.Mock). + mockImplementation((SOME_VALUE, successCallback, errorCallback) => { + setTimeout(() => { + successCallback({}); + }); + }); + // act + // assert + utilityService.startActivityForResult({'key':''}).then((response) => { + expect(response).toEqual({}); + expect(window['sbutility']['startActivityForResult']).toHaveBeenCalledWith({'key':''}, expect.any(Function), expect.any(Function)); + done() + }); + }); + it('should reject to startActivityForResult Method with 0', (done) => { + // arrange + (window['sbutility']['startActivityForResult'] as jest.Mock). + mockImplementation((SOME_VALUE, successCallback, errorCallback) => { + setTimeout(() => { + errorCallback('err'); + }); + }); + // act + // assert + utilityService.startActivityForResult({'key':''}).catch((err) => { + expect(err).toEqual('err'); + expect(window['sbutility']['startActivityForResult']).toHaveBeenCalledWith({'key':''}, expect.any(Function), expect.any(Function)); + done() + }); + }); + }); + + + describe('openFileManager()', () => { + it('should delegate to openFileManager Method with 1', (done) => { + // arrange + (window['sbutility']['openFileManager'] as jest.Mock). + mockImplementation((successCallback, errorCallback) => { + setTimeout(() => { + successCallback({data:{}}); + }); + }); + // act + // assert + utilityService.openFileManager().then((response) => { + expect(response).toEqual({data:{}}); + expect(window['sbutility']['openFileManager']).toHaveBeenCalledWith(expect.any(Function), expect.any(Function)); + done() + }); + }); + it('should reject to openFileManager Method with 0', (done) => { + // arrange + (window['sbutility']['openFileManager'] as jest.Mock). + mockImplementation((successCallback, errorCallback) => { + setTimeout(() => { + errorCallback('err'); + }); + }); + // act + // assert + utilityService.openFileManager().catch((err) => { + expect(err).toEqual(err); + expect(window['sbutility']['openFileManager']).toHaveBeenCalledWith(expect.any(Function), expect.any(Function)); + done() + }); + }); + }); + }); diff --git a/src/util/content-util.spec.ts b/src/util/content-util.spec.ts index e4f00c13d0..14d5bfec92 100644 --- a/src/util/content-util.spec.ts +++ b/src/util/content-util.spec.ts @@ -203,4 +203,14 @@ describe('ContentUtil', () => { { apply: true, name: 'new_user_type' }]); }); }); + + describe('isTrackable', () => { + it('should return 0 if mimetype is collection', () => { + // arrange + const content = {trackable: {enabled: true}, mimeType: 'application/vnd.ekstep.content-collection', contentData: {trackable: true}} + // act + ContentUtil.isTrackable(content); + // assert + }) + }) });