diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/Pipfile b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/Pipfile new file mode 100644 index 000000000..03f589cad --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/Pipfile @@ -0,0 +1,17 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pytest = "*" +pytest-html = "*" +pytest-metadata = "*" +requests = "*" + +[dev-packages] +black = "*" +flake8 = "*" + +[requires] +python_version = "3.11" diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/Pipfile.lock b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/Pipfile.lock new file mode 100644 index 000000000..a40cf3ffe --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/Pipfile.lock @@ -0,0 +1,285 @@ +{ + "_meta": { + "hash": { + "sha256": "917d5c85bd6545dedfbce6aedbd76bd1516993e65943ecfbf7affbece9a2a0ab" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.11" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "black": { + "hashes": [ + "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", + "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", + "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", + "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", + "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", + "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", + "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", + "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", + "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", + "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", + "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", + "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", + "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", + "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", + "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", + "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", + "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", + "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", + "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", + "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", + "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", + "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", + "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", + "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", + "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3" + ], + "index": "pypi", + "version": "==23.3.0" + }, + "certifi": { + "hashes": [ + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" + ], + "markers": "python_version >= '3.6'", + "version": "==2023.5.7" + }, + "charset-normalizer": { + "hashes": [ + "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", + "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", + "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", + "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", + "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", + "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", + "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", + "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", + "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", + "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", + "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", + "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", + "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", + "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", + "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", + "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", + "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", + "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", + "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", + "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", + "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", + "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", + "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", + "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", + "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", + "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", + "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", + "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", + "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", + "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", + "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", + "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", + "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", + "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", + "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", + "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", + "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", + "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", + "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", + "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", + "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", + "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", + "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", + "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", + "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", + "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", + "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", + "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", + "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", + "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", + "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", + "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", + "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", + "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", + "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", + "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", + "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", + "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", + "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", + "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", + "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", + "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", + "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", + "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", + "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", + "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", + "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", + "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", + "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", + "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", + "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", + "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", + "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", + "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", + "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.1.0" + }, + "click": { + "hashes": [ + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.3" + }, + "flake8": { + "hashes": [ + "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7", + "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181" + ], + "index": "pypi", + "version": "==6.0.0" + }, + "idna": { + "hashes": [ + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "markers": "python_version >= '3.5'", + "version": "==3.4" + }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "packaging": { + "hashes": [ + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + ], + "markers": "python_version >= '3.7'", + "version": "==23.1" + }, + "pathspec": { + "hashes": [ + "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687", + "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293" + ], + "markers": "python_version >= '3.7'", + "version": "==0.11.1" + }, + "platformdirs": { + "hashes": [ + "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc", + "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e" + ], + "markers": "python_version >= '3.7'", + "version": "==3.8.0" + }, + "pluggy": { + "hashes": [ + "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", + "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3" + ], + "markers": "python_version >= '3.7'", + "version": "==1.2.0" + }, + "py": { + "hashes": [ + "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", + "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==1.11.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053", + "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610" + ], + "markers": "python_version >= '3.6'", + "version": "==2.10.0" + }, + "pyflakes": { + "hashes": [ + "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf", + "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd" + ], + "markers": "python_version >= '3.6'", + "version": "==3.0.1" + }, + "pytest": { + "hashes": [ + "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", + "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" + ], + "index": "pypi", + "version": "==7.4.0" + }, + "pytest-html": { + "hashes": [ + "sha256:868c08564a68d8b2c26866f1e33178419bb35b1e127c33784a28622eb827f3f3", + "sha256:c4e2f4bb0bffc437f51ad2174a8a3e71df81bbc2f6894604e604af18fbe687c3" + ], + "index": "pypi", + "version": "==3.2.0" + }, + "pytest-metadata": { + "hashes": [ + "sha256:769a9c65d2884bd583bc626b0ace77ad15dbe02dd91a9106d47fd46d9c2569ca", + "sha256:a17b1e40080401dc23177599208c52228df463db191c1a573ccdffacd885e190" + ], + "index": "pypi", + "version": "==3.0.0" + }, + "requests": { + "hashes": [ + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + ], + "index": "pypi", + "version": "==2.31.0" + }, + "urllib3": { + "hashes": [ + "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1", + "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.3" + } + }, + "develop": {} +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/SurveyExperimentIntegrationTest.kt b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/SurveyExperimentIntegrationTest.kt new file mode 100644 index 000000000..277135cb7 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/SurveyExperimentIntegrationTest.kt @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.experimentintegration + +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.helpers.HomeActivityTestRule +import org.mozilla.fenix.helpers.TestHelper +import org.mozilla.fenix.ui.robots.browserScreen +import org.mozilla.fenix.ui.robots.homeScreen + +/** + * Tests for verifying functionality of the message survey surface + */ +class SurveyExperimentIntegrationTest { + private val surveyURL = "qsurvey.mozilla.com" + private val experimentName = "Viewpoint" + + @get:Rule + val activityTestRule = HomeActivityTestRule() + + @Before + fun setUp() { + TestHelper.appContext.settings().showSecretDebugMenuThisSession = true + } + + @After + fun tearDown() { + TestHelper.appContext.settings().showSecretDebugMenuThisSession = false + } + + @Test + fun checkSurveyNavigatesCorrectly() { + browserScreen { + verifySurveyButton() + }.clickSurveyButton {} + + homeScreen { + }.openThreeDotMenu { + }.openSettings { + }.openExperimentsMenu { + verifyExperimentExists(experimentName) + } + } +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/__init__.py b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/conftest.py b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/conftest.py new file mode 100644 index 000000000..19ddbf691 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/conftest.py @@ -0,0 +1,138 @@ +import json +import os +from pathlib import Path +import subprocess +import time + +import pytest +import requests + +from experimentintegration.gradlewbuild import GradlewBuild + +KLAATU_SERVER_URL = "http://localhost:1378" +KLAATU_LOCAL_SERVER_URL = "http://localhost:1378" + +here = Path() + + +def load_branches(): + branches = [] + data = requests.get(f"{KLAATU_SERVER_URL}/experiment").json() + for item in reversed(data): + if isinstance(item, dict): + exit() + else: + data = item + break + experiment = requests.get(data).json() + for item in experiment["branches"]: + branches.append(item["slug"]) + return branches + + +@pytest.fixture +def gradlewbuild_log(pytestconfig, tmpdir): + gradlewbuild_log = f"{tmpdir.join('gradlewbuild.log')}" + pytestconfig._gradlewbuild_log = gradlewbuild_log + yield gradlewbuild_log + + +@pytest.fixture +def gradlewbuild(gradlewbuild_log): + yield GradlewBuild(gradlewbuild_log) + + +@pytest.fixture(name="experiment_data") +def fixture_experiment_data(experiment_url): + data = requests.get(experiment_url).json() + del(data["branches"][0]["features"][0]["value"]["message-under-experiment"]) + for item in data["branches"][0]["features"][0]["value"]["messages"].values(): + for count, trigger in enumerate(item["trigger"]): + if "USER_EN_SPEAKER" not in trigger: + del(item["trigger"][count]) + return [data] + + +@pytest.fixture(name="experiment_url", scope="module") +def fixture_experiment_url(): + data = requests.get(f"{KLAATU_LOCAL_SERVER_URL}/experiment").json() + url = None + for item in data: + if isinstance(item, dict): + continue + else: + url = item + yield url + return_data = {"url": url} + requests.put(f"{KLAATU_SERVER_URL}/experiment", json=return_data) + + +@pytest.fixture(name="json_data") +def fixture_json_data(tmp_path, experiment_data): + path = tmp_path / "data" + path.mkdir() + json_path = path / "data.json" + with open(json_path, "w", encoding="utf-8") as f: + # URL of experiment/klaatu server + data = {"data": experiment_data} + json.dump(data, f) + return json_path + + +@pytest.fixture(name="experiment_slug") +def fixture_experiment_slug(experiment_data): + return experiment_data[0]["slug"] + + +@pytest.fixture(name="start_app") +def fixture_start_app(): + def _(): + command = f"nimbus-cli --app fenix --channel developer open" + try: + out = subprocess.check_output( + command, + cwd=os.path.join(here, os.pardir), + stderr=subprocess.STDOUT, + universal_newlines=True, + shell=True, + ) + except subprocess.CalledProcessError as e: + out = e.output + raise + finally: + with open(gradlewbuild_log, "w") as f: + f.write(out) + time.sleep( + 15 + ) # Wait a while as there's no real way to know when the app has started + + return _ + + +@pytest.fixture(name="send_test_results", autouse=True) +def fixture_send_test_results(): + yield + here = Path() + + with open(f"{here.resolve()}/results/index.html", "rb") as f: + files = {"file": f} + requests.post(f"{KLAATU_SERVER_URL}/test_results", files=files) + + +@pytest.fixture(name="setup_experiment", params=load_branches(), autouse=True) +def fixture_setup_experiment(experiment_slug, json_data, gradlewbuild_log, request): + def _(): + command = f"nimbus-cli --app fenix --channel developer enroll {experiment_slug} --branch {request.param} --file {json_data} --reset-app" + try: + out = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + out = e.output + raise + finally: + with open(gradlewbuild_log, "w") as f: + f.write(f"{out}") + time.sleep( + 15 + ) # Wait a while as there's no real way to know when the app has started + + return _ diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/gradlewbuild.py b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/gradlewbuild.py new file mode 100644 index 000000000..77fc18e58 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/gradlewbuild.py @@ -0,0 +1,45 @@ +import logging +import os +import subprocess + +from syncintegration.adbrun import ADBrun + +here = os.path.dirname(__file__) +logging.getLogger(__name__).addHandler(logging.NullHandler()) + + +class GradlewBuild(object): + binary = "./gradlew" + logger = logging.getLogger() + adbrun = ADBrun() + + def __init__(self, log): + self.log = log + + def test(self, identifier): + # self.adbrun.launch() + + # Change path accordingly to go to root folder to run gradlew + os.chdir("../../../../../../../..") + cmd = f"adb shell am instrument -w -e class org.mozilla.fenix.experimentintegration.{identifier} org.mozilla.fenix.debug.test/androidx.test.runner.AndroidJUnitRunner" + + self.logger.info("Running cmd: {}".format(cmd)) + + out = "" + try: + out = subprocess.check_output( + cmd, encoding="utf8", shell=True, stderr=subprocess.STDOUT + ) + if "FAILURES" in out: + raise (AssertionError(out)) + except subprocess.CalledProcessError as e: + out = e.output + raise + finally: + # Set the path correctly + tests_path = ( + "app/src/androidTest/java/org/mozilla/fenix/experimentintegration/" + ) + os.chdir(tests_path) + with open(self.log, "w") as f: + f.write(str(out)) diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/launchSimScript.sh b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/launchSimScript.sh new file mode 100755 index 000000000..648397f0e --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/launchSimScript.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e + +echo "Waiting emulator is ready..." +~/Library/Android/sdk/emulator/emulator -avd Pixel_3_API_28 -wipe-data -no-boot-anim -screen no-touch & + +bootanim="" +failcounter=0 +timeout_in_sec=360 + +until [[ "$bootanim" =~ "stopped" ]]; do + bootanim=`~/Library/Android/sdk/platform-tools/adb -e shell getprop init.svc.bootanim 2>&1 &` + if [[ "$bootanim" =~ "device not found" || "$bootanim" =~ "device offline" + || "$bootanim" =~ "running" ]]; then + let "failcounter += 1" + echo "Waiting for emulator to start" + if [[ $failcounter -gt timeout_in_sec ]]; then + echo "Timeout ($timeout_in_sec seconds) reached; failed to start emulator" + exit 1 + fi + fi + sleep 1 +done + +echo "Emulator is ready" +sleep 10 diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/pytest.ini b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/pytest.ini new file mode 100644 index 000000000..465cd779e --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +addopts = --verbose --html=results/index.html --self-contained-html +log_cli = true +log_cli_level = info diff --git a/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/test_integration.py b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/test_integration.py new file mode 100644 index 000000000..63ef8a494 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/test_integration.py @@ -0,0 +1,3 @@ +def test_survey_navigates_correctly(setup_experiment, gradlewbuild): + setup_experiment() + gradlewbuild.test("SurveyExperimentIntegrationTest#checkSurveyNavigatesCorrectly") diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt index 8680bdc4b..c9e1d4ebf 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -862,12 +862,35 @@ class BrowserRobot { } } + fun verifySurveyButton() { + val button = mDevice.findObject( + UiSelector().text( + getStringResource( + R.string.preferences_take_survey, + ), + ), + ) + assertTrue(button.waitForExists(waitingTime)) + } + fun clickOpenLinksInAppsDismissCFRButton() = itemWithResIdContainingText( "$packageName:id/dismiss", getStringResource(R.string.open_in_app_cfr_negative_button_text), ).click() + fun clickTakeSurveyButton() { + val button = mDevice.findObject( + UiSelector().text( + getStringResource( + R.string.preferences_take_survey, + ), + ), + ) + button.waitForExists(waitingTime) + button.click() + } + fun longClickToolbar() = mDevice.findObject(By.res("$packageName:id/mozac_browser_toolbar_url_view")).click(LONG_CLICK_DURATION) fun verifyDownloadPromptIsDismissed() = @@ -1180,6 +1203,14 @@ class BrowserRobot { DownloadRobot().interact() return DownloadRobot.Transition() } + + fun clickSurveyButton(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { + surveyButton.waitForExists(waitingTime) + surveyButton.click() + + BrowserRobot().interact() + return Transition() + } } } @@ -1335,3 +1366,6 @@ private val contextMenuShareLink = // Open in external app option private val contextMenuOpenInExternalApp = itemContainingText(getStringResource(R.string.mozac_feature_contextmenu_open_link_in_external_app)) + +private val surveyButton = + itemContainingText(getStringResource(R.string.preferences_take_survey)) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt index bccc3c225..309343303 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt @@ -425,6 +425,15 @@ class SettingsRobot { SettingsSubMenuHttpsOnlyModeRobot().interact() return SettingsSubMenuHttpsOnlyModeRobot.Transition() } + + fun openExperimentsMenu(interact: SettingsSubMenuExperimentsRobot.() -> Unit): SettingsSubMenuExperimentsRobot.Transition { + scrollToElementByText("Nimbus Experiments") + fun nimbusExperimentsButton() = mDevice.findObject(textContains("Nimbus Experiments")) + nimbusExperimentsButton().click() + + SettingsSubMenuExperimentsRobot().interact() + return SettingsSubMenuExperimentsRobot.Transition() + } } companion object { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt index e691bcc55..5203940d7 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt @@ -35,6 +35,7 @@ import org.mozilla.fenix.helpers.TestHelper import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.TestHelper.mDevice import org.mozilla.fenix.helpers.TestHelper.packageName +import org.mozilla.fenix.helpers.click import org.mozilla.fenix.settings.SupportUtils import java.text.SimpleDateFormat import java.time.LocalDateTime diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuExperimentsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuExperimentsRobot.kt new file mode 100644 index 000000000..376b820e3 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuExperimentsRobot.kt @@ -0,0 +1,41 @@ +package org.mozilla.fenix.ui.robots + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription +import androidx.test.uiautomator.UiSelector +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.click + +/** + * Implementation of Robot Pattern for the experiments sub menu. + */ +class SettingsSubMenuExperimentsRobot { + + class Transition { + + fun goBackToHomeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { + goBackButton().click() + + HomeScreenRobot().interact() + return HomeScreenRobot.Transition() + } + + fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition { + goBackButton().click() + + SettingsRobot().interact() + return SettingsRobot.Transition() + } + } + + private fun getExperiment(experimentName: String): UiSelector? { + return UiSelector().textContains(experimentName) + } + + fun verifyExperimentExists(title: String) { + val experiment = getExperiment(title) + + checkNotNull(experiment) + } +} +private fun goBackButton() = onView(withContentDescription(R.string.action_bar_up_description)) diff --git a/automation/taskcluster/androidTest/flank-arm-legacy-api-tests.yml b/automation/taskcluster/androidTest/flank-arm-legacy-api-tests.yml index cf27fa52f..067913661 100644 --- a/automation/taskcluster/androidTest/flank-arm-legacy-api-tests.yml +++ b/automation/taskcluster/androidTest/flank-arm-legacy-api-tests.yml @@ -22,6 +22,7 @@ gcloud: test-targets: - notPackage org.mozilla.fenix.screenshots - notPackage org.mozilla.fenix.syncintegration + - notPackage org.mozilla.fenix.experimentintegration - class org.mozilla.fenix.ui.NavigationToolbarTest#visitURLTest - class org.mozilla.fenix.ui.NavigationToolbarTest#goBackTest - class org.mozilla.fenix.ui.NavigationToolbarTest#goForwardTest diff --git a/automation/taskcluster/androidTest/flank-arm64-v8a.yml b/automation/taskcluster/androidTest/flank-arm64-v8a.yml index b72182cbf..d679f8ea7 100644 --- a/automation/taskcluster/androidTest/flank-arm64-v8a.yml +++ b/automation/taskcluster/androidTest/flank-arm64-v8a.yml @@ -21,6 +21,7 @@ gcloud: test-targets: - notPackage org.mozilla.fenix.screenshots - notPackage org.mozilla.fenix.syncintegration + - notPackage org.mozilla.fenix.experimentintegration device: - model: Pixel2.arm diff --git a/automation/taskcluster/androidTest/flank-x86.yml b/automation/taskcluster/androidTest/flank-x86.yml index de74c2306..0b6e13b0b 100644 --- a/automation/taskcluster/androidTest/flank-x86.yml +++ b/automation/taskcluster/androidTest/flank-x86.yml @@ -22,6 +22,7 @@ gcloud: test-targets: - notPackage org.mozilla.fenix.screenshots - notPackage org.mozilla.fenix.syncintegration + - notPackage org.mozilla.fenix.experimentintegration device: - model: Pixel2