Skip to content
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
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ module.exports = {
'app/components/UI/Card/**/*.{js,jsx,ts,tsx}',
'app/components/Snaps/**/*.{js,jsx,ts,tsx}',
'app/components/UI/Predict/**/*.{js,jsx,ts,tsx}',
'app/components/UI/Ramp/**/*.{js,jsx,ts,tsx}',
'app/components/UI/Rewards/**/*.{js,jsx,ts,tsx}',
'app/components/UI/Perps/**/*.{js,jsx,ts,tsx}',
],
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/performance-test-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ on:
required: true
type: string
description: 'Unified BrowserStack build name for all sessions'
browserstack_playground_url:
required: false
type: string
description: 'BrowserStack URL for the RN playground APK (mm-connect tests only)'
sentry_target:
required: false
type: string
Expand Down Expand Up @@ -190,6 +194,9 @@ jobs:
echo "BROWSERSTACK_DEVICE=${{ matrix.device.name }}"
echo "BROWSERSTACK_OS_VERSION=${{ matrix.device.os_version }}"
echo "BROWSERSTACK_${{ inputs.platform == 'android' && 'ANDROID' || 'IOS' }}_${{ inputs.build_type == 'onboarding' && 'CLEAN_' || '' }}APP_URL=${{ inputs.browserstack_app_url }}"
if [ -n "${{ inputs.browserstack_playground_url }}" ]; then
echo "BROWSERSTACK_RN_PLAYGROUND_URL=${{ inputs.browserstack_playground_url }}"
fi
echo "TEST_PLATFORM=${{ inputs.platform }}"
echo "QA_APP_VERSION=${{ inputs.app_version }}"
echo "BROWSERSTACK_BUILD_NAME=${{ inputs.browserstack_build_name }}"
Expand Down
41 changes: 39 additions & 2 deletions .github/workflows/run-performance-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,18 +325,54 @@ jobs:
browserstack_build_name: ${{ needs.set-build-names.outputs.ios_build_name }}
secrets: inherit

fetch-rn-playground-apk-upload-to-browserstack:
name: Fetch RN Playground APK and Upload to BrowserStack
runs-on: ubuntu-latest
needs: [wait-for-onboarding-completion]
if: always() && !cancelled()
outputs:
browserstack-playground-url: ${{ steps.upload-playground.outputs.browserstack-url }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Fetch playground APK from GitHub Releases
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RN_PLAYGROUND_APK_VERSION: ${{ vars.RN_PLAYGROUND_APK_VERSION || '' }}
run: ./scripts/fetch-rn-playground-apk.sh --output ./tmp/rn-playground.apk

- name: Upload playground APK to BrowserStack
id: upload-playground
run: |
echo "Uploading RN Playground APK to BrowserStack..."
RESPONSE=$(curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
-F "file=@./tmp/rn-playground.apk" \
-F "custom_id=RN-Playground-${{ github.run_id }}")

APP_URL=$(echo "$RESPONSE" | jq -r '.app_url')
if [ -z "$APP_URL" ] || [ "$APP_URL" = "null" ]; then
echo "Error: Failed to upload playground APK to BrowserStack"
echo "Response: $RESPONSE"
exit 1
fi

echo "browserstack-url=$APP_URL" >> "$GITHUB_OUTPUT"
echo "Playground APK uploaded: $APP_URL"

run-android-mm-connect-tests:
name: Run Android MM-Connect Tests
uses: ./.github/workflows/performance-test-runner.yml
needs:
[
read-device-matrix,
trigger-android-dual-versions,
wait-for-onboarding-completion,
fetch-rn-playground-apk-upload-to-browserstack,
set-build-names,
determine-branch-name,
]
if: always() && !cancelled() && (needs.trigger-android-dual-versions.result == 'skipped' || needs.trigger-android-dual-versions.result == 'success') && (inputs.browserstack_app_url_android_imported_wallet != '' || needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url != '')
if: always() && !cancelled() && (needs.trigger-android-dual-versions.result == 'skipped' || needs.trigger-android-dual-versions.result == 'success') && (inputs.browserstack_app_url_android_imported_wallet != '' || needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url != '') && needs.fetch-rn-playground-apk-upload-to-browserstack.result == 'success'
with:
platform: android
build_type: mm-connect
Expand All @@ -346,6 +382,7 @@ jobs:
app_version: ${{ needs.trigger-android-dual-versions.outputs.with-srp-version || 'Manual-Input' }}
branch_name: ${{ needs.determine-branch-name.outputs.branch_name }}
browserstack_build_name: ${{ needs.set-build-names.outputs.android_build_name }}
browserstack_playground_url: ${{ needs.fetch-rn-playground-apk-upload-to-browserstack.outputs.browserstack-playground-url }}
secrets: inherit

aggregate-results:
Expand Down
9 changes: 5 additions & 4 deletions .yarn/patches/appwright-npm-0.1.45-f282bc1c1b.patch
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,13 @@ index 638c5df686413ffe050ef7e7329b9e9446e98002..6599e6dd453de2cc38a4c299d3509e85
const sessionData = await getSessionDetails(sessionId);
const sessionDetails = sessionData?.automation_session;
const videoURL = sessionDetails?.video_url;
@@ -241,8 +248,16 @@ class BrowserStackDeviceProvider {
@@ -241,8 +248,17 @@ class BrowserStackDeviceProvider {
capabilities: {
"bstack:options": {
debug: true,
+ local: true,
+ local: process.env.BROWSERSTACK_LOCAL?.toLowerCase() !== 'false',
+ ...(process.env.BROWSERSTACK_LOCAL_IDENTIFIER ? { localIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER } : {}),
+ ...(process.env.BROWSERSTACK_RN_PLAYGROUND_URL ? { otherApps: [process.env.BROWSERSTACK_RN_PLAYGROUND_URL] } : {}),
+ networkProfile : '4g-lte-advanced-good',
interactiveDebugging: true,
+ selfHeal: true,
Expand All @@ -165,7 +166,7 @@ index 638c5df686413ffe050ef7e7329b9e9446e98002..6599e6dd453de2cc38a4c299d3509e85
appiumVersion: "2.6.0",
enableCameraImageInjection: this.project.use.device?.enableCameraImageInjection,
idleTimeout: 180,
@@ -250,10 +264,10 @@ class BrowserStackDeviceProvider {
@@ -250,10 +265,10 @@ class BrowserStackDeviceProvider {
osVersion: this.project.use.device.osVersion,
platformName: platformName,
deviceOrientation: this.project.use.device?.orientation,
Expand All @@ -177,7 +178,7 @@ index 638c5df686413ffe050ef7e7329b9e9446e98002..6599e6dd453de2cc38a4c299d3509e85
: process.env.USER,
},
"appium:autoGrantPermissions": true,
@@ -261,6 +275,25 @@ class BrowserStackDeviceProvider {
@@ -261,6 +276,25 @@ class BrowserStackDeviceProvider {
"appium:autoAcceptAlerts": true,
"appium:fullReset": true,
"appium:settings[snapshotMaxDepth]": 62,
Expand Down
13 changes: 0 additions & 13 deletions .yarn/patches/appwright-patch-9fcd858d19.patch

This file was deleted.

29 changes: 29 additions & 0 deletions app/actions/onboarding/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ import {
saveOnboardingEvent,
clearOnboardingEvents,
setCompletedOnboarding,
setAccountType,
clearAccountType,
SAVE_EVENT,
CLEAR_EVENTS,
SET_COMPLETED_ONBOARDING,
SET_ACCOUNT_TYPE,
CLEAR_ACCOUNT_TYPE,
} from '.';
import { ITrackingEvent } from '../../core/Analytics/MetaMetrics.types';
import { AccountType } from '../../constants/onboarding';

describe('Onboarding actions', () => {
describe('saveOnboardingEvent', () => {
Expand Down Expand Up @@ -38,4 +43,28 @@ describe('Onboarding actions', () => {
});
});
});

describe('setAccountType', () => {
it('creates an action to set accountType', () => {
expect(setAccountType(AccountType.Metamask)).toEqual({
type: SET_ACCOUNT_TYPE,
accountType: AccountType.Metamask,
});
});

it('creates an action with social login account type', () => {
expect(setAccountType(AccountType.MetamaskGoogle)).toEqual({
type: SET_ACCOUNT_TYPE,
accountType: AccountType.MetamaskGoogle,
});
});
});

describe('clearAccountType', () => {
it('creates an action to clear accountType', () => {
expect(clearAccountType()).toEqual({
type: CLEAR_ACCOUNT_TYPE,
});
});
});
});
29 changes: 28 additions & 1 deletion app/actions/onboarding/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { ITrackingEvent } from '../../core/Analytics/MetaMetrics.types';
import { AccountType } from '../../constants/onboarding';

export const SAVE_EVENT = 'SAVE_EVENT';
export const CLEAR_EVENTS = 'CLEAR_EVENTS';
export const SET_COMPLETED_ONBOARDING = 'SET_COMPLETED_ONBOARDING';
export const SET_ACCOUNT_TYPE = 'SET_ACCOUNT_TYPE';
export const CLEAR_ACCOUNT_TYPE = 'CLEAR_ACCOUNT_TYPE';

interface SaveEventAction {
type: typeof SAVE_EVENT;
Expand All @@ -18,10 +21,21 @@ export interface SetCompletedOnboardingAction {
completedOnboarding: boolean;
}

interface SetAccountTypeAction {
type: typeof SET_ACCOUNT_TYPE;
accountType: AccountType;
}

interface ClearAccountTypeAction {
type: typeof CLEAR_ACCOUNT_TYPE;
}

export type OnboardingActionTypes =
| SaveEventAction
| ClearEventsAction
| SetCompletedOnboardingAction;
| SetCompletedOnboardingAction
| SetAccountTypeAction
| ClearAccountTypeAction;

export function saveOnboardingEvent(
eventArgs: [ITrackingEvent],
Expand All @@ -46,3 +60,16 @@ export function setCompletedOnboarding(
completedOnboarding,
};
}

export function setAccountType(accountType: AccountType): SetAccountTypeAction {
return {
type: SET_ACCOUNT_TYPE,
accountType,
};
}

export function clearAccountType(): ClearAccountTypeAction {
return {
type: CLEAR_ACCOUNT_TYPE,
};
}
12 changes: 8 additions & 4 deletions app/components/UI/Bridge/components/TokenSelectorItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,11 @@ const FiatBalanceView = ({
}

return (
<Text variant={TextVariant.BodyLGMedium} numberOfLines={1}>
<Text
variant={TextVariant.BodyMD}
color={TextColor.Alternative}
numberOfLines={1}
>
{balance}
</Text>
);
Expand Down Expand Up @@ -268,7 +272,7 @@ export const TokenSelectorItem: React.FC<TokenSelectorItemProps> = ({
>
<Box style={styles.tokenMainInfo} gap={4}>
<Text
variant={TextVariant.BodyLGMedium}
variant={TextVariant.BodyMDMedium}
numberOfLines={1}
ellipsizeMode="tail"
>
Expand All @@ -293,8 +297,8 @@ export const TokenSelectorItem: React.FC<TokenSelectorItemProps> = ({
<View style={styles.skeleton} />
) : (
<Text
variant={TextVariant.BodyMD}
color={TextColor.Alternative}
variant={TextVariant.BodyMDMedium}
color={TextColor.Default}
numberOfLines={1}
style={styles.rightValue}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ const MarketInsightsEntryCard: React.FC<MarketInsightsEntryCardProps> = ({
return (
<Pressable
onPress={onPress}
style={({ pressed }) => tw.style('px-4 mt-2', pressed && 'opacity-80')}
style={({ pressed }) =>
tw.style('px-4 mt-2 mb-4', pressed && 'opacity-80')
}
testID={testID}
>
<Box gap={2}>
Expand Down
1 change: 1 addition & 0 deletions app/components/UI/OptinMetrics/OptinMetrics.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface OptinMetricsRouteParams {
onContinue?: () => void;
accountType?: string;
}

export interface LinkParams {
Expand Down
86 changes: 86 additions & 0 deletions app/components/UI/OptinMetrics/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MetaMetricsOptInSelectorsIDs } from './MetaMetricsOptIn.testIds';
import { Platform } from 'react-native';
import Device from '../../../util/device';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { AccountType } from '../../../constants/onboarding';

const { InteractionManager } = jest.requireActual('react-native');

Expand Down Expand Up @@ -195,6 +196,91 @@ describe('OptinMetrics', () => {
});
});

describe('account_type property in events', () => {
it('includes account_type in ANALYTICS_PREFERENCE_SELECTED when accountType route param is provided', async () => {
renderScreen(
OptinMetrics,
{ name: 'OptinMetrics' },
{ state: {} },
{ accountType: AccountType.Imported },
);

fireEvent.press(
screen.getByRole('button', {
name: strings('privacy_policy.continue'),
}),
);

await waitFor(() => {
expect(mockAnalytics.trackEvent).toHaveBeenCalledWith(
expect.objectContaining({
name: 'Analytics Preference Selected',
properties: expect.objectContaining({
is_metrics_opted_in: true,
location: 'onboarding_metametrics',
updated_after_onboarding: false,
account_type: AccountType.Imported,
}),
}),
);
});
});

it('includes account_type in METRICS_OPT_OUT when accountType route param is provided', async () => {
renderScreen(
OptinMetrics,
{ name: 'OptinMetrics' },
{ state: {} },
{ accountType: AccountType.Metamask },
);

const basicUsageCheckbox = screen.getByText(
strings('privacy_policy.gather_basic_usage_title'),
);
fireEvent.press(basicUsageCheckbox);

fireEvent.press(
screen.getByRole('button', {
name: strings('privacy_policy.continue'),
}),
);

await waitFor(() => {
expect(mockAnalytics.trackEvent).toHaveBeenCalledWith(
expect.objectContaining({
name: MetaMetricsEvents.METRICS_OPT_OUT.category,
properties: expect.objectContaining({
updated_after_onboarding: false,
location: 'onboarding_metametrics',
account_type: AccountType.Metamask,
}),
}),
);
});
});

it('does not include account_type when accountType route param is not provided', async () => {
renderScreen(OptinMetrics, { name: 'OptinMetrics' }, { state: {} });

fireEvent.press(
screen.getByRole('button', {
name: strings('privacy_policy.continue'),
}),
);

await waitFor(() => {
expect(mockAnalytics.trackEvent).toHaveBeenCalledWith(
expect.objectContaining({
name: 'Analytics Preference Selected',
properties: expect.not.objectContaining({
account_type: expect.anything(),
}),
}),
);
});
});
});

describe('Basic usage data collection checkbox', () => {
it('should display basic usage checkbox title', () => {
renderScreen(OptinMetrics, { name: 'OptinMetrics' }, { state: {} });
Expand Down
Loading
Loading