import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';
import Constants from 'expo-constants';
import React from 'react';
import { Alert, FlatList, Linking, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { useSafeArea } from 'react-native-safe-area-context';
import { getTestModules } from '../TestModules';
import PlatformTouchable from '../components/PlatformTouchable';
import Colors from '../constants/Colors';
function ListItem({ title, onPressItem, selected, id }) {
const onPress = () => onPressItem(id);
return (
{title}
);
}
function createQueryString(tests) {
if (!Array.isArray(tests) || !tests.every((v) => typeof v === 'string')) {
throw new Error(
`test-suite: Cannot create query string for runner. Expected array of strings, instead got: ${tests}`
);
}
const uniqueTests = [...new Set(tests)];
// Skip encoding or React Navigation will encode twice
return uniqueTests.join(' ');
}
export default class SelectScreen extends React.PureComponent {
state = {
selected: new Set(),
modules: [],
};
constructor(props) {
super(props);
if (global.ErrorUtils) {
const originalErrorHandler = global.ErrorUtils.getGlobalHandler();
global.ErrorUtils.setGlobalHandler((error, isFatal) => {
// Prevent optionalRequire from failing
if (
isFatal &&
(error.message.includes('Native module cannot be null') ||
error.message.includes(
`from NativeViewManagerAdapter isn't exported by @unimodules/react-native-adapter. Views of this type may not render correctly. Exported view managers: `
))
) {
console.log('Caught require error');
} else {
originalErrorHandler(error, isFatal);
}
});
}
}
componentWillUnmount() {
if (this._openUrlSubscription != null) {
this._openUrlSubscription.remove();
this._openUrlSubscription = null;
}
}
checkLinking = (incomingTests) => {
// TODO(Bacon): bare-expo should pass a space-separated string.
const tests = incomingTests.split(',').map((v) => v.trim());
const query = createQueryString(tests);
this.props.navigation.navigate('run', { tests: query });
};
_handleOpenURL = ({ url }) => {
url = url || '';
// TODO: Use Expo Linking library once parseURL is implemented for web
if (url.includes('/select/')) {
const selectedTests = url.split('/').pop();
if (selectedTests) {
this.checkLinking(selectedTests);
return;
}
}
if (url.includes('/all')) {
// Test all available modules
const query = createQueryString(getTestModules().map((m) => m.name));
this.props.navigation.navigate('run', {
tests: query,
});
return;
}
// Application wasn't started from a deep link which we handle. So, we can load test modules.
this._loadTestModules();
};
_loadTestModules = () => {
this.setState({
modules: getTestModules(),
});
};
componentDidMount() {
this._openUrlSubscription = Linking.addEventListener('url', this._handleOpenURL);
Linking.getInitialURL()
.then((url) => {
this._handleOpenURL({ url });
})
.catch((err) => console.error('Failed to load initial URL', err));
}
_keyExtractor = ({ name }) => name;
_onPressItem = (id) => {
this.setState((state) => {
const selected = new Set(state.selected);
if (selected.has(id)) selected.delete(id);
else selected.add(id);
return { selected };
});
};
_renderItem = ({ item: { name } }) => (
);
_selectAll = () => {
this.setState((prevState) => {
if (prevState.selected.size === prevState.modules.length) {
return { selected: new Set() };
}
return { selected: new Set(prevState.modules.map((item) => item.name)) };
});
};
_navigateToTests = () => {
const { selected } = this.state;
if (selected.length === 0) {
Alert.alert('Cannot Run Tests', 'You must select at least one test to run.');
} else {
const query = createQueryString([...selected]);
this.props.navigation.navigate('run', { tests: query });
}
};
render() {
const { selected } = this.state;
const allSelected = selected.size === this.state.modules.length;
const buttonTitle = allSelected ? 'Deselect All' : 'Select All';
return (
// eslint-disable-next-line react/jsx-fragments
<>
>
);
}
}
function Footer({ buttonTitle, canRunTests, onToggle, onRun }) {
const { bottom, left, right } = useSafeArea();
const isRunningInDetox = Constants.manifest && Constants.manifest.slug === 'bare-expo';
const paddingVertical = 16;
return (
);
}
function FooterButton({ title, style, ...props }) {
return (
{title}
);
}
const HORIZONTAL_MARGIN = 24;
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
},
footerButtonTitle: {
fontSize: 18,
color: Colors.tintColor,
},
footerButton: {
flex: 1,
justifyContent: 'center',
marginHorizontal: HORIZONTAL_MARGIN,
},
listItem: {
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
paddingVertical: 14,
paddingHorizontal: HORIZONTAL_MARGIN,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#dddddd',
},
label: {
color: 'black',
fontSize: 18,
},
buttonRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: '#dddddd',
backgroundColor: 'white',
},
contentContainerStyle: {
paddingBottom: 128,
},
});