Skip to content

Commit 20a47fa

Browse files
authored
Add ad blocking variants to v3 onboarding (#1623)
* Add ad blocking variants to v3 onboarding - Add optional 'Enhanced Ad Blocking' row to the settings step. - Add optional 'YouTube Ad Blocking' row to the settings step. - Update Duck Player step copy when YouTube Ad Blocking is enabled. * Empty Netlify build cache * Revert "Empty Netlify build cache" This reverts commit 0049864. * Use object shorthand * Move preview URL implementation to mock-transport.js * Document onboarding test URL parameters * Remove duplicate locale parameter in onboarding docs
1 parent 34d1d80 commit 20a47fa

File tree

11 files changed

+302
-5
lines changed

11 files changed

+302
-5
lines changed

special-pages/pages/onboarding/app/components/ListItem.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export const availableIcons = /** @type {const} */ ([
2121
'v3/home.svg',
2222
'v3/import.svg',
2323
'v3/session-restore.svg',
24+
'v3/ads.svg',
25+
'v3/video-player.svg',
2426
]);
2527

2628
const prefix = 'assets/img/steps/';

special-pages/pages/onboarding/app/components/v3/data.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@ export const stepsConfig = {
8383
content: <SettingsStep data={settingsRowItems} />,
8484
};
8585
},
86-
duckPlayerSingle: ({ t, advance, beforeAfter }) => {
86+
duckPlayerSingle: ({ t, globalState, advance, beforeAfter }) => {
87+
const isYouTubeAdBlockingEnabled = globalState.values['youtube-ad-blocking']?.enabled ?? false;
88+
const title = isYouTubeAdBlockingEnabled ? t('duckPlayer_alt_title') : t('duckPlayer_title');
89+
const subtitle = isYouTubeAdBlockingEnabled ? t('duckPlayer_alt_subtitle') : t('duckPlayer_subtitle');
8790
const beforeAfterState = beforeAfter.get();
8891
const longestText = [t('beforeAfter_duckPlayer_show'), t('beforeAfter_duckPlayer_hide')].reduce((acc, cur) => {
8992
return cur.length > acc.length ? cur : acc;
@@ -92,8 +95,8 @@ export const stepsConfig = {
9295
return {
9396
variant: 'box',
9497
heading: {
95-
title: t('duckPlayer_title'),
96-
subtitle: t('duckPlayer_subtitle'),
98+
title,
99+
subtitle,
97100
speechBubble: true,
98101
},
99102
dismissButton: {
@@ -204,6 +207,24 @@ export const settingsRowItems = {
204207
acceptText: t('row_home-shortcut_accept'),
205208
accepButtonVariant: 'secondary',
206209
}),
210+
'ad-blocking': (t) => ({
211+
id: 'ad-blocking',
212+
icon: 'v3/ads.svg',
213+
title: t('row_ad-blocking_title_v3'),
214+
secondaryText: t('row_ad-blocking_desc_v3'),
215+
kind: 'one-time',
216+
acceptText: t('row_ad-blocking_accept_v3'),
217+
accepButtonVariant: 'primary',
218+
}),
219+
'youtube-ad-blocking': (t) => ({
220+
id: 'youtube-ad-blocking',
221+
icon: 'v3/video-player.svg',
222+
title: t('row_youtube-ad-blocking_title_v3'),
223+
secondaryText: t('row_youtube-ad-blocking_desc_v3'),
224+
kind: 'one-time',
225+
acceptText: t('row_youtube-ad-blocking_accept_v3'),
226+
accepButtonVariant: 'primary',
227+
}),
207228
};
208229

209230
export const stepDefinitions = {

special-pages/pages/onboarding/app/data.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,28 @@ export const settingsRowItems = {
217217
kind: 'toggle',
218218
acceptText: t('row_home-shortcut_accept'),
219219
}),
220+
// Intended only for use with v3
221+
'ad-blocking': (t) => ({
222+
id: 'ad-blocking',
223+
icon: 'v3/ads.svg',
224+
title: t('row_ad-blocking_title_v3'),
225+
secondaryText: t('row_ad-blocking_desc_v3'),
226+
summary: t('row_ad-blocking_title_v3'),
227+
kind: 'one-time',
228+
acceptText: t('row_ad-blocking_accept_v3'),
229+
accepButtonVariant: 'primary',
230+
}),
231+
// Intended only for use with v3
232+
'youtube-ad-blocking': (t) => ({
233+
id: 'youtube-ad-blocking',
234+
icon: 'v3/video-player.svg',
235+
title: t('row_youtube-ad-blocking_title_v3'),
236+
secondaryText: t('row_youtube-ad-blocking_desc_v3'),
237+
summary: t('row_youtube-ad-blocking_title_v3'),
238+
kind: 'one-time',
239+
acceptText: t('row_youtube-ad-blocking_accept_v3'),
240+
accepButtonVariant: 'primary',
241+
}),
220242
};
221243

222244
/**

special-pages/pages/onboarding/app/global.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ export function GlobalProvider({ order, children, stepDefinitions, messaging, fi
143143
bookmarks: 'idle',
144144
'session-restore': 'idle',
145145
'home-shortcut': 'idle',
146+
'ad-blocking': 'idle',
147+
'youtube-ad-blocking': 'idle',
146148
},
147149
});
148150

@@ -278,6 +280,17 @@ async function handleSystemSettingUpdate(action, messaging, platform) {
278280
}
279281
break;
280282
}
283+
case 'ad-blocking':
284+
case 'youtube-ad-blocking': {
285+
if (!current) {
286+
messaging.setAdBlocking(payload);
287+
} else {
288+
if (payload.enabled) {
289+
messaging.setAdBlocking(payload);
290+
}
291+
}
292+
return payload;
293+
}
281294
}
282295
if ('value' in payload) {
283296
return { enabled: payload.enabled, value: payload.value };

special-pages/pages/onboarding/app/messages.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,13 @@ export class OnboardingMessages {
158158
reportInitException(params) {
159159
this.messaging.notify('reportInitException', params);
160160
}
161+
162+
/**
163+
* Sent when the user wants to enable or disable ad blocking.
164+
*
165+
* @param {import('./types').BooleanSystemValue} params
166+
*/
167+
setAdBlocking(params) {
168+
this.messaging.notify('setAdBlocking', params);
169+
}
161170
}

special-pages/pages/onboarding/app/types.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { useContext } from 'preact/hooks';
1010
* | 'bookmarks'
1111
* | 'session-restore'
1212
* | 'home-shortcut'
13+
* | 'ad-blocking'
14+
* | 'youtube-ad-blocking'
1315
* } SystemValueId - Each setting that can be updated should have a unique ID listed here.
1416
*/
1517

special-pages/pages/onboarding/integration-tests/onboarding.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class OnboardingPage {
2929
requestDockOptIn: {},
3030
requestImport: { enabled: true },
3131
stepCompleted: {},
32+
setAdBlocking: {},
3233
reportPageException: {},
3334
init: {
3435
stepDefinitions: {
@@ -382,6 +383,34 @@ export class OnboardingPage {
382383
]);
383384
}
384385

386+
async enableEnhancedAdBlocking() {
387+
const { page } = this;
388+
await page.getByRole('button', { name: 'Turn on Enhanced Ad Blocking' }).click();
389+
await expect(page.getByRole('img', { name: 'Completed Action' })).toBeVisible();
390+
await this.didSetAdBlocking();
391+
}
392+
393+
async enableYouTubeAdBlocking() {
394+
const { page } = this;
395+
await page.getByRole('button', { name: 'Block Ads' }).click();
396+
await expect(page.getByRole('img', { name: 'Completed Action' })).toBeVisible();
397+
await this.didSetAdBlocking();
398+
}
399+
400+
async didSetAdBlocking() {
401+
const calls = await this.mocks.outgoing({ names: ['setAdBlocking'] });
402+
expect(calls).toMatchObject([
403+
{
404+
payload: {
405+
context: 'specialPages',
406+
featureName: 'onboarding',
407+
method: 'setAdBlocking',
408+
params: { enabled: true },
409+
},
410+
},
411+
]);
412+
}
413+
385414
async startBrowsing() {
386415
const { page } = this;
387416
await page.getByRole('button', { name: 'Start Browsing' }).click();
@@ -514,6 +543,7 @@ export class OnboardingPage {
514543

515544
async completesOrderV3() {
516545
const { page } = this;
546+
517547
/* Welcome */
518548
await page.getByText('Welcome to DuckDuckGo').nth(1).waitFor({ timeout: 1000 });
519549

@@ -580,4 +610,86 @@ export class OnboardingPage {
580610
await page.getByRole('button', { name: 'Show Home Button' }).click();
581611
await this.startBrowsing();
582612
}
613+
614+
async completesOrderV3WithAdBlocking() {
615+
const { page } = this;
616+
617+
/* Welcome */
618+
await page.getByText('Welcome to DuckDuckGo').nth(1).waitFor({ timeout: 1000 });
619+
620+
/* Get started */
621+
await page.getByText('Hi there').nth(1).waitFor({ timeout: 1500 });
622+
await page.getByRole('button', { name: 'Let’s Do It' }).click();
623+
624+
/* Make default */
625+
await page.getByText('Protections activated').nth(1).waitFor({ timeout: 1000 });
626+
await page.getByRole('button', { name: 'Make DuckDuckGo Your Default' }).click();
627+
await page.getByText('Excellent!').nth(1).waitFor({ timeout: 1000 });
628+
await page.getByRole('button', { name: 'Next' }).click();
629+
630+
/* System settings (with ad-blocking) */
631+
await page.getByText('Let’s get you set up!').nth(1).waitFor({ timeout: 1000 });
632+
const dockButton = this.build.switch({
633+
windows: () => page.getByRole('button', { name: 'Pin to Taskbar' }),
634+
apple: () => page.getByRole('button', { name: 'Keep in Dock' }),
635+
});
636+
await dockButton.click();
637+
await page.getByRole('button', { name: 'Turn on Enhanced Ad Blocking', exact: true }).click();
638+
await page.getByRole('button', { name: 'Import Now', exact: true }).click();
639+
await page.getByRole('button', { name: 'Next' }).click();
640+
641+
/* Duckplayer */
642+
await page.getByText('Drowning in ads').nth(1).waitFor({ timeout: 1000 });
643+
await page.getByLabel('See Without Duck Player').click();
644+
await page.getByLabel('See With Duck Player').click();
645+
await page.getByRole('button', { name: 'Next' }).click();
646+
647+
/* Customize */
648+
await page.getByText('Let’s customize a few things').nth(1).waitFor({ timeout: 1000 });
649+
await page.getByRole('button', { name: 'Show Bookmarks Bar' }).click();
650+
await page.getByRole('button', { name: 'Enable Session Restore' }).click();
651+
await page.getByRole('button', { name: 'Show Home Button' }).click();
652+
await this.startBrowsing();
653+
}
654+
655+
async completesOrderV3WithYouTubeAdBlocking() {
656+
const { page } = this;
657+
/* Welcome */
658+
await page.getByText('Welcome to DuckDuckGo').nth(1).waitFor({ timeout: 1000 });
659+
660+
/* Get started */
661+
await page.getByText('Hi there').nth(1).waitFor({ timeout: 1500 });
662+
await page.getByRole('button', { name: 'Let’s Do It' }).click();
663+
664+
/* Make default */
665+
await page.getByText('Protections activated').nth(1).waitFor({ timeout: 1000 });
666+
await page.getByRole('button', { name: 'Make DuckDuckGo Your Default' }).click();
667+
await page.getByText('Excellent!').nth(1).waitFor({ timeout: 1000 });
668+
await page.getByRole('button', { name: 'Next' }).click();
669+
670+
/* System settings (with youtube ad-blocking) */
671+
await page.getByText('Let’s get you set up!').nth(1).waitFor({ timeout: 1000 });
672+
const dockButton = this.build.switch({
673+
windows: () => page.getByRole('button', { name: 'Pin to Taskbar' }),
674+
apple: () => page.getByRole('button', { name: 'Keep in Dock' }),
675+
});
676+
await dockButton.click();
677+
await page.getByRole('button', { name: 'Block Ads', exact: true }).click();
678+
await page.getByRole('button', { name: 'Import Now', exact: true }).click();
679+
await page.getByRole('button', { name: 'Next' }).click();
680+
681+
/* Duckplayer - alternate title/subtitle */
682+
await page.getByText('Watch YouTube privately with Duck Player').nth(1).waitFor({ timeout: 1000 });
683+
await expect(page.locator('h2')).toContainText("Watching videos in Duck Player won't influence your YouTube recommendations.");
684+
await page.getByLabel('See Without Duck Player').click();
685+
await page.getByLabel('See With Duck Player').click();
686+
await page.getByRole('button', { name: 'Next' }).click();
687+
688+
/* Customize */
689+
await page.getByText('Let’s customize a few things').nth(1).waitFor({ timeout: 1000 });
690+
await page.getByRole('button', { name: 'Show Bookmarks Bar' }).click();
691+
await page.getByRole('button', { name: 'Enable Session Restore' }).click();
692+
await page.getByRole('button', { name: 'Show Home Button' }).click();
693+
await this.startBrowsing();
694+
}
583695
}

special-pages/pages/onboarding/integration-tests/onboarding.spec.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,36 @@ test.describe('onboarding', () => {
5353
await onboarding.openPage({ env: 'app', page: 'systemSettings' });
5454
await onboarding.keepInTaskbar();
5555
});
56+
test('Then I can turn on enhanced ad blocking', async ({ page }, workerInfo) => {
57+
const onboarding = OnboardingPage.create(page, workerInfo);
58+
onboarding.withInitData({
59+
stepDefinitions: {
60+
systemSettings: {
61+
rows: ['dock', 'ad-blocking', 'import'],
62+
},
63+
},
64+
order: 'v3',
65+
});
66+
await onboarding.reducedMotion();
67+
await onboarding.openPage({ env: 'app', page: 'systemSettings' });
68+
await onboarding.skippedCurrent();
69+
await onboarding.enableEnhancedAdBlocking();
70+
});
71+
test('Then I can turn on YouTube ad blocking', async ({ page }, workerInfo) => {
72+
const onboarding = OnboardingPage.create(page, workerInfo);
73+
onboarding.withInitData({
74+
stepDefinitions: {
75+
systemSettings: {
76+
rows: ['dock', 'youtube-ad-blocking', 'import'],
77+
},
78+
},
79+
order: 'v3',
80+
});
81+
await onboarding.reducedMotion();
82+
await onboarding.openPage({ env: 'app', page: 'systemSettings' });
83+
await onboarding.skippedCurrent();
84+
await onboarding.enableYouTubeAdBlocking();
85+
});
5686
test('The I can skip all', async ({ page }, workerInfo) => {
5787
const onboarding = OnboardingPage.create(page, workerInfo);
5888
await onboarding.reducedMotion();
@@ -173,6 +203,36 @@ test.describe('onboarding', () => {
173203
await onboarding.openPage();
174204
await onboarding.completesOrderV3WithoutSettings();
175205
});
206+
test('shows v3 flow with ad blocking', async ({ page }, workerInfo) => {
207+
const onboarding = OnboardingPage.create(page, workerInfo);
208+
onboarding.withInitData({
209+
stepDefinitions: {
210+
systemSettings: {
211+
rows: ['dock', 'ad-blocking', 'import'],
212+
},
213+
},
214+
order: 'v3',
215+
});
216+
await onboarding.reducedMotion();
217+
await onboarding.darkMode();
218+
await onboarding.openPage();
219+
await onboarding.completesOrderV3WithAdBlocking();
220+
});
221+
test('shows v3 flow with YouTube ad blocking', async ({ page }, workerInfo) => {
222+
const onboarding = OnboardingPage.create(page, workerInfo);
223+
onboarding.withInitData({
224+
stepDefinitions: {
225+
systemSettings: {
226+
rows: ['dock', 'youtube-ad-blocking', 'import'],
227+
},
228+
},
229+
order: 'v3',
230+
});
231+
await onboarding.reducedMotion();
232+
await onboarding.darkMode();
233+
await onboarding.openPage();
234+
await onboarding.completesOrderV3WithYouTubeAdBlocking();
235+
});
176236
test.describe('Given I am on the settings step', () => {
177237
test('When I have choosen to add to dock/taskbar', async ({ page }, workerInfo) => {
178238
const onboarding = OnboardingPage.create(page, workerInfo);

special-pages/pages/onboarding/public/locales/en/onboarding.json

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,14 @@
285285
"title": "No targeted ads. No targeted recommendations. Just your video.",
286286
"note": "Subtitle for a page that shows the benefits of using the Duck Player feature to watch YouTube videos more privately."
287287
},
288+
"duckPlayer_alt_title": {
289+
"title": "Watch YouTube privately with Duck Player",
290+
"note": "Alternative title for Duck Player step when YouTube ad blocking is also offered."
291+
},
292+
"duckPlayer_alt_subtitle": {
293+
"title": "Watching videos in Duck Player won't influence your YouTube recommendations.",
294+
"note": "Alternative subtitle for Duck Player step when YouTube ad blocking is also offered."
295+
},
288296
"customize_title_v3": {
289297
"title": "Let’s customize a few things…",
290298
"note": "Title for a page that allows the user to customize specific settings in the DuckDuckGo browser."
@@ -345,6 +353,30 @@
345353
"title": "Get to DuckDuckGo faster.",
346354
"note": "Instructs the user to add DuckDuckGo to their taskbar by clicking Yes on a dialog screen."
347355
},
356+
"row_ad-blocking_title_v3": {
357+
"title": "Browse faster with even fewer ads",
358+
"note": "Title for the enhanced ad blocking setting."
359+
},
360+
"row_ad-blocking_desc_v3": {
361+
"title": "Our protections block most ads; enhanced blocking stops even more.",
362+
"note": "Description for the enhanced ad blocking setting."
363+
},
364+
"row_ad-blocking_accept_v3": {
365+
"title": "Turn on Enhanced Ad Blocking",
366+
"note": "Button text to enable enhanced ad blocking."
367+
},
368+
"row_youtube-ad-blocking_title_v3": {
369+
"title": "Watch YouTube ad-free",
370+
"note": "Title for the YouTube ad blocking setting."
371+
},
372+
"row_youtube-ad-blocking_desc_v3": {
373+
"title": "No targeted ads, just your video.",
374+
"note": "Description for the YouTube ad blocking setting."
375+
},
376+
"row_youtube-ad-blocking_accept_v3": {
377+
"title": "Block Ads",
378+
"note": "Button text to enable YouTube ad blocking."
379+
},
348380
"comparison_searchPrivately": {
349381
"title": "Search privately by default",
350382
"note": "The description of a browser privacy feature in the comparison table."
@@ -393,5 +425,4 @@
393425
"title": "Safari",
394426
"note": "Brand name of the Apple Safari browser"
395427
}
396-
397428
}

0 commit comments

Comments
 (0)