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

E2E testing #655

Merged
merged 9 commits into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
21 changes: 0 additions & 21 deletions .detoxrc.json

This file was deleted.

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ app/services/lnd/generated/*
android/api-8350101450647692243-59029-b9bec84a7e5a.json
android/fastlane/report.xml
android/fastlane/metadata/android/en-US/
android/app/release/

galoy.code-workspace

.phrase.yml
ios/GoogleService-Info.plist

.yalc
yalc.lock
yalc.lock
2 changes: 2 additions & 0 deletions app/components/onboarding/onboarding.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react"
import { StyleSheet, Text, View } from "react-native"
import { Button } from "react-native-elements"
import { testProps } from "../../../utils/testProps"
import { color } from "../../theme"
import { palette } from "../../theme/palette"
import type { ScreenType } from "../../types/jsx"
Expand Down Expand Up @@ -68,6 +69,7 @@ export const OnboardingScreen: ScreenType = ({
</View>
{action && (
<Button
{...testProps(nextTitle)}
title={nextTitle || "Next"}
onPress={action}
containerStyle={styles.buttonContainer}
Expand Down
3 changes: 2 additions & 1 deletion app/components/version/version.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { TextStyleProp } from "react-native/Libraries/StyleSheet/StyleSheet
import type { ComponentType } from "../../types/jsx"
import type { RootStackParamList } from "../../navigation/stack-param-lists"
import { useI18nContext } from "@app/i18n/i18n-react"
import { testProps } from "../../../utils/testProps"

const styles = StyleSheet.create({
version: {
Expand Down Expand Up @@ -36,7 +37,7 @@ export const VersionComponent: ComponentType = ({ style }: { style?: TextStylePr

return (
<Pressable onPress={() => setSecretMenuCounter(secretMenuCounter + 1)}>
<Text style={[styles.version, style]}>
<Text {...testProps("Version Build Text")} style={[styles.version, style]}>
v{VersionNumber.appVersion} build {VersionNumber.buildVersion}
{"\n"}
{/* network: {Config.BITCOIN_NETWORK} TODO */}
Expand Down
4 changes: 4 additions & 0 deletions app/screens/debug-screen/debug-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useAppConfig } from "@app/hooks/use-app-config"
import { usePersistentStateContext } from "@app/store/persistent-state"
import { GALOY_INSTANCES } from "@app/config/galoy-instances"
import { useEffect } from "react"
import { testProps } from "../../../utils/testProps"

const styles = EStyleSheet.create({
button: {
Expand Down Expand Up @@ -107,6 +108,7 @@ export const DebugScreen: ScreenType = () => {
<Text>USD per 1 sat: {usdPerSat ? `$${usdPerSat}` : "No price data"}</Text>
<Text>Hermes: {String(Boolean(usingHermes))}</Text>
<ButtonGroup
{...testProps("Galoy Instance Button")}
onPress={(index) => {
setGaloyInstance(GALOY_INSTANCES[index])
logout()
Expand All @@ -119,12 +121,14 @@ export const DebugScreen: ScreenType = () => {
containerStyle={styles.container}
/>
<GaloyInput
{...testProps("Input access token")}
placeholder={"Input access token"}
value={tempToken}
onChangeText={setTempToken}
selectTextOnFocus
/>
<Button
{...testProps("Change Token Button")}
title="Change token"
style={styles.button}
onPress={async () => {
Expand Down
4 changes: 2 additions & 2 deletions app/screens/get-started-screen/get-started-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { VersionComponent } from "../../components/version"
import { RootStackParamList } from "../../navigation/stack-param-lists"
import { palette } from "../../theme/palette"
import type { ScreenType } from "../../types/jsx"
import { testProps } from "../../../utils/testProps"

import BitcoinBeachLogo from "./bitcoin-beach-logo.png"

Expand Down Expand Up @@ -72,8 +73,7 @@ export const GetStartedScreen: ScreenType = ({ navigation }: Props) => {
titleStyle={styles.buttonTitle}
onPress={() => navigation.replace("welcomeFirst")}
containerStyle={styles.buttonContainer}
testID="getStarted"
accessibilityLabel="getStarted"
{...testProps(LL.GetStartedScreen.getStarted())}
/>
</View>
</Screen>
Expand Down
2 changes: 2 additions & 0 deletions app/screens/move-money-screen/move-money-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { BottomTabNavigationProp } from "@react-navigation/bottom-tabs"
import { CompositeNavigationProp, useIsFocused } from "@react-navigation/native"
import { useI18nContext } from "@app/i18n/i18n-react"
import { StableSatsModal } from "@app/components/stablesats-modal"
import { testProps } from "../../../utils/testProps"

const styles = EStyleSheet.create({
bottom: {
Expand Down Expand Up @@ -395,6 +396,7 @@ export const MoveMoneyScreen: ScreenType = ({
</View>

<Button
{...testProps("Settings Button")}
daviroo marked this conversation as resolved.
Show resolved Hide resolved
buttonStyle={styles.topButton}
containerStyle={styles.separator}
onPress={() => navigation.navigate("settings")}
Expand Down
1 change: 1 addition & 0 deletions app/screens/receive-bitcoin-screen/receive-bitcoin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const ReceiveBitcoinScreen = ({
{
text: LL.common.later(),
// todo: add analytics
// eslint-disable-next-line no-console
onPress: () => console.log("Cancel/Later Pressed"),
daviroo marked this conversation as resolved.
Show resolved Hide resolved
style: "cancel",
},
Expand Down
17 changes: 13 additions & 4 deletions app/screens/welcome-screens/welcome-screens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import BankShop from "./cc-bank-shop-01.svg"
import MascotDollarBitcoin from "./honey-badger-money-bitcoin-01.svg"
import HoneyBadgerShovel from "./honey-badger-shovel-01.svg"
import { useI18nContext } from "@app/i18n/i18n-react"
import { testProps } from "../../../utils/testProps"

const styles = EStyleSheet.create({
$color: palette.white,
Expand Down Expand Up @@ -62,17 +63,23 @@ export const WelcomeFirstScreen: ScreenType = ({ navigation }: Props) => {
<Screen backgroundColor={palette.lightBlue} statusBar="light-content">
<OnboardingScreen Svg={MascotDollarBitcoin}>
<Text style={styles.title}>Bitcoin:</Text>
<Text style={styles.text}>{LL.WelcomeFirstScreen.care()}</Text>
<Text {...testProps(LL.WelcomeFirstScreen.care())} style={styles.text}>
{LL.WelcomeFirstScreen.care()}
</Text>
</OnboardingScreen>
</Screen>
<Screen backgroundColor={palette.lightBlue}>
<OnboardingScreen Svg={BitcoinBitcoin}>
<Text style={styles.text}>{LL.WelcomeFirstScreen.bank()}</Text>
<Text {...testProps(LL.WelcomeFirstScreen.bank())} style={styles.text}>
{LL.WelcomeFirstScreen.bank()}
</Text>
</OnboardingScreen>
</Screen>
<Screen backgroundColor={palette.lightBlue} statusBar="light-content">
<OnboardingScreen Svg={BankShop}>
<Text style={styles.text}>{LL.WelcomeFirstScreen.before()}</Text>
<Text {...testProps(LL.WelcomeFirstScreen.before())} style={styles.text}>
{LL.WelcomeFirstScreen.before()}
</Text>
</OnboardingScreen>
</Screen>
<Screen backgroundColor={palette.lightBlue} statusBar="light-content">
Expand All @@ -83,7 +90,9 @@ export const WelcomeFirstScreen: ScreenType = ({ navigation }: Props) => {
Svg={HoneyBadgerShovel}
nextTitle={LL.WelcomeFirstScreen.learnToEarn()}
>
<Text style={styles.text}>{LL.WelcomeFirstScreen.learn()}</Text>
<Text {...testProps(LL.WelcomeFirstScreen.learn())} style={styles.text}>
{LL.WelcomeFirstScreen.learn()}
</Text>
</OnboardingScreen>
</Screen>
</Swiper>
Expand Down
2 changes: 1 addition & 1 deletion app/store/persistent-state/state-migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ export const defaultPersistentState: PersistentState = {
theme: defaultTheme,
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const deserializeAndMigratePersistentState = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data: any,
): Promise<PersistentState> => {
if (Boolean(data) && data.schemaVersion in stateMigrations) {
Expand Down
23 changes: 23 additions & 0 deletions bin/get-testing-device.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# search for android device
if [ "$TEST_DEVICE_ANDROID" ]; then
echo "android exists"
else
TEST_DEVICE_ANDROID=$(adb devices -l | grep model | awk -F':' '{print $3}' | awk '{split($0,a," "); print a[1]}')
fi
echo $TEST_DEVICE_ANDROID


# search for ios simulator
if [ "$TEST_DEVICE_IOS" ]; then
echo "ios exists"
else
TEST_DEVICE_IOS=$(xcrun simctl list devices booted | grep Booted | awk -F'(' '{print $1}' )
fi
echo $TEST_DEVICE_IOS

# TODO - TO SEARCH FOR A PHYSICAL DEVICE
# xcrun xctrace list devices

export TEST_DEVICE_ANDROID=$TEST_DEVICE_ANDROID
export TEST_DEVICE_IOS=$TEST_DEVICE_IOS

42 changes: 42 additions & 0 deletions docs/e2e-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# E2E Testing

## To Test locally with Appium and Webdriver:

1. run the debug version of the app `yarn android` or `yarn ios`
2. In a new terminal run `yarn start:appium`
3. In a new terminal run `yarn test:e2e:android` or `yarn test:e2e:ios`

## To Test with Browserstack (cloud devices):

\*\* this will eventually be integrated into CI, but for now you can test locally if you have
access to browserstack.com

```
export BROWSERSTACK_USER=YOURUSER
export BROWSERSTACK_ACCESS_KEY=YOURKEY
export BROWSERSTACK_APP_ID=bs://YOURAPPID
```

run `yarn test:browserstack:android`

## Getting the Name of an Android or IOS device

There is a script in `bin/get-testing-device.sh` that will automatically get the name of the android or ios device and set the env vars `TEST_DEVICE_ANDROID` and `TEST_DEVICE_IOS`

You can also manually set the environment variable for the test device like this:

Android

```
TEST_DEVICE_ANDROID="Pixel 3 API 29" yarn test:e2e:android
```

IOS

```
TEST_DEVICE_IOS="iPhone 13" yarn test:e2e:ios
```

## Authenticated Tests

To run the authenticated tests you need to set the env variable `GALOY_TOKEN`. The e2e test will navigate to the settings/build version page and input the token
47 changes: 47 additions & 0 deletions e2e/01-welcome-screen-flow.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { i18nObject } from "../app/i18n/i18n-util"
import { loadLocale } from "../app/i18n/i18n-util.sync"
import { swipe, selector } from "./utils"

describe("Welcome Screen Flow", async () => {
loadLocale("en")
const LL = i18nObject("en")
const timeout = 30000
beforeEach(async () => {
console.info("[beforeAll]")
})
afterEach(async () => {
console.info("[afterAll] Done with testing!")
await browser.pause(1500)
})
it("loads and clicks 'Get Started button' ", async () => {
const getStartedButton = await $(selector(LL.GetStartedScreen.getStarted()))
await getStartedButton.waitForDisplayed({ timeout })
await getStartedButton.click()
expect(true).toBeTruthy()
})

it("swipes Why Should I Care?", async () => {
const caresText = await $(selector(LL.WelcomeFirstScreen.care(), "StaticText"))
await caresText.waitForDisplayed({ timeout })
await swipe()
})

it("swipes Bitcoin is designed to let you...bank", async () => {
const bankText = await $(selector(LL.WelcomeFirstScreen.bank(), "StaticText"))
await bankText.waitForDisplayed({ timeout })
await swipe()
})

it("swipes Before Bitcoin people had to...", async () => {
const beforeText = await $(selector(LL.WelcomeFirstScreen.before(), "StaticText"))
await beforeText.waitForDisplayed({ timeout })
await swipe()
})

it("clicks 'Learn to Earn' and enters the main app", async () => {
const learnButton = await $(selector(LL.WelcomeFirstScreen.learnToEarn()))
await learnButton.waitForDisplayed({ timeout })
await learnButton.click()
expect(true).toBeTruthy()
})
})
72 changes: 72 additions & 0 deletions e2e/02-login-flow.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { i18nObject } from "../app/i18n/i18n-util"
import { loadLocale } from "../app/i18n/i18n-util.sync"
import { goBack, selector, enter } from "./utils"

describe("Login Flow", async () => {
loadLocale("en")
const LL = i18nObject("en")
const timeout = 30000
beforeEach(async () => {
console.info("[beforeAll]")
})
afterEach(async () => {
console.info("[afterAll] Done with testing!")
await browser.pause(5000)
})
it("clicks Settings Icon", async () => {
const settingsButton = await $(selector("Settings Button"))
await settingsButton.waitForDisplayed({ timeout })
await settingsButton.click()
})

it("taps Build version 3 times", async () => {
const buildButton = await $(selector("Version Build Text", "StaticText"))
await buildButton.waitForDisplayed({ timeout })
await buildButton.click()
await buildButton.click()
await buildButton.click()
})

it("click staging environment", async () => {
await browser.pause(1000)
const instanceButton = await $(selector("Galoy Instance Button", "Other"))
await instanceButton.waitForDisplayed({ timeout })
const { x, y } = await instanceButton.getLocation()
const { width, height } = await instanceButton.getSize()
// calc the midpoint center because we want to click the second button - in the middle
const midpointX = width / 2 + x
const midpointY = height / 2 + y
await browser.touchAction({ action: "tap", x: midpointX, y: midpointY })
})

it("input token", async () => {
try {
const tokenInput = await $(selector("Input access token", "TextField"))
await tokenInput.waitForDisplayed({ timeout })
await tokenInput.click()
await browser.pause(500)
await tokenInput.sendKeys(process.env.GALOY_TOKEN?.split(""))
await enter(tokenInput)
} catch (e) {
// TODO this passes but throws an error on ios even tho it works
}
})

it("click change token", async () => {
const changeTokenButton = await $(selector("Change Token Button"))
await changeTokenButton.waitForDisplayed({ timeout })
await changeTokenButton.click()
})

it("click go back to settings screen", async () => {
const backButton = await $(goBack())
await backButton.waitForDisplayed({ timeout })
await backButton.click()
})

it("click go back to home screen", async () => {
const backButton = await $(goBack())
await backButton.waitForDisplayed({ timeout })
await backButton.click()
})
})
Loading