Skip to content

Commit

Permalink
Merge branch 'Expensify:main' into arrow-feature-signed
Browse files Browse the repository at this point in the history
  • Loading branch information
JediWattson authored Sep 19, 2022
2 parents 468cba3 + 0302ce7 commit f1e3411
Show file tree
Hide file tree
Showing 97 changed files with 2,691 additions and 883 deletions.
1 change: 1 addition & 0 deletions .env.temp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PUSHER_DEV_SUFFIX=-14fec3ac47964662914a7b6a69ba5e41
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const completedContributorPlusChecklist = `- [x] I have verified the author chec
- [x] Android / Chrome
- [x] MacOS / Chrome
- [x] MacOS / Desktop
- [x] I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
- [x] If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
- [x] I verified proper code patterns were followed (see [Reviewing the code](https://github.com/Expensify/App/blob/main/contributingGuides/PR_REVIEW_GUIDELINES.md#reviewing-the-code))
- [x] I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. \`toggleReport\` and not \`onIconClick\`).
- [x] I verified that comments were added to code that is not self explanatory
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/javascript/contributorChecklist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const completedContributorPlusChecklist = `- [x] I have verified the author chec
- [x] Android / Chrome
- [x] MacOS / Chrome
- [x] MacOS / Desktop
- [x] I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
- [x] If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
- [x] I verified proper code patterns were followed (see [Reviewing the code](https://github.com/Expensify/App/blob/main/contributingGuides/PR_REVIEW_GUIDELINES.md#reviewing-the-code))
- [x] I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. \`toggleReport\` and not \`onIconClick\`).
- [x] I verified that comments were added to code that is not self explanatory
Expand Down
3 changes: 3 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ module.exports = {
'./public',
'../assets/css',
],
core: {
builder: 'webpack5',
},
};
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ This application is built with the following principles.

1. **Cross Platform 99.9999%**
1. A feature isn't done until it works on all platforms. Accordingly, don't even bother writing a platform-specific code block because you're just going to need to undo it.
1. If the reason you can't write cross platform code is because there is a bug in ReactNative that is preventing it from working, the correct action is to fix RN and submit a PR upstream -- not to hack around RN bugs with platform-specific code paths.
1. If the reason you can't write cross-platform code is because there is a bug in ReactNative that is preventing it from working, the correct action is to fix RN and submit a PR upstream -- not to hack around RN bugs with platform-specific code paths.
1. If there is a feature that simply doesn't exist on all platforms and thus doesn't exist in RN, rather than doing if (platform=iOS) { }, instead write a "shim" library that is implemented with NOOPs on the other platforms. For example, rather than injecting platform-specific multi-tab code (which can only work on browsers, because it's the only platform with multiple tabs), write a TabManager class that just is NOOP for non-browser platforms. This encapsulates the platform-specific code into a platform library, rather than sprinkling through the business logic.
1. Put all platform specific code in dedicated files and folders, like /platform, and reject any PR that attempts to put platform-specific code anywhere else. This maintains a strict separation between business logic and platform code.

Expand Down
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 1001020000
versionName "1.2.0-0"
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.0</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.0</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};
}, {});
});
Loading

0 comments on commit f1e3411

Please sign in to comment.