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

[No QA] Add Pusher Automated Tests #1153

Merged
merged 31 commits into from
Jan 18, 2021
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f0a9e60
change name
marcaaron Nov 6, 2020
1c37dd5
fix created being in the wrong order
marcaaron Nov 6, 2020
d4e9ef1
fix style
marcaaron Nov 10, 2020
29d02a1
fix style then get rid of async
marcaaron Nov 10, 2020
5970909
fix style
marcaaron Nov 10, 2020
c409ac3
Merge remote-tracking branch 'origin' into marcaaron-pusher-tests
marcaaron Nov 12, 2020
ccc3778
fix tests to use Onyx
marcaaron Nov 12, 2020
68f1d37
fix conflicts
marcaaron Dec 2, 2020
478f094
fix API.js
marcaaron Dec 2, 2020
918fa0a
tests broke somehow
marcaaron Dec 2, 2020
781f82b
fix conflicts
marcaaron Jan 1, 2021
da7746e
Fix this test up
marcaaron Jan 1, 2021
f65b202
remove some things
marcaaron Jan 1, 2021
fa53652
add comment
marcaaron Jan 1, 2021
6d59757
fix conflicts
marcaaron Jan 6, 2021
e8e0b3c
fix conflicts
marcaaron Jan 8, 2021
2f8dae6
fix ua mocks
marcaaron Jan 8, 2021
39684fe
remove uneeded
marcaaron Jan 8, 2021
9704aa9
Mock Pusher methods instead of adding the IS_JEST_RUNNING
marcaaron Jan 9, 2021
c6a235a
remove CONFIG dependency
marcaaron Jan 9, 2021
8e55d77
fix comment
marcaaron Jan 9, 2021
7298525
Merge remote-tracking branch 'origin' into marcaaron-pusher-tests
marcaaron Jan 12, 2021
cd18cdb
Fix up comment. Use Onyx.set instead of waitForPromisesToResolve()
marcaaron Jan 12, 2021
2ae40c9
Improve tests. Add TestHelper.
marcaaron Jan 12, 2021
fb69894
fix lint
marcaaron Jan 12, 2021
84bbfb6
Respond to Tims feedback.
marcaaron Jan 12, 2021
422ae85
update react-native-onyx
marcaaron Jan 13, 2021
739bbb7
dont test strict equal since the timestamp will make the test flaky
marcaaron Jan 13, 2021
fefa0ef
remove momnet
marcaaron Jan 13, 2021
b895dce
fix conflicts
marcaaron Jan 16, 2021
acb4b80
add comment
marcaaron Jan 16, 2021
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
5 changes: 5 additions & 0 deletions __mocks__/@react-native-community/push-notification-ios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
addEventListener: jest.fn(),
requestPermissions: jest.fn(() => Promise.resolve()),
getInitialNotification: jest.fn(() => Promise.resolve()),
};
3 changes: 3 additions & 0 deletions __mocks__/pusher-js/react-native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {PusherMock} from 'pusher-js-mock';

export default PusherMock;
9 changes: 9 additions & 0 deletions __mocks__/urbanairship-react-native.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
export default {
setUserNotificationsEnabled: jest.fn(),
};

const EventType = {
NotificationResponse: 'notificationResponse',
PushReceived: 'pushReceived',
};

export {
EventType,
};
6 changes: 6 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"jest-circus": "^26.5.2",
"jest-cli": "^26.5.2",
"metro-react-native-babel-preset": "^0.61.0",
"pusher-js-mock": "^0.3.3",
"react-hot-loader": "^4.12.21",
"react-native-svg-transformer": "^0.14.3",
"react-native-version": "^4.0.0",
Expand Down
73 changes: 73 additions & 0 deletions tests/actions/ReportTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import moment from 'moment';
import Onyx from 'react-native-onyx';
import ONYXKEYS from '../../src/ONYXKEYS';
import * as Pusher from '../../src/libs/Pusher/pusher';
import PusherConnectionManager from '../../src/libs/PusherConnectionManager';
import CONFIG from '../../src/CONFIG';
import {subscribeToReportCommentEvents} from '../../src/libs/actions/Report';
import waitForPromisesToResolve from '../utils/waitForPromisesToResolve';

describe('actions/Report', () => {
it('should subscribe to the private-user-accountID channel and handle reportComment events', () => {
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
const REPORT_ID = 1;
const ACTION_ID = 1;
const REPORT_ACTION = {
actionName: 'ADDCOMMENT',
automatic: false,
created: 'Nov 6 2020 9:14am PST',
message: [{type: 'COMMENT', html: 'Testing a comment', text: 'Testing a comment'}],
person: [{type: 'TEXT', style: 'strong', text: 'Test User'}],
shouldShow: true,
timestamp: moment().unix(),
actorAccountID: 1,
sequenceNumber: ACTION_ID,
isAttachment: false,
loading: false,
};

// When using the Pusher mock the act of calling Pusher.isSubscribed will create a
// channel already in a subscribed state. These methods are normally used to prevent
// duplicated subscriptions, but we don't need them for this test.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// duplicated subscriptions, but we don't need them for this test.
// duplicated subscriptions, but we don't need them for this test so forcing them to return false will make the testing less complex.

This adds a little more clarity.

Pusher.isSubscribed = jest.fn().mockReturnValue(false);
Pusher.isAlreadySubscribing = jest.fn().mockReturnValue(false);

// Connect to Pusher
PusherConnectionManager.init();
Pusher.init({
appKey: CONFIG.PUSHER.APP_KEY,
cluster: CONFIG.PUSHER.CLUSTER,
authEndpoint: `${CONFIG.EXPENSIFY.URL_API_ROOT}api?command=Push_Authenticate`,
});

let reportActions;
Onyx.connect({
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`,
callback: val => reportActions = val,
});

// Set up Onyx with some test user data
Onyx.set(ONYXKEYS.SESSION, {accountID: 1, email: 'test@test.com'});
return waitForPromisesToResolve()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still haven't quite gotten the hang of these, so forgive me if this isn't how it's meant to be done... is the test that has been written any different than doing this?

return Onyx.set(ONYXKEYS.SESSION, {accountID: 1, email: 'test@test.com'})

My thinking is just that it's odd to see waitForPromisesToResolve() being used when the promise returned from Onyx.set() should resolve and work as well (and it's more obvious what is happening because the test uses the same promise chain).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense! And I wouldn't mind updating it, but want to walk you through my thought process here to see if you have similar thoughts...

We could wait for Onyx.set() instead of doing waitForPromisesToResolve(), but I was thinking that for consistency we might want to default to waitForPromisesToResolve(). Even though unnecessary it might make it easier to understand how these tests are structured and deal with async code that doesn't necessarily have an "await-able" promise. Maybe this practice actually makes it harder to tell that's what waitForPromisesToResolve() is for?

Another thought I keep having is that if "everything async returned a Promise" (or even just the async "actions") then the test code might be more straight-forward and it would be more obvious to tell what exactly we are waiting for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change this for now since I think I did have it this way at one point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, you brought up some really good points. What I'd like to avoid is creating a situation where people get too promise-happy and just return promises from everything. That essentially promotes a ton of promise chains, and that was what plagued the application from an early age. I had to do a lot of work to remove all those promise chains and I'd hate to go back down that path. So, now that I think about it, I think your original solution here is actually superior.

.then(() => {
subscribeToReportCommentEvents();
return waitForPromisesToResolve();
})
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
.then(() => {
// Now that we are subscribed we need to simulate a reportComment action Pusher event.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Now that we are subscribed

Subscribed to what? Is this referring to the original Pusher subscription or to Onyx? This reads a little weird because I would expect the thing before this to be code that does subscribing, but the thing before this is adding the report action.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make this more explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably made more sense before when the thing immediately before this was subscribeToReportComments()

// Then verify that action was handled correctly and merged into the reportActions.
const channel = Pusher.getChannel('private-user-accountID-1');
channel.emit('reportComment', {
reportID: REPORT_ID,
reportAction: REPORT_ACTION,
});

// Once this happens we should see the comment get processed by the callback and added to the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Once this happens

"this" could be referring to a number of things, so be more explicit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the callback

This is also ambiguous. Is it the pusher callback, the onyx callback, or a different callback entirely?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make this more explicit.

// storage so we must wait for promises to resolve again and then verify the data is in Onyx.
return waitForPromisesToResolve();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see how maybe this is necessary to prevent any race conditions with the local data stored in reportActions not being up-to-date with Onyx yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right. The callback should happen in realtime and then we set the data async via Onyx.

})
.then(() => {
const resultAction = reportActions[ACTION_ID];
expect(resultAction).toEqual(REPORT_ACTION);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a different assertion might make it a little more clear what's being tested and prevents the need to modify the contents of REPORT_ACTION (which is not quite intuitive as to why it's necessary to modify REPORT_ACTION).

Suggestion:

expect(resultAction.loading).toEqual(false);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is much better

});
});
});