Skip to content

Commit

Permalink
Merge branch 'Expensify:main' into rm-setNativeProps-optionsSelector
Browse files Browse the repository at this point in the history
  • Loading branch information
rushatgabhane authored Sep 18, 2022
2 parents bfdbbc5 + 56dcf61 commit 813b3d4
Show file tree
Hide file tree
Showing 61 changed files with 1,227 additions and 311 deletions.
6 changes: 6 additions & 0 deletions __mocks__/@react-native-firebase/crashlytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// <App> uses <ErrorBoundary> and we need to mock the imported crashlytics module
// due to an error that happens otherwise https://github.com/invertase/react-native-firebase/issues/2475
export default {
log: jest.fn(),
recordError: jest.fn(),
};
8 changes: 7 additions & 1 deletion __mocks__/pusher-js/react-native.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {PusherMock} from 'pusher-js-mock';

export default PusherMock;
class PusherMockWithDisconnect extends PusherMock {
disconnect() {
return jest.fn();
}
}

export default PusherMockWithDisconnect;
44 changes: 44 additions & 0 deletions __mocks__/react-native-safe-area-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, {forwardRef} from 'react';
import {View} from 'react-native';

const insets = {
top: 0, right: 0, bottom: 0, left: 0,
};

function withSafeAreaInsets(WrappedComponent) {
const WithSafeAreaInsets = props => (
<WrappedComponent
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
// eslint-disable-next-line react/prop-types
ref={props.forwardedRef}
insets={insets}
/>
);
return forwardRef((props, ref) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<WithSafeAreaInsets {...props} forwardedRef={ref} />
));
}

const SafeAreaView = View;
const SafeAreaProvider = props => props.children;
const SafeAreaConsumer = props => props.children(insets);
const SafeAreaInsetsContext = {
Consumer: SafeAreaConsumer,
};

const useSafeAreaFrame = jest.fn(() => ({
x: 0, y: 0, width: 390, height: 844,
}));
const useSafeAreaInsets = jest.fn(() => insets);

export {
SafeAreaProvider,
SafeAreaConsumer,
SafeAreaInsetsContext,
withSafeAreaInsets,
SafeAreaView,
useSafeAreaFrame,
useSafeAreaInsets,
};
72 changes: 72 additions & 0 deletions __mocks__/react-native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// eslint-disable-next-line no-restricted-imports
import * as ReactNative from 'react-native';
import _ from 'underscore';
import CONST from '../src/CONST';

jest.doMock('react-native', () => {
let url = 'https://new.expensify.com/';
const getInitialURL = () => Promise.resolve(url);

let appState = 'active';
let count = 0;
const changeListeners = {};

// Tests will run with the app in a typical small screen size by default. We do this since the react-native test renderer
// runs against index.native.js source and so anything that is testing a component reliant on withWindowDimensions()
// would be most commonly assumed to be on a mobile phone vs. a tablet or desktop style view. This behavior can be
// overridden by explicitly setting the dimensions inside a test via Dimensions.set()
let dimensions = CONST.TESTING.SCREEN_SIZE.SMALL;

return Object.setPrototypeOf(
{
NativeModules: {
...ReactNative.NativeModules,
BootSplash: {
getVisibilityStatus: jest.fn(),
hide: jest.fn(),
},
StartupTimer: {stop: jest.fn()},
},
Linking: {
...ReactNative.Linking,
getInitialURL,
setInitialURL(newUrl) {
url = newUrl;
},
},
AppState: {
...ReactNative.AppState,
get currentState() {
return appState;
},
emitCurrentTestState(state) {
appState = state;
_.each(changeListeners, listener => listener(appState));
},
addEventListener(type, listener) {
if (type === 'change') {
const originalCount = count;
changeListeners[originalCount] = listener;
++count;
return {
remove: () => {
delete changeListeners[originalCount];
},
};
}

return ReactNative.AppState.addEventListener(type, listener);
},
},
Dimensions: {
...ReactNative.Dimensions,
addEventListener: jest.fn(),
get: () => dimensions,
set: (newDimensions) => {
dimensions = newDimensions;
},
},
},
ReactNative,
);
});
12 changes: 11 additions & 1 deletion __mocks__/urbanairship-react-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ const EventType = {
PushReceived: 'pushReceived',
};

export default {
const UrbanAirship = {
setUserNotificationsEnabled: jest.fn(),
clearNotifications: jest.fn(),
addListener: jest.fn(),
getNamedUser: jest.fn(),
enableUserPushNotifications: () => Promise.resolve(false),
setNamedUser: jest.fn(),
removeAllListeners: jest.fn(),
setBadgeNumber: jest.fn(),
};

export default UrbanAirship;

export {
EventType,
UrbanAirship,
};
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001020006
versionName "1.2.0-6"
versionCode 1001020100
versionName "1.2.1-0"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()

if (isNewArchitectureEnabled()) {
Expand Down
4 changes: 2 additions & 2 deletions ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.0</string>
<string>1.2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand All @@ -30,7 +30,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.2.0.6</string>
<string>1.2.1.0</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
4 changes: 2 additions & 2 deletions ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.0</string>
<string>1.2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.2.0.6</string>
<string>1.2.1.0</string>
</dict>
</plist>
63 changes: 56 additions & 7 deletions jest/setup.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,64 @@
import fs from 'fs';
import path from 'path';
import 'react-native-gesture-handler/jestSetup';
import _ from 'underscore';

require('react-native-reanimated/lib/reanimated2/jestUtils').setUpTests();

// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
jest.mock('react-native-blob-util', () => ({}));

// These two mocks are required as per setup instructions for react-navigation testing
// https://reactnavigation.org/docs/testing/#mocking-native-modules
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
jest.mock('react-native-reanimated', () => {
const Reanimated = require('react-native-reanimated/mock');
Reanimated.default.call = () => {};
return Reanimated;
});

// Set up manual mocks for methods used in the actions so our test does not fail.
jest.mock('../src/libs/Notification/PushNotification', () => ({
// There is no need for a jest.fn() since we don't need to make assertions against it.
register: () => {},
deregister: () => {},
// The main app uses a NativeModule called BootSplash to show/hide a splash screen. Since we can't use this in the node environment
// where tests run we simulate a behavior where the splash screen is always hidden (similar to web which has no splash screen at all).
jest.mock('../src/libs/BootSplash', () => ({
hide: jest.fn(),
getVisibilityStatus: jest.fn().mockResolvedValue('hidden'),
}));

jest.mock('react-native-blob-util', () => ({}));
// Local notifications (a.k.a. browser notifications) do not run in native code. Our jest tests will also run against
// any index.native.js files as they are using a react-native plugin. However, it is useful to mock this behavior so that we
// can test the expected web behavior and see if a browser notification would be shown or not.
jest.mock('../src/libs/Notification/LocalNotification', () => ({
showCommentNotification: jest.fn(),
}));

/**
* @param {String} imagePath
*/
function mockImages(imagePath) {
const imageFilenames = fs.readdirSync(path.resolve(__dirname, `../assets/${imagePath}/`));
// eslint-disable-next-line rulesdir/prefer-early-return
_.each(imageFilenames, (fileName) => {
if (/\.svg/.test(fileName)) {
jest.mock(`../assets/${imagePath}/${fileName}`, () => () => '');
}
});
}

// We are mocking all images so that Icons and other assets cannot break tests. In the testing environment, importing things like .svg
// directly will lead to undefined variables instead of a component or string (which is what React expects). Loading these assets is
// not required as the test environment does not actually render any UI anywhere and just needs them to noop so the test renderer
// (which is a virtual implemented DOM) can do it's thing.
mockImages('images');
mockImages('images/avatars');
mockImages('images/bankicons');
mockImages('images/product-illustrations');
jest.mock('../src/components/Icon/Expensicons', () => {
const reduce = require('underscore').reduce;
const Expensicons = jest.requireActual('../src/components/Icon/Expensicons');
return reduce(Expensicons, (prev, _curr, key) => {
// We set the name of the anonymous mock function here so we can dynamically build the list of mocks and access the
// "name" property to use in accessibility hints for element querying
const fn = () => '';
Object.defineProperty(fn, 'name', {value: key});
return {...prev, [key]: fn};
}, {});
});
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.2.0-6",
"version": "1.2.1-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down Expand Up @@ -203,6 +203,7 @@
"<rootDir>/node_modules/"
],
"testMatch": [
"**/tests/ui/**/*.[jt]s?(x)",
"**/tests/unit/**/*.[jt]s?(x)",
"**/tests/actions/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[jt]s?(x)"
Expand Down
9 changes: 8 additions & 1 deletion src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ const CONST = {
FREQUENTLY_USED_EMOJIS: 'expensify_frequentlyUsedEmojis',
},
DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'},
DEFAULT_ACCOUNT_DATA: {error: '', success: '', loading: false},
DEFAULT_ACCOUNT_DATA: {errors: null, success: '', loading: false},
APP_STATE: {
ACTIVE: 'active',
BACKGROUND: 'background',
Expand Down Expand Up @@ -827,6 +827,13 @@ const CONST = {
INCORRECT_PASSWORD: 2,
},
},
TESTING: {
SCREEN_SIZE: {
SMALL: {
width: 300, height: 700, scale: 1, fontScale: 1,
},
},
},
};

export default CONST;
3 changes: 2 additions & 1 deletion src/components/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as Expensicons from './Icon/Expensicons';

const propTypes = {
/** Whether checkbox is checked */
isChecked: PropTypes.bool.isRequired,
isChecked: PropTypes.bool,

/** A function that is called when the box/label is pressed */
onPress: PropTypes.func.isRequired,
Expand All @@ -33,6 +33,7 @@ const propTypes = {
};

const defaultProps = {
isChecked: false,
hasError: false,
disabled: false,
style: [],
Expand Down
2 changes: 1 addition & 1 deletion src/components/DisplayNames/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Text from '../Text';

// As we don't have to show tooltips of the Native platform so we simply render the full display names list.
const DisplayNames = props => (
<Text style={props.textStyles} numberOfLines={props.numberOfLines}>
<Text accessibilityLabel={props.accessibilityLabel} style={props.textStyles} numberOfLines={props.numberOfLines}>
{props.fullTitle}
</Text>
);
Expand Down
9 changes: 7 additions & 2 deletions src/components/OptionRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ const OptionRow = (props) => {
<TouchableOpacity
ref={el => touchableRef = el}
onPress={(e) => {
e.preventDefault();
if (e) {
e.preventDefault();
}

props.onSelectRow(props.option, touchableRef);
}}
disabled={props.isDisabled}
Expand All @@ -145,7 +148,7 @@ const OptionRow = (props) => {
props.isDisabled && styles.cursorDisabled,
]}
>
<View style={sidebarInnerRowStyle}>
<View accessibilityHint={props.accessibilityHint} style={sidebarInnerRowStyle}>
<View
style={[
styles.flexRow,
Expand Down Expand Up @@ -183,6 +186,7 @@ const OptionRow = (props) => {
}
<View style={contentContainerStyles}>
<DisplayNames
accessibilityLabel="Chat user display names"
fullTitle={props.option.text}
displayNamesWithTooltips={displayNamesWithTooltips}
tooltipEnabled={props.showTitleTooltip}
Expand All @@ -192,6 +196,7 @@ const OptionRow = (props) => {
/>
{props.option.alternateText ? (
<Text
accessibilityLabel={props.alternateTextAccessibilityLabel}
style={alternateTextStyle}
numberOfLines={1}
>
Expand Down
2 changes: 2 additions & 0 deletions src/components/OptionsList/BaseOptionsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ class BaseOptionsList extends Component {
renderItem({item, index, section}) {
return (
<OptionRow
alternateTextAccessibilityLabel={this.props.optionRowAlternateTextAccessibilityLabel}
accessibilityHint={this.props.optionRowAccessibilityHint}
option={item}
mode={this.props.optionMode}
showTitleTooltip={this.props.showTitleTooltip}
Expand Down
Loading

0 comments on commit 813b3d4

Please sign in to comment.