VPN landing page headline experiment [fix #14131] (#14144)

Remove VPN pricing position experiment, move pricing to top [fix #14176]
This commit is contained in:
Craig Cook 2024-02-06 17:17:27 -08:00 коммит произвёл GitHub
Родитель 30edb0e1fd
Коммит 72bc0add9d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
12 изменённых файлов: 167 добавлений и 248 удалений

Просмотреть файл

@ -33,8 +33,8 @@
{% set _params = '?utm_source=' ~ _utm_source ~ '&utm_medium=referral&utm_campaign=' ~ _utm_campaign %}
{% block experiments %}
{% if switch('experiment-vpn-pricing-position') %}
{{ js_bundle('mozilla-vpn-landing-pricing-position-experiment') }}
{% if switch('experiment-vpn-headlines', ['en-US', 'en-CA', 'en-GB', 'fr', 'de']) %}
{{ js_bundle('mozilla-vpn-landing-headlines-experiment') }}
{% endif %}
{% endblock %}
@ -55,6 +55,17 @@
{% include 'products/vpn/includes/subnav-refresh.html' %}
{% endblock %}
{% if LANG == "de" %}
{% set headline_exp_v2_sub = 'Schalte den Rundumschutz für Webaktivitäten und Standort vor Hackern und Trackern ein.' %}
{% set headline_exp_v3_sub = 'Schütze deine Online-Aktivitäten und deinen Standort mit nur einem Klick.' %}
{% elif LANG == "fr" %}
{% set headline_exp_v2_sub = 'Masquez intégralement vos activités sur le Web et votre emplacement aux yeux des hackers et des traqueurs.' %}
{% set headline_exp_v3_sub = 'Protégez vos activités numériques et votre emplacement en un seul clic.' %}
{% else %}
{% set headline_exp_v2_sub = 'Switch on complete web activity and location protection from hackers and trackers.' %}
{% set headline_exp_v3_sub = 'Protect your digital activity and physical location with just one click.' %}
{% endif %}
{% block content %}
<main>
{% if vpn_affiliate_attribution_enabled %}
@ -83,7 +94,15 @@
media_class='mzp-l-split-h-center',
media_after=True
) %}
{% if entrypoint_experiment == 'vpn-headlines' and entrypoint_variation == '2' %}
<h1 class="u-title-xl">{{ ftl('vpn-landing-powerful-privacy-for-peace') }}</h1>
<h2 class="c-page-subtitle u-title-xs">{{ headline_exp_v2_sub }}</h2>
{% elif entrypoint_experiment == 'vpn-headlines' and entrypoint_variation == '3' %}
<h1>Mozilla VPN</h1>
<h2 class="c-page-subtitle u-title-xs">{{ headline_exp_v3_sub }}</h2>
{% else %}
<h1>{{ ftl('vpn-landing-powerful-privacy-for-peace') }}</h1>
{% endif %}
{% if vpn_available %}
<p class="c-main-cta">
@ -140,7 +159,25 @@
</p>
{% endcall %}
{% block top_pricing_position %}{% endblock %}
{# note: `id=#pricing` is used as an anchor link from android in-product subscription flows, so do not remove (issue 10039) #}
<section id="pricing" class="mzp-l-content mzp-t-content-xl">
{% if vpn_available %}
<header class="c-pricing-main-header">
<h3 class="c-section-title">{{ ftl('vpn-landing-one-subscription-for-all-your') }}</h3>
</header>
{% include 'products/vpn/includes/pricing-refresh.html' %}
{% else %}
<header class="c-pricing-main-header">
<h2 class="c-section-title">{{ ftl('vpn-shared-mozilla-vpn-is-not-yet-available') }}</h2>
<a class="mzp-c-button mzp-t-product mzp-t-xl" href="{{ url('products.vpn.invite') }}" data-cta-type="button" data-cta-text="Join the VPN Waitlist">
{{ ftl('vpn-shared-waitlist-link') }}
</a>
</header>
{% endif %}
</section>
<section class="c-how-vpn-helps mzp-l-content mzp-t-content-xl">
<h2 class="c-section-title">{{ ftl('vpn-landing-how-a-vpn-helps-you') }}</h2>
@ -294,28 +331,6 @@
<p>{{ ftl('vpn-landing-mozilla-is-a-non-profit-backed') }}</p>
{% endcall %}
{% block default_pricing_position %}
{# note: `id=#pricing` is used as an anchor link from android in-product subscription flows, so do not remove (issue 10039) #}
<section id="pricing" class="mzp-l-content mzp-t-content-xl">
{% if vpn_available %}
<header class="c-pricing-main-header">
<h3 class="c-section-title">{{ ftl('vpn-landing-one-subscription-for-all-your') }}</h3>
</header>
{% include 'products/vpn/includes/pricing-refresh.html' %}
{% else %}
<header class="c-pricing-main-header">
<h2 class="c-section-title">{{ ftl('vpn-shared-mozilla-vpn-is-not-yet-available') }}</h2>
<a class="mzp-c-button mzp-t-product mzp-t-xl" href="{{ url('products.vpn.invite') }}" data-cta-type="button" data-cta-text="Join the VPN Waitlist">
{{ ftl('vpn-shared-waitlist-link') }}
</a>
</header>
{% endif %}
</section>
{% endblock %}
{% include 'products/vpn/includes/press.html' %}
<section class="c-learn-more mzp-l-content mzp-t-content-xl">

Просмотреть файл

@ -33,12 +33,6 @@
{% set _utm_campaign = 'vpn-product-page' %}
{% set _params = '?utm_source=' ~ _utm_source ~ '&utm_medium=referral&utm_campaign=' ~ _utm_campaign %}
{% block experiments %}
{% if switch('experiment-vpn-pricing-position') %}
{{ js_bundle('mozilla-vpn-landing-pricing-position-experiment') }}
{% endif %}
{% endblock %}
{% block page_css %}
{{ css_bundle('mozilla-vpn-landing') }}
{% endblock %}
@ -96,7 +90,39 @@
<p>{{ ftl('vpn-landing-privacy-desc') }}</p>
{% endcall %}
{% block top_pricing_position %}{% endblock %}
{# note: `id=#pricing` is used as an anchor link from android in-product subscription flows, so do not remove (issue 10039) #}
<div id="pricing" class="mzp-is-anchor-link">
{% if vpn_available %}
{% include 'products/vpn/includes/pricing-plus-relay.html' %}
{% else %}
<div class="vpn-waitlist-feature-block">
{% call vpn_content_block(
class_name='vpn-content-block-price t-highlight'
) %}
<div class="l-columns-two">
<div class="l-column-first">
<a class="mzp-c-button mzp-t-xl" href="{{ url('products.vpn.invite') }}" data-cta-type="button" data-cta-text="Join the VPN Waitlist" data-cta-position="secondary">
{{ ftl('vpn-shared-waitlist-link') }}
</a>
<p class="availability-copy">
{{ ftl('vpn-shared-available-countries-v6') }}
</p>
</div>
<div class="l-column-last">
<ul class="mzp-u-list-styled vpn-feature-list">
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-devices', devices=connect_devices) }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-servers', servers=connect_servers, countries=connect_countries) }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-encrypt') }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-bandwidth') }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-activity') }}</li>
</ul>
</div>
</div>
{% endcall %}
</div>
{% endif %}
</div>
{% call vpn_content_media(
heading=ftl('vpn-landing-fast-secure-heading'),
@ -217,42 +243,6 @@
{% endcall %}
</div>
{% endif %}
{% block default_pricing_position %}
{# note: `id=#pricing` is used as an anchor link from android in-product subscription flows, so do not remove (issue 10039) #}
<div id="pricing" class="mzp-is-anchor-link">
{% if vpn_available %}
{% include 'products/vpn/includes/pricing-plus-relay.html' %}
{% else %}
<div class="vpn-waitlist-feature-block">
{% call vpn_content_block(
class_name='vpn-content-block-price t-highlight'
) %}
<div class="l-columns-two">
<div class="l-column-first">
<a class="mzp-c-button mzp-t-xl" href="{{ url('products.vpn.invite') }}" data-cta-type="button" data-cta-text="Join the VPN Waitlist" data-cta-position="secondary">
{{ ftl('vpn-shared-waitlist-link') }}
</a>
<p class="availability-copy">
{{ ftl('vpn-shared-available-countries-v6') }}
</p>
</div>
<div class="l-column-last">
<ul class="mzp-u-list-styled vpn-feature-list">
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-devices', devices=connect_devices) }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-servers', servers=connect_servers, countries=connect_countries) }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-encrypt') }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-bandwidth') }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-activity') }}</li>
</ul>
</div>
</div>
{% endcall %}
</div>
{% endif %}
</div>
{% endblock %}
</div>
<section id="faq" class="vpn-faq mzp-l-content mzp-t-content-lg">

Просмотреть файл

@ -1,7 +0,0 @@
{#
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 https://mozilla.org/MPL/2.0/.
#}
{% extends "products/vpn/landing.html" %}

Просмотреть файл

@ -1,45 +0,0 @@
{#
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 https://mozilla.org/MPL/2.0/.
#}
{% extends "products/vpn/landing.html" %}
{% block top_pricing_position %}
{# note: `id=#pricing` is used as an anchor link from android in-product subscription flows, so do not remove (issue 10039) #}
<div id="pricing" class="mzp-is-anchor-link c-pricing-top">
{% if vpn_available %}
{% include 'products/vpn/includes/pricing-plus-relay.html' %}
{% else %}
<div class="vpn-waitlist-feature-block">
{% call vpn_content_block(
class_name='vpn-content-block-price t-highlight'
) %}
<div class="l-columns-two">
<div class="l-column-first">
<a class="mzp-c-button mzp-t-xl" href="{{ url('products.vpn.invite') }}" data-cta-type="button" data-cta-text="Join the VPN Waitlist" data-cta-position="secondary">
{{ ftl('vpn-shared-waitlist-link') }}
</a>
<p class="availability-copy">
{{ ftl('vpn-shared-available-countries-v6') }}
</p>
</div>
<div class="l-column-last">
<ul class="mzp-u-list-styled vpn-feature-list">
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-devices', devices=connect_devices) }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-servers', servers=connect_servers, countries=connect_countries) }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-encrypt') }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-bandwidth') }}</li>
<li class="vpn-feature-list-item">{{ ftl('vpn-shared-features-activity') }}</li>
</ul>
</div>
</div>
{% endcall %}
</div>
{% endif %}
</div>
{% endblock %}
{% block default_pricing_position %}{% endblock %}

Просмотреть файл

@ -1,7 +0,0 @@
{#
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 https://mozilla.org/MPL/2.0/.
#}
{% extends "products/vpn/landing-refresh.html" %}

Просмотреть файл

@ -1,31 +0,0 @@
{#
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 https://mozilla.org/MPL/2.0/.
#}
{% extends "products/vpn/landing-refresh.html" %}
{% block top_pricing_position %}
{# note: `id=#pricing` is used as an anchor link from android in-product subscription flows, so do not remove (issue 10039) #}
<section id="pricing" class="mzp-l-content mzp-t-content-xl">
{% if vpn_available %}
<header class="c-pricing-main-header">
<h3 class="c-section-title">{{ ftl('vpn-landing-one-subscription-for-all-your') }}</h3>
</header>
{% include 'products/vpn/includes/pricing-refresh.html' %}
{% else %}
<header class="c-pricing-main-header">
<h2 class="c-section-title">{{ ftl('vpn-shared-mozilla-vpn-is-not-yet-available') }}</h2>
<a class="mzp-c-button mzp-t-product mzp-t-xl" href="{{ url('products.vpn.invite') }}" data-cta-type="button" data-cta-text="Join the VPN Waitlist">
{{ ftl('vpn-shared-waitlist-link') }}
</a>
</header>
{% endif %}
</section>
{% endblock %}
{% block default_pricing_position %}{% endblock %}

Просмотреть файл

@ -137,46 +137,6 @@ class TestVPNLandingPage(TestCase):
ctx = render_mock.call_args[0][2]
self.assertFalse(ctx["relay_bundle_available_in_country"])
# start pricing position experiment tests
def test_vpn_landing_refresh_pricing_position_1_template(self, render_mock):
req = RequestFactory().get("/products/vpn/?entrypoint_experiment=vpn-pricing-position&entrypoint_variation=1", HTTP_CF_IPCOUNTRY="fr")
req.locale = "en-US"
view = views.vpn_landing_page
view(req)
template = render_mock.call_args[0][1]
assert template == "products/vpn/variants/landing-refresh-1.html"
def test_vpn_landing_refresh_pricing_position_2_template(self, render_mock):
req = RequestFactory().get("/products/vpn/?entrypoint_experiment=vpn-pricing-position&entrypoint_variation=2", HTTP_CF_IPCOUNTRY="fr")
req.locale = "en-US"
view = views.vpn_landing_page
view(req)
template = render_mock.call_args[0][1]
assert template == "products/vpn/variants/landing-refresh-2.html"
def test_vpn_landing_legacy_pricing_position_1_template(self, render_mock):
req = RequestFactory().get(
"/products/vpn/?entrypoint_experiment=vpn-pricing-position&entrypoint_variation=1&xv=legacy", HTTP_CF_IPCOUNTRY="fr"
)
req.locale = "en-US"
view = views.vpn_landing_page
view(req)
template = render_mock.call_args[0][1]
assert template == "products/vpn/variants/landing-1.html"
def test_vpn_landing_legacy_pricing_position_2_template(self, render_mock):
req = RequestFactory().get(
"/products/vpn/?entrypoint_experiment=vpn-pricing-position&entrypoint_variation=2&xv=legacy", HTTP_CF_IPCOUNTRY="fr"
)
req.locale = "en-US"
view = views.vpn_landing_page
view(req)
template = render_mock.call_args[0][1]
assert template == "products/vpn/variants/landing-2.html"
# end pricing position experiment tests
@patch("bedrock.products.views.l10n_utils.render", return_value=HttpResponse())
class TestVPNPricingPage(TestCase):

Просмотреть файл

@ -57,22 +57,16 @@ def vpn_landing_page(request):
entrypoint_variation = request.GET.get("entrypoint_variation", None)
# ensure experiment parameters matches pre-defined values
if entrypoint_variation not in ["1", "2"]:
if entrypoint_variation not in ["1", "2", "3"]:
entrypoint_variation = None
if entrypoint_experiment not in ["vpn-pricing-position"]:
if entrypoint_experiment not in ["vpn-headlines"]:
entrypoint_experiment = None
if ftl_file_is_active("products/vpn/landing-2023") and experience != "legacy":
if entrypoint_experiment == "vpn-pricing-position" and entrypoint_variation in ["1", "2"]:
template_name = "products/vpn/variants/landing-refresh-{}.html".format(entrypoint_variation)
else:
template_name = "products/vpn/landing-refresh.html"
template_name = "products/vpn/landing-refresh.html"
else:
if entrypoint_experiment == "vpn-pricing-position" and entrypoint_variation in ["1", "2"]:
template_name = "products/vpn/variants/landing-{}.html".format(entrypoint_variation)
else:
template_name = "products/vpn/landing.html"
template_name = "products/vpn/landing.html"
context = {
"vpn_available": vpn_available_in_country,

Просмотреть файл

@ -14,6 +14,15 @@ main {
line-height: $vpn-title-line-height;
}
.u-title-xl {
@include font-size($vpn-title-lg-size);
line-height: 1.2;
@media #{$mq-md} {
@include font-size($vpn-title-xl-size);
}
}
.u-title-lg {
@include font-size($vpn-title-md-size);
line-height: 1.2;
@ -32,6 +41,15 @@ main {
}
}
.u-title-xs {
@include font-size($vpn-title-2xs-size);
line-height: 1.2;
@media #{$mq-md} {
@include font-size($vpn-title-xs-size);
}
}
.u-body-lg {
@include text-body-lg;
}
@ -46,6 +64,11 @@ main {
}
}
.c-page-subtitle {
font-weight: normal;
margin-bottom: $spacing-2xl;
}
.c-section-title {
@include font-size($vpn-title-md-size);
text-align: center;

Просмотреть файл

@ -0,0 +1,65 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
import TrafficCop from '@mozmeao/trafficcop';
import { isApprovedToRun } from '../../base/experiment-utils.es6.js';
const href = window.location.href;
const initTrafficCop = () => {
if (href.indexOf('entrypoint_variation=1') !== -1) {
// UA
window.dataLayer.push({
'data-ex-variant': 'vpn-headline-v1',
'data-ex-name': 'vpn-landing-headlines-test'
});
// GA4
window.dataLayer.push({
event: 'experiment_view',
id: 'vpn-landing-headlines-test',
variant: 'vpn-headline-v1'
});
} else if (href.indexOf('entrypoint_variation=2') !== -1) {
// UA
window.dataLayer.push({
'data-ex-variant': 'vpn-headline-v2',
'data-ex-name': 'vpn-landing-headlines-test'
});
// GA4
window.dataLayer.push({
event: 'experiment_view',
id: 'vpn-landing-headlines-test',
variant: 'vpn-headline-v2'
});
} else if (href.indexOf('entrypoint_variation=3') !== -1) {
// UA
window.dataLayer.push({
'data-ex-variant': 'vpn-headline-v3',
'data-ex-name': 'vpn-landing-headlines-test'
});
// GA4
window.dataLayer.push({
event: 'experiment_view',
id: 'vpn-landing-headlines-test',
variant: 'vpn-headline-v3'
});
} else if (TrafficCop) {
// Avoid entering automated tests into random experiments.
if (isApprovedToRun()) {
const fife = new TrafficCop({
id: 'vpn-headlines',
variations: {
'entrypoint_experiment=vpn-headlines&entrypoint_variation=1': 33, // control
'entrypoint_experiment=vpn-headlines&entrypoint_variation=2': 33, // v2
'entrypoint_experiment=vpn-headlines&entrypoint_variation=3': 33 // v3
}
});
fife.init();
}
}
};
initTrafficCop();

Просмотреть файл

@ -1,38 +0,0 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
import TrafficCop from '@mozmeao/trafficcop';
import { isApprovedToRun } from '../../base/experiment-utils.es6.js';
const href = window.location.href;
const initTrafficCop = () => {
if (href.indexOf('entrypoint_variation=1') !== -1) {
window.dataLayer.push({
'data-ex-variant': 'pricing-bottom',
'data-ex-name': 'vpn-pricing-position'
});
} else if (href.indexOf('entrypoint_variation=2') !== -1) {
window.dataLayer.push({
'data-ex-variant': 'pricing-top',
'data-ex-name': 'vpn-pricing-position'
});
} else if (TrafficCop) {
// Avoid entering automated tests into random experiments.
if (isApprovedToRun()) {
const briscoe = new TrafficCop({
id: 'vpn-pricing-position',
variations: {
'entrypoint_experiment=vpn-pricing-position&entrypoint_variation=1': 50, // control, pricing at bottom
'entrypoint_experiment=vpn-pricing-position&entrypoint_variation=2': 50 // pricing at top
}
});
briscoe.init();
}
}
};
initTrafficCop();

Просмотреть файл

@ -1946,9 +1946,9 @@
},
{
"files": [
"js/products/vpn/landing-experiment-pricing-position.es6.js"
"js/products/vpn/landing-experiment-headlines.es6.js"
],
"name": "mozilla-vpn-landing-pricing-position-experiment"
"name": "mozilla-vpn-landing-headlines-experiment"
},
{
"files": [