From c1e13043634f4b943b9464d26f04bd4d13907d76 Mon Sep 17 00:00:00 2001 From: Josh Schrader Date: Tue, 20 Dec 2022 03:28:04 -0700 Subject: [PATCH 1/9] Implement subscription expiring message --- .../contactSupportLink.js | 3 ++ .../isExpiring.js | 12 +++++++ .../manageAccountLink.js | 3 ++ .../manifest.json | 36 +++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 addons/message_subscription_expiring/contactSupportLink.js create mode 100644 addons/message_subscription_expiring/isExpiring.js create mode 100644 addons/message_subscription_expiring/manageAccountLink.js create mode 100644 addons/message_subscription_expiring/manifest.json diff --git a/addons/message_subscription_expiring/contactSupportLink.js b/addons/message_subscription_expiring/contactSupportLink.js new file mode 100644 index 0000000000..79dfebded1 --- /dev/null +++ b/addons/message_subscription_expiring/contactSupportLink.js @@ -0,0 +1,3 @@ +((api) => { + api.urlOpener.openLink(api.urlOpener.LinkHelpSupport); +}); diff --git a/addons/message_subscription_expiring/isExpiring.js b/addons/message_subscription_expiring/isExpiring.js new file mode 100644 index 0000000000..b33b038b65 --- /dev/null +++ b/addons/message_subscription_expiring/isExpiring.js @@ -0,0 +1,12 @@ +(function(api, condition) { + // Show message only if within 1 week of expiring. + let weekBeforeExpireMSecs = api.subscriptionData.expiresOn - 1000 * 60 * 60 * 24; + + if (Date.now() < api.subscriptionData.expiresOn && + Date.now() >= weekBeforeExpireMSecs) { + api.addon.date = weekBeforeExpireMSecs; + condition.enable(); + } else { + condition.disable(); + } +}) diff --git a/addons/message_subscription_expiring/manageAccountLink.js b/addons/message_subscription_expiring/manageAccountLink.js new file mode 100644 index 0000000000..8548799870 --- /dev/null +++ b/addons/message_subscription_expiring/manageAccountLink.js @@ -0,0 +1,3 @@ +((api) => { + api.urlOpener.openLink(api.urlOpener.LinkAccount); +}); diff --git a/addons/message_subscription_expiring/manifest.json b/addons/message_subscription_expiring/manifest.json new file mode 100644 index 0000000000..4cc4b0efc3 --- /dev/null +++ b/addons/message_subscription_expiring/manifest.json @@ -0,0 +1,36 @@ +{ + "api_version": "0.1", + "id": "message_subscription_expiring", + "name": "Subscription expiring message", + "type": "message", + "conditions": { + "javascript": "isExpiring.js" + }, + "message": { + "id": "message_subscription_expiring", + "title": "Your subscription is ending soon", + "subtitle": "Your Mozilla VPN subscription will end in one week. After that, you’ll no longer be able to use Mozilla VPN.", + "badge": "warning", + "blocks": [ + { + "id": "c_1", + "type": "text", + "content": "Don’t lose your subscription — please go to your account page to renew your access to Mozilla VPN. If you’re having any issues, please contact support to get help." + }, + { + "id": "c_2", + "type": "button", + "style": "primary", + "content": "Manage account", + "javascript": "manageAccountLink.js" + }, + { + "id": "c_3", + "type": "button", + "style": "link", + "content": "Contact support", + "javascript": "contactSupportLink.js" + } + ] + } +} From 4c0912797bcabda906978a8e1489e8508d5ca854 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Thu, 19 Jan 2023 18:02:47 +0100 Subject: [PATCH 2/9] EXTRA: Only retry functional tests on CI --- .github/workflows/functional_tests.yaml | 6 +++--- .github/workflows/wasm.yaml | 8 ++++---- package.json | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/functional_tests.yaml b/.github/workflows/functional_tests.yaml index 7b947eedf4..32a6097488 100644 --- a/.github/workflows/functional_tests.yaml +++ b/.github/workflows/functional_tests.yaml @@ -23,7 +23,7 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v3 - with: + with: submodules: 'true' - name: Install build dependecies @@ -87,7 +87,7 @@ jobs: functionaltests: name: Functional tests - needs: + needs: - build_test_app runs-on: ubuntu-22.04 timeout-minutes: 45 @@ -143,7 +143,7 @@ jobs: export HEADLESS=yes export TZ=Europe/London mkdir -p $ARTIFACT_DIR - xvfb-run -a npm run functionalTest -- ${{matrix.test.path}} + xvfb-run -a npm run functionalTest -- --retries 3 ${{matrix.test.path}} env: ARTIFACT_DIR: ${{ runner.temp }}/artifacts MVPN_BIN: ./build/dummyvpn diff --git a/.github/workflows/wasm.yaml b/.github/workflows/wasm.yaml index bf50938656..3449125370 100644 --- a/.github/workflows/wasm.yaml +++ b/.github/workflows/wasm.yaml @@ -26,7 +26,7 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v3 - with: + with: submodules: 'true' - name: Install Qt @@ -122,8 +122,8 @@ jobs: with: name: WebAssembly Build Qt6 # Destination path - path: wasm - + path: wasm + - name: Build addons shell: bash run: ./scripts/addon/generate_all_tests.py -q /opt/$QTVERSION/gcc_64/bin @@ -137,4 +137,4 @@ jobs: command: | export PATH=$GECKOWEBDRIVER:$(npm bin):$PATH export HEADLESS=yes - xvfb-run -a npm run functionalTestWasm -- ${{matrix.test.path}} + xvfb-run -a npm run functionalTestWasm -- --retries 3 ${{matrix.test.path}} diff --git a/package.json b/package.json index e3cd766bf3..f19cf578a4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "scripts": { - "functionalTest": "mocha --require ./tests/functional/setupVpn.js --timeout 30000 --retries 3", - "functionalTestWasm": "mocha --require ./tests/functional/setupWasm.js --timeout 30000 --retries 3" + "functionalTest": "mocha --require ./tests/functional/setupVpn.js --timeout 30000", + "functionalTestWasm": "mocha --require ./tests/functional/setupWasm.js --timeout 30000" }, "devDependencies": { "body-parser": "^1.20.0", From a279566e2394c7b618a2e852b15c00b8f2544848 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Thu, 19 Jan 2023 18:23:25 +0100 Subject: [PATCH 3/9] Load production addons on tests --- scripts/addon/generate_all_tests.py | 16 ++++++++++++---- tests/functional/servers/addon.js | 16 +++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/scripts/addon/generate_all_tests.py b/scripts/addon/generate_all_tests.py index 798a3a0c32..271c1fbb9b 100755 --- a/scripts/addon/generate_all_tests.py +++ b/scripts/addon/generate_all_tests.py @@ -19,15 +19,23 @@ args = parser.parse_args() generateall_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "generate_all.py") -addons_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", "tests", "functional", "addons") -for file in os.listdir(addons_path): - manifest_path = os.path.join(addons_path, file, "manifest.json") +# Generate production addons files +build_cmd = [sys.executable, generateall_path] +if args.qtpath: + build_cmd.append("-q") + build_cmd.append(args.qtpath) +subprocess.call(build_cmd) + +# Generate test addons files +test_addons_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", "tests", "functional", "addons") +for file in os.listdir(test_addons_path): + manifest_path = os.path.join(test_addons_path, file, "manifest.json") if os.path.exists(manifest_path): print(f"Ignoring path {file} because the manifest already exists.") continue - build_cmd = [sys.executable, generateall_path, "-p", os.path.join(addons_path, file)] + build_cmd = [sys.executable, generateall_path, "-p", os.path.join(test_addons_path, file)] if args.qtpath: build_cmd.append("-q") build_cmd.append(args.qtpath) diff --git a/tests/functional/servers/addon.js b/tests/functional/servers/addon.js index c51702a0eb..3ed1337d65 100644 --- a/tests/functional/servers/addon.js +++ b/tests/functional/servers/addon.js @@ -6,7 +6,8 @@ const Server = require('./server.js'); const fs = require('fs'); const path = require('path'); -const ADDON_PATH = './tests/functional/addons'; +const TEST_ADDONS_PATH = './tests/functional/addons'; +const PROD_ADDONS_PATH = './addons'; // This function exposes all the files for a particular addon scenario through // the addon server. @@ -15,8 +16,7 @@ function createScenario(scenario, addonPath) { if (!fs.existsSync(generatedPath)) { const manifestPath = path.join(addonPath, 'manifest.json'); if (!fs.existsSync(manifestPath)) { - throw new Error(`No generated and not manifest file! ${ - manifestPath} should exist! Have you executed \`./scripts/addon/generate_all_tests.py'?`); + throw new Error(`No generated and not manifest file! ${manifestPath} should exist! Have you executed \`./scripts/addon/generate_all_tests.py'?`); } const obj = {}; @@ -65,17 +65,19 @@ function createScenario(scenario, addonPath) { let server = null; module.exports = { async start(headerCheck = true) { - let scenarios = {}; + // Generate production addon scenarios + let scenarios = { ...createScenario("prod", PROD_ADDONS_PATH) }; - const dirs = fs.readdirSync(ADDON_PATH); + // Generate test addon scenarios + const dirs = fs.readdirSync(TEST_ADDONS_PATH); for (const dir of dirs) { - const addonPath = path.join(ADDON_PATH, dir); + const addonPath = path.join(TEST_ADDONS_PATH, dir); const stat = fs.statSync(addonPath); if (!stat.isDirectory()) { continue; } - scenarios = {...scenarios, ...createScenario(dir, addonPath)}; + scenarios = { ...scenarios, ...createScenario(dir, addonPath) }; } const endpoints = { From 2873359a9be0671fb2f22e04d5d6e091c43add02 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Mon, 23 Jan 2023 17:26:08 +0100 Subject: [PATCH 4/9] Add tests for subscription expiring message --- .../isExpiring.js | 9 ++- src/apps/vpn/inspector/inspectorhandler.cpp | 18 +++++ tests/functional/helper.js | 8 ++ .../functional/servers/guardian_endpoints.js | 2 + tests/functional/setupVpn.js | 2 + tests/functional/testAddons.js | 73 +++++++++++++++++++ 6 files changed, 108 insertions(+), 4 deletions(-) diff --git a/addons/message_subscription_expiring/isExpiring.js b/addons/message_subscription_expiring/isExpiring.js index b33b038b65..6f2f2db2df 100644 --- a/addons/message_subscription_expiring/isExpiring.js +++ b/addons/message_subscription_expiring/isExpiring.js @@ -1,12 +1,13 @@ (function(api, condition) { // Show message only if within 1 week of expiring. - let weekBeforeExpireMSecs = api.subscriptionData.expiresOn - 1000 * 60 * 60 * 24; + const weekBeforeExpireMSecs = api.subscriptionData.expiresOn - 1000 * 60 * 60 * 24 * 7; + const subscriptionExpiry = api.subscriptionData.expiresOn; + const now = Date.now(); - if (Date.now() < api.subscriptionData.expiresOn && - Date.now() >= weekBeforeExpireMSecs) { + if (now < subscriptionExpiry && now >= weekBeforeExpireMSecs) { api.addon.date = weekBeforeExpireMSecs; condition.enable(); } else { condition.disable(); } -}) +}); diff --git a/src/apps/vpn/inspector/inspectorhandler.cpp b/src/apps/vpn/inspector/inspectorhandler.cpp index 67c775a22f..acafcb3d03 100644 --- a/src/apps/vpn/inspector/inspectorhandler.cpp +++ b/src/apps/vpn/inspector/inspectorhandler.cpp @@ -567,6 +567,24 @@ static QList s_commands{ return obj; }}, + InspectorCommand{"messages", "Returns a list of the loaded messages ids", 0, + [](InspectorHandler*, const QList&) { + QJsonObject obj; + + AddonManager* am = AddonManager::instance(); + Q_ASSERT(am); + + QJsonArray messages; + am->forEach([&](Addon* addon) { + if (addon->type() == "message") { + messages.append(addon->id()); + } + }); + + obj["value"] = messages; + return obj; + }}, + InspectorCommand{"translate", "Translate a string", 1, [](InspectorHandler*, const QList& arguments) { QJsonObject obj; diff --git a/tests/functional/helper.js b/tests/functional/helper.js index c6010bda85..48dde4bbde 100644 --- a/tests/functional/helper.js +++ b/tests/functional/helper.js @@ -499,6 +499,14 @@ module.exports = { return json.value; }, + async messages() { + const json = await this._writeCommand('messages'); + assert( + json.type === 'messages' && !('error' in json), + `Command failed: ${json.error}`); + return json.value; + }, + async screenCapture() { const json = await this._writeCommand('screen_capture'); assert( diff --git a/tests/functional/servers/guardian_endpoints.js b/tests/functional/servers/guardian_endpoints.js index 0468b73307..dffabb0711 100644 --- a/tests/functional/servers/guardian_endpoints.js +++ b/tests/functional/servers/guardian_endpoints.js @@ -39,6 +39,8 @@ const SubscriptionDetails = { }, }; +exports.SubscriptionDetails = SubscriptionDetails; + const VALIDATORS = { guardianLoginVerify: { type: 'object', diff --git a/tests/functional/setupVpn.js b/tests/functional/setupVpn.js index 68d023b205..449ab815ec 100644 --- a/tests/functional/setupVpn.js +++ b/tests/functional/setupVpn.js @@ -46,6 +46,8 @@ async function startAndConnect() { await vpn.connect(vpnWS, {hostname: '127.0.0.1'}); } +exports.startAndConnect = startAndConnect; + exports.mochaHooks = { async beforeAll() { // Check VPN app exists. If not, bail. diff --git a/tests/functional/testAddons.js b/tests/functional/testAddons.js index 181ceef07a..34b8d3fa04 100644 --- a/tests/functional/testAddons.js +++ b/tests/functional/testAddons.js @@ -5,6 +5,8 @@ const assert = require('assert'); const queries = require('./queries.js'); const vpn = require('./helper.js'); +const { SubscriptionDetails } = require('./servers/guardian_endpoints.js') +const { startAndConnect } = require('./setupVpn.js') describe('Addons', function() { this.ctx.authenticationNeeded = true; @@ -127,4 +129,75 @@ describe('Addons', function() { await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === exitCountryCode); }); + + describe('test message_subscription_expiring addon condition', async () => { + async function checkForSubscriptionExpiringMessage(ctx, subscriptionExpirationCases, shouldBeAvailable) { + for (const expiresOn of subscriptionExpirationCases) { + const mockDetails = { ...SubscriptionDetails }; + // We are faking a Stripe subscription, so this value is expected to be in seconds. + mockDetails.subscription.current_period_end = expiresOn / 1000; + ctx.guardianSubscriptionDetailsCallback = () => { + ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; + ctx.guardianOverrideEndpoints + .GETs['/api/v1/vpn/subscriptionDetails'] + .body = mockDetails; + }; + + // Restart the VPN to load the new sub details. + await vpn.quit(); + await startAndConnect(); + + // Load all production addons. + // These are loaded all together, so we don't know the exact number of addons. + await vpn.resetAddons('prod'); + await vpn.waitForCondition(async () => ( + parseInt(await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) > 0 + )); + + await vpn.waitForCondition(async () => { + const loadedMessages = await vpn.messages(); + console.log(loadedMessages) + const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); + + return shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable; + }); + } + } + + it('message is enabled when subscription is about to expire', async () => { + // 1 to 7 days out from expiring. + const subscriptionExpirationCases = Array.from( + { length: 7 }, + (_, i) => Date.now() + 1000 * 60 * 60 * 24 * (i + 1) + ); + + await checkForSubscriptionExpiringMessage(this.ctx, subscriptionExpirationCases, true); + }); + + it('message is not enabled when subscription is not about to expire', async () => { + const subscriptionExpirationCases = [ + // Seven days out + a minute from expiring. + Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60, + // Eight days from expiring. + Date.now() + 1000 * 60 * 60 * 24 * 8, + // One month from expiring. + Date.now() + 1000 * 60 * 60 * 24 * 30, + ] + + await checkForSubscriptionExpiringMessage(this.ctx, subscriptionExpirationCases, false); + }); + + it('message is not enabled when subscription is already expired', async () => { + const subscriptionExpirationCases = [ + // Literally, has just expired. + Date.now(), + // Has been expired for a day. + Date.now() - 1000 * 60 * 60 * 24, + // Has been expired for 30 days. + Date.now() - 1000 * 60 * 60 * 24 * 30, + ] + + await checkForSubscriptionExpiringMessage(this.ctx, subscriptionExpirationCases, false); + }); + }); }); From 2eeec1e2080105342810062a3adab912969d9e23 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Tue, 31 Jan 2023 17:10:24 +0100 Subject: [PATCH 5/9] Address review comments --- tests/functional/testAddons.js | 340 ++++++++++++++++----------------- 1 file changed, 162 insertions(+), 178 deletions(-) diff --git a/tests/functional/testAddons.js b/tests/functional/testAddons.js index 34b8d3fa04..6a28c481fa 100644 --- a/tests/functional/testAddons.js +++ b/tests/functional/testAddons.js @@ -11,142 +11,166 @@ const { startAndConnect } = require('./setupVpn.js') describe('Addons', function() { this.ctx.authenticationNeeded = true; - it('Empty addon index', async () => { - await vpn.resetAddons('01_empty_manifest'); - await vpn.waitForCondition(async () => { - return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 0; - }); - }); - - it('Broken addon index', async () => { - await vpn.resetAddons('03_single_addon'); - await vpn.waitForCondition(async () => { - return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; - }); - - await vpn.fetchAddons('02_broken_manifest'); - await vpn.waitForCondition(async () => { - return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; - }); - }); - - it('Addons are loaded', async () => { - await vpn.resetAddons('03_single_addon'); - await vpn.waitForCondition(async () => { - return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; - }); - - await vpn.fetchAddons('01_empty_manifest'); - await vpn.waitForCondition(async () => { - return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 0; - }); - - await vpn.fetchAddons('03_single_addon'); - await vpn.waitForCondition(async () => { - return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; - }); - }); - - it('Settings rollback - location', async () => { - // Loading the custom tutorial - await vpn.resetAddons('05_settings_rollback'); - - await vpn.waitForCondition(async () => { - return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; - }); - - const exitCityName = - await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName'); - const exitCountryCode = - await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode'); - - // Let's start the tutorial - await vpn.waitForQueryAndClick(queries.navBar.SETTINGS.visible()); - await vpn.waitForQueryAndClick( - queries.screenSettings.TIPS_AND_TRICKS.visible()); - await vpn.waitForQueryAndClick( - queries.screenSettings.TUTORIAL_LIST_HIGHLIGHT.visible()); - - // Confirmation dialog for settings-rollback - await vpn.waitForQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - assert( - (await vpn.getQueryProperty( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), - 'text')) === 'Continue'); - await vpn.clickOnQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - - await vpn.waitForCondition(async () => { - return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - 'Vienna'; - }); - - assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - 'Vienna'); - assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === - 'at'); - - await vpn.waitForQuery(queries.screenHome.TUTORIAL_LEAVE.visible()); - await vpn.waitForQueryAndClick( - queries.screenHome.SERVER_LIST_BUTTON.visible()); - - // Final dialog - await vpn.waitForQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - assert( - (await vpn.getQueryProperty( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), - 'text')) === 'Let’s go!'); - await vpn.clickOnQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - - await vpn.waitForCondition(async () => { - return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - exitCityName; - }); - - assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - exitCityName); - assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === - exitCountryCode); - }); + // it('Empty addon index', async () => { + // await vpn.resetAddons('01_empty_manifest'); + // await vpn.waitForCondition(async () => { + // return parseInt( + // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + // 0; + // }); + // }); + + // it('Broken addon index', async () => { + // await vpn.resetAddons('03_single_addon'); + // await vpn.waitForCondition(async () => { + // return parseInt( + // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + // 1; + // }); + + // await vpn.fetchAddons('02_broken_manifest'); + // await vpn.waitForCondition(async () => { + // return parseInt( + // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + // 1; + // }); + // }); + + // it('Addons are loaded', async () => { + // await vpn.resetAddons('03_single_addon'); + // await vpn.waitForCondition(async () => { + // return parseInt( + // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + // 1; + // }); + + // await vpn.fetchAddons('01_empty_manifest'); + // await vpn.waitForCondition(async () => { + // return parseInt( + // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + // 0; + // }); + + // await vpn.fetchAddons('03_single_addon'); + // await vpn.waitForCondition(async () => { + // return parseInt( + // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + // 1; + // }); + // }); + + // it('Settings rollback - location', async () => { + // // Loading the custom tutorial + // await vpn.resetAddons('05_settings_rollback'); + + // await vpn.waitForCondition(async () => { + // return parseInt( + // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + // 1; + // }); + + // const exitCityName = + // await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName'); + // const exitCountryCode = + // await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode'); + + // // Let's start the tutorial + // await vpn.waitForQueryAndClick(queries.navBar.SETTINGS.visible()); + // await vpn.waitForQueryAndClick( + // queries.screenSettings.TIPS_AND_TRICKS.visible()); + // await vpn.waitForQueryAndClick( + // queries.screenSettings.TUTORIAL_LIST_HIGHLIGHT.visible()); + + // // Confirmation dialog for settings-rollback + // await vpn.waitForQuery( + // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + // assert( + // (await vpn.getQueryProperty( + // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), + // 'text')) === 'Continue'); + // await vpn.clickOnQuery( + // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + + // await vpn.waitForCondition(async () => { + // return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + // 'Vienna'; + // }); + + // assert( + // await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + // 'Vienna'); + // assert( + // await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === + // 'at'); + + // await vpn.waitForQuery(queries.screenHome.TUTORIAL_LEAVE.visible()); + // await vpn.waitForQueryAndClick( + // queries.screenHome.SERVER_LIST_BUTTON.visible()); + + // // Final dialog + // await vpn.waitForQuery( + // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + // assert( + // (await vpn.getQueryProperty( + // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), + // 'text')) === 'Let’s go!'); + // await vpn.clickOnQuery( + // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + + // await vpn.waitForCondition(async () => { + // return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + // exitCityName; + // }); + + // assert( + // await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + // exitCityName); + // assert( + // await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === + // exitCountryCode); + // }); describe('test message_subscription_expiring addon condition', async () => { - async function checkForSubscriptionExpiringMessage(ctx, subscriptionExpirationCases, shouldBeAvailable) { - for (const expiresOn of subscriptionExpirationCases) { - const mockDetails = { ...SubscriptionDetails }; - // We are faking a Stripe subscription, so this value is expected to be in seconds. - mockDetails.subscription.current_period_end = expiresOn / 1000; - ctx.guardianSubscriptionDetailsCallback = () => { - ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; - ctx.guardianOverrideEndpoints - .GETs['/api/v1/vpn/subscriptionDetails'] - .body = mockDetails; - }; - - // Restart the VPN to load the new sub details. - await vpn.quit(); - await startAndConnect(); - + const subscriptionAboutToExpire = Array.from( + { length: 7 }, + // 1 to 7 days out from expiring. + (_, i) => [Date.now() + 1000 * 60 * 60 * 24 * (i + 1), true] + ); + + const subscriptionIsFarFromExpiring = [ + // Seven days out + a minute from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60, false], + // Eight days from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 8, false], + // One month from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 30, false], + ]; + + const subscriptionHasExpired = [ + // Literally, has just expired. + [Date.now(), false], + // Has been expired for a day. + [Date.now() - 1000 * 60 * 60 * 24, false], + // Has been expired for 30 days. + [Date.now() - 1000 * 60 * 60 * 24 * 30, false], + ]; + + [ + ...subscriptionAboutToExpire, + ...subscriptionIsFarFromExpiring, + ...subscriptionHasExpired, + ].forEach(([expiresOn, shouldBeAvailable]) => { + const mockDetails = { ...SubscriptionDetails }; + // We are faking a Stripe subscription, so this value is expected to be in seconds. + mockDetails.subscription.current_period_end = expiresOn / 1000; + this.ctx.guardianSubscriptionDetailsCallback = () => { + this.ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; + this.ctx.guardianOverrideEndpoints + .GETs['/api/v1/vpn/subscriptionDetails'] + .body = mockDetails; + }; + + it(`message display is correct for subscription expiration on ${expiresOn}`, async () => { // Load all production addons. // These are loaded all together, so we don't know the exact number of addons. await vpn.resetAddons('prod'); @@ -154,50 +178,10 @@ describe('Addons', function() { parseInt(await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) > 0 )); - await vpn.waitForCondition(async () => { - const loadedMessages = await vpn.messages(); - console.log(loadedMessages) - const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); - - return shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable; - }); - } - } - - it('message is enabled when subscription is about to expire', async () => { - // 1 to 7 days out from expiring. - const subscriptionExpirationCases = Array.from( - { length: 7 }, - (_, i) => Date.now() + 1000 * 60 * 60 * 24 * (i + 1) - ); - - await checkForSubscriptionExpiringMessage(this.ctx, subscriptionExpirationCases, true); - }); - - it('message is not enabled when subscription is not about to expire', async () => { - const subscriptionExpirationCases = [ - // Seven days out + a minute from expiring. - Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60, - // Eight days from expiring. - Date.now() + 1000 * 60 * 60 * 24 * 8, - // One month from expiring. - Date.now() + 1000 * 60 * 60 * 24 * 30, - ] - - await checkForSubscriptionExpiringMessage(this.ctx, subscriptionExpirationCases, false); - }); - - it('message is not enabled when subscription is already expired', async () => { - const subscriptionExpirationCases = [ - // Literally, has just expired. - Date.now(), - // Has been expired for a day. - Date.now() - 1000 * 60 * 60 * 24, - // Has been expired for 30 days. - Date.now() - 1000 * 60 * 60 * 24 * 30, - ] - - await checkForSubscriptionExpiringMessage(this.ctx, subscriptionExpirationCases, false); + const loadedMessages = await vpn.messages(); + const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); + assert(shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable); + }); }); }); }); From 1de44689def06066098e25bd526c3273e9eee713 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Wed, 1 Feb 2023 15:12:30 +0100 Subject: [PATCH 6/9] Make test work --- tests/functional/testAddons.js | 318 +++++++++++++++++---------------- 1 file changed, 165 insertions(+), 153 deletions(-) diff --git a/tests/functional/testAddons.js b/tests/functional/testAddons.js index 6a28c481fa..0bac2cf449 100644 --- a/tests/functional/testAddons.js +++ b/tests/functional/testAddons.js @@ -11,166 +11,174 @@ const { startAndConnect } = require('./setupVpn.js') describe('Addons', function() { this.ctx.authenticationNeeded = true; - // it('Empty addon index', async () => { - // await vpn.resetAddons('01_empty_manifest'); - // await vpn.waitForCondition(async () => { - // return parseInt( - // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - // 0; - // }); - // }); - - // it('Broken addon index', async () => { - // await vpn.resetAddons('03_single_addon'); - // await vpn.waitForCondition(async () => { - // return parseInt( - // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - // 1; - // }); - - // await vpn.fetchAddons('02_broken_manifest'); - // await vpn.waitForCondition(async () => { - // return parseInt( - // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - // 1; - // }); - // }); - - // it('Addons are loaded', async () => { - // await vpn.resetAddons('03_single_addon'); - // await vpn.waitForCondition(async () => { - // return parseInt( - // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - // 1; - // }); - - // await vpn.fetchAddons('01_empty_manifest'); - // await vpn.waitForCondition(async () => { - // return parseInt( - // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - // 0; - // }); - - // await vpn.fetchAddons('03_single_addon'); - // await vpn.waitForCondition(async () => { - // return parseInt( - // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - // 1; - // }); - // }); - - // it('Settings rollback - location', async () => { - // // Loading the custom tutorial - // await vpn.resetAddons('05_settings_rollback'); - - // await vpn.waitForCondition(async () => { - // return parseInt( - // await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - // 1; - // }); - - // const exitCityName = - // await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName'); - // const exitCountryCode = - // await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode'); - - // // Let's start the tutorial - // await vpn.waitForQueryAndClick(queries.navBar.SETTINGS.visible()); - // await vpn.waitForQueryAndClick( - // queries.screenSettings.TIPS_AND_TRICKS.visible()); - // await vpn.waitForQueryAndClick( - // queries.screenSettings.TUTORIAL_LIST_HIGHLIGHT.visible()); - - // // Confirmation dialog for settings-rollback - // await vpn.waitForQuery( - // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - // assert( - // (await vpn.getQueryProperty( - // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), - // 'text')) === 'Continue'); - // await vpn.clickOnQuery( - // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - - // await vpn.waitForCondition(async () => { - // return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - // 'Vienna'; - // }); - - // assert( - // await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - // 'Vienna'); - // assert( - // await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === - // 'at'); - - // await vpn.waitForQuery(queries.screenHome.TUTORIAL_LEAVE.visible()); - // await vpn.waitForQueryAndClick( - // queries.screenHome.SERVER_LIST_BUTTON.visible()); - - // // Final dialog - // await vpn.waitForQuery( - // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - // assert( - // (await vpn.getQueryProperty( - // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), - // 'text')) === 'Let’s go!'); - // await vpn.clickOnQuery( - // queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); - - // await vpn.waitForCondition(async () => { - // return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - // exitCityName; - // }); - - // assert( - // await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - // exitCityName); - // assert( - // await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === - // exitCountryCode); - // }); + it('Empty addon index', async () => { + await vpn.resetAddons('01_empty_manifest'); + await vpn.waitForCondition(async () => { + return parseInt( + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 0; + }); + }); + + it('Broken addon index', async () => { + await vpn.resetAddons('03_single_addon'); + await vpn.waitForCondition(async () => { + return parseInt( + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; + }); + + await vpn.fetchAddons('02_broken_manifest'); + await vpn.waitForCondition(async () => { + return parseInt( + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; + }); + }); + + it('Addons are loaded', async () => { + await vpn.resetAddons('03_single_addon'); + await vpn.waitForCondition(async () => { + return parseInt( + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; + }); + + await vpn.fetchAddons('01_empty_manifest'); + await vpn.waitForCondition(async () => { + return parseInt( + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 0; + }); + + await vpn.fetchAddons('03_single_addon'); + await vpn.waitForCondition(async () => { + return parseInt( + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; + }); + }); + + it('Settings rollback - location', async () => { + // Loading the custom tutorial + await vpn.resetAddons('05_settings_rollback'); + + await vpn.waitForCondition(async () => { + return parseInt( + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; + }); + + const exitCityName = + await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName'); + const exitCountryCode = + await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode'); + + // Let's start the tutorial + await vpn.waitForQueryAndClick(queries.navBar.SETTINGS.visible()); + await vpn.waitForQueryAndClick( + queries.screenSettings.TIPS_AND_TRICKS.visible()); + await vpn.waitForQueryAndClick( + queries.screenSettings.TUTORIAL_LIST_HIGHLIGHT.visible()); + + // Confirmation dialog for settings-rollback + await vpn.waitForQuery( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + assert( + (await vpn.getQueryProperty( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), + 'text')) === 'Continue'); + await vpn.clickOnQuery( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + + await vpn.waitForCondition(async () => { + return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + 'Vienna'; + }); + + assert( + await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + 'Vienna'); + assert( + await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === + 'at'); + + await vpn.waitForQuery(queries.screenHome.TUTORIAL_LEAVE.visible()); + await vpn.waitForQueryAndClick( + queries.screenHome.SERVER_LIST_BUTTON.visible()); + + // Final dialog + await vpn.waitForQuery( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + assert( + (await vpn.getQueryProperty( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), + 'text')) === 'Let’s go!'); + await vpn.clickOnQuery( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + + await vpn.waitForCondition(async () => { + return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + exitCityName; + }); + + assert( + await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + exitCityName); + assert( + await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === + exitCountryCode); + }); describe('test message_subscription_expiring addon condition', async () => { - const subscriptionAboutToExpire = Array.from( - { length: 7 }, - // 1 to 7 days out from expiring. - (_, i) => [Date.now() + 1000 * 60 * 60 * 24 * (i + 1), true] - ); - - const subscriptionIsFarFromExpiring = [ - // Seven days out + a minute from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60, false], + const testCases = [ + ...Array.from( + { length: 7 }, + // 1 to 7 days out from expiring. + (_, i) => [Date.now() + 1000 * 60 * 60 * 24 * (i + 1), true, `is ${i + 1} day(s) away`] + ), + // Seven days out + five minutes from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60 * 5, false, "is 7 days and five minutes away"], // Eight days from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 8, false], + [Date.now() + 1000 * 60 * 60 * 24 * 8, false, "is 8 days away"], // One month from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 30, false], - ]; - - const subscriptionHasExpired = [ + [Date.now() + 1000 * 60 * 60 * 24 * 30, false, "is one month away"], // Literally, has just expired. - [Date.now(), false], + [Date.now(), false, "just happened"], // Has been expired for a day. - [Date.now() - 1000 * 60 * 60 * 24, false], + [Date.now() - 1000 * 60 * 60 * 24, false, "was yesterday"], // Has been expired for 30 days. - [Date.now() - 1000 * 60 * 60 * 24 * 30, false], + [Date.now() - 1000 * 60 * 60 * 24 * 30, false, "was last month"], ]; - [ - ...subscriptionAboutToExpire, - ...subscriptionIsFarFromExpiring, - ...subscriptionHasExpired, - ].forEach(([expiresOn, shouldBeAvailable]) => { + const getNextTestCase = testCases[Symbol.iterator](); + function setNextSubscriptionExpiry(ctx) { const mockDetails = { ...SubscriptionDetails }; - // We are faking a Stripe subscription, so this value is expected to be in seconds. - mockDetails.subscription.current_period_end = expiresOn / 1000; - this.ctx.guardianSubscriptionDetailsCallback = () => { - this.ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; - this.ctx.guardianOverrideEndpoints - .GETs['/api/v1/vpn/subscriptionDetails'] - .body = mockDetails; - }; - - it(`message display is correct for subscription expiration on ${expiresOn}`, async () => { + const nextTestCase = getNextTestCase.next().value; + + if (nextTestCase) { + const [ expiresOn ] = nextTestCase; + // We are faking a Stripe subscription, so this value is expected to be in seconds. + mockDetails.subscription.current_period_end = expiresOn / 1000; + + ctx.guardianSubscriptionDetailsCallback = () => { + ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; + ctx.guardianOverrideEndpoints + .GETs['/api/v1/vpn/subscriptionDetails'] + .body = mockDetails; + }; + } + } + + // We call this once before all tests to set up the first test, + // we can't use beforeEach because that is executed after the guardian endpoints are overriden. + // + // We need to setup for the next test before it even starts for the overrides to apply. + setNextSubscriptionExpiry(this.ctx); + afterEach(() => setNextSubscriptionExpiry(this.ctx)); + + testCases.forEach(([_, shouldBeAvailable, testCase]) => { + it(`message display is correct when subscription expiration ${testCase}`, async () => { // Load all production addons. // These are loaded all together, so we don't know the exact number of addons. await vpn.resetAddons('prod'); @@ -178,9 +186,13 @@ describe('Addons', function() { parseInt(await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) > 0 )); - const loadedMessages = await vpn.messages(); - const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); - assert(shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable); + + // Check if the message is there or not. + await vpn.waitForCondition(async () => { + const loadedMessages = await vpn.messages(); + const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); + return shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable; + }); }); }); }); From 9530093757be451a29a25b3d0a55623668e0e951 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Wed, 8 Feb 2023 14:43:47 +0100 Subject: [PATCH 7/9] Skip sub expiring tests when in wasm --- tests/functional/testAddons.js | 212 +++++++++++++++++---------------- 1 file changed, 108 insertions(+), 104 deletions(-) diff --git a/tests/functional/testAddons.js b/tests/functional/testAddons.js index 0bac2cf449..60a27b6819 100644 --- a/tests/functional/testAddons.js +++ b/tests/functional/testAddons.js @@ -5,18 +5,18 @@ const assert = require('assert'); const queries = require('./queries.js'); const vpn = require('./helper.js'); -const { SubscriptionDetails } = require('./servers/guardian_endpoints.js') -const { startAndConnect } = require('./setupVpn.js') +const { SubscriptionDetails } = require('./servers/guardian_endpoints.js'); +const { env, TestingEnvironments } = require('./helper.js'); -describe('Addons', function() { +describe('Addons', function () { this.ctx.authenticationNeeded = true; it('Empty addon index', async () => { await vpn.resetAddons('01_empty_manifest'); await vpn.waitForCondition(async () => { return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 0; + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 0; }); }); @@ -24,15 +24,15 @@ describe('Addons', function() { await vpn.resetAddons('03_single_addon'); await vpn.waitForCondition(async () => { return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; }); await vpn.fetchAddons('02_broken_manifest'); await vpn.waitForCondition(async () => { return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; }); }); @@ -40,22 +40,22 @@ describe('Addons', function() { await vpn.resetAddons('03_single_addon'); await vpn.waitForCondition(async () => { return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; }); await vpn.fetchAddons('01_empty_manifest'); await vpn.waitForCondition(async () => { return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 0; + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 0; }); await vpn.fetchAddons('03_single_addon'); await vpn.waitForCondition(async () => { return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; }); }); @@ -65,135 +65,139 @@ describe('Addons', function() { await vpn.waitForCondition(async () => { return parseInt( - await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === - 1; + await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) === + 1; }); const exitCityName = - await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName'); + await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName'); const exitCountryCode = - await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode'); + await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode'); // Let's start the tutorial await vpn.waitForQueryAndClick(queries.navBar.SETTINGS.visible()); await vpn.waitForQueryAndClick( - queries.screenSettings.TIPS_AND_TRICKS.visible()); + queries.screenSettings.TIPS_AND_TRICKS.visible()); await vpn.waitForQueryAndClick( - queries.screenSettings.TUTORIAL_LIST_HIGHLIGHT.visible()); + queries.screenSettings.TUTORIAL_LIST_HIGHLIGHT.visible()); // Confirmation dialog for settings-rollback await vpn.waitForQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); assert( - (await vpn.getQueryProperty( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), - 'text')) === 'Continue'); + (await vpn.getQueryProperty( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), + 'text')) === 'Continue'); await vpn.clickOnQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); await vpn.waitForCondition(async () => { return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - 'Vienna'; + 'Vienna'; }); assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - 'Vienna'); + await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + 'Vienna'); assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === - 'at'); + await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === + 'at'); await vpn.waitForQuery(queries.screenHome.TUTORIAL_LEAVE.visible()); await vpn.waitForQueryAndClick( - queries.screenHome.SERVER_LIST_BUTTON.visible()); + queries.screenHome.SERVER_LIST_BUTTON.visible()); // Final dialog await vpn.waitForQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); assert( - (await vpn.getQueryProperty( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), - 'text')) === 'Let’s go!'); + (await vpn.getQueryProperty( + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible(), + 'text')) === 'Let’s go!'); await vpn.clickOnQuery( - queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); + queries.screenHome.TUTORIAL_POPUP_PRIMARY_BUTTON.visible()); await vpn.waitForCondition(async () => { return await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - exitCityName; + exitCityName; }); assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === - exitCityName); + await vpn.getVPNProperty('VPNCurrentServer', 'exitCityName') === + exitCityName); assert( - await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === - exitCountryCode); + await vpn.getVPNProperty('VPNCurrentServer', 'exitCountryCode') === + exitCountryCode); }); - describe('test message_subscription_expiring addon condition', async () => { - const testCases = [ - ...Array.from( - { length: 7 }, - // 1 to 7 days out from expiring. - (_, i) => [Date.now() + 1000 * 60 * 60 * 24 * (i + 1), true, `is ${i + 1} day(s) away`] - ), - // Seven days out + five minutes from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60 * 5, false, "is 7 days and five minutes away"], - // Eight days from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 8, false, "is 8 days away"], - // One month from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 30, false, "is one month away"], - // Literally, has just expired. - [Date.now(), false, "just happened"], - // Has been expired for a day. - [Date.now() - 1000 * 60 * 60 * 24, false, "was yesterday"], - // Has been expired for 30 days. - [Date.now() - 1000 * 60 * 60 * 24 * 30, false, "was last month"], - ]; - - const getNextTestCase = testCases[Symbol.iterator](); - function setNextSubscriptionExpiry(ctx) { - const mockDetails = { ...SubscriptionDetails }; - const nextTestCase = getNextTestCase.next().value; - - if (nextTestCase) { - const [ expiresOn ] = nextTestCase; - // We are faking a Stripe subscription, so this value is expected to be in seconds. - mockDetails.subscription.current_period_end = expiresOn / 1000; - - ctx.guardianSubscriptionDetailsCallback = () => { - ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; - ctx.guardianOverrideEndpoints - .GETs['/api/v1/vpn/subscriptionDetails'] - .body = mockDetails; - }; + // This tests cannot be run in WASM due to Qt limitations. + // See: https://mozilla-hub.atlassian.net/browse/VPN-4127 + if (typeof process === "object") { + describe('test message_subscription_expiring addon condition', async () => { + const testCases = [ + ...Array.from( + { length: 7 }, + // 1 to 7 days out from expiring. + (_, i) => [Date.now() + 1000 * 60 * 60 * 24 * (i + 1), true, `is ${i + 1} day(s) away`] + ), + // Seven days out + five minutes from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60 * 5, false, "is 7 days and five minutes away"], + // Eight days from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 8, false, "is 8 days away"], + // One month from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 30, false, "is one month away"], + // Literally, has just expired. + [Date.now(), false, "just happened"], + // Has been expired for a day. + [Date.now() - 1000 * 60 * 60 * 24, false, "was yesterday"], + // Has been expired for 30 days. + [Date.now() - 1000 * 60 * 60 * 24 * 30, false, "was last month"], + ]; + + const getNextTestCase = testCases[Symbol.iterator](); + function setNextSubscriptionExpiry(ctx) { + const mockDetails = { ...SubscriptionDetails }; + const nextTestCase = getNextTestCase.next().value; + + if (nextTestCase) { + const [expiresOn] = nextTestCase; + // We are faking a Stripe subscription, so this value is expected to be in seconds. + mockDetails.subscription.current_period_end = expiresOn / 1000; + + ctx.guardianSubscriptionDetailsCallback = () => { + ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; + ctx.guardianOverrideEndpoints + .GETs['/api/v1/vpn/subscriptionDetails'] + .body = mockDetails; + }; + } } - } - - // We call this once before all tests to set up the first test, - // we can't use beforeEach because that is executed after the guardian endpoints are overriden. - // - // We need to setup for the next test before it even starts for the overrides to apply. - setNextSubscriptionExpiry(this.ctx); - afterEach(() => setNextSubscriptionExpiry(this.ctx)); - - testCases.forEach(([_, shouldBeAvailable, testCase]) => { - it(`message display is correct when subscription expiration ${testCase}`, async () => { - // Load all production addons. - // These are loaded all together, so we don't know the exact number of addons. - await vpn.resetAddons('prod'); - await vpn.waitForCondition(async () => ( - parseInt(await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) > 0 - )); - - - // Check if the message is there or not. - await vpn.waitForCondition(async () => { - const loadedMessages = await vpn.messages(); - const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); - return shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable; + + // We call this once before all tests to set up the first test, + // we can't use beforeEach because that is executed after the guardian endpoints are overriden. + // + // We need to setup for the next test before it even starts for the overrides to apply. + setNextSubscriptionExpiry(this.ctx); + afterEach(() => setNextSubscriptionExpiry(this.ctx)); + + testCases.forEach(([_, shouldBeAvailable, testCase]) => { + it(`message display is correct when subscription expiration ${testCase}`, async () => { + // Load all production addons. + // These are loaded all together, so we don't know the exact number of addons. + await vpn.resetAddons('prod'); + await vpn.waitForCondition(async () => ( + parseInt(await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) > 0 + )); + + + // Check if the message is there or not. + await vpn.waitForCondition(async () => { + const loadedMessages = await vpn.messages(); + const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); + return shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable; + }); }); }); }); - }); + } }); From 3db7454e130bffe88e7f160c37b286e42c24b366 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Wed, 8 Feb 2023 15:37:04 +0100 Subject: [PATCH 8/9] Add min version 2.15 before 2.14 is already feature frozen --- addons/message_subscription_expiring/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/message_subscription_expiring/manifest.json b/addons/message_subscription_expiring/manifest.json index 4cc4b0efc3..e70d6ea554 100644 --- a/addons/message_subscription_expiring/manifest.json +++ b/addons/message_subscription_expiring/manifest.json @@ -4,7 +4,8 @@ "name": "Subscription expiring message", "type": "message", "conditions": { - "javascript": "isExpiring.js" + "javascript": "isExpiring.js", + "min_client_version": "2.15.0" }, "message": { "id": "message_subscription_expiring", From 4f010701aa95ce8a8a799ae140ccdac6ecb07599 Mon Sep 17 00:00:00 2001 From: Beatriz Rizental Date: Wed, 8 Feb 2023 16:32:10 +0100 Subject: [PATCH 9/9] Skip on wasm the right way --- tests/functional/testAddons.js | 132 +++++++++++++++++---------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/tests/functional/testAddons.js b/tests/functional/testAddons.js index 60a27b6819..cc16564b50 100644 --- a/tests/functional/testAddons.js +++ b/tests/functional/testAddons.js @@ -130,74 +130,76 @@ describe('Addons', function () { exitCountryCode); }); - // This tests cannot be run in WASM due to Qt limitations. - // See: https://mozilla-hub.atlassian.net/browse/VPN-4127 - if (typeof process === "object") { - describe('test message_subscription_expiring addon condition', async () => { - const testCases = [ - ...Array.from( - { length: 7 }, - // 1 to 7 days out from expiring. - (_, i) => [Date.now() + 1000 * 60 * 60 * 24 * (i + 1), true, `is ${i + 1} day(s) away`] - ), - // Seven days out + five minutes from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60 * 5, false, "is 7 days and five minutes away"], - // Eight days from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 8, false, "is 8 days away"], - // One month from expiring. - [Date.now() + 1000 * 60 * 60 * 24 * 30, false, "is one month away"], - // Literally, has just expired. - [Date.now(), false, "just happened"], - // Has been expired for a day. - [Date.now() - 1000 * 60 * 60 * 24, false, "was yesterday"], - // Has been expired for 30 days. - [Date.now() - 1000 * 60 * 60 * 24 * 30, false, "was last month"], - ]; - - const getNextTestCase = testCases[Symbol.iterator](); - function setNextSubscriptionExpiry(ctx) { - const mockDetails = { ...SubscriptionDetails }; - const nextTestCase = getNextTestCase.next().value; - - if (nextTestCase) { - const [expiresOn] = nextTestCase; - // We are faking a Stripe subscription, so this value is expected to be in seconds. - mockDetails.subscription.current_period_end = expiresOn / 1000; - - ctx.guardianSubscriptionDetailsCallback = () => { - ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; - ctx.guardianOverrideEndpoints - .GETs['/api/v1/vpn/subscriptionDetails'] - .body = mockDetails; - }; - } + describe('test message_subscription_expiring addon condition', async () => { + const testCases = [ + ...Array.from( + { length: 7 }, + // 1 to 7 days out from expiring. + (_, i) => [Date.now() + 1000 * 60 * 60 * 24 * (i + 1), true, `is ${i + 1} day(s) away`] + ), + // Seven days out + five minutes from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 7 + 1000 * 60 * 5, false, "is 7 days and five minutes away"], + // Eight days from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 8, false, "is 8 days away"], + // One month from expiring. + [Date.now() + 1000 * 60 * 60 * 24 * 30, false, "is one month away"], + // Literally, has just expired. + [Date.now(), false, "just happened"], + // Has been expired for a day. + [Date.now() - 1000 * 60 * 60 * 24, false, "was yesterday"], + // Has been expired for 30 days. + [Date.now() - 1000 * 60 * 60 * 24 * 30, false, "was last month"], + ]; + + const getNextTestCase = testCases[Symbol.iterator](); + function setNextSubscriptionExpiry(ctx) { + const mockDetails = { ...SubscriptionDetails }; + const nextTestCase = getNextTestCase.next().value; + + if (nextTestCase) { + const [expiresOn] = nextTestCase; + // We are faking a Stripe subscription, so this value is expected to be in seconds. + mockDetails.subscription.current_period_end = expiresOn / 1000; + + ctx.guardianSubscriptionDetailsCallback = () => { + ctx.guardianOverrideEndpoints.GETs['/api/v1/vpn/subscriptionDetails'].status = 200; + ctx.guardianOverrideEndpoints + .GETs['/api/v1/vpn/subscriptionDetails'] + .body = mockDetails; + }; } + } + + // We call this once before all tests to set up the first test, + // we can't use beforeEach because that is executed after the guardian endpoints are overriden. + // + // We need to setup for the next test before it even starts for the overrides to apply. + setNextSubscriptionExpiry(this.ctx); + afterEach(() => setNextSubscriptionExpiry(this.ctx)); + + testCases.forEach(([_, shouldBeAvailable, testCase]) => { + it(`message display is correct when subscription expiration ${testCase}`, async () => { + // This tests cannot be run in WASM due to Qt limitations. + // See: https://mozilla-hub.atlassian.net/browse/VPN-4127 + if (this.ctx.wasm) { + return; + } + + // Load all production addons. + // These are loaded all together, so we don't know the exact number of addons. + await vpn.resetAddons('prod'); + await vpn.waitForCondition(async () => ( + parseInt(await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) > 0 + )); - // We call this once before all tests to set up the first test, - // we can't use beforeEach because that is executed after the guardian endpoints are overriden. - // - // We need to setup for the next test before it even starts for the overrides to apply. - setNextSubscriptionExpiry(this.ctx); - afterEach(() => setNextSubscriptionExpiry(this.ctx)); - - testCases.forEach(([_, shouldBeAvailable, testCase]) => { - it(`message display is correct when subscription expiration ${testCase}`, async () => { - // Load all production addons. - // These are loaded all together, so we don't know the exact number of addons. - await vpn.resetAddons('prod'); - await vpn.waitForCondition(async () => ( - parseInt(await vpn.getVPNProperty('VPNAddonManager', 'count'), 10) > 0 - )); - - - // Check if the message is there or not. - await vpn.waitForCondition(async () => { - const loadedMessages = await vpn.messages(); - const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); - return shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable; - }); + + // Check if the message is there or not. + await vpn.waitForCondition(async () => { + const loadedMessages = await vpn.messages(); + const isSubscriptionExpiringMessageAvailable = loadedMessages.includes("message_subscription_expiring"); + return shouldBeAvailable ? isSubscriptionExpiringMessageAvailable : !isSubscriptionExpiringMessageAvailable; }); }); }); - } + }); });