Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VPN-2452 - Implement subscription expiring message #5743

Merged
merged 9 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/functional_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
with:
submodules: 'true'

- name: Install build dependecies
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:

functionaltests:
name: Functional tests
needs:
needs:
- build_test_app
runs-on: ubuntu-22.04
timeout-minutes: 45
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/wasm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
with:
submodules: 'true'

- name: Install Qt
Expand Down Expand Up @@ -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
Expand All @@ -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}}
3 changes: 3 additions & 0 deletions addons/message_subscription_expiring/contactSupportLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
((api) => {
api.urlOpener.openLink(api.urlOpener.LinkHelpSupport);
});
13 changes: 13 additions & 0 deletions addons/message_subscription_expiring/isExpiring.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(function(api, condition) {
// Show message only if within 1 week of expiring.
const weekBeforeExpireMSecs = api.subscriptionData.expiresOn - 1000 * 60 * 60 * 24 * 7;
const subscriptionExpiry = api.subscriptionData.expiresOn;
const now = Date.now();

if (now < subscriptionExpiry && now >= weekBeforeExpireMSecs) {
api.addon.date = weekBeforeExpireMSecs;
condition.enable();
} else {
condition.disable();
}
});
3 changes: 3 additions & 0 deletions addons/message_subscription_expiring/manageAccountLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
((api) => {
api.urlOpener.openLink(api.urlOpener.LinkAccount);
});
37 changes: 37 additions & 0 deletions addons/message_subscription_expiring/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"api_version": "0.1",
"id": "message_subscription_expiring",
"name": "Subscription expiring message",
"type": "message",
"conditions": {
"javascript": "isExpiring.js",
"min_client_version": "2.15.0"
},
"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"
}
]
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
16 changes: 12 additions & 4 deletions scripts/addon/generate_all_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions src/apps/vpn/inspector/inspectorhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,24 @@ static QList<InspectorCommand> s_commands{
return obj;
}},

InspectorCommand{"messages", "Returns a list of the loaded messages ids", 0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a similar method for guides. Wondering if we should return all the add-ons with types.

[](InspectorHandler*, const QList<QByteArray>&) {
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<QByteArray>& arguments) {
QJsonObject obj;
Expand Down
8 changes: 8 additions & 0 deletions tests/functional/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
16 changes: 9 additions & 7 deletions tests/functional/servers/addon.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 = {};
Expand Down Expand Up @@ -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 = {
Expand Down
2 changes: 2 additions & 0 deletions tests/functional/servers/guardian_endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const SubscriptionDetails = {
},
};

exports.SubscriptionDetails = SubscriptionDetails;

const VALIDATORS = {
guardianLoginVerify: {
type: 'object',
Expand Down
2 changes: 2 additions & 0 deletions tests/functional/setupVpn.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ async function startAndConnect() {
await vpn.connect(vpnWS, {hostname: '127.0.0.1'});
}

exports.startAndConnect = startAndConnect;
brizental marked this conversation as resolved.
Show resolved Hide resolved

exports.mochaHooks = {
async beforeAll() {
// Check VPN app exists. If not, bail.
Expand Down
Loading