From cecf169460982685383010c631a773e0005ae0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20H=C3=A1jek?= Date: Fri, 7 Feb 2025 10:35:55 +0100 Subject: [PATCH] feat(e2e): Migrated analytics tests to playwright (#16872) * feat(e2e): Migrated analytics tests to playwright * fix(e2e): Fixed desktop bridge connection * fix(e2e): Unified waiting for requests --- .github/workflows/test-suite-desktop-e2e.yml | 4 +- .github/workflows/test-suite-web-e2e.yml | 3 - .../e2e/support/analytics.ts | 4 + .../pageActions/settings/settingsActions.ts | 2 + .../e2e/tests/analytics/events.test.ts | 179 ++++++++++++++ .../e2e/tests/analytics/toggle.test.ts | 143 +++++++++++ .../e2e/tests/analytics/events.test.ts | 231 ------------------ .../e2e/tests/analytics/toggle.test.ts | 163 ------------ 8 files changed, 330 insertions(+), 399 deletions(-) create mode 100644 packages/suite-desktop-core/e2e/tests/analytics/events.test.ts create mode 100644 packages/suite-desktop-core/e2e/tests/analytics/toggle.test.ts delete mode 100644 packages/suite-web/e2e/tests/analytics/events.test.ts delete mode 100644 packages/suite-web/e2e/tests/analytics/toggle.test.ts diff --git a/.github/workflows/test-suite-desktop-e2e.yml b/.github/workflows/test-suite-desktop-e2e.yml index 5ed4c363d88..55b06b9ec63 100644 --- a/.github/workflows/test-suite-desktop-e2e.yml +++ b/.github/workflows/test-suite-desktop-e2e.yml @@ -44,8 +44,8 @@ jobs: # CONTAINERS: "trezor-user-env-unix" # - TEST_GROUP: "@group=passphrase" # CONTAINERS: "trezor-user-env-unix" - # - TEST_GROUP: "@group=other" - # CONTAINERS: "trezor-user-env-unix" + - TEST_GROUP: "@group=other" + CONTAINERS: "trezor-user-env-unix" - TEST_GROUP: "@group=wallet" CONTAINERS: "trezor-user-env-unix bitcoin-regtest" diff --git a/.github/workflows/test-suite-web-e2e.yml b/.github/workflows/test-suite-web-e2e.yml index 2d2a467ed08..89f3229843e 100644 --- a/.github/workflows/test-suite-web-e2e.yml +++ b/.github/workflows/test-suite-web-e2e.yml @@ -106,9 +106,6 @@ jobs: - TEST_GROUP: "@group_passphrase" CONTAINERS: "trezor-user-env-unix" CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1" - - TEST_GROUP: "@group_other" - CONTAINERS: "trezor-user-env-unix" - CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1" - TEST_GROUP: "@group_wallet" CONTAINERS: "trezor-user-env-unix bitcoin-regtest" CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1" diff --git a/packages/suite-desktop-core/e2e/support/analytics.ts b/packages/suite-desktop-core/e2e/support/analytics.ts index 3cd7ba0dde9..7ce95737bbb 100644 --- a/packages/suite-desktop-core/e2e/support/analytics.ts +++ b/packages/suite-desktop-core/e2e/support/analytics.ts @@ -25,6 +25,10 @@ export class AnalyticsFixture { return event; } + extractRequestTypes() { + return this.requests.map(request => request['c_type']); + } + //TODO: #15811 To be refactored @step() async interceptAnalytics() { diff --git a/packages/suite-desktop-core/e2e/support/pageActions/settings/settingsActions.ts b/packages/suite-desktop-core/e2e/support/pageActions/settings/settingsActions.ts index 6cd0a1c1a43..b551fdb0bef 100644 --- a/packages/suite-desktop-core/e2e/support/pageActions/settings/settingsActions.ts +++ b/packages/suite-desktop-core/e2e/support/pageActions/settings/settingsActions.ts @@ -65,6 +65,7 @@ export class SettingsActions { this.page.getByTestId(`@settings/language-select/option/${language}`); readonly checkSeedButton: Locator; readonly metadataSwitch: Locator; + readonly analyticsSwitch: Locator; constructor( private readonly page: Page, @@ -98,6 +99,7 @@ export class SettingsActions { this.languageInput = this.page.getByTestId('@settings/language-select/input'); this.checkSeedButton = this.page.getByTestId('@settings/device/check-seed-button'); this.metadataSwitch = this.page.getByTestId('@settings/metadata-switch'); + this.analyticsSwitch = this.page.getByTestId('@analytics/toggle-switch'); } @step() diff --git a/packages/suite-desktop-core/e2e/tests/analytics/events.test.ts b/packages/suite-desktop-core/e2e/tests/analytics/events.test.ts new file mode 100644 index 00000000000..6c18fd32b33 --- /dev/null +++ b/packages/suite-desktop-core/e2e/tests/analytics/events.test.ts @@ -0,0 +1,179 @@ +import { EventType } from '@trezor/suite-analytics'; +import { ExtractByEventType } from '@trezor/suite-web/e2e/support/types'; + +import { expect, test } from '../../support/fixtures'; + +test.describe('Analytics Events', { tag: ['@group=suite', '@webOnly'] }, () => { + test.use({ + startEmulator: false, + }); + test.beforeEach(async ({ trezorUserEnvLink, onboardingPage }) => { + await trezorUserEnvLink.stopBridge(); + await trezorUserEnvLink.stopEmu(); + await onboardingPage.disableFirmwareHashCheck(); + }); + + test('reports transport-type, suite-ready and device-connect/device-disconnect events when analytics is initialized and enabled', async ({ + page, + analytics, + onboardingPage, + settingsPage, + trezorUserEnvLink, + }) => { + // go to settings and enable analytics (makes analytics enabled and initialized) + await settingsPage.navigateTo('application'); + await settingsPage.analyticsSwitch.click(); + await settingsPage.closeSettings(); + + await trezorUserEnvLink.startEmu({ wipe: true, model: 'T3T1', version: '2.8.1' }); + await trezorUserEnvLink.setupEmu({ + passphrase_protection: true, + }); + + await trezorUserEnvLink.startBridge(); + + // reload to activate bridge and start testing app with enabled analytics + await page.reload(); + await analytics.interceptAnalytics(); + await onboardingPage.optionallyDismissFwHashCheckError(); + await page.getByTestId('@onboarding/exit-app-button').click(); + + // suite-ready is logged 1st, just check that it is reported when app is initialized and enabled + // device-connect is logged 2nd + // transport-type is logged 3rd + expect(analytics.requests[0]).toHaveProperty('c_type', EventType.SuiteReady); + expect(analytics.requests[1]).toHaveProperty('c_type', EventType.DeviceConnect); + expect(analytics.requests[2]).toHaveProperty('c_type', EventType.TransportType); + expect(analytics.requests).toHaveLength(3); + + const deviceConnectEvent = analytics.findAnalyticsEventByType< + ExtractByEventType + >(EventType.DeviceConnect); + expect(deviceConnectEvent.mode).toBe('normal'); // not in BL + expect(deviceConnectEvent.firmware).toBe('2.8.1'); // 2.6.0 is hardcoded in startEmu to always match + expect(deviceConnectEvent.firmwareRevision).toBe( + // good to check because of phishing + '632b9561559b7ab6824bb7eeac072874e07b7b82', // https://github.com/trezor/trezor-firmware/releases/tag/core%2Fv2.6.0 + ); + expect(deviceConnectEvent.bootloaderHash).toBe(''); + expect(deviceConnectEvent.backup_type).toBe('Bip39'); + expect(deviceConnectEvent.pin_protection).toBe('false'); + expect(deviceConnectEvent.passphrase_protection).toBe('true'); // set in startEmu + expect(deviceConnectEvent.totalInstances).toBe('1'); + expect(deviceConnectEvent.isBitcoinOnly).toBe('false'); + expect(deviceConnectEvent.totalDevices).toBe('1'); + expect(deviceConnectEvent.language).toBe('en-US'); + expect(deviceConnectEvent.model).toBe('T3T1'); + + const transportTypeEvent = analytics.findAnalyticsEventByType< + ExtractByEventType + >(EventType.TransportType); + expect(transportTypeEvent.type).toBe('BridgeTransport'); + expect(parseInt(transportTypeEvent.version, 10)).not.toBeNaN(); + + // device-disconnect is logged 4th + await trezorUserEnvLink.stopEmu(); + + await expect.poll(() => analytics.requests).toHaveLength(4); // Poll to prevent race condition + expect(analytics.requests[3]).toHaveProperty('c_type', EventType.DeviceDisconnect); + }); + + test('reports suite-ready after enabling analytics on app initial run', async ({ + analytics, + page, + analyticsPage, + settingsPage, + onboardingPage, + trezorUserEnvLink, + }) => { + await trezorUserEnvLink.startEmu({ wipe: true, model: 'T3T1' }); + await trezorUserEnvLink.setupEmu({ + passphrase_protection: true, + }); + + await trezorUserEnvLink.startBridge(); + + await analytics.interceptAnalytics(); + + await settingsPage.navigateTo('application'); + + // change language + await page.getByTestId('@settings/language-select/input').scrollIntoViewIfNeeded(); + await page.getByTestId('@settings/language-select/input').click(); + await page.getByTestId('@settings/language-select/option/cs').click(); + + // change fiat + await page.getByTestId('@settings/fiat-select/input').scrollIntoViewIfNeeded(); + await page.getByTestId('@settings/fiat-select/input').click(); + await page.getByTestId('@settings/fiat-select/option/czk').click(); + + // change BTC units + await page.getByTestId('@settings/btc-units-select/input').scrollIntoViewIfNeeded(); + await page.getByTestId('@settings/btc-units-select/input').click(); + await page.getByTestId('@settings/btc-units-select/option/Satoshis').click(); + + // change dark mode + await page.getByTestId('@theme/color-scheme-select/input').scrollIntoViewIfNeeded(); + await page.getByTestId('@theme/color-scheme-select/input').click(); + await page.getByTestId('@theme/color-scheme-select/option/dark').click(); + + // disable btc, enable ethereum and holesky + await page.getByTestId('@settings/menu/wallet').click(); + await page.getByTestId('@settings/wallet/network/btc').click(); + await page.getByTestId('@settings/wallet/network/eth').click(); + await page.getByTestId('@settings/wallet/network/thol').click(); + + // custom eth backend + await page.getByTestId('@settings/wallet/network/eth/advance').click(); + await page.getByTestId('@settings/advance/select-type/input').click(); + await page.getByTestId('@settings/advance/select-type/option/blockbook').click(); + await page.getByTestId('@settings/advance/url').fill('https://eth.marek.pl/'); + await page.getByTestId('@settings/advance/button/save').click(); + + await settingsPage.closeSettings(); + await onboardingPage.optionallyDismissFwHashCheckError(); + + expect(analytics.requests).toHaveLength(0); + await analyticsPage.continueButton.click(); + await page.getByTestId('@onboarding/exit-app-button').click(); + + expect(analytics.requests.length).toBeGreaterThanOrEqual(2); + expect(analytics.requests[0]).toHaveProperty('c_type', EventType.SettingsAnalytics); + expect(analytics.requests[1]).toHaveProperty('c_type', EventType.SuiteReady); + + // settings/analytics + const settingsAnalyticsEvent = analytics.findAnalyticsEventByType< + ExtractByEventType + >(EventType.SettingsAnalytics); + expect(settingsAnalyticsEvent.value).toBe('true'); + + // suite-ready reflects state when app was launched, does not include changes + const suiteReadyEvent = analytics.findAnalyticsEventByType< + ExtractByEventType + >(EventType.SuiteReady); + expect(suiteReadyEvent.language).toBe('en'); + expect(suiteReadyEvent.enabledNetworks).toBe('btc'); + expect(suiteReadyEvent.customBackends).toBe(''); + expect(suiteReadyEvent.localCurrency).toBe('usd'); + expect(suiteReadyEvent.bitcoinUnit).toBe('BTC'); + expect(suiteReadyEvent.discreetMode).toBe('false'); + expect(suiteReadyEvent.screenWidth).toBeDefined(); + expect(suiteReadyEvent.screenHeight).toBeDefined(); + expect(suiteReadyEvent.platformLanguages).toBeDefined(); + expect(suiteReadyEvent.tor).toBe('false'); + expect(suiteReadyEvent.labeling).toBeDefined(); + expect(suiteReadyEvent.rememberedStandardWallets).toBe('0'); + expect(suiteReadyEvent.rememberedHiddenWallets).toBe('0'); + expect(suiteReadyEvent.theme).toBe('light'); + expect(parseInt(suiteReadyEvent.suiteVersion, 10)).not.toBeNaN(); + expect(suiteReadyEvent.earlyAccessProgram).toBe('false'); + expect(parseInt(suiteReadyEvent.browserVersion, 10)).not.toBeNaN(); + expect(suiteReadyEvent.osName).toBeDefined(); + expect(parseInt(suiteReadyEvent.osVersion, 10)).not.toBeNaN(); + const viewport = page.viewportSize(); + expect(suiteReadyEvent.windowWidth).toBe(viewport?.width.toString()); + expect(suiteReadyEvent.windowHeight).toBe(viewport?.height.toString()); + expect(suiteReadyEvent.autodetectLanguage).toBe('true'); + expect(suiteReadyEvent.autodetectTheme).toBe('true'); + }); +}); diff --git a/packages/suite-desktop-core/e2e/tests/analytics/toggle.test.ts b/packages/suite-desktop-core/e2e/tests/analytics/toggle.test.ts new file mode 100644 index 00000000000..c0a22152fbe --- /dev/null +++ b/packages/suite-desktop-core/e2e/tests/analytics/toggle.test.ts @@ -0,0 +1,143 @@ +import { EventType } from '@trezor/suite-analytics'; + +import { expect, test } from '../../support/fixtures'; + +test.describe('Analytics Toggle - Enabling and Disabling', { tag: ['@group=other'] }, () => { + test.beforeEach(async ({ analytics, onboardingPage }) => { + await analytics.interceptAnalytics(); + await onboardingPage.disableFirmwareHashCheck(); + }); + + test('should respect disabled analytics in onboarding with following enabling in settings', async ({ + analytics, + page, + analyticsPage, + onboardingPage, + dashboardPage, + settingsPage, + }) => { + // pass through onboarding with disabled analytics + await expect(settingsPage.analyticsSwitch.locator('input')).toBeChecked(); + await settingsPage.analyticsSwitch.click(); + await expect(settingsPage.analyticsSwitch.locator('input')).not.toBeChecked(); + + await analyticsPage.continueButton.click(); // Click the button and trigger the request + await expect.poll(() => analytics.requests).toHaveLength(1); + + // assert that only "analytics/dispose" event was fired + const disposeRequest = analytics.requests[0]; + expect(disposeRequest).toHaveProperty('c_type', EventType.SettingsAnalytics); + expect(disposeRequest).toHaveProperty('value', 'false'); + expect(disposeRequest).toHaveProperty('c_session_id'); + expect(disposeRequest).toHaveProperty('c_instance_id'); + expect(disposeRequest).toHaveProperty('c_timestamp'); + expect(disposeRequest.c_timestamp).toMatch(/^\d+$/); + + await page.getByTestId('@onboarding/exit-app-button').click(); + + if (onboardingPage.isModelWithSecureElement()) { + await onboardingPage.passThroughAuthenticityCheck(); + } + + await onboardingPage.onboardingViewOnlyEnableButton.click(); + await onboardingPage.viewOnlyTooltipGotItButton.click(); + + // reload app (important, app needs time to save initialRun flag into storage) to change session id + await page.getByTestId('@suite/loading').waitFor({ state: 'hidden' }); + await dashboardPage.discoveryShouldFinish(); + await page.reload(); + + // go to settings, analytics should not enabled and no additional analytics requests should be fired + await settingsPage.navigateTo('application'); + await expect(settingsPage.analyticsSwitch.locator('input')).not.toBeChecked(); + expect(analytics.requests).toHaveLength(1); + + // enable analytics and check "analytics/enable" event was fired + await settingsPage.analyticsSwitch.click(); + await expect(settingsPage.analyticsSwitch.locator('input')).toBeChecked(); + await expect.poll(() => analytics.requests).toHaveLength(2); + + const enableRequest = analytics.requests[1]; + expect(enableRequest).toHaveProperty('c_type', EventType.SettingsAnalytics); + expect(enableRequest).toHaveProperty('c_session_id'); + expect(enableRequest).toHaveProperty('c_instance_id'); + expect(enableRequest).toHaveProperty('c_timestamp'); + expect(enableRequest.c_timestamp).toMatch(/^\d+$/); + expect(analytics.requests).toHaveLength(2); + + // check that timestamps are different + expect(disposeRequest.c_timestamp).not.toEqual(enableRequest.c_timestamp); + + // check that session ids changed after reload + expect(disposeRequest.c_session_id).not.toEqual(enableRequest.c_session_id); + + // check that instance ids are the same after reload + expect(disposeRequest.c_instance_id).toEqual(enableRequest.c_instance_id); + + // change fiat and check that it was logged + await page.getByTestId('@settings/fiat-select/input').scrollIntoViewIfNeeded(); // Shouldn't be necessary, but without it the dropdown doesn't open + await page.getByTestId('@settings/fiat-select/input').click(); + await page.getByTestId('@settings/fiat-select/option/huf').click(); + await expect.poll(() => analytics.requests).toHaveLength(3); + expect(analytics.requests[2]).toHaveProperty('c_type', EventType.SettingsGeneralChangeFiat); + expect(analytics.requests[2]).toHaveProperty('fiat', 'huf'); + expect(analytics.requests[2]).toHaveProperty( + 'c_instance_id', + analytics.requests[1].c_instance_id, + ); + expect(analytics.requests).toHaveLength(3); + + // open device modal and check that it was logged + await dashboardPage.openDeviceSwitcher(); + await expect.poll(() => analytics.requests).toHaveLength(4); + + const deviceModalRequest = analytics.requests[3]; + expect(deviceModalRequest).toHaveProperty('c_type', EventType.RouterLocationChange); + expect(analytics.requests).toHaveLength(4); + }); + + test('should respect enabled analytics in onboarding with following disabling in settings', async ({ + analytics, + page, + analyticsPage, + onboardingPage, + settingsPage, + }) => { + // pass through onboarding with enabled analytics + await expect(settingsPage.analyticsSwitch.locator('input')).toBeChecked(); + + await analyticsPage.continueButton.click(); // Click the button and trigger the request + await expect.poll(() => analytics.requests.length).toBeGreaterThan(2); + + // assert that more than 1 event was fired and it was "suite/ready" and "analytics/enable" for sure + expect(analytics.requests.length).toBeGreaterThan(1); + expect(analytics.extractRequestTypes()).toContain(EventType.SuiteReady); + expect(analytics.extractRequestTypes()).toContain(EventType.SettingsAnalytics); + + // finish onboarding + await page.getByTestId('@onboarding/exit-app-button').click(); + if (onboardingPage.isModelWithSecureElement()) { + await onboardingPage.passThroughAuthenticityCheck(); + } + + // go to settings, analytics should be enabled + await settingsPage.navigateTo('application'); + await expect(settingsPage.analyticsSwitch.locator('input')).toBeChecked(); + + // disable analytics + await settingsPage.analyticsSwitch.click(); + await expect(settingsPage.analyticsSwitch.locator('input')).not.toBeChecked(); + + // change fiat and check that it was not logged + await page.getByTestId('@settings/fiat-select/input').scrollIntoViewIfNeeded(); // Shouldn't be necessary, but without it the dropdown doesn't open + await page.getByTestId('@settings/fiat-select/input').click(); + await page.getByTestId('@settings/fiat-select/option/huf').click(); + + // check that analytics disable event was fired + await expect.poll(() => analytics.requests.length).toBeGreaterThan(3); + expect(analytics.extractRequestTypes()).toContain(EventType.SettingsAnalytics); + + // check that "settings/general/change-fiat" event was not fired + expect(analytics.extractRequestTypes()).not.toContain(EventType.SettingsGeneralChangeFiat); + }); +}); diff --git a/packages/suite-web/e2e/tests/analytics/events.test.ts b/packages/suite-web/e2e/tests/analytics/events.test.ts deleted file mode 100644 index 56e598cb752..00000000000 --- a/packages/suite-web/e2e/tests/analytics/events.test.ts +++ /dev/null @@ -1,231 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions */ -// @group_suite -// @retry=2 - -import { EventType } from '@trezor/suite-analytics'; - -import { onNavBar } from '../../support/pageObjects/topBarObject'; -import { ExtractByEventType, Requests } from '../../support/types'; -import { forceLightMode } from '../../support/utils/shortcuts'; - -let requests: Requests; - -const windowWidth = 1440; -const windowHeight = 2560; - -describe('Analytics Events', () => { - beforeEach(() => { - cy.viewport(windowWidth, windowHeight).resetDb(); - requests = []; - }); - - it('reports transport-type, suite-ready and device-connect/device-disconnect events when analytics is initialized and enabled', () => { - cy.prefixedVisit('/'); - cy.disableFirmwareHashCheck(); - - // go to settings and enable analytics (makes analytics enabled and initialized) - onNavBar.openSettings(); - cy.getTestElement('@analytics/toggle-switch').click({ force: true }); - cy.getTestElement('@settings/menu/close').click(); - - cy.task('startEmu', { wipe: true, model: 'T2T1', version: '2.6.0' }); - cy.task('setupEmu', { - needs_backup: false, - passphrase_protection: true, - }); - - cy.task('startBridge'); - - // reload to activate bridge and start testing app with enabled analytics - cy.reload(); - cy.interceptDataTrezorIo(requests); - - // suite-ready is logged 1st, just check that it is reported when app is initialized and enabled - // device-connect is logged 2nd - // transport-type is logged 3th - - // wait until loaded - cy.getTestElement('@onboarding/exit-app-button'); - cy.wrap(requests).should('have.length', 3); - cy.wrap(requests).its(0).should('have.property', 'c_type', EventType.SuiteReady); - cy.wrap(requests).its(1).should('have.property', 'c_type', EventType.DeviceConnect); - cy.wrap(requests).its(2).should('have.property', 'c_type', EventType.TransportType); - - // device-connect - cy.findAnalyticsEventByType>( - requests, - EventType.DeviceConnect, - ).then(deviceConnectEvent => { - expect(deviceConnectEvent.mode).to.equal('normal'); // not in BL - expect(deviceConnectEvent.firmware).to.equal('2.6.0'); // 2.6.0 is hardcoded in startEmu to always match - expect(deviceConnectEvent.firmwareRevision).to.equal( - // good to check because of phishing - '88e1f8c7a5c7615723664c64b0a25adc0c409dee', // https://github.com/trezor/trezor-firmware/releases/tag/core%2Fv2.6.0 - ); - expect(deviceConnectEvent.bootloaderHash).to.equal(''); - expect(deviceConnectEvent.backup_type).to.equal('Bip39'); - expect(deviceConnectEvent.pin_protection).to.equal('false'); - expect(deviceConnectEvent.passphrase_protection).to.equal('true'); // set in startEmu - expect(deviceConnectEvent.totalInstances).to.equal('1'); - expect(deviceConnectEvent.isBitcoinOnly).to.equal('false'); - expect(deviceConnectEvent.totalDevices).to.equal('1'); - expect(deviceConnectEvent.language).to.equal('en-US'); - expect(deviceConnectEvent.model).to.equal('T2T1'); - }); - - // transport-type - cy.findAnalyticsEventByType>( - requests, - EventType.TransportType, - ).then(transportTypeEvent => { - expect(transportTypeEvent.type).to.equal('BridgeTransport'); - expect(parseInt(transportTypeEvent.version, 10)).to.not.equal(NaN); - }); - - // device-disconnect is logged 4th - cy.task('stopEmu'); - cy.wrap(requests).should('have.length', 4); - cy.wrap(requests).its(3).should('have.property', 'c_type', EventType.DeviceDisconnect); - }); - - it('reports suite-ready after enabling analytics on app initial run', () => { - cy.task('startEmu', { wipe: true }); - cy.task('setupEmu', { - needs_backup: false, - }); - cy.task('startBridge'); - - cy.interceptDataTrezorIo(requests); - - cy.prefixedVisit('/', forceLightMode); - cy.disableFirmwareHashCheck(); - // The next step is to press Settings Icon, and that can be done from DeviceCompromised modal as well as from a normal scren. - // But it can happen that Cypress tries to click it immediately, but fails, because the app is rerendering right now (modal is disappearing) - cy.wait(500); - - // change few settings to see if it is different from default values in suite-ready - onNavBar.openSettings(); - - // change language - cy.getTestElement('@settings/language-select/input').click(); - cy.getTestElement('@settings/language-select/option/cs').click({ force: true }); - - // change fiat - cy.getTestElement('@settings/fiat-select/input').click(); - cy.getTestElement('@settings/fiat-select/option/czk').click({ force: true }); - - // change BTC units - cy.getTestElement('@settings/btc-units-select/input').click(); - cy.getTestElement('@settings/btc-units-select/option/Satoshis').click({ force: true }); // sat - - // change dark mode - cy.getTestElement('@theme/color-scheme-select/input').click(); - cy.getTestElement('@theme/color-scheme-select/option/dark').click({ force: true }); - - // disable btc, enable ethereum and holesky - cy.getTestElement('@settings/menu/wallet').click(); - cy.getTestElement('@settings/wallet/network/btc').click(); - cy.getTestElement('@settings/wallet/network/eth').click(); - cy.getTestElement('@settings/wallet/network/thol').click(); - - // custom eth backend - cy.getTestElement('@settings/wallet/network/eth/advance').click(); - cy.getTestElement('@settings/advance/select-type/input').click(); - cy.getTestElement('@settings/advance/select-type/option/blockbook').click(); - cy.getTestElement('@settings/advance/url').type('https://eth.marek.pl/'); - cy.getTestElement('@settings/advance/button/save').click(); - - cy.getTestElement('@settings/menu/close').click(); - - // nothing should be reported until analytics is initialized and enabled - cy.wrap(requests).should('have.length', 0); - - cy.getTestElement('@analytics/continue-button').click(); - cy.getTestElement('@onboarding/exit-app-button'); - - // settings/analytics and suite-ready should be reported now - // settings/analytics is logged 1st - // suite-ready is logged 2nd - // other events are from queue - cy.wrap(requests).should('have.length.at.least', 2); - - cy.wrap(requests).its(0).should('have.property', 'c_type', EventType.SettingsAnalytics); - cy.wrap(requests).its(1).should('have.property', 'c_type', EventType.SuiteReady); - - // settings/analytics - cy.findAnalyticsEventByType>( - requests, - EventType.SettingsAnalytics, - ).then(deviceConnectEvent => { - expect(deviceConnectEvent.value).to.equal('true'); - }); - - // suite-ready reflects state when app was launched, does not include changes - cy.findAnalyticsEventByType>( - requests, - EventType.SuiteReady, - ).then(suiteReadyEvent => { - expect(suiteReadyEvent.language).to.equal('en'); - expect(suiteReadyEvent.enabledNetworks).to.equal('btc'); - expect(suiteReadyEvent.customBackends).to.equal(''); - expect(suiteReadyEvent.localCurrency).to.equal('usd'); - expect(suiteReadyEvent.bitcoinUnit).to.equal('BTC'); - expect(suiteReadyEvent.discreetMode).to.equal('false'); - expect(suiteReadyEvent.screenWidth).to.exist.and.not.to.be.empty; - expect(suiteReadyEvent.screenHeight).to.exist.and.not.to.be.empty; - expect(suiteReadyEvent.platformLanguages).to.exist.and.not.to.be.empty; - expect(suiteReadyEvent.tor).to.equal('false'); - expect(suiteReadyEvent.labeling).to.exist; - expect(suiteReadyEvent.rememberedStandardWallets).to.equal('0'); - expect(suiteReadyEvent.rememberedHiddenWallets).to.equal('0'); - expect(suiteReadyEvent.theme).to.equal('light'); - expect(parseInt(suiteReadyEvent.suiteVersion, 10)).to.not.equal(NaN); - expect(suiteReadyEvent.earlyAccessProgram).to.equal('false'); - expect(suiteReadyEvent.browserName).to.equal(Cypress.browser.name); - expect(parseInt(suiteReadyEvent.browserVersion, 10)).to.not.equal(NaN); - expect(suiteReadyEvent.osName).to.exist.and.not.to.be.empty; - expect(parseInt(suiteReadyEvent.osVersion, 10)).to.not.equal(NaN); - expect(suiteReadyEvent.windowWidth).to.equal(windowWidth.toString()); - expect(suiteReadyEvent.windowHeight).to.equal(windowHeight.toString()); - expect(suiteReadyEvent.autodetectLanguage).to.equal('true'); - expect(suiteReadyEvent.autodetectTheme).to.equal('true'); - }); - - requests = []; - cy.reload(); - cy.interceptDataTrezorIo(requests); - - cy.getTestElement('@onboarding/exit-app-button'); - - // suite-ready reflecting changes - cy.findAnalyticsEventByType>( - requests, - EventType.SuiteReady, - ).then(suiteReadyEvent => { - expect(suiteReadyEvent.language).to.equal('cs'); - expect(suiteReadyEvent.enabledNetworks).to.equal('eth,thol'); - expect(suiteReadyEvent.customBackends).to.equal('eth'); - expect(suiteReadyEvent.localCurrency).to.equal('czk'); - expect(suiteReadyEvent.bitcoinUnit).to.equal('sat'); - expect(suiteReadyEvent.discreetMode).to.equal('false'); - expect(suiteReadyEvent.screenWidth).to.exist.and.not.to.be.empty; - expect(suiteReadyEvent.screenHeight).to.exist.and.not.to.be.empty; - expect(suiteReadyEvent.platformLanguages).to.exist.and.not.to.be.empty; - expect(suiteReadyEvent.tor).to.equal('false'); - expect(suiteReadyEvent.labeling).to.exist; - expect(suiteReadyEvent.rememberedStandardWallets).to.equal('0'); - expect(suiteReadyEvent.rememberedHiddenWallets).to.equal('0'); - expect(suiteReadyEvent.theme).to.equal('dark'); - expect(parseInt(suiteReadyEvent.suiteVersion, 10)).to.not.equal(NaN); - expect(suiteReadyEvent.earlyAccessProgram).to.equal('false'); - expect(suiteReadyEvent.browserName).to.equal(Cypress.browser.name); - expect(parseInt(suiteReadyEvent.browserVersion, 10)).to.not.equal(NaN); - expect(suiteReadyEvent.osName).to.exist.and.not.to.be.empty; - expect(parseInt(suiteReadyEvent.osVersion, 10)).to.not.equal(NaN); - expect(suiteReadyEvent.windowWidth).to.equal(windowWidth.toString()); - expect(suiteReadyEvent.windowHeight).to.equal(windowHeight.toString()); - expect(suiteReadyEvent.autodetectLanguage).to.equal('false'); - expect(suiteReadyEvent.autodetectTheme).to.equal('false'); - }); - }); -}); diff --git a/packages/suite-web/e2e/tests/analytics/toggle.test.ts b/packages/suite-web/e2e/tests/analytics/toggle.test.ts deleted file mode 100644 index b9dbbea2d9c..00000000000 --- a/packages/suite-web/e2e/tests/analytics/toggle.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -// @group_other -// @retry=2 - -import { urlSearchParams } from '@trezor/suite/src/utils/suite/metadata'; -import { EventType } from '@trezor/suite-analytics'; - -import { onNavBar } from '../../support/pageObjects/topBarObject'; - -type Requests = ReturnType[]; -let requests: Requests; -const instance = new RegExp(/^[A-Za-z0-9]{10,10}$/); -const timestamp = new RegExp(/^[0-9]{13,16}$/); - -describe('Analytics Toggle - Enablement and Disablement', () => { - beforeEach(() => { - cy.task('startEmu', { wipe: true }); - cy.task('setupEmu'); - cy.task('startBridge'); - cy.viewport('macbook-13').resetDb(); - - requests = []; - cy.interceptDataTrezorIo(requests).as('data-fetch'); - - cy.prefixedVisit('/'); - cy.disableFirmwareHashCheck(); - }); - - it('should respect disabled analytics in onboarding with following enabling in settings', () => { - // pass through onboarding with disabled analytics - cy.getTestElement('@analytics/toggle-switch').find('input').should('be.checked'); - cy.getTestElement('@analytics/toggle-switch').click({ force: true }); - cy.getTestElement('@analytics/toggle-switch').find('input').should('not.be.checked'); - cy.getTestElement('@analytics/continue-button').click(); - - // assert that only "analytics/dispose" event was fired - cy.wait('@data-fetch'); - cy.wrap(requests).its(0).should('have.property', 'c_type', EventType.SettingsAnalytics); - cy.wrap(requests).its(0).should('have.property', 'value', 'false'); - cy.wrap(requests).its(0).its('c_session_id').as('sessionId0'); - cy.wrap(requests).its(0).its('c_instance_id').as('instanceId0'); - cy.wrap(requests) - .its(0) - .should('have.property', 'c_timestamp') - .should('match', timestamp) - .as('timestamp0'); - cy.wrap(requests).should('have.length', 1); - - // finish onboarding - cy.getTestElement('@onboarding/exit-app-button').click(); - cy.getTestElement('@onboarding/viewOnly/enable').click(); - cy.getTestElement('@viewOnlyTooltip/gotIt', { timeout: 10_000 }) - .should('be.visible') - .click(); - - // reload app (important, app needs time to save initialRun flag into storage) to change session id - cy.getTestElement('@suite/loading').should('not.exist'); - cy.discoveryShouldFinish(); - cy.safeReload(); - - // go to settings, analytics should not enabled and no additional analytics requests should be fired - onNavBar.openSettings(); - cy.getTestElement('@analytics/toggle-switch').find('input').should('not.be.checked'); - cy.wrap(requests).should('have.length', 1); - - // enable analytics and check "analytics/enable" event was fired - cy.getTestElement('@analytics/toggle-switch').click({ force: true }); - cy.getTestElement('@analytics/toggle-switch').find('input').should('be.checked'); - cy.wait('@data-fetch'); - cy.wrap(requests).its(1).should('have.property', 'c_type', EventType.SettingsAnalytics); - cy.wrap(requests).its(1).its('c_session_id').as('sessionId1'); - cy.wrap(requests).its(1).its('c_instance_id').as('instanceId1'); - cy.wrap(requests) - .its(1) - .should('have.property', 'c_timestamp') - .should('match', timestamp) - .as('timestamp1'); - cy.wrap(requests).should('have.length', 2); - - // check that timestamp changes between requests - cy.get('@timestamp0').then(timestamp0 => { - cy.get('@timestamp1').then(timestamp1 => { - expect(timestamp0).not.to.equal(timestamp1); - }); - }); - - // check that session ids changed after reload; - cy.get('@sessionId0').then(sessionId0 => { - cy.get('@sessionId1').then(sessionId1 => { - expect(sessionId0).not.to.equal(sessionId1); - }); - }); - - // check that instnace ids are same after reload; - cy.get('@instanceId0').then(instanceId0 => { - cy.get('@instanceId1').then(instanceId1 => { - expect(instanceId0).to.equal(instanceId1); - }); - }); - - // change fiat and check that it was logged - cy.getTestElement('@settings/fiat-select/input').click({ force: true }); - cy.getTestElement('@settings/fiat-select/option/huf').click({ force: true }); - cy.wait('@data-fetch'); - cy.wrap(requests) - .its(2) - .should('have.property', 'c_type', EventType.SettingsGeneralChangeFiat); - cy.wrap(requests).its(2).should('have.property', 'fiat', 'huf'); - cy.wrap(requests).its(2).should('have.property', 'c_instance_id').should('match', instance); - cy.wrap(requests).should('have.length', 3); - - // open device modal and check that it was logged - cy.getTestElement('@menu/switch-device').click(); - cy.wait('@data-fetch'); - cy.wrap(requests).its(3).should('have.property', 'c_type', EventType.RouterLocationChange); - cy.wrap(requests).should('have.length', 4); - }); - - it('should respect enabled analytics in onboarding with following disabling in settings', () => { - // pass through onboarding with enabled analytics - cy.getTestElement('@analytics/toggle-switch').find('input').should('be.checked'); - cy.getTestElement('@analytics/continue-button').click(); - - // assert that more than 1 event was fired and it was "suite/ready" and "analytics/enable" for sure - cy.wait('@data-fetch'); - cy.wrap(requests).its('length').should('be.gt', 1); - cy.wrap(requests) - .then(list => Cypress._.map(list, 'c_type')) - .should('include', EventType.SuiteReady); - cy.wrap(requests) - .then(list => Cypress._.map(list, 'c_type')) - .should('include', EventType.SettingsAnalytics); - - // finish onboarding - cy.getTestElement('@onboarding/exit-app-button').click(); - - // go to settings, analytics should be enabled - onNavBar.openSettings(); - cy.getTestElement('@analytics/toggle-switch').find('input').should('be.checked'); - - // disable analytics - cy.getTestElement('@analytics/toggle-switch').click({ force: true }); - cy.getTestElement('@analytics/toggle-switch').find('input').should('not.be.checked'); - - // change fiat - cy.getTestElement('@settings/fiat-select/input').click({ force: true }); - cy.getTestElement('@settings/fiat-select/option/huf').click({ force: true }); - cy.wait('@data-fetch'); - cy.wrap(requests).its('length').as('length1'); - - // check that analytics disable event was fired - cy.wrap(requests) - .then(list => Cypress._.map(list, 'c_type')) - .should('include', EventType.SettingsAnalytics); - cy.wrap(requests).its('length').as('length0'); - - // check that "settings/general/change-fiat" event was not fired - cy.get('@length0').then(l0 => { - cy.get('@length1').then(l1 => { - expect(l1).to.equal(l0); - }); - }); - }); -});