diff --git a/with-maestro/.eas/build/build-and-maestro-test.yml b/with-maestro/.eas/build/build-and-maestro-test.yml index c204d794..8fe7361f 100644 --- a/with-maestro/.eas/build/build-and-maestro-test.yml +++ b/with-maestro/.eas/build/build-and-maestro-test.yml @@ -5,5 +5,5 @@ build: - eas/maestro_test: inputs: flow_path: | - maestro/home.yml - maestro/expand_test.yml + maestro/dev_build/home.yml + maestro/dev_build/expand_test.yml diff --git a/with-maestro/README.md b/with-maestro/README.md index cfc7deac..49f8934d 100644 --- a/with-maestro/README.md +++ b/with-maestro/README.md @@ -7,9 +7,33 @@ Supports Expo Android

-## 🚀 How to use +> _Prerequisite:_ Install the Maestro app following [these instructions](https://maestro.mobile.dev/getting-started/installing-maestro). + +## 🚀 Quick start - Install with `yarn` or `npm install`. + +## 🚀 Build and test locally with Expo Go + +- Start the app in Expo Go: + - Android: `yarn start` to start the packager, then press `a` to install and start Expo Go on Android + - iOS: `yarn start` to start the packager, then press `i` to install and start Expo Go on iOS +- In a separate terminal, execute a Maestro test flow: + - Home screen test: `maestro test maestro/expo_go/home.yml` + - Expanding component test: `maestro test maestro/expo_go/expand_test.yml` +- Once the test flow starts and Expo Go starts, select "Reload" or the app name from the Expo Go UI in the simulator/emulator. Once the dev menu is hidden and this app is visible, the test flow will continue. + +## 🚀 Build and test locally with a development build + +- Build the development build and start it on your simulator/emulator: + - Android: `yarn android`. + - iOS: `yarn ios`. +- In a separate terminal, execute a Maestro test flow: + - Home screen test: `maestro test maestro/dev_build/home.yml` + - Expanding component test: `maestro test maestro/dev_build/expand_test.yml` + +## 🚀 Build and test on EAS + - Initialize the app as an EAS project with `eas init`. - iOS: - Start an EAS run to build and test the app with `eas build -e build-and-maestro-test -p ios`. @@ -17,8 +41,9 @@ - Follow the [instructions to disable the new Android builds infrastructure for the EAS project](https://docs.expo.dev/build-reference/e2e-tests/#disable-new-android-builds-infrastructure). - Start an EAS run to build and test the app with `eas build -e build-and-maestro-test -p android`. -> _Note:_ The Maestro flows in the [maestro](./maestro/) folder must have the app's package name (Android) or bundle identifier (iOS) defined. To make this example work out of the box without changes, the [app.json](./app.json) and the Maestro flows are preconfigured with these values set to `dev.expo.eastestsexample`. In your actual development, these should be changed to the correct values for your app. +> _Note:_ The Maestro flows in the [maestro/dev_build](./maestro/dev_build) folder must have the app's package name (Android) or bundle identifier (iOS) defined. To make this example work out of the box without changes, the [app.json](./app.json) and the Maestro flows for dev builds are preconfigured with these values set to `dev.expo.eastestsexample`. In your actual development, these should be changed to the correct values for your app. ## 📝 Further information -See the Expo guide: [Run E2E Tests On EAS Build](https://docs.expo.dev/build-reference/e2e-tests/). +- [Expo guide on E2E tests with EAS](https://docs.expo.dev/build-reference/e2e-tests/) +- [Maestro guide on creating React Native tests](https://maestro.mobile.dev/platform-support/react-native) diff --git a/with-maestro/app.json b/with-maestro/app.json index a832bb01..9af42153 100644 --- a/with-maestro/app.json +++ b/with-maestro/app.json @@ -1,5 +1,6 @@ { "expo": { + "scheme": "dev.expo.eastestsexample", "plugins": [ "expo-router" ], diff --git a/with-maestro/app/(tabs)/_layout.tsx b/with-maestro/app/(tabs)/_layout.tsx index 22a49b62..2ae0ad2a 100644 --- a/with-maestro/app/(tabs)/_layout.tsx +++ b/with-maestro/app/(tabs)/_layout.tsx @@ -1,35 +1,32 @@ import { Tabs } from 'expo-router'; import React from 'react'; -import { TabBarIcon } from '@/components/navigation/TabBarIcon'; -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; - export default function TabLayout() { - const colorScheme = useColorScheme(); - return ( + }} + > ( - - ), + tabBarIcon: () => null, + tabBarLabelStyle: { + fontSize: 18, + }, }} /> ( - - ), + tabBarIcon: () => null, + tabBarLabelStyle: { + fontSize: 18, + }, }} /> diff --git a/with-maestro/app/(tabs)/explore.tsx b/with-maestro/app/(tabs)/explore.tsx index e480218a..e18a0142 100644 --- a/with-maestro/app/(tabs)/explore.tsx +++ b/with-maestro/app/(tabs)/explore.tsx @@ -1,102 +1,31 @@ -import Ionicons from '@expo/vector-icons/Ionicons'; -import { StyleSheet, Image, Platform } from 'react-native'; +import { StyleSheet, Text, SafeAreaView } from 'react-native'; import { Collapsible } from '@/components/Collapsible'; -import { ExternalLink } from '@/components/ExternalLink'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; export default function TabTwoScreen() { return ( - }> - - Explore - - This app includes example code to help you get started. + + Explore - - This app has two screens:{' '} - app/(tabs)/index.tsx and{' '} - app/(tabs)/explore.tsx - - - The layout file in app/(tabs)/_layout.tsx{' '} - sets up the tab navigator. - - - Learn more - + This app has two screens - - - You can open this project on Android, iOS, and the web. To open the web version, press{' '} - w in the terminal running this project. - - - - - For static images, you can use the @2x and{' '} - @3x suffixes to provide files for - different screen densities - - - - Learn more - - - - - Open app/_layout.tsx to see how to load{' '} - - custom fonts such as this one. - - - - Learn more - - - - - This template has light and dark mode support. The{' '} - useColorScheme() hook lets you inspect - what the user's current color scheme is, and so you can adjust UI colors accordingly. - - - Learn more - - - - - This template includes an example of an animated component. The{' '} - components/HelloWave.tsx component uses - the powerful react-native-reanimated library - to create a waving hand animation. - - {Platform.select({ - ios: ( - - The components/ParallaxScrollView.tsx{' '} - component provides a parallax effect for the header image. - - ), - })} - - + ); } const styles = StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -90, - left: -35, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', + container: { + flex: 1, gap: 8, + width: '80%', + height: '80%', + marginTop: '20%', + marginLeft: '10%', + }, + title: { + alignItems: 'center', + fontSize: 18, + fontWeight: 'bold', + marginBottom: 20, }, }); diff --git a/with-maestro/app/(tabs)/index.tsx b/with-maestro/app/(tabs)/index.tsx index 324aeb76..433dc79c 100644 --- a/with-maestro/app/(tabs)/index.tsx +++ b/with-maestro/app/(tabs)/index.tsx @@ -1,70 +1,26 @@ -import { Image, StyleSheet, Platform } from 'react-native'; - -import { HelloWave } from '@/components/HelloWave'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; +import { StyleSheet, Text, SafeAreaView } from 'react-native'; export default function HomeScreen() { return ( - - }> - - Welcome! - - - - Step 1: Try it - - Edit app/(tabs)/index.tsx to see changes. - Press{' '} - - {Platform.select({ ios: 'cmd + d', android: 'cmd + m' })} - {' '} - to open developer tools. - - - - Step 2: Explore - - Tap the Explore tab to learn more about what's included in this starter app. - - - - Step 3: Get a fresh start - - When you're ready, run{' '} - npm run reset-project to get a fresh{' '} - app directory. This will move the current{' '} - app to{' '} - app-example. - - - + + Welcome! + ); } const styles = StyleSheet.create({ - titleContainer: { - flexDirection: 'row', - alignItems: 'center', + container: { + flex: 1, gap: 8, + width: '80%', + height: '80%', + marginTop: '20%', + marginLeft: '10%', }, - stepContainer: { - gap: 8, - marginBottom: 8, - }, - reactLogo: { - height: 178, - width: 290, - bottom: 0, - left: 0, - position: 'absolute', + title: { + alignItems: 'center', + fontSize: 18, + fontWeight: 'bold', + marginBottom: 20, }, }); diff --git a/with-maestro/app/(tabs)/tv_focus.tsx b/with-maestro/app/(tabs)/tv_focus.tsx deleted file mode 100644 index 85553856..00000000 --- a/with-maestro/app/(tabs)/tv_focus.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import Ionicons from '@expo/vector-icons/Ionicons'; -import { StyleSheet, Platform } from 'react-native'; - -import { Collapsible } from '@/components/Collapsible'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; -import { EventHandlingDemo } from '@/components/EventHandlingDemo'; -import { useScale } from '@/hooks/useScale'; - -export default function FocusDemoScreen() { - const styles = useFocusDemoScreenStyles(); - const scale = useScale(); - return ( - - } - > - - TV event handling demo - - - Demo of focus handling and TV remote event handling in{' '} - Pressable and{' '} - Touchable components. - - - - On TV platforms, these components have "onFocus()" and "onBlur()" - props, in addition to the usual "onPress()". These can be used to - modify the style of the component when it is navigated to or navigated - away from by the TV focus engine. In addition, the functional forms of - the Pressable style prop and the Pressable content, which in React - Native core take a "pressed" boolean parameter, can also take - "focused" as a parameter on TV platforms. - - - As you use the arrow keys to navigate around the screen, the demo uses - the above props to update lists of recent events. - - - {Platform.isTV ? ( - - ) : ( - - Run this on Apple TV or Android TV to see the demo. - - )} - - ); -} - -const useFocusDemoScreenStyles = function () { - const scale = useScale(); - return StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -45 * scale, - left: 0, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', - gap: 8 * scale, - }, - }); -}; diff --git a/with-maestro/app/+not-found.tsx b/with-maestro/app/+not-found.tsx index 963b04fb..efa60df9 100644 --- a/with-maestro/app/+not-found.tsx +++ b/with-maestro/app/+not-found.tsx @@ -1,19 +1,16 @@ import { Link, Stack } from 'expo-router'; -import { StyleSheet } from 'react-native'; - -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; +import { StyleSheet, Text, View } from 'react-native'; export default function NotFoundScreen() { return ( <> - - This screen doesn't exist. + + This screen doesn't exist. - Go to home screen! + Go to home screen! - + ); } diff --git a/with-maestro/app/_layout.tsx b/with-maestro/app/_layout.tsx index 2e37cdd8..56c0d208 100644 --- a/with-maestro/app/_layout.tsx +++ b/with-maestro/app/_layout.tsx @@ -1,37 +1,10 @@ -import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; -import { useFonts } from 'expo-font'; import { Stack } from 'expo-router'; -import * as SplashScreen from 'expo-splash-screen'; -import { useEffect } from 'react'; -import 'react-native-reanimated'; - -import { useColorScheme } from '@/hooks/useColorScheme'; - -// Prevent the splash screen from auto-hiding before asset loading is complete. -SplashScreen.preventAutoHideAsync(); export default function RootLayout() { - const colorScheme = useColorScheme(); - const [loaded] = useFonts({ - SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), - }); - - useEffect(() => { - if (loaded) { - SplashScreen.hideAsync(); - } - }, [loaded]); - - if (!loaded) { - return null; - } - return ( - - - - - - + + + + ); } diff --git a/with-maestro/assets/fonts/SpaceMono-Regular.ttf b/with-maestro/assets/fonts/SpaceMono-Regular.ttf deleted file mode 100755 index 28d7ff71..00000000 Binary files a/with-maestro/assets/fonts/SpaceMono-Regular.ttf and /dev/null differ diff --git a/with-maestro/assets/images/adaptive-icon.png b/with-maestro/assets/images/adaptive-icon.png deleted file mode 100644 index 03d6f6b6..00000000 Binary files a/with-maestro/assets/images/adaptive-icon.png and /dev/null differ diff --git a/with-maestro/assets/images/favicon.png b/with-maestro/assets/images/favicon.png deleted file mode 100644 index e75f697b..00000000 Binary files a/with-maestro/assets/images/favicon.png and /dev/null differ diff --git a/with-maestro/assets/images/icon.png b/with-maestro/assets/images/icon.png deleted file mode 100644 index a0b1526f..00000000 Binary files a/with-maestro/assets/images/icon.png and /dev/null differ diff --git a/with-maestro/assets/images/partial-react-logo.png b/with-maestro/assets/images/partial-react-logo.png deleted file mode 100644 index 66fd9570..00000000 Binary files a/with-maestro/assets/images/partial-react-logo.png and /dev/null differ diff --git a/with-maestro/assets/images/react-logo.png b/with-maestro/assets/images/react-logo.png deleted file mode 100644 index 9d72a9ff..00000000 Binary files a/with-maestro/assets/images/react-logo.png and /dev/null differ diff --git a/with-maestro/assets/images/react-logo@2x.png b/with-maestro/assets/images/react-logo@2x.png deleted file mode 100644 index 2229b130..00000000 Binary files a/with-maestro/assets/images/react-logo@2x.png and /dev/null differ diff --git a/with-maestro/assets/images/react-logo@3x.png b/with-maestro/assets/images/react-logo@3x.png deleted file mode 100644 index a99b2032..00000000 Binary files a/with-maestro/assets/images/react-logo@3x.png and /dev/null differ diff --git a/with-maestro/assets/images/splash.png b/with-maestro/assets/images/splash.png deleted file mode 100644 index 0e89705a..00000000 Binary files a/with-maestro/assets/images/splash.png and /dev/null differ diff --git a/with-maestro/components/Collapsible.tsx b/with-maestro/components/Collapsible.tsx index c326473d..ba4c410a 100644 --- a/with-maestro/components/Collapsible.tsx +++ b/with-maestro/components/Collapsible.tsx @@ -1,30 +1,24 @@ -import Ionicons from '@expo/vector-icons/Ionicons'; import { PropsWithChildren, useState } from 'react'; -import { StyleSheet, TouchableOpacity, useColorScheme } from 'react-native'; +import { StyleSheet, TouchableOpacity, Text, View } from 'react-native'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; -import { Colors } from '@/constants/Colors'; - -export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { +export function Collapsible({ + children, + title, +}: PropsWithChildren & { title: string }) { const [isOpen, setIsOpen] = useState(false); - const theme = useColorScheme() ?? 'light'; return ( - + setIsOpen((value) => !value)} - activeOpacity={0.8}> - - {title} + activeOpacity={0.8} + > + {isOpen ? 'V' : '>'} + {title} - {isOpen && {children}} - + {isOpen && {children}} + ); } diff --git a/with-maestro/components/EventHandlingDemo.tsx b/with-maestro/components/EventHandlingDemo.tsx deleted file mode 100644 index ec8f5b88..00000000 --- a/with-maestro/components/EventHandlingDemo.tsx +++ /dev/null @@ -1,254 +0,0 @@ -import { - StyleSheet, - Text, - View, - useTVEventHandler, - Platform, - Pressable, - TouchableHighlight, - TouchableNativeFeedback, - TouchableOpacity, - GestureResponderEvent, -} from 'react-native'; -import { useState } from 'react'; - -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; -import { useScale } from '@/hooks/useScale'; -import { useThemeColor } from '@/hooks/useThemeColor'; - -export function EventHandlingDemo() { - const [remoteEventLog, setRemoteEventLog] = useState([]); - const [pressableEventLog, setPressableEventLog] = useState([]); - - const logWithAppendedEntry = (log: string[], entry: string) => { - const limit = 3; - const newEventLog = log.slice(0, limit - 1); - newEventLog.unshift(entry); - return newEventLog; - }; - - const updatePressableLog = (entry: string) => { - setPressableEventLog((log) => logWithAppendedEntry(log, entry)); - }; - - useTVEventHandler((event) => { - const { eventType, eventKeyAction } = event; - if (eventType !== 'focus' && eventType !== 'blur') { - setRemoteEventLog((log) => - logWithAppendedEntry( - log, - `type=${eventType}, action=${ - eventKeyAction !== undefined ? eventKeyAction : '' - }`, - ), - ); - } - }); - - const styles = useDemoStyles(); - - return ( - - - - - {Platform.OS === 'android' ? ( - - ) : null} - - - - Focus/press events - - {remoteEventLog.join('\n')} - - - - Remote control events - - {pressableEventLog.join('\n')} - - - - - ); -} - -const PressableButton = (props: { - title: string; - log: (entry: string) => void; -}) => { - const styles = useDemoStyles(); - - return ( - props.log(`${props.title} focus`)} - onBlur={() => props.log(`${props.title} blur`)} - onPress={() => props.log(`${props.title} pressed`)} - onLongPress={( - event: GestureResponderEvent & { eventKeyAction?: number }, - ) => - props.log( - `${props.title} long press ${ - event.eventKeyAction === 0 ? 'start' : 'end' - }`, - ) - } - style={({ pressed, focused }) => - pressed || focused ? styles.pressableFocused : styles.pressable - } - > - {({ focused }) => { - return ( - - {focused ? `${props.title} focused` : props.title} - - ); - }} - - ); -}; - -const TouchableOpacityButton = (props: { - title: string; - log: (entry: string) => void; -}) => { - const styles = useDemoStyles(); - - return ( - props.log(`${props.title} focus`)} - onBlur={() => props.log(`${props.title} blur`)} - onPress={() => props.log(`${props.title} pressed`)} - onLongPress={( - event: GestureResponderEvent & { eventKeyAction?: number }, - ) => - props.log( - `${props.title} long press ${ - event.eventKeyAction === 0 ? 'start' : 'end' - }`, - ) - } - > - {props.title} - - ); -}; - -const TouchableHighlightButton = (props: { - title: string; - log: (entry: string) => void; -}) => { - const styles = useDemoStyles(); - const underlayColor = useThemeColor({}, 'tint'); - - return ( - props.log(`${props.title} focus`)} - onBlur={() => props.log(`${props.title} blur`)} - onPress={() => props.log(`${props.title} pressed`)} - onLongPress={( - event: GestureResponderEvent & { eventKeyAction?: number }, - ) => - props.log( - `${props.title} long press ${ - event.eventKeyAction === 0 ? 'start' : 'end' - }`, - ) - } - > - {props.title} - - ); -}; - -const TouchableNativeFeedbackButton = (props: { - title: string; - log: (entry: string) => void; -}) => { - const styles = useDemoStyles(); - - return ( - props.log(`${props.title} focus`)} - onBlur={() => props.log(`${props.title} blur`)} - onPress={() => props.log(`${props.title} pressed`)} - onLongPress={( - event: GestureResponderEvent & { eventKeyAction?: number }, - ) => - props.log( - `${props.title} long press ${ - event.eventKeyAction === 0 ? 'start' : 'end' - }`, - ) - } - > - - {props.title} - - - ); -}; - -const useDemoStyles = function () { - const scale = useScale(); - const highlightColor = useThemeColor({}, 'link'); - const backgroundColor = useThemeColor({}, 'background'); - const tintColor = useThemeColor({}, 'tint'); - const textColor = useThemeColor({}, 'text'); - return StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - logContainer: { - flexDirection: 'row', - padding: 5 * scale, - margin: 5 * scale, - alignItems: 'flex-start', - justifyContent: 'flex-start', - }, - logText: { - height: 100 * scale, - width: 200 * scale, - fontSize: 10 * scale, - margin: 5 * scale, - alignSelf: 'flex-start', - justifyContent: 'flex-start', - }, - pressable: { - borderColor: highlightColor, - backgroundColor: textColor, - borderWidth: 1, - borderRadius: 5 * scale, - margin: 5 * scale, - }, - pressableFocused: { - borderColor: highlightColor, - backgroundColor: tintColor, - borderWidth: 1, - borderRadius: 5 * scale, - margin: 5 * scale, - }, - pressableText: { - color: backgroundColor, - fontSize: 15 * scale, - }, - }); -}; diff --git a/with-maestro/components/ExternalLink.tsx b/with-maestro/components/ExternalLink.tsx deleted file mode 100644 index 8f05675b..00000000 --- a/with-maestro/components/ExternalLink.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Link } from 'expo-router'; -import { openBrowserAsync } from 'expo-web-browser'; -import { type ComponentProps } from 'react'; -import { Platform } from 'react-native'; - -type Props = Omit, 'href'> & { href: string }; - -export function ExternalLink({ href, ...rest }: Props) { - return ( - { - if (Platform.OS !== 'web') { - // Prevent the default behavior of linking to the default browser on native. - event.preventDefault(); - // Open the link in an in-app browser. - await openBrowserAsync(href); - } - }} - /> - ); -} diff --git a/with-maestro/components/HelloWave.tsx b/with-maestro/components/HelloWave.tsx deleted file mode 100644 index f4b6ea54..00000000 --- a/with-maestro/components/HelloWave.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { StyleSheet } from 'react-native'; -import Animated, { - useSharedValue, - useAnimatedStyle, - withTiming, - withRepeat, - withSequence, -} from 'react-native-reanimated'; - -import { ThemedText } from '@/components/ThemedText'; - -export function HelloWave() { - const rotationAnimation = useSharedValue(0); - - rotationAnimation.value = withRepeat( - withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })), - 4 // Run the animation 4 times - ); - - const animatedStyle = useAnimatedStyle(() => ({ - transform: [{ rotate: `${rotationAnimation.value}deg` }], - })); - - return ( - - 👋 - - ); -} - -const styles = StyleSheet.create({ - text: { - fontSize: 28, - lineHeight: 32, - marginTop: -6, - }, -}); diff --git a/with-maestro/components/ParallaxScrollView.tsx b/with-maestro/components/ParallaxScrollView.tsx deleted file mode 100644 index 0a354199..00000000 --- a/with-maestro/components/ParallaxScrollView.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import type { PropsWithChildren, ReactElement } from 'react'; -import { StyleSheet, useColorScheme } from 'react-native'; -import Animated, { - interpolate, - useAnimatedRef, - useAnimatedStyle, - useScrollViewOffset, -} from 'react-native-reanimated'; - -import { ThemedView } from '@/components/ThemedView'; - -const HEADER_HEIGHT = 250; - -type Props = PropsWithChildren<{ - headerImage: ReactElement; - headerBackgroundColor: { dark: string; light: string }; -}>; - -export default function ParallaxScrollView({ - children, - headerImage, - headerBackgroundColor, -}: Props) { - const colorScheme = useColorScheme() ?? 'light'; - const scrollRef = useAnimatedRef(); - const scrollOffset = useScrollViewOffset(scrollRef); - - const headerAnimatedStyle = useAnimatedStyle(() => { - return { - transform: [ - { - translateY: interpolate( - scrollOffset.value, - [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] - ), - }, - { - scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]), - }, - ], - }; - }); - - return ( - - - - {headerImage} - - {children} - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - header: { - height: 250, - overflow: 'hidden', - }, - content: { - flex: 1, - padding: 32, - gap: 16, - overflow: 'hidden', - }, -}); diff --git a/with-maestro/components/ThemedText.tsx b/with-maestro/components/ThemedText.tsx deleted file mode 100644 index c0e1a78f..00000000 --- a/with-maestro/components/ThemedText.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Text, type TextProps, StyleSheet } from 'react-native'; - -import { useThemeColor } from '@/hooks/useThemeColor'; - -export type ThemedTextProps = TextProps & { - lightColor?: string; - darkColor?: string; - type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; -}; - -export function ThemedText({ - style, - lightColor, - darkColor, - type = 'default', - ...rest -}: ThemedTextProps) { - const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); - - return ( - - ); -} - -const styles = StyleSheet.create({ - default: { - fontSize: 16, - lineHeight: 24, - }, - defaultSemiBold: { - fontSize: 16, - lineHeight: 24, - fontWeight: '600', - }, - title: { - fontSize: 32, - fontWeight: 'bold', - lineHeight: 32, - }, - subtitle: { - fontSize: 20, - fontWeight: 'bold', - }, - link: { - lineHeight: 30, - fontSize: 16, - color: '#0a7ea4', - }, -}); diff --git a/with-maestro/components/ThemedView.tsx b/with-maestro/components/ThemedView.tsx deleted file mode 100644 index 4d2cb09d..00000000 --- a/with-maestro/components/ThemedView.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { View, type ViewProps } from 'react-native'; - -import { useThemeColor } from '@/hooks/useThemeColor'; - -export type ThemedViewProps = ViewProps & { - lightColor?: string; - darkColor?: string; -}; - -export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) { - const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background'); - - return ; -} diff --git a/with-maestro/components/__tests__/ThemedText-test.tsx b/with-maestro/components/__tests__/ThemedText-test.tsx deleted file mode 100644 index 1ac32250..00000000 --- a/with-maestro/components/__tests__/ThemedText-test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from 'react'; -import renderer from 'react-test-renderer'; - -import { ThemedText } from '../ThemedText'; - -it(`renders correctly`, () => { - const tree = renderer.create(Snapshot test!).toJSON(); - - expect(tree).toMatchSnapshot(); -}); diff --git a/with-maestro/components/__tests__/__snapshots__/ThemedText-test.tsx.snap b/with-maestro/components/__tests__/__snapshots__/ThemedText-test.tsx.snap deleted file mode 100644 index b68e53e9..00000000 --- a/with-maestro/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` - - Snapshot test! - -`; diff --git a/with-maestro/components/navigation/TabBarIcon.tsx b/with-maestro/components/navigation/TabBarIcon.tsx deleted file mode 100644 index b7302c3f..00000000 --- a/with-maestro/components/navigation/TabBarIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/ - -import Ionicons from '@expo/vector-icons/Ionicons'; -import { type IconProps } from '@expo/vector-icons/build/createIconSet'; -import { type ComponentProps } from 'react'; - -export function TabBarIcon({ style, ...rest }: IconProps['name']>) { - return ; -} diff --git a/with-maestro/constants/Colors.ts b/with-maestro/constants/Colors.ts deleted file mode 100644 index 14e67844..00000000 --- a/with-maestro/constants/Colors.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Below are the colors that are used in the app. The colors are defined in the light and dark mode. - * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. - */ - -const tintColorLight = '#0a7ea4'; -const tintColorDark = '#fff'; - -export const Colors = { - light: { - text: '#11181C', - background: '#fff', - tint: tintColorLight, - icon: '#687076', - tabIconDefault: '#687076', - tabIconSelected: tintColorLight, - }, - dark: { - text: '#ECEDEE', - background: '#151718', - tint: tintColorDark, - icon: '#9BA1A6', - tabIconDefault: '#9BA1A6', - tabIconSelected: tintColorDark, - }, -}; diff --git a/with-maestro/constants/TextStyles.ts b/with-maestro/constants/TextStyles.ts deleted file mode 100644 index 1295e9f0..00000000 --- a/with-maestro/constants/TextStyles.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Below are text styles used in the app, primarily in the ThemedText component. - */ - -import { TextStyle } from 'react-native'; - -export const textStyles = function ( - scale: number, - linkColor: string, -): { - [key: string]: TextStyle & { fontSize: number; lineHeight: number }; -} { - return { - default: { - fontSize: 16 * scale, - lineHeight: 24 * scale, - }, - defaultSemiBold: { - fontSize: 16 * scale, - lineHeight: 24 * scale, - fontWeight: '600', - }, - title: { - fontSize: 32 * scale, - fontWeight: 'bold', - lineHeight: 32 * scale, - }, - subtitle: { - fontSize: 20 * scale, - lineHeight: 20 * scale, - fontWeight: 'bold', - }, - link: { - lineHeight: 30 * scale, - fontSize: 16 * scale, - color: linkColor, - }, - }; -}; diff --git a/with-maestro/eas.json b/with-maestro/eas.json index ec65eafe..829af28d 100644 --- a/with-maestro/eas.json +++ b/with-maestro/eas.json @@ -1,6 +1,7 @@ { "cli": { - "version": ">= 3.15.1" + "version": ">= 12.1.0", + "appVersionSource": "remote" }, "build": { "build-and-maestro-test": { @@ -14,6 +15,19 @@ "simulator": true, "image": "latest" } + }, + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "preview": { + "distribution": "internal" + }, + "production": { + "autoIncrement": true } + }, + "submit": { + "production": {} } } diff --git a/with-maestro/hooks/useColorScheme.ts b/with-maestro/hooks/useColorScheme.ts deleted file mode 100644 index 17e3c63e..00000000 --- a/with-maestro/hooks/useColorScheme.ts +++ /dev/null @@ -1 +0,0 @@ -export { useColorScheme } from 'react-native'; diff --git a/with-maestro/hooks/useColorScheme.web.ts b/with-maestro/hooks/useColorScheme.web.ts deleted file mode 100644 index 6dcd80d3..00000000 --- a/with-maestro/hooks/useColorScheme.web.ts +++ /dev/null @@ -1,8 +0,0 @@ -// NOTE: The default React Native styling doesn't support server rendering. -// Server rendered styles should not change between the first render of the HTML -// and the first render on the client. Typically, web developers will use CSS media queries -// to render different styles on the client and server, these aren't directly supported in React Native -// but can be achieved using a styling library like Nativewind. -export function useColorScheme() { - return 'light'; -} diff --git a/with-maestro/hooks/useScale.ts b/with-maestro/hooks/useScale.ts deleted file mode 100644 index ea2e3e11..00000000 --- a/with-maestro/hooks/useScale.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Platform, useWindowDimensions } from 'react-native'; - -export function useScale(): number { - const { width } = useWindowDimensions(); - return Platform.isTV ? width / 1000 : 1; -} diff --git a/with-maestro/hooks/useScale.web.ts b/with-maestro/hooks/useScale.web.ts deleted file mode 100644 index ea142642..00000000 --- a/with-maestro/hooks/useScale.web.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function useScale(): number { - return 1.0; -} diff --git a/with-maestro/hooks/useTextStyles.ts b/with-maestro/hooks/useTextStyles.ts deleted file mode 100644 index cf752b47..00000000 --- a/with-maestro/hooks/useTextStyles.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { textStyles } from '@/constants/TextStyles'; -import { useThemeColor } from './useThemeColor'; -import { useScale } from './useScale'; - -export function useTextStyles() { - const linkColor = useThemeColor({}, 'link'); - const scale = useScale() ?? 1.0; - return textStyles(scale, linkColor); -} diff --git a/with-maestro/hooks/useThemeColor.ts b/with-maestro/hooks/useThemeColor.ts deleted file mode 100644 index ae43b47b..00000000 --- a/with-maestro/hooks/useThemeColor.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Learn more about light and dark modes: - * https://docs.expo.dev/guides/color-schemes/ - */ - -import { useColorScheme } from 'react-native'; - -import { Colors } from '@/constants/Colors'; - -export function useThemeColor( - props: { light?: string; dark?: string }, - colorName: keyof typeof Colors.light & keyof typeof Colors.dark -) { - const theme = useColorScheme() ?? 'light'; - const colorFromProps = props[theme]; - - if (colorFromProps) { - return colorFromProps; - } else { - return Colors[theme][colorName]; - } -} diff --git a/with-maestro/maestro/expand_test.yml b/with-maestro/maestro/dev_build/expand_test.yml similarity index 100% rename from with-maestro/maestro/expand_test.yml rename to with-maestro/maestro/dev_build/expand_test.yml diff --git a/with-maestro/maestro/home.yml b/with-maestro/maestro/dev_build/home.yml similarity index 100% rename from with-maestro/maestro/home.yml rename to with-maestro/maestro/dev_build/home.yml diff --git a/with-maestro/maestro/expo_go/expand_test.yml b/with-maestro/maestro/expo_go/expand_test.yml new file mode 100644 index 00000000..5ab3647a --- /dev/null +++ b/with-maestro/maestro/expo_go/expand_test.yml @@ -0,0 +1,6 @@ +appId: host.exp.Exponent # This is the app ID for testing with Expo Go. +--- +- launchApp +- tapOn: 'Explore.*' +- tapOn: '.*File-based routing' +- assertVisible: 'This app has two screens.*' diff --git a/with-maestro/maestro/expo_go/home.yml b/with-maestro/maestro/expo_go/home.yml new file mode 100644 index 00000000..39ad3372 --- /dev/null +++ b/with-maestro/maestro/expo_go/home.yml @@ -0,0 +1,4 @@ +appId: host.exp.Exponent # This is the app ID for testing with Expo Go. +--- +- launchApp +- assertVisible: 'Welcome!' diff --git a/with-maestro/package.json b/with-maestro/package.json index 1294919d..76770ad5 100644 --- a/with-maestro/package.json +++ b/with-maestro/package.json @@ -5,7 +5,6 @@ "scripts": { "prebuild": "expo prebuild --clean", "start": "expo start", - "reset-project": "node ./scripts/reset-project.js", "android": "expo run:android", "ios": "expo run:ios", "web": "expo start --web", @@ -16,25 +15,21 @@ "preset": "jest-expo" }, "dependencies": { - "@expo/vector-icons": "^14.0.2", "@react-navigation/native": "^6.0.2", "expo": "~51.0.28", "expo-constants": "~16.0.2", "expo-font": "~12.0.9", "expo-linking": "~6.3.1", "expo-router": "~3.5.23", - "expo-splash-screen": "~0.27.5", "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.7", - "expo-web-browser": "~13.0.3", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.5", "react-native-gesture-handler": "~2.16.1", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", - "react-native-screens": "3.31.1", - "react-native-web": "~0.19.10" + "react-native-screens": "3.31.1" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/with-maestro/scripts/reset-project.js b/with-maestro/scripts/reset-project.js deleted file mode 100755 index 4512e162..00000000 --- a/with-maestro/scripts/reset-project.js +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It moves the /app directory to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json and safely delete this file after running it. - */ - -const fs = require('fs'); -const path = require('path'); - -const root = process.cwd(); -const oldDirPath = path.join(root, 'app'); -const newDirPath = path.join(root, 'app-example'); -const newAppDirPath = path.join(root, 'app'); - -const indexContent = `import { Text, View } from "react-native"; - -export default function Index() { - return ( - - Edit app/index.tsx to edit this screen. - - ); -} -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return ( - - - - ); -} -`; - -fs.rename(oldDirPath, newDirPath, (error) => { - if (error) { - return console.error(`Error renaming directory: ${error}`); - } - console.log('/app moved to /app-example.'); - - fs.mkdir(newAppDirPath, { recursive: true }, (error) => { - if (error) { - return console.error(`Error creating new app directory: ${error}`); - } - console.log('New /app directory created.'); - - const indexPath = path.join(newAppDirPath, 'index.tsx'); - fs.writeFile(indexPath, indexContent, (error) => { - if (error) { - return console.error(`Error creating index.tsx: ${error}`); - } - console.log('app/index.tsx created.'); - - const layoutPath = path.join(newAppDirPath, '_layout.tsx'); - fs.writeFile(layoutPath, layoutContent, (error) => { - if (error) { - return console.error(`Error creating _layout.tsx: ${error}`); - } - console.log('app/_layout.tsx created.'); - }); - }); - }); -});