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

Brody/swap v2 e2e #5915

Merged
merged 20 commits into from
Aug 5, 2024
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
14 changes: 8 additions & 6 deletions e2e/3_homeScreen.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
checkIfExists,
checkIfExistsByText,
swipe,
waitAndTap,
afterAllcleanApp,
tap,
delayTime,
} from './helpers';

const RAINBOW_TEST_WALLET = 'rainbowtestwallet.eth';
Expand Down Expand Up @@ -41,19 +42,20 @@ describe('Home Screen', () => {
});

it('tapping "Swap" opens the swap screen', async () => {
await waitAndTap('swap-button');
await tap('swap-button');
await delayTime('long');
await checkIfExists('swap-screen');
await swipe('swap-screen', 'down', 'slow');
await swipe('swap-screen', 'down', 'fast');
});

it('tapping "Send" opens the send screen', async () => {
await waitAndTap('send-button');
await tap('send-button');
await checkIfVisible('send-asset-form-field');
await swipe('send-asset-form-field', 'down');
await swipe('send-asset-form-field', 'down', 'fast');
});

it('tapping "Copy" shows copy address toast', async () => {
await waitAndTap('receive-button');
await tap('receive-button');
await checkIfVisible('address-copied-toast');
});
});
143 changes: 143 additions & 0 deletions e2e/9_swaps.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* // Other tests to consider:
* - Flip assets
* - exchange button onPress
* - disable button states once https://github.com/rainbow-me/rainbow/pull/5785 gets merged
* - swap execution
* - token search (both from userAssets and output token list)
* - custom gas panel
* - flashbots
* - slippage
* - explainer sheets
* - switching wallets inside of swap screen
*/

import {
importWalletFlow,
sendETHtoTestWallet,
checkIfVisible,
beforeAllcleanApp,
afterAllcleanApp,
fetchElementAttributes,
tap,
tapByText,
delayTime,
} from './helpers';

import { expect } from '@jest/globals';
import { WALLET_VARS } from './testVariables';

describe('Swap Sheet Interaction Flow', () => {
beforeAll(async () => {
await beforeAllcleanApp({ hardhat: true });
});
afterAll(async () => {
await afterAllcleanApp({ hardhat: true });
});

it('Import a wallet and go to welcome', async () => {
await importWalletFlow(WALLET_VARS.EMPTY_WALLET.PK);
});

it('Should send ETH to test wallet', async () => {
// send 20 eth
await sendETHtoTestWallet();
});

it('Should show Hardhat Toast after pressing Connect To Hardhat', async () => {
await tap('dev-button-hardhat');
await checkIfVisible('testnet-toast-Hardhat');

// validate it has the expected funds of 20 eth
const attributes = await fetchElementAttributes('fast-coin-info');
expect(attributes.label).toContain('Ethereum');
expect(attributes.label).toContain('20');
});

it('Should open swap screen with 50% inputAmount for inputAsset', async () => {
await device.disableSynchronization();
await tap('swap-button');
await delayTime('long');
await tap('token-to-buy-dai-1');
const swapInput = await fetchElementAttributes('swap-asset-input');

// expect inputAsset === .5 * eth balance
expect(swapInput.label).toContain('ETH');
expect(swapInput.label).toContain('10');
});

it('Should be able to go to review and execute a swap', async () => {
await tapByText('Review');
const reviewActionElements = await fetchElementAttributes('swap-action-button');
expect(reviewActionElements.elements[0].label).toContain('ETH');
expect(reviewActionElements.elements[1].label).toContain('DAI');
expect(reviewActionElements.elements[2].label).toContain('Tap to Swap');

/*
*
* Everything from this point fails. Un-comment out the following line to see behavior.
* Currently some spots have chainId 1 and chainId 1337 for various things. I suspect
* there is some issue with one of these things. Log the variables in getNonceAndPerformSwap
* to see the behavior.
*
* To run this test:
*
* yarn clean:ios && yarn fast && yarn start:clean
* yarn detox:ios:build && yarn detox test -c ios.sim.debug 8_swaps.spec.ts
*
*/

// await tapByText('Tap to Swap');
});

it.skip('Should be able to verify swap is happening', async () => {
// await delayTime('very-long');
// const activityListElements = await fetchElementAttributes('wallet-activity-list');
// expect(activityListElements.label).toContain('ETH');
// expect(activityListElements.label).toContain('DAI');
// await tapByText('Swapping');
// await delayTime('long');
// const transactionSheet = await checkIfVisible('transaction-details-sheet');
// expect(transactionSheet).toBeTruthy();
});

it.skip('Should open swap screen from ProfileActionRowButton with largest user asset', async () => {
/**
* tap swap button
* wait for Swap header to be visible
* grab highest user asset balance from userAssetsStore
* expect inputAsset.uniqueId === highest user asset uniqueId
*/
});

it.skip('Should open swap screen from asset chart with that asset selected', async () => {
/**
* tap any user asset (store const uniqueId here)
* wait for Swap header to be visible
* expect inputAsset.uniqueId === const uniqueId ^^
*/
});

it.skip('Should open swap screen from dapp browser control panel with largest user asset', async () => {
/**
* tap swap button
* wait for Swap header to be visible
* grab highest user asset balance from userAssetsStore
* expect inputAsset.uniqueId === highest user asset uniqueId
*/
});

it.skip('Should not be able to type in output amount if cross-chain quote', async () => {
/**
* tap swap button
* wait for Swap header to be visible
* select different chain in output list chain selector
* select any asset in output token list
* focus output amount
* attempt to type any number in the SwapNumberPad
* attempt to remove a character as well
*
* ^^ expect both of those to not change the outputAmount
*/
});
});
13 changes: 12 additions & 1 deletion e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { JsonRpcProvider } from '@ethersproject/providers';
import { Wallet } from '@ethersproject/wallet';
import { expect, device, element, by, waitFor } from 'detox';
import { parseEther } from '@ethersproject/units';
import { IosElementAttributes, AndroidElementAttributes } from 'detox/detox';

const TESTING_WALLET = '0x3Cb462CDC5F809aeD0558FBEe151eD5dC3D3f608';
const TESTING_WALLET = '0x3637f053D542E6D00Eee42D656dD7C59Fa33a62F';

const DEFAULT_TIMEOUT = 20_000;
const android = device.getPlatform() === 'android';
Expand Down Expand Up @@ -70,6 +71,16 @@ export async function tap(elementId: string | RegExp) {
}
}

interface CustomElementAttributes {
elements: Array<IosElementAttributes | AndroidElementAttributes>;
}

type ElementAttributes = IosElementAttributes & AndroidElementAttributes & CustomElementAttributes;

export const fetchElementAttributes = async (testId: string): Promise<ElementAttributes> => {
return (await element(by.id(testId)).getAttributes()) as ElementAttributes;
};

export async function waitAndTap(elementId: string | RegExp, timeout = DEFAULT_TIMEOUT) {
await delayTime('medium');
try {
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
"nuke": "./scripts/nuke.sh",
"detox:android": "detox build -c android.emu.debug && detox test -c android.emu.debug --loglevel verbose",
"detox:android:release": "detox build -c android.emu.release && detox test -c android.emu.release",
"detox:ios:tests": "detox test -c ios.sim.debug --maxWorkers 3 -- --bail 1",
"detox:ios": "detox build -c ios.sim.debug | xcpretty --color && yarn detox:ios:tests",
"detox:ios:release": "detox build -c ios.sim.release && detox test -c ios.sim.release --maxWorkers 3 -- --bail 1",
"detox:ios:build": "detox build -c ios.sim.debug | xcpretty --color ",
"detox:ios:tests": "detox test -c ios.sim.debug --maxWorkers 2 -- --bail 1",
"detox:ios": "yarn detox:ios:build && yarn detox:ios:tests",
"detox:ios:release": "detox build -c ios.sim.release && detox test -c ios.sim.release --maxWorkers 2 -- --bail 1",
"ds:install": "cd src/design-system/docs && yarn install",
"ds": "cd src/design-system/docs && yarn dev",
"fast": "yarn install && yarn setup && yarn install-pods-fast",
Expand Down
6 changes: 4 additions & 2 deletions src/__swaps__/screens/Swap/components/CoinRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface InputCoinRowProps {
onPress: (asset: ParsedSearchAsset | null) => void;
output?: false | undefined;
uniqueId: string;
testID?: string;
}

type PartialAsset = Pick<SearchAsset, 'address' | 'chainId' | 'colors' | 'icon_url' | 'mainnetAddress' | 'name' | 'symbol' | 'uniqueId'>;
Expand All @@ -62,11 +63,12 @@ interface OutputCoinRowProps extends PartialAsset {
output: true;
nativePriceChange?: string;
isTrending?: boolean;
testID?: string;
}

type CoinRowProps = InputCoinRowProps | OutputCoinRowProps;

export const CoinRow = memo(function CoinRow({ isFavorite, onPress, output, uniqueId, ...assetProps }: CoinRowProps) {
export const CoinRow = memo(function CoinRow({ isFavorite, onPress, output, uniqueId, testID, ...assetProps }: CoinRowProps) {
const inputAsset = userAssetsStore(state => (output ? undefined : state.getUserAsset(uniqueId)));
const outputAsset = output ? (assetProps as PartialAsset) : undefined;

Expand Down Expand Up @@ -116,7 +118,7 @@ export const CoinRow = memo(function CoinRow({ isFavorite, onPress, output, uniq
if (!address || !chainId) return null;

return (
<Box style={{ height: COIN_ROW_WITH_PADDING_HEIGHT, width: '100%' }}>
<Box testID={testID} style={{ height: COIN_ROW_WITH_PADDING_HEIGHT, width: '100%' }}>
<Columns alignVertical="center">
<Column>
<ButtonPressAnimation disallowInterruption onPress={onPressHandler} scaleTo={0.95}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { pulsingConfig, sliderConfig } from '../constants';
import { GasSettings } from '../hooks/useCustomGas';
import { useSwapEstimatedGasFee } from '../hooks/useEstimatedGasFee';
import { useSwapContext } from '../providers/swap-provider';
import { SpringConfig } from 'react-native-reanimated/lib/typescript/animation/springUtils';

type EstimatedSwapGasFeeProps = { gasSettings?: GasSettings } & Partial<
Pick<TextProps, 'align' | 'color' | 'size' | 'weight' | 'tabularNumbers'>
Expand Down Expand Up @@ -48,7 +49,7 @@ const GasFeeText = memo(function GasFeeText({
color: withTiming(isLoading.value ? zeroAmountColor : textColor, TIMING_CONFIGS.slowFadeConfig),
opacity: isLoading.value
? withRepeat(withSequence(withTiming(0.5, pulsingConfig), withTiming(1, pulsingConfig)), -1, true)
: withSpring(1, sliderConfig),
: withSpring(1, sliderConfig as SpringConfig),
}));

return (
Expand Down
42 changes: 26 additions & 16 deletions src/__swaps__/screens/Swap/components/FadeMask.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import { Box, Columns, Column, globalColors } from '@/design-system';
import LinearGradient from 'react-native-linear-gradient';
import { IS_TEST } from '@/env';
import { View } from 'react-native';

export const FadeMask = ({
fadeEdgeInset = 6,
Expand All @@ -22,14 +24,18 @@ export const FadeMask = ({
<Box height="full" width={{ custom: fadeEdgeInset }} />
</Column>
<Column width="content">
<Box
as={LinearGradient}
colors={['transparent', globalColors.grey100]}
end={{ x: 1, y: 0.5 }}
height="full"
start={{ x: 0, y: 0.5 }}
width={{ custom: fadeWidth }}
/>
{IS_TEST ? (
<Box as={View} height="full" width={{ custom: fadeWidth }} />
) : (
<Box
as={LinearGradient}
colors={['transparent', globalColors.grey100]}
end={{ x: 1, y: 0.5 }}
height="full"
start={{ x: 0, y: 0.5 }}
width={{ custom: fadeWidth }}
/>
)}
</Column>
</>
) : null}
Expand All @@ -39,14 +45,18 @@ export const FadeMask = ({
{!side || side === 'right' ? (
<>
<Column width="content">
<Box
as={LinearGradient}
colors={[globalColors.grey100, 'transparent']}
end={{ x: 1, y: 0.5 }}
height="full"
start={{ x: 0, y: 0.5 }}
width={{ custom: fadeWidth }}
/>
{IS_TEST ? (
<Box as={View} height="full" width={{ custom: fadeWidth }} />
) : (
<Box
as={LinearGradient}
colors={[globalColors.grey100, 'transparent']}
end={{ x: 1, y: 0.5 }}
height="full"
start={{ x: 0, y: 0.5 }}
width={{ custom: fadeWidth }}
/>
)}
</Column>
<Column width="content">
<Box height="full" width={{ custom: fadeEdgeInset }} />
Expand Down
5 changes: 4 additions & 1 deletion src/__swaps__/screens/Swap/components/SwapActionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function SwapButton({
disabled,
opacity,
children,
testID,
}: {
asset: DerivedValue<ExtendedAnimatedAssetWithColors | null>;
borderRadius?: number;
Expand All @@ -47,6 +48,7 @@ function SwapButton({
disabled?: DerivedValue<boolean | undefined>;
opacity?: DerivedValue<number | undefined>;
children?: React.ReactNode;
testID?: string;
}) {
const { isDarkMode } = useColorMode();
const fallbackColor = useForegroundColor('label');
Expand Down Expand Up @@ -110,6 +112,7 @@ function SwapButton({
return (
<Animated.View style={buttonWrapperStyles}>
<Box
testID={testID}
as={Animated.View}
paddingHorizontal={{ custom: small ? 14 : 20 - (outline ? 2 : 0) }}
paddingLeft={small && icon ? '10px' : undefined}
Expand Down Expand Up @@ -268,7 +271,7 @@ export const SwapActionButton = ({
style={[hugContent && feedActionButtonStyles.buttonWrapper, style]}
>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<SwapButton {...props} disabled={disabled}>
<SwapButton {...props} disabled={disabled} testID={'swap-action-button'}>
{holdProgress && <HoldProgress holdProgress={holdProgress} />}
</SwapButton>
</GestureHandlerButton>
Expand Down
Loading
Loading