Add card filter icons and tests

Signed-off-by: tylerhekman-procore <tyler.hekman@procore.com>
This commit is contained in:
tylerhekman-procore
2023-04-06 02:53:34 -07:00
parent 2ef8aee1d0
commit ec175f513c
6 changed files with 555 additions and 9 deletions
+1 -1
View File
@@ -4,5 +4,5 @@
Add a new "Archived" Filter Options to the Github Pull Requests Dashboard.
When toggling this option on, the dashboard will displays PRs in archived repositories.
When toggling this option on, the dashboard will display PRs from archived repositories.
These PRs will not be displayed in the default filter.
@@ -25,6 +25,8 @@ type Props = {
authorName: string;
authorAvatar?: string;
repositoryName: string;
isDraft: boolean;
repositoryIsArchived: boolean;
};
const Card: FunctionComponent<Props> = (props: PropsWithChildren<Props>) => {
@@ -36,6 +38,8 @@ const Card: FunctionComponent<Props> = (props: PropsWithChildren<Props>) => {
authorName,
authorAvatar,
repositoryName,
isDraft,
repositoryIsArchived,
children,
} = props;
@@ -51,6 +55,8 @@ const Card: FunctionComponent<Props> = (props: PropsWithChildren<Props>) => {
authorName={authorName}
authorAvatar={authorAvatar}
repositoryName={repositoryName}
isDraft={isDraft}
repositoryIsArchived={repositoryIsArchived}
/>
{children}
</Box>
@@ -14,9 +14,11 @@
* limitations under the License.
*/
import React, { FunctionComponent } from 'react';
import { Typography, Box } from '@material-ui/core';
import { Typography, Box, Tooltip } from '@material-ui/core';
import { getElapsedTime } from '../../utils/functions';
import { UserHeader } from '../UserHeader';
import { DraftPrIcon } from '../icons/DraftPr';
import UnarchiveIcon from '@material-ui/icons/Unarchive';
type Props = {
title: string;
@@ -25,6 +27,8 @@ type Props = {
authorName: string;
authorAvatar?: string;
repositoryName: string;
isDraft: boolean;
repositoryIsArchived: boolean;
};
const CardHeader: FunctionComponent<Props> = (props: Props) => {
@@ -35,6 +39,8 @@ const CardHeader: FunctionComponent<Props> = (props: Props) => {
authorName,
authorAvatar,
repositoryName,
isDraft,
repositoryIsArchived,
} = props;
return (
@@ -45,6 +51,22 @@ const CardHeader: FunctionComponent<Props> = (props: Props) => {
</Typography>
<UserHeader name={authorName} avatar={authorAvatar} />
</Box>
<Box display="flex" justifyContent="left">
{isDraft && (
<Tooltip title="Draft PR">
<Box display="flex" justifyContent="center" alignItems="center">
<DraftPrIcon />
</Box>
</Tooltip>
)}
{repositoryIsArchived && (
<Tooltip title="Repository is archived">
<Box display="flex" justifyContent="center" alignItems="center">
<UnarchiveIcon />
</Box>
</Tooltip>
)}
</Box>
<Typography component="h3">
<b>{title}</b>
</Typography>
@@ -0,0 +1,261 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { EntityTeamPullRequestsCard } from '../EntityTeamPullRequestsCard';
import { PullRequestsColumn } from '../../utils/types';
import { render } from '@testing-library/react';
import { fireEvent } from '@testing-library/react';
jest.mock('../../hooks/useUserRepositoriesAndTeam', () => {
return {
useUserRepositoriesAndTeam: () => {
return {
loading: false,
repositories: ['team-login/team-repo'],
teamMembers: ['team-member'],
teamMembersOrganization: 'test-org',
};
},
};
});
jest.mock('../../hooks/usePullRequestsByTeam', () => {
const buildPullRequest = (
prTitle: string,
authorLogin: string,
repoName: string,
isDraft: boolean,
isArchived: boolean,
) => {
return {
id: 'id',
title: prTitle,
url: 'url',
lastEditedAt: 'last-edited-at',
latestReviews: {
nodes: [],
},
mergeable: true,
state: 'state',
reviewDecision: null,
createdAt: 'created-at',
repository: {
name: repoName,
owner: {
login: 'team-login',
},
isArchived: isArchived,
},
isDraft: isDraft,
author: {
login: authorLogin,
avatarUrl: 'avatar-url',
id: 'id',
email: 'email',
name: 'name',
},
};
};
const pullRequests: PullRequestsColumn[] = [
{
title: 'column',
content: [
buildPullRequest(
'non-team-non-draft-non-archive',
'non-team-member',
'team-repo',
false,
false,
),
buildPullRequest(
'non-team-non-draft-is-archive',
'non-team-member',
'team-repo',
false,
true,
),
buildPullRequest(
'non-team-is-draft-non-archive',
'non-team-member',
'team-repo',
true,
false,
),
buildPullRequest(
'non-team-is-draft-is-archive',
'non-team-member',
'team-repo',
true,
true,
),
buildPullRequest(
'is-team-non-draft-non-archive',
'team-member',
'non-team-repo',
false,
false,
),
buildPullRequest(
'is-team-non-draft-is-archive',
'team-member',
'non-team-repo',
false,
true,
),
buildPullRequest(
'is-team-is-draft-non-archive',
'team-member',
'non-team-repo',
true,
false,
),
buildPullRequest(
'is-team-is-draft-is-archive',
'team-member',
'non-team-repo',
true,
true,
),
],
},
];
return {
usePullRequestsByTeam: () => {
return {
loading: false,
pullRequests: pullRequests,
refreshPullRequest: () => {},
};
},
};
});
describe('EntityTeamPullRequestsCard', () => {
it('should render', async () => {
await render(<EntityTeamPullRequestsCard />);
});
describe('non-team PRs', () => {
describe('non-draft PRs', () => {
it('should show non-team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, queryAllByTitle } = await render(
<EntityTeamPullRequestsCard />,
);
expect(getByText('non-team-non-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
it('should show non-team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsCard />);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('non-team-non-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
});
describe('draft PRs', () => {
it('should show draft non-team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsCard />);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
expect(getByText('non-team-is-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
it('should show draft non-team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsCard />);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('non-team-is-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
});
});
describe('team PRs', () => {
describe('non-draft PRs', () => {
it('should show team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsCard />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
expect(getByText('is-team-non-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
it('should show team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsCard />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('is-team-non-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
});
describe('draft PRs', () => {
it('should show draft team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsCard />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
expect(getByText('is-team-is-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
it('should show draft team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsCard />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('is-team-is-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
});
});
});
@@ -0,0 +1,261 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { EntityTeamPullRequestsContent } from '../EntityTeamPullRequestsContent';
import { PullRequestsColumn } from '../../utils/types';
import { render } from '@testing-library/react';
import { fireEvent } from '@testing-library/react';
jest.mock('../../hooks/useUserRepositoriesAndTeam', () => {
return {
useUserRepositoriesAndTeam: () => {
return {
loading: false,
repositories: ['team-login/team-repo'],
teamMembers: ['team-member'],
teamMembersOrganization: 'test-org',
};
},
};
});
jest.mock('../../hooks/usePullRequestsByTeam', () => {
const buildPullRequest = (
prTitle: string,
authorLogin: string,
repoName: string,
isDraft: boolean,
isArchived: boolean,
) => {
return {
id: 'id',
title: prTitle,
url: 'url',
lastEditedAt: 'last-edited-at',
latestReviews: {
nodes: [],
},
mergeable: true,
state: 'state',
reviewDecision: null,
createdAt: 'created-at',
repository: {
name: repoName,
owner: {
login: 'team-login',
},
isArchived: isArchived,
},
isDraft: isDraft,
author: {
login: authorLogin,
avatarUrl: 'avatar-url',
id: 'id',
email: 'email',
name: 'name',
},
};
};
const pullRequests: PullRequestsColumn[] = [
{
title: 'column',
content: [
buildPullRequest(
'non-team-non-draft-non-archive',
'non-team-member',
'team-repo',
false,
false,
),
buildPullRequest(
'non-team-non-draft-is-archive',
'non-team-member',
'team-repo',
false,
true,
),
buildPullRequest(
'non-team-is-draft-non-archive',
'non-team-member',
'team-repo',
true,
false,
),
buildPullRequest(
'non-team-is-draft-is-archive',
'non-team-member',
'team-repo',
true,
true,
),
buildPullRequest(
'is-team-non-draft-non-archive',
'team-member',
'non-team-repo',
false,
false,
),
buildPullRequest(
'is-team-non-draft-is-archive',
'team-member',
'non-team-repo',
false,
true,
),
buildPullRequest(
'is-team-is-draft-non-archive',
'team-member',
'non-team-repo',
true,
false,
),
buildPullRequest(
'is-team-is-draft-is-archive',
'team-member',
'non-team-repo',
true,
true,
),
],
},
];
return {
usePullRequestsByTeam: () => {
return {
loading: false,
pullRequests: pullRequests,
refreshPullRequest: () => {},
};
},
};
});
describe('EntityTeamPullRequestsContent', () => {
it('should render', async () => {
await render(<EntityTeamPullRequestsContent />);
});
describe('non-team PRs', () => {
describe('non-draft PRs', () => {
it('should show non-team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, queryAllByTitle } = await render(
<EntityTeamPullRequestsContent />,
);
expect(getByText('non-team-non-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
it('should show non-team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsContent />);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('non-team-non-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
});
describe('draft PRs', () => {
it('should show draft non-team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsContent />);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
expect(getByText('non-team-is-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
it('should show draft non-team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsContent />);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('non-team-is-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
});
});
describe('team PRs', () => {
describe('non-draft PRs', () => {
it('should show team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsContent />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
expect(getByText('is-team-non-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
it('should show team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsContent />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('is-team-non-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(0);
});
});
describe('draft PRs', () => {
it('should show draft team PRs for un-archived repos when archived option is not checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsContent />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
expect(getByText('is-team-is-draft-non-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(0);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
it('should show draft team PRs for archived repos when archived option is checked', async () => {
const { getByText, getAllByText, getByTitle, queryAllByTitle } =
await render(<EntityTeamPullRequestsContent />);
const teamToggle = getByTitle('Show PRs from your team');
fireEvent.click(teamToggle);
const draftToggle = getByTitle('Show draft PRs');
fireEvent.click(draftToggle);
const archiveToggle = getByTitle('Show archived repos');
fireEvent.click(archiveToggle);
expect(getByText('is-team-is-draft-is-archive')).toBeInTheDocument();
expect(getAllByText('non-team-repo')).toHaveLength(1);
expect(queryAllByTitle('Repository is archived')).toHaveLength(1);
expect(queryAllByTitle('Draft PR')).toHaveLength(1);
});
});
});
});
@@ -52,21 +52,17 @@ const PullRequestCard: FunctionComponent<Props> = (props: Props) => {
const commentsReviews = getCommentedReviews(reviews);
const changeRequests = getChangeRequests(reviews);
// Resolved in this way to satisfy merge - Author: tylerhekman-procore
// This information should probably be moved into dedicated CardHeader fields
// instead of title prefixes
const cardTitleModifiers = [isDraft && '🔧 DRAFT', repositoryIsArchived && '📁 ARCHIVED'].filter(Boolean);
const cardTitle = cardTitleModifiers.length > 0 ? `(${cardTitleModifiers.join(' | ')}) - ${title}` : title;
return (
<Card
title={cardTitle}
title={title}
createdAt={createdAt}
updatedAt={updatedAt}
authorName={author.login}
authorAvatar={author.avatarUrl}
repositoryName={repositoryName}
prUrl={url}
isDraft={isDraft}
repositoryIsArchived={repositoryIsArchived}
>
{!!approvedReviews.length && (
<UserHeaderList