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

Authenticate user for trip #76

Merged
merged 9 commits into from
Jul 28, 2020
89 changes: 81 additions & 8 deletions frontend/src/components/ViewActivities/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,92 @@
import React from 'react';
import ActivityList from './activitylist.js';

import app from '../Firebase';

import { getUserUid } from '../AuthUtils';
import ActivityList from './activitylist.js';
import * as DB from '../../constants/database.js';

/**
* The whole view activities page.
*
* The view activities page. First checks that the current user is authorized to
* view the current trip (i.e. they are a collaborator for it). If so, the
* ActivityList component is displayed as normal. If not, an error is displayed
* instead.
*
* @param {Object} props This component expects the following props:
* - `tripId` {string} The trip's ID. This is sent to the component through the URL.
* - `tripId` {string} The trip's ID. This is sent to the component through the URL.
*/
class ViewActivities extends React.Component {
/** @inheritdoc */
constructor(props) {
super(props);
this.tripId = props.match.params.tripId;
this.state = {
collaborators: undefined,
isLoading: true,
error: undefined
}
}

/** @inheritdoc */
componentDidMount() {
app.firestore()
.collection(DB.COLLECTION_TRIPS)
.doc(this.tripId)
.get()
.then(doc => {
this.setState({
collaborators: doc.get(DB.TRIPS_COLLABORATORS),
isLoading: false,
error: undefined
});
})
.catch(e => {
this.setState({
collaborators: undefined,
isLoading: true,
error: e
})
});
}

/** @inheritdoc */
render() {
return (
<div className='activity-page'>
<ActivityList tripId={this.props.match.params.tripId}/>
</div>
)
// Case where there was a Firebase error.
if (this.state.error !== undefined) {
// TODO (Issue #74): Redirect to an error page instead.
return (
<div>
Oops, looks like something went wrong. Please wait a few minutes and
try again.
</div>
);
}
// Case where the trip details are still being fetched.
if (this.state.isLoading) {
// TODO (Issue #25): Please remember to make this a blank div in the
// deployed build lol.
return <div>Loading Part 2: Electric Boogaloo</div>;
}
// Case where the trip could not be found. A field is returned undefined if
// the trip does not exist, so we check that the retrieved collaborators is
// undefined.
else if (this.state.collaborators === undefined) {
// TODO (Issue #74): Redirect to an error page instead.
return <div>Sorry, we couldn't find the trip you were looking for.</div>;
}
// Case where the current user is not authorized to view the page
else if (!this.state.collaborators.includes(getUserUid())) {
// TODO (Issue #74): Redirect to an error page instead.
return <div>Sorry, you're not authorized to view this trip.</div>;
}
else {
return (
<div className='activity-page'>
<ActivityList tripId={this.tripId}/>
</div>
);
}
}
}

Expand Down
73 changes: 73 additions & 0 deletions frontend/src/components/ViewActivities/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { render, screen, cleanup } from '@testing-library/react';
import ViewActivities from './index.js';
import authUtils from '../AuthUtils';

const FAKE_USER = 'totally-legit-user';
const FAKE_TRIPID = '12345';
const RESULT_AUTHORIZED = FAKE_TRIPID;
const RESULT_NOT_AUTHORIZED =
'Sorry, you\'re not authorized to view this trip.';
const RESULT_TRIP_DOESNT_EXIST =
'Sorry, we couldn\'t find the trip you were looking for.';

// Mock the getUserUid auth utility function to return a fake UID as given by
// FAKE_USER.
jest.mock('../AuthUtils');
authUtils.getUserUid.mockReturnValue(FAKE_USER);

// Mock the ActivityList component to simply render the passed-in tripId.
jest.mock('./activitylist.js', () => (props) => (
<div>{props.tripId}</div>
));

// Mock the different collaborator fields that can be returned from Firebase
// Firestore. The first time, it returns an array containing the fake user. The
// second time, it returns an array that does not contain the fake user. The
// third time, it returns undefined (imitating Firebase being unable to find the
// trip).
const mockGet = jest.fn()
.mockResolvedValueOnce({ get: function() {return [FAKE_USER]} })
.mockResolvedValueOnce({ get: function() {return []} })
.mockResolvedValueOnce({ get: function() {return undefined} });
jest.mock('firebase/app', () => {
return {
initializeApp: () => {
return {
firestore: () => {
return {
collection: (collectionPath) => {
return {
doc: (documentPath) => {
return {
get: mockGet
};
}
};
}
};
}
};
}
};
});

describe('ViewActivities page', () => {
beforeEach(() => {
render(<ViewActivities match={{params: {tripId: FAKE_TRIPID}}}/>);
});

afterEach(cleanup);

it('Displays ActivityList when the user is a collaborator', () => {
expect(screen.getByText(RESULT_AUTHORIZED)).toBeInTheDocument();
});

it('Displays the relevant error when the user is not a collaborator', () => {
expect(screen.getByText(RESULT_NOT_AUTHORIZED)).toBeInTheDocument();
});

it('Displays the relevant error when the trip could not be found', () => {
expect(screen.getByText(RESULT_TRIP_DOESNT_EXIST)).toBeInTheDocument();
})
});
2 changes: 1 addition & 1 deletion frontend/src/constants/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ export const ACTIVITIES_END_TIME = 'end_time';
export const ACTIVITIES_TITLE = 'title';
export const ACTIVITIES_DESCRIPTION = 'description';
export const ACTIVITIES_START_COUNTRY = 'start_country';
export const ACTIVITIES_END_COUNTRY = 'end_country';
export const ACTIVITIES_END_COUNTRY = 'end_country';