From fcb68ad0af45c94579038b064d9819266e520d63 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Thu, 5 Dec 2024 20:43:51 +0100 Subject: [PATCH] chore: transfer ownership of auth & profile sync E2E from notifications to identity (#12569) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR takes care of all the necessary changes in order to decouple Auth & Profile Sync E2E from the notifications feature. This also underlines the ownership change from @MetaMask/notifications to @MetaMask/identity. The changes made here will help transitioning to cleaner separation of concerns for features leveraging profile sync (i.e Notifications, Account syncing...). ⚠️ This PR does not add missing tests nor introduces any changes. This is only moving files around and separating concerns. ☝️ This PR has the "No E2E Smoke Needed" label since, even though we're moving e2e files, these E2E tests are not ran yet (they were - and still are - commented in `bitrise.yml`) ## **Related issues** Fixes: ## **Manual testing steps** No testing steps since this PR does not change the implementation of existing features. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .github/CODEOWNERS | 1 + bitrise.yml | 20 +++---- .../account-syncing/mockData.js | 0 ...c-after-adding-custom-name-account.spec.js | 27 +++++---- .../sync-after-onboarding.spec.js | 23 +++---- e2e/specs/identity/utils/constants.js | 7 +++ e2e/specs/identity/utils/helpers.js | 10 ++++ e2e/specs/identity/utils/mocks.js | 60 +++++++++++++++++++ .../userStorageMockttpController.js | 0 .../userStorageMockttpController.test.js | 0 e2e/specs/notifications/utils/mocks.js | 13 +--- e2e/tags.js | 7 +-- 12 files changed, 119 insertions(+), 49 deletions(-) rename e2e/specs/{notifications => identity}/account-syncing/mockData.js (100%) rename e2e/specs/{notifications => identity}/account-syncing/sync-after-adding-custom-name-account.spec.js (83%) rename e2e/specs/{notifications => identity}/account-syncing/sync-after-onboarding.spec.js (78%) create mode 100644 e2e/specs/identity/utils/constants.js create mode 100644 e2e/specs/identity/utils/helpers.js create mode 100644 e2e/specs/identity/utils/mocks.js rename e2e/specs/{notifications => identity}/utils/user-storage/userStorageMockttpController.js (100%) rename e2e/specs/{notifications => identity}/utils/user-storage/userStorageMockttpController.test.js (100%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dcadb5a34df..169e5ccc3dc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -66,6 +66,7 @@ app/store/util/notifications @MetaMask/notifications app/actions/identity @MetaMask/identity app/util/identity @MetaMask/identity app/components/UI/ProfileSyncing @MetaMask/identity +e2e/specs/identity @MetaMask/identity # LavaMoat Team ses.cjs @MetaMask/supply-chain diff --git a/bitrise.yml b/bitrise.yml index 86e7b34e28d..2716f7bf237 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -149,8 +149,8 @@ stages: - run_ios_api_specs: {} - run_tag_smoke_accounts_ios: {} - run_tag_smoke_accounts_android: {} - # - run_tag_smoke_notifications_ios: {} - # - run_tag_smoke_notifications_android: {} + # - run_tag_smoke_identity_ios: {} + # - run_tag_smoke_identity_android: {} # - run_tag_smoke_assets_ios: {} - run_tag_smoke_assets_android: {} - run_tag_smoke_confirmations_ios: {} @@ -177,8 +177,8 @@ stages: - run_tag_smoke_confirmations_android: {} - run_tag_smoke_accounts_ios: {} - run_tag_smoke_accounts_android: {} - # - run_tag_smoke_notifications_ios: {} - # - run_tag_smoke_notifications_android: {} + # - run_tag_smoke_identity_ios: {} + # - run_tag_smoke_identity_android: {} # - run_tag_smoke_assets_ios: {} - run_tag_smoke_assets_android: {} # - run_tag_smoke_swaps_ios: {} @@ -595,20 +595,20 @@ workflows: - TEST_SUITE_TAG: '.*SmokeAccounts.*' after_run: - android_e2e_test - run_tag_smoke_notifications_ios: + run_tag_smoke_identity_ios: envs: - - TEST_SUITE_FOLDER: './e2e/specs/notifications/*' - - TEST_SUITE_TAG: '.*SmokeNotifications.*' + - TEST_SUITE_FOLDER: './e2e/specs/identity/*' + - TEST_SUITE_TAG: '.*SmokeIdentity.*' after_run: - ios_e2e_test - run_tag_smoke_notifications_android: + run_tag_smoke_identity_android: meta: bitrise.io: stack: linux-docker-android-22.04 machine_type_id: elite-xl envs: - - TEST_SUITE_FOLDER: './e2e/specs/notifications/*' - - TEST_SUITE_TAG: '.*SmokeNotifications.*' + - TEST_SUITE_FOLDER: './e2e/specs/identity/*' + - TEST_SUITE_TAG: '.*SmokeIdentity.*' after_run: - android_e2e_test run_tag_smoke_assets_ios: diff --git a/e2e/specs/notifications/account-syncing/mockData.js b/e2e/specs/identity/account-syncing/mockData.js similarity index 100% rename from e2e/specs/notifications/account-syncing/mockData.js rename to e2e/specs/identity/account-syncing/mockData.js diff --git a/e2e/specs/notifications/account-syncing/sync-after-adding-custom-name-account.spec.js b/e2e/specs/identity/account-syncing/sync-after-adding-custom-name-account.spec.js similarity index 83% rename from e2e/specs/notifications/account-syncing/sync-after-adding-custom-name-account.spec.js rename to e2e/specs/identity/account-syncing/sync-after-adding-custom-name-account.spec.js index d95992c5cd7..a8c40885fe5 100644 --- a/e2e/specs/notifications/account-syncing/sync-after-adding-custom-name-account.spec.js +++ b/e2e/specs/identity/account-syncing/sync-after-adding-custom-name-account.spec.js @@ -1,8 +1,8 @@ import { SDK } from '@metamask/profile-sync-controller'; import { - NOTIFICATIONS_TEAM_PASSWORD, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_STORAGE_KEY, + IDENTITY_TEAM_PASSWORD, + IDENTITY_TEAM_SEED_PHRASE, + IDENTITY_TEAM_STORAGE_KEY, } from '../utils/constants'; import { startMockServer, @@ -16,10 +16,10 @@ import AccountListBottomSheet from '../../../pages/wallet/AccountListBottomSheet import Assertions from '../../../utils/Assertions'; import AddAccountBottomSheet from '../../../pages/wallet/AddAccountBottomSheet'; import AccountActionsBottomSheet from '../../../pages/wallet/AccountActionsBottomSheet'; -import { mockNotificationServices } from '../utils/mocks'; -import { SmokeNotifications } from '../../../tags'; +import { mockIdentityServices } from '../utils/mocks'; +import { SmokeIdentity } from '../../../tags'; -describe(SmokeNotifications('Account syncing'), () => { +describe(SmokeIdentity('Account syncing'), () => { const NEW_ACCOUNT_NAME = 'My third account'; let decryptedAccountNames = ''; @@ -29,8 +29,9 @@ describe(SmokeNotifications('Account syncing'), () => { const mockServer = await startMockServer(); - const { userStorageMockttpControllerInstance } = - await mockNotificationServices(mockServer); + const { userStorageMockttpControllerInstance } = await mockIdentityServices( + mockServer, + ); userStorageMockttpControllerInstance.setupPath('accounts', mockServer, { getResponse: accountsSyncMockResponse, @@ -40,7 +41,7 @@ describe(SmokeNotifications('Account syncing'), () => { accountsSyncMockResponse.map(async (response) => { const decryptedAccountName = await SDK.Encryption.decryptString( response.Data, - NOTIFICATIONS_TEAM_STORAGE_KEY, + IDENTITY_TEAM_STORAGE_KEY, ); return JSON.parse(decryptedAccountName).n; }), @@ -58,8 +59,8 @@ describe(SmokeNotifications('Account syncing'), () => { it('syncs newly added accounts with custom names', async () => { await importWalletWithRecoveryPhrase( - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, + IDENTITY_TEAM_SEED_PHRASE, + IDENTITY_TEAM_PASSWORD, ); await WalletView.tapIdenticon(); @@ -94,8 +95,8 @@ describe(SmokeNotifications('Account syncing'), () => { }); await importWalletWithRecoveryPhrase( - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, + IDENTITY_TEAM_SEED_PHRASE, + IDENTITY_TEAM_PASSWORD, ); await WalletView.tapIdenticon(); diff --git a/e2e/specs/notifications/account-syncing/sync-after-onboarding.spec.js b/e2e/specs/identity/account-syncing/sync-after-onboarding.spec.js similarity index 78% rename from e2e/specs/notifications/account-syncing/sync-after-onboarding.spec.js rename to e2e/specs/identity/account-syncing/sync-after-onboarding.spec.js index 38925f2922a..8ac7efd733a 100644 --- a/e2e/specs/notifications/account-syncing/sync-after-onboarding.spec.js +++ b/e2e/specs/identity/account-syncing/sync-after-onboarding.spec.js @@ -1,8 +1,8 @@ import { SDK } from '@metamask/profile-sync-controller'; import { - NOTIFICATIONS_TEAM_PASSWORD, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_STORAGE_KEY, + IDENTITY_TEAM_PASSWORD, + IDENTITY_TEAM_SEED_PHRASE, + IDENTITY_TEAM_STORAGE_KEY, } from '../utils/constants'; import { startMockServer, @@ -14,17 +14,18 @@ import TestHelpers from '../../../helpers'; import WalletView from '../../../pages/wallet/WalletView'; import AccountListBottomSheet from '../../../pages/wallet/AccountListBottomSheet'; import Assertions from '../../../utils/Assertions'; -import { mockNotificationServices } from '../utils/mocks'; -import { SmokeNotifications } from '../../../tags'; +import { mockIdentityServices } from '../utils/mocks'; +import { SmokeIdentity } from '../../../tags'; -describe(SmokeNotifications('Account syncing'), () => { +describe(SmokeIdentity('Account syncing'), () => { beforeAll(async () => { const mockServer = await startMockServer({ mockUrl: 'https://user-storage.api.cx.metamask.io/api/v1/userstorage', }); - const { userStorageMockttpControllerInstance } = - await mockNotificationServices(mockServer); + const { userStorageMockttpControllerInstance } = await mockIdentityServices( + mockServer, + ); userStorageMockttpControllerInstance.setupPath('accounts', mockServer, { getResponse: accountsSyncMockResponse, @@ -48,15 +49,15 @@ describe(SmokeNotifications('Account syncing'), () => { accountsSyncMockResponse.map(async (response) => { const decryptedAccountName = await SDK.Encryption.decryptString( response.Data, - NOTIFICATIONS_TEAM_STORAGE_KEY, + IDENTITY_TEAM_STORAGE_KEY, ); return JSON.parse(decryptedAccountName).n; }), ); await importWalletWithRecoveryPhrase( - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, + IDENTITY_TEAM_SEED_PHRASE, + IDENTITY_TEAM_PASSWORD, ); await WalletView.tapIdenticon(); diff --git a/e2e/specs/identity/utils/constants.js b/e2e/specs/identity/utils/constants.js new file mode 100644 index 00000000000..cb2a8e21c7c --- /dev/null +++ b/e2e/specs/identity/utils/constants.js @@ -0,0 +1,7 @@ +// As we rely on profile syncing for most of our features, we need to use the same SRP for all of our tests +export const IDENTITY_TEAM_SEED_PHRASE = + 'leisure swallow trip elbow prison wait rely keep supply hole general mountain'; +export const IDENTITY_TEAM_PASSWORD = 'notify_password'; +// You can use the storage key below to generate mock data +export const IDENTITY_TEAM_STORAGE_KEY = + '0d55d30da233959674d14076737198c05ae3fb8631a17e20d3c28c60dddd82f7'; diff --git a/e2e/specs/identity/utils/helpers.js b/e2e/specs/identity/utils/helpers.js new file mode 100644 index 00000000000..7eed1dd5382 --- /dev/null +++ b/e2e/specs/identity/utils/helpers.js @@ -0,0 +1,10 @@ +export const determineIfFeatureEntryFromURL = (url) => { + const decodedUrl = decodeURIComponent(url); + return ( + decodedUrl.substring(decodedUrl.lastIndexOf('userstorage') + 12).split('/') + .length === 2 + ); +}; + +export const getDecodedProxiedURL = (url) => + decodeURIComponent(String(new URL(url).searchParams.get('url'))); diff --git a/e2e/specs/identity/utils/mocks.js b/e2e/specs/identity/utils/mocks.js new file mode 100644 index 00000000000..a8509c988e0 --- /dev/null +++ b/e2e/specs/identity/utils/mocks.js @@ -0,0 +1,60 @@ +import { AuthenticationController } from '@metamask/profile-sync-controller'; +import { UserStorageMockttpController } from './user-storage/userStorageMockttpController'; +import { getDecodedProxiedURL } from './helpers'; + +const AuthMocks = AuthenticationController.Mocks; + +/** + * E2E mock setup for identity APIs (Auth, UserStorage, Profile syncing) + * + * @param server - server obj used to mock our endpoints + * @param userStorageMockttpController - optional controller to mock user storage endpoints + */ +export async function mockIdentityServices(server) { + // Auth + mockAPICall(server, AuthMocks.getMockAuthNonceResponse()); + mockAPICall(server, AuthMocks.getMockAuthLoginResponse()); + mockAPICall(server, AuthMocks.getMockAuthAccessTokenResponse()); + + // Storage + const userStorageMockttpControllerInstance = + new UserStorageMockttpController(); + + userStorageMockttpControllerInstance.setupPath('accounts', server); + userStorageMockttpControllerInstance.setupPath('networks', server); + + return { + userStorageMockttpControllerInstance, + }; +} + +function mockAPICall(server, response) { + let requestRuleBuilder; + + if (response.requestMethod === 'GET') { + requestRuleBuilder = server.forGet('/proxy'); + } + + if (response.requestMethod === 'POST') { + requestRuleBuilder = server.forPost('/proxy'); + } + + if (response.requestMethod === 'PUT') { + requestRuleBuilder = server.forPut('/proxy'); + } + + if (response.requestMethod === 'DELETE') { + requestRuleBuilder = server.forDelete('/proxy'); + } + + requestRuleBuilder + ?.matching((request) => { + const url = getDecodedProxiedURL(request.url); + + return url.includes(String(response.url)); + }) + .thenCallback(() => ({ + statusCode: 200, + json: response.response, + })); +} diff --git a/e2e/specs/notifications/utils/user-storage/userStorageMockttpController.js b/e2e/specs/identity/utils/user-storage/userStorageMockttpController.js similarity index 100% rename from e2e/specs/notifications/utils/user-storage/userStorageMockttpController.js rename to e2e/specs/identity/utils/user-storage/userStorageMockttpController.js diff --git a/e2e/specs/notifications/utils/user-storage/userStorageMockttpController.test.js b/e2e/specs/identity/utils/user-storage/userStorageMockttpController.test.js similarity index 100% rename from e2e/specs/notifications/utils/user-storage/userStorageMockttpController.test.js rename to e2e/specs/identity/utils/user-storage/userStorageMockttpController.test.js diff --git a/e2e/specs/notifications/utils/mocks.js b/e2e/specs/notifications/utils/mocks.js index 155e84a325a..dbf8e8b5e73 100644 --- a/e2e/specs/notifications/utils/mocks.js +++ b/e2e/specs/notifications/utils/mocks.js @@ -1,33 +1,24 @@ -import { AuthenticationController } from '@metamask/profile-sync-controller'; import { NotificationServicesController, NotificationServicesPushController, } from '@metamask/notification-services-controller'; -import { UserStorageMockttpController } from './user-storage/userStorageMockttpController'; +import { UserStorageMockttpController } from '../../identity/utils/user-storage/userStorageMockttpController'; import { getDecodedProxiedURL } from './helpers'; -const AuthMocks = AuthenticationController.Mocks; const NotificationMocks = NotificationServicesController.Mocks; const PushMocks = NotificationServicesPushController.Mocks; /** - * E2E mock setup for notification APIs (Auth, UserStorage, Notifications, Push Notifications, Profile syncing) + * E2E mock setup for notification APIs (Notifications, Push Notifications) * * @param server - server obj used to mock our endpoints * @param userStorageMockttpController - optional controller to mock user storage endpoints */ export async function mockNotificationServices(server) { - // Auth - mockAPICall(server, AuthMocks.getMockAuthNonceResponse()); - mockAPICall(server, AuthMocks.getMockAuthLoginResponse()); - mockAPICall(server, AuthMocks.getMockAuthAccessTokenResponse()); - // Storage const userStorageMockttpControllerInstance = new UserStorageMockttpController(); - userStorageMockttpControllerInstance.setupPath('accounts', server); - userStorageMockttpControllerInstance.setupPath('networks', server); userStorageMockttpControllerInstance.setupPath('notifications', server); // Notifications diff --git a/e2e/tags.js b/e2e/tags.js index e6a28eb0045..1251ff890fe 100644 --- a/e2e/tags.js +++ b/e2e/tags.js @@ -6,7 +6,7 @@ const tags = { SmokeSwaps: 'SmokeSwaps', SmokeRest: 'SmokeRest', smokeAssets: 'smokeAssets', - smokeNotifications: 'smokeNotifications', + smokeIdentity: 'smokeIdentity', smokeMultiChain: 'SmokeMultiChain', }; @@ -17,8 +17,7 @@ const SmokeConfirmations = (testName) => `${tags.smokeConfirmations} ${testName}`; const SmokeSwaps = (testName) => `${tags.SmokeSwaps} ${testName}`; const SmokeAssets = (testName) => `${tags.smokeAssets} ${testName}`; -const SmokeNotifications = (testName) => - `${tags.smokeNotifications} ${testName}`; +const SmokeIdentity = (testName) => `${tags.smokeIdentity} ${testName}`; const SmokeMultiChain = (testName) => `${tags.smokeMultiChain} ${testName}`; export { @@ -28,6 +27,6 @@ export { SmokeConfirmations, SmokeSwaps, SmokeAssets, - SmokeNotifications, + SmokeIdentity, SmokeMultiChain, };