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

feat(macos): allow direct navigation to breakage form #139

Merged
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
5 changes: 5 additions & 0 deletions build/app/public/css/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,11 @@ body.environment--macos, body.environment--browser, body.environment--windows, b
.environment--ios .top-nav__done:active {
opacity: 0.5;
}
.environment--macos .top-nav__done {
height: auto;
line-height: normal;
bottom: 0;
}

.status-list--right .status-list__item {
padding-left: 0;
Expand Down
54 changes: 31 additions & 23 deletions build/app/public/js/base.js

Large diffs are not rendered by default.

28 changes: 15 additions & 13 deletions integration-tests/DashboardPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,6 @@ export class DashboardPage {
return dash
}

static async macos(page) {
const dash = new DashboardPage(page, { name: 'macos' })
await dash.withMarker()
await dash.loadPage()
await dash.withMocks()
await page.waitForFunction(() => typeof window.__playwright !== 'undefined')
return dash
}

/**
* @param {import("@playwright/test").Page} page
* @returns {Promise<DashboardPage>}
Expand All @@ -185,12 +176,13 @@ export class DashboardPage {
/**
* @param {import("@playwright/test").Page} page
* @param {object} [opts]
* @param {import('../schema/__generated__/schema.types').EventOrigin['screen']} opts.screen
* @param {import('../schema/__generated__/schema.types').EventOrigin['screen']} [opts.screen]
* @param {'ios' | 'macos'} opts.platform
*/
static async ios(page, opts) {
static async webkit(page, opts) {
/** @type {import('../schema/__generated__/schema.types').EventOrigin['screen']} */
const screen = opts?.screen || 'primaryScreen'
const dash = new DashboardPage(page, { name: 'ios' })
const dash = new DashboardPage(page, { name: opts?.platform ?? 'ios' })
await dash.withMarker()
await dash.loadPage({ screen })
await dash.withMocks()
Expand Down Expand Up @@ -295,6 +287,16 @@ export class DashboardPage {
await this.page.locator('.breakage-form').locator('a:has-text("Done")').waitFor()
await expect(this.page.locator('.breakage-form .top-nav a')).toHaveCount(1)
}

async showsOnlyCloseButton() {
await this.page.locator('.breakage-form').locator('a:has-text("Close")').waitFor()
await expect(this.page.locator('.breakage-form .top-nav a')).toHaveCount(1)
}

async selectClose() {
await this.page.locator('.breakage-form .top-nav a').filter({ hasText: 'Close' }).click()
}

/**
* @param {"grant"} permission
* @return {Promise<void>}
Expand All @@ -308,7 +310,7 @@ export class DashboardPage {
if (this.platform.name === 'android') {
return this.backButton().click()
}
if (this.platform.name === 'ios') {
if (this.platform.name === 'ios' || this.platform.name === 'macos') {
return await this.page.getByRole('button', { name: 'Done' }).click()
}
throw new Error('unreachable. clickClose must be handled')
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/Mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export class Mocks {
expect(calls).toMatchObject([['close', undefined]])
return
}
if (this.platform.name === 'ios') {
if (this.platform.name === 'ios' || this.platform.name === 'macos') {
const calls = await this.outgoing({ names: ['privacyDashboardClose'] })
expect(calls).toMatchObject([['privacyDashboardClose', {}]])
return
Expand Down
36 changes: 18 additions & 18 deletions integration-tests/ios.spec-int.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { toggleFlows } from './utils/common-flows'

test.describe('page data (no trackers)', () => {
test('should fetch initial data', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates.protectionsOn])
await dash.showsPrimaryScreen()
})
test('should accept updates when on trackers list screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates.protectionsOn])
await dash.viewTrackerCompanies()
await dash.screenshot('tracker-list-before.png')
Expand All @@ -19,7 +19,7 @@ test.describe('page data (no trackers)', () => {
await dash.screenshot('tracker-list-after.png')
})
test('should accept updates when on non-trackers list screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates.protectionsOn])
await dash.viewThirdParties()
await dash.screenshot('non-tracker-list-before.png')
Expand All @@ -28,7 +28,7 @@ test.describe('page data (no trackers)', () => {
await dash.screenshot('non-tracker-list-after.png')
})
test('does not alter the appearance of connection panel', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates.protectionsOn])
await dash.viewConnection()
await dash.screenshot('connection-before.png')
Expand All @@ -39,20 +39,20 @@ test.describe('page data (no trackers)', () => {

test.describe('page data (with trackers)', () => {
test('should display correct primary screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates.cnn])
await dash.showsPrimaryScreen()
await dash.screenshot('primary-screen.png')
})
})

test.describe('Protections toggle', () => {
toggleFlows((page) => DashboardPage.ios(page))
toggleFlows((page) => DashboardPage.webkit(page))
})

test.describe('open external links', () => {
test('should call ios interface for links', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.reducedMotion()
await dash.addState([testDataStates.protectionsOn])
await dash.viewTrackerCompanies()
Expand All @@ -63,20 +63,20 @@ test.describe('open external links', () => {

test.describe('localization', () => {
test('should load with `pl` locale', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates['locale-pl']])
await dash.hasPolishLinkTextForConnectionInfo()
})
test('should load with `fr` locale', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates['locale-fr']])
await dash.hasFrenchLinkTextForConnectionInfo()
})
})

test.describe('Close', () => {
test('pressing close should call native API on iOS', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates.protectionsOn])
await dash.clickClose()
await dash.mocks.calledForClose()
Expand All @@ -86,20 +86,20 @@ test.describe('Close', () => {
test.describe('cookie prompt management', () => {
test.describe('none-configurable', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates['consent-managed']])
await dash.indicatesCookiesWereManaged()
})
})
test.describe('configurable', () => {
test.describe('non-cosmetic', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates['consent-managed-configurable']])
await dash.indicatesCookiesWereManaged()
})
test('secondary screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates['consent-managed-configurable']])
await dash.viewCookiePromptManagement()
await dash.disableCookiesInSettings()
Expand All @@ -111,12 +111,12 @@ test.describe('cookie prompt management', () => {

test.describe('cosmetic', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates['consent-managed-configurable-cosmetic']])
await dash.indicatesCookiesWereHidden()
})
test('secondary screen', async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.addState([testDataStates['consent-managed-configurable-cosmetic']])
await dash.viewCookiePromptManagement()
await dash.disableCookiesInSettings()
Expand All @@ -131,7 +131,7 @@ test.describe('cookie prompt management', () => {
test.describe('opening breakage form', () => {
test('shows breakage form only', async ({ page }) => {
/** @type {DashboardPage} */
const dash = await DashboardPage.ios(page, { screen: 'breakageForm' })
const dash = await DashboardPage.webkit(page, { screen: 'breakageForm', platform: 'ios' })
await dash.addState([testDataStates.google])
await dash.screenshot('breakage-form-only.png')
await dash.breakageFormIsVisible()
Expand All @@ -156,7 +156,7 @@ if (!process.env.CI) {
]
for (const { name, state } of states) {
test(name, async ({ page }) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
await dash.screenshotEachScreenForState(name, state)
})
}
Expand Down Expand Up @@ -203,7 +203,7 @@ if (!process.env.CI) {
]
for (const { name, state } of states) {
test(name, async ({ page }, testInfo) => {
const dash = await DashboardPage.ios(page)
const dash = await DashboardPage.webkit(page)
const screen = await dash.screenshotPrimary(name, state)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { certificate, ...rest } = state
Expand Down
42 changes: 27 additions & 15 deletions integration-tests/macos.spec-int.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,39 @@ import { desktopBreakageForm, settingPermissions, toggleFlows } from './utils/co

test.describe('initial page data', () => {
test('should fetch initial data', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates.protectionsOn])
await dash.showsPrimaryScreen()
})
})

test.describe('breakage form', () => {
desktopBreakageForm((page) => DashboardPage.macos(page))
desktopBreakageForm((page) => DashboardPage.webkit(page, { platform: 'macos' }))
})

test.describe('Protections toggle', () => {
toggleFlows((page) => DashboardPage.macos(page))
toggleFlows((page) => DashboardPage.webkit(page, { platform: 'macos' }))
})

test.describe('permissions', () => {
settingPermissions((page) => DashboardPage.macos(page))
settingPermissions((page) => DashboardPage.webkit(page, { platform: 'macos' }))
})

test.describe('opening breakage form', () => {
test('shows breakage form only', async ({ page }) => {
/** @type {DashboardPage} */
const dash = await DashboardPage.webkit(page, { screen: 'breakageForm', platform: 'macos' })
await dash.addState([testDataStates.google])
await dash.breakageFormIsVisible()
await dash.showsOnlyCloseButton()
await dash.selectClose()
await dash.mocks.calledForClose()
})
})

test.describe('open external links', () => {
test('should call webkit interface for external links', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates.protectionsOn])
await dash.viewTrackerCompanies()
await dash.clickAboutLink()
Expand All @@ -35,7 +47,7 @@ test.describe('open external links', () => {

test.describe('setting the height', () => {
test('should send the initial height to native', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates.protectionsOn])
await dash.mocks.calledForInitialHeight()
})
Expand All @@ -44,19 +56,19 @@ test.describe('setting the height', () => {
test.describe('cookie prompt management', () => {
test.describe('none-configurable', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed']])
await dash.indicatesCookiesWereManaged()
})
})
test.describe('configurable', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed-configurable']])
await dash.indicatesCookiesWereManaged()
})
test('secondary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed-configurable']])
await dash.viewCookiePromptManagement()
await dash.disableCookiesInSettings()
Expand All @@ -76,14 +88,14 @@ if (!process.env.CI) {
test.describe('screenshots', () => {
for (const { name, state } of states) {
test(name, async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.screenshotEachScreenForState(name, state)
})
}
})
test.describe('screenshots for cookies (none-configurable)', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed']])
await dash.indicatesCookiesWereManaged()
await dash.screenshot('consent-managed.png')
Expand All @@ -92,13 +104,13 @@ if (!process.env.CI) {
test.describe('screenshots for cookies (configurable)', () => {
test.describe('non-cosmetic', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed-configurable']])
await dash.indicatesCookiesWereManaged()
await dash.screenshot('consent-managed-configurable.png')
})
test('secondary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed-configurable']])
await dash.viewCookiePromptManagement()
await dash.screenshot('consent-managed-configurable-secondary.png')
Expand All @@ -108,13 +120,13 @@ if (!process.env.CI) {
})
test.describe('cosmetic', () => {
test('primary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed-configurable-cosmetic']])
await dash.indicatesCookiesWereHidden()
await dash.screenshot('consent-managed-configurable-primary-cosmetic.png')
})
test('secondary screen', async ({ page }) => {
const dash = await DashboardPage.macos(page)
const dash = await DashboardPage.webkit(page, { platform: 'macos' })
await dash.addState([testDataStates['consent-managed-configurable-cosmetic']])
await dash.viewCookiePromptManagement()
await dash.screenshot('consent-managed-configurable-secondary-cosmetic.png')
Expand Down
21 changes: 1 addition & 20 deletions shared/js/browser/ios-communication.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* @category integrations
*/
import invariant from 'tiny-invariant'
import { CheckBrokenSiteReportHandledMessage, CloseMessage, setupColorScheme } from './common.js'
import { CheckBrokenSiteReportHandledMessage, setupColorScheme } from './common.js'
import { backgroundMessage, getBackgroundTabData, fetch as macosFetch, setupShared } from './macos-communication.js'

/**
Expand All @@ -37,20 +37,6 @@ export function setup() {
setupShared()
}

/**
* Close the Dashboard.
* @category Webkit Message Handlers
* @param {{}} args - An empty object to keep the `webkit` message handlers happy
* @example
* ```js
* window.webkit.messageHandlers.privacyDashboardClose.postMessage(args)
* ```
*/
export function privacyDashboardClose(args) {
invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required')
window.webkit.messageHandlers.privacyDashboardClose.postMessage(args)
}

/**
* On iOS, the breakage report form is handled natively - so all the dashboard needs
* to do in this situation is ping the correct message to the backend.
Expand All @@ -72,11 +58,6 @@ export function privacyDashboardShowReportBrokenSite(args) {
* @type {import("./common.js").fetcher}
*/
async function fetch(message) {
if (message instanceof CloseMessage) {
privacyDashboardClose({})
return
}

if (message instanceof CheckBrokenSiteReportHandledMessage) {
privacyDashboardShowReportBrokenSite({})
return false // Return true to prevent HTML form from showing
Expand Down
Loading
Loading