Skip to content

Commit

Permalink
Update Account tabs and add group list (#838)
Browse files Browse the repository at this point in the history
 - Uses the existing user profile information obtained
   in OAuth signin process and updates the profile page.
 - Downgrades the ESLint version to make it compatible
   with eslintrc configuration file.
  • Loading branch information
atomicgamedeveloper authored Jul 4, 2024
1 parent 1431828 commit 9bd4c1b
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 101 deletions.
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"dotenv": "^16.1.4",
"eslint": "^8.54.0",
"eslint": "8.54.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.29.0",
Expand Down
3 changes: 1 addition & 2 deletions client/src/route/auth/Account.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import * as React from 'react';
import Layout from 'page/Layout';
import { Typography } from '@mui/material';
import TabComponent from 'components/tab/TabComponent';
import { TabData } from 'components/tab/subcomponents/TabRender';
import tabs from './AccountTabData';

function AccountContent() {
const AccountTab: TabData[] = tabs.map((tab) => ({
label: tab.label,
body: <Typography variant="body1">{tab.body}</Typography>,
body: tab.body,
}));

return (
Expand Down
92 changes: 88 additions & 4 deletions client/src/route/auth/AccountTabData.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,97 @@
import { ITabs } from 'route/IData';
import * as React from 'react';
import { useAuth } from 'react-oidc-context';
import { TabData } from 'components/tab/subcomponents/TabRender';

const tabs: ITabs[] = [
function ListGroups(groups: string[]): React.ReactNode[] {
const boldGroups = groups.map((group) =>
React.createElement('b', null, group),
);

const userBelongsToOneGroup = groups.length === 1;
if (userBelongsToOneGroup) {
return boldGroups;
}

const groupListing: React.ReactNode[] = [];
boldGroups
.slice(0, -1)
.map((groupElement) => groupListing.push(groupElement, ', '));
groupListing.splice(groupListing.length - 1, 1, [
' and ',
boldGroups.slice(-1),
]);
return groupListing;
}

function GroupParagraph(groups: string[], name: React.ReactNode) {
const userBelongsToAnyGroups = groups.length > 0;
if (!userBelongsToAnyGroups) {
return (
<p>
<b>{name}</b> does not belong to any groups.
</p>
);
}

const groupListing = ListGroups(groups);
const groupSuffix = groups.length > 1 ? 's' : '';
return (
<p>
<b>{name}</b> belongs to {React.Children.toArray(groupListing)} group
{groupSuffix}.
</p>
);
}

function ProfileTab() {
const { user } = useAuth();
const name = (user?.profile.preferred_username as string | undefined) ?? '';
const pfp = user?.profile.picture;
const profileUrl = user?.profile.profile;

const groups = (user?.profile.groups as string[] | string | undefined) ?? [];
const isGroupsAString = typeof groups === 'string';
const groupsArray = isGroupsAString ? [groups] : groups;
const groupParagraph = GroupParagraph(groupsArray, name);

return (
<div>
<h2>Profile</h2>
<img src={pfp} data-testid="profile-picture" />
<p>
The username is <b>{name}</b>. See more details on{' '}
<b>
<a href={profileUrl}>SSO OAuth Provider.</a>
</b>
</p>
{groupParagraph}
</div>
);
}

function SettingsTab() {
const profileUrl = useAuth().user?.profile.profile;
return (
<div>
<h2>Settings</h2>
<p>
Edit the profile on{' '}
<b>
<a href={profileUrl}>SSO OAuth Provider.</a>
</b>
</p>
</div>
);
}

const tabs: TabData[] = [
{
label: 'Profile',
body: `Profile - potentially visible to other users.`,
body: <ProfileTab />,
},
{
label: 'Settings',
body: `Account settings - private to a user.`,
body: <SettingsTab />,
},
];

Expand Down
113 changes: 106 additions & 7 deletions client/test/unitTests/Routes/Account.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,111 @@
import * as React from 'react';
import Account from 'route/auth/Account';
import tabs from 'route/auth/AccountTabData';
import { InitRouteTests, itDisplaysContentOfTabs } from '../testUtils';
import { render, screen } from '@testing-library/react';
import { useAuth } from 'react-oidc-context';

describe('Account Page', () => {
const tabLabels: string[] = [];
tabs.forEach((tab) => tabLabels.push(tab.label));
InitRouteTests(<Account />);
jest.mock('react-oidc-context');

itDisplaysContentOfTabs(tabs);
describe('AccountTabs', () => {
type Profile = {
preferred_username: string | undefined;
picture: string | undefined;
profile: string | undefined;
groups: string[] | string | undefined;
};

const mockProfile: Profile = {
preferred_username: 'user1',
picture: 'pfp.jpg',
profile: 'test.com',
groups: 'group-one',
};

function setupTest(groups: string[] | string) {
mockProfile.groups = groups;
const mockuser = { profile: mockProfile };
(useAuth as jest.Mock).mockReturnValue({
user: mockuser,
});
render(<Account />);
}

afterEach(() => {
jest.clearAllMocks();
});

function testStaticElements() {
const profilePicture = screen.getByTestId('profile-picture');
expect(profilePicture).toBeInTheDocument();
expect(profilePicture).toHaveAttribute('src', 'pfp.jpg');

const username = screen.getAllByText('user1');
expect(username).not.toBeNull();
expect(username).toHaveLength(2);

const profileLink = screen.getByRole('link', {
name: /SSO OAuth Provider/i,
});
expect(profileLink).toBeInTheDocument();
expect(profileLink).toHaveAttribute('href', 'test.com');
}

test('renders AccountTabs with correct profile information when user is in 0 groups', () => {
setupTest([]);

testStaticElements();

const groupInfo = screen.getByText(/belong to/);
expect(groupInfo).toHaveProperty(
'innerHTML',
'<b>user1</b> does not belong to any groups.',
);
});

test('renders AccountTabs with correct profile information when user is in 1 group', () => {
setupTest('group-one');

testStaticElements();

const groupInfo = screen.getByText(/belongs to/);
expect(groupInfo).toHaveProperty(
'innerHTML',
'<b>user1</b> belongs to <b>group-one</b> group.',
);
});

test('renders AccountTabs with correct profile information when user is in 2 groups', () => {
setupTest(['first-group', 'second-group']);

testStaticElements();

const groupInfo = screen.getByText(/belongs to/);
expect(groupInfo).toHaveProperty(
'innerHTML',
'<b>user1</b> belongs to <b>first-group</b> and <b>second-group</b> groups.',
);
});

test('renders AccountTabs with correct profile information when user is in 3 groups', () => {
setupTest(['group1', 'group2', 'group3']);

testStaticElements();

const groupInfo = screen.getByText(/belongs to/);
expect(groupInfo).toHaveProperty(
'innerHTML',
'<b>user1</b> belongs to <b>group1</b>, <b>group2</b> and <b>group3</b> groups.',
);
});

test('renders AccountTabs with correct profile information when user is in more than 3 groups', () => {
setupTest(['g1', 'g2', 'g3', 'g4', 'g5']);

testStaticElements();

const groupInfo = screen.getByText(/belongs to/);
expect(groupInfo).toHaveProperty(
'innerHTML',
'<b>user1</b> belongs to <b>g1</b>, <b>g2</b>, <b>g3</b>, <b>g4</b> and <b>g5</b> groups.',
);
});
});
Loading

0 comments on commit 9bd4c1b

Please sign in to comment.