Skip to content

Commit 0a53abf

Browse files
committed
chore: Test frontend/pages/sites/SiteListItem.jsx and refactor/remove frontend/pages/sites/PublishedState.jsx
1 parent 8499bab commit 0a53abf

File tree

5 files changed

+204
-65
lines changed

5 files changed

+204
-65
lines changed

frontend/pages/sites/PublishedState.jsx

-26
This file was deleted.

frontend/pages/sites/SiteListItem.jsx

+14-39
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,43 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { Link, useNavigate } from 'react-router-dom';
3+
import { Link } from 'react-router-dom';
4+
import { dateAndTime } from '@util/datetime';
45

56
import GitHubLink from '@shared/GitHubLink';
6-
import ButtonLink from '@shared/ButtonLink';
7-
import siteActions from '@actions/siteActions';
87
import { sandboxMsg } from '@util';
9-
import { ORGANIZATION, USER } from '@propTypes';
10-
11-
import PublishedState from './PublishedState';
12-
import RepoLastVerified from './RepoLastVerified';
8+
import { ORGANIZATION } from '@propTypes';
139

1410
function getSiteName(site) {
1511
return `${site.owner}/${site.repository}`;
1612
}
1713

18-
const handleRemoveSite = (site, user, navigate) => (event) => {
19-
event.preventDefault();
20-
siteActions
21-
.removeUserFromSite(site.id, user.id)
22-
.then(() => siteActions.fetchSites())
23-
.then(() => navigate('/sites'));
24-
};
25-
26-
function SiteListItem({ organization, site, user }) {
27-
const navigate = useNavigate();
14+
function SiteListItem({ organization, site }) {
2815
return (
2916
<li className="usa-card tablet-lg:grid-col-6">
3017
<div className="usa-card__container bg-base-lightest">
3118
<div className="usa-card__header">
3219
<h2 className="usa-card__heading text-normal">
33-
{!site.isActive || (organization && !organization.isActive) ? (
20+
{(site && !site.isActive) || (organization && !organization.isActive) ? (
3421
`${getSiteName(site)} (Inactive)`
3522
) : (
36-
<Link to={`/sites/${site.id}`} title="View site settings">
23+
<Link to={`/sites/${site.id}/builds`} title="View site builds">
3724
{getSiteName(site)}
3825
</Link>
3926
)}{' '}
4027
</h2>
4128
</div>
4229
<div className="usa-card__body">
43-
{organization && <h3>{`organization - ${organization.name}`}</h3>}
44-
<RepoLastVerified site={site} userUpdated={user.updatedAt} />
45-
{organization?.isSandbox && (
30+
<h3>{`organization - ${organization && organization.name}`}</h3>
31+
{organization && organization.isSandbox && (
4632
<p>
4733
<em>{sandboxMsg(organization.daysUntilSandboxCleaning, 'site')}</em>
4834
</p>
4935
)}
50-
<PublishedState site={site} />
36+
<p>
37+
{site && site.publishedAt
38+
? `Last published on ${dateAndTime(site.publishedAt)}`
39+
: 'Please wait for build to complete or check logs for error message.'}
40+
</p>
5141
</div>
5242
<div className="usa-card__footer usa-button-group">
5343
<div className="usa-button-group__item">
@@ -58,24 +48,14 @@ function SiteListItem({ organization, site, user }) {
5848
isButton
5949
/>
6050
</div>
61-
{!organization && (
62-
<div className="usa-button-group__item">
63-
<ButtonLink
64-
className="usa-button--secondary"
65-
clickHandler={handleRemoveSite(site, user, navigate)}
66-
>
67-
Remove
68-
</ButtonLink>
69-
</div>
70-
)}
7151
</div>
7252
</div>
7353
</li>
7454
);
7555
}
7656

7757
SiteListItem.propTypes = {
78-
organization: ORGANIZATION,
58+
organization: ORGANIZATION.isRequired,
7959
site: PropTypes.shape({
8060
repository: PropTypes.string,
8161
owner: PropTypes.string,
@@ -86,11 +66,6 @@ SiteListItem.propTypes = {
8666
viewLink: PropTypes.string,
8767
isActive: PropTypes.bool,
8868
}).isRequired,
89-
user: USER.isRequired,
90-
};
91-
92-
SiteListItem.defaultProps = {
93-
organization: null,
9469
};
9570

9671
export default SiteListItem;
+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
import { MemoryRouter } from 'react-router-dom';
5+
import { createFixtureSite } from '../../../test/frontend/support/data/sites';
6+
import { createFixtureOrg } from '../../../test/frontend/support/data/organizations';
7+
import * as datetimeUtils from '@util/datetime';
8+
9+
import SiteListItem from './SiteListItem';
10+
import GitHubLink from '@shared/GitHubLink';
11+
12+
const mockSite = createFixtureSite({ name: 'test-site' });
13+
const mockOrganization = createFixtureOrg({ name: 'test-org' });
14+
15+
jest.mock('@shared/GitHubLink', () => {
16+
return jest.fn(() => null);
17+
});
18+
19+
describe('<SiteListItem />', () => {
20+
it('renders the organization name in a heading element', () => {
21+
render(
22+
/* MemoryRouter allows us to render components that use react Link */
23+
<MemoryRouter>
24+
<SiteListItem organization={mockOrganization} site={mockSite} />
25+
</MemoryRouter>,
26+
);
27+
const orgName = screen.getByRole('heading', {
28+
name: `organization - ${mockOrganization.name}`,
29+
});
30+
expect(orgName).toBeInTheDocument();
31+
});
32+
33+
// eslint-disable-next-line max-len
34+
it('renders the site name by concatenating the site owner and repo name in a heading element', () => {
35+
render(
36+
/* MemoryRouter allows us to render components that use react Link */
37+
<MemoryRouter>
38+
<SiteListItem organization={mockOrganization} site={mockSite} />
39+
</MemoryRouter>,
40+
);
41+
const combinedName = `${mockSite.owner}/${mockSite.repository}`;
42+
const text = screen.getByText(combinedName);
43+
expect(text).toBeInTheDocument();
44+
});
45+
46+
describe('renders the last published state', () => {
47+
let dateAndTimeSpy;
48+
49+
beforeEach(() => {
50+
dateAndTimeSpy = jest.spyOn(datetimeUtils, 'dateAndTime');
51+
});
52+
afterEach(() => {
53+
dateAndTimeSpy.mockRestore();
54+
});
55+
56+
it('renders the "Last published on" message when publishedAt exists', () => {
57+
const formattedDate = datetimeUtils.dateAndTime(mockSite.publishedAt);
58+
59+
render(
60+
<MemoryRouter>
61+
<SiteListItem organization={mockOrganization} site={mockSite} />
62+
</MemoryRouter>,
63+
);
64+
65+
const message = screen.getByText(`Last published on ${formattedDate}`);
66+
expect(message).toBeInTheDocument();
67+
});
68+
69+
it('displays "Please wait" if publishedAt is null', () => {
70+
const unPublishedSite = {
71+
...mockSite,
72+
publishedAt: null,
73+
};
74+
render(
75+
<MemoryRouter>
76+
<SiteListItem organization={mockOrganization} site={unPublishedSite} />
77+
</MemoryRouter>,
78+
);
79+
const message = screen.getByText(
80+
'Please wait for build to complete or check logs for error message.',
81+
);
82+
expect(message).toBeInTheDocument();
83+
expect(dateAndTimeSpy).not.toHaveBeenCalled();
84+
});
85+
});
86+
87+
it('renders a button link to the GitHub repo for this site', () => {
88+
render(
89+
<MemoryRouter>
90+
<SiteListItem organization={mockOrganization} site={mockSite} />
91+
</MemoryRouter>,
92+
);
93+
expect(GitHubLink).toHaveBeenCalledWith(
94+
{
95+
owner: mockSite.owner,
96+
repository: mockSite.repository,
97+
text: 'View repo',
98+
isButton: true,
99+
},
100+
expect.anything(), // ref
101+
);
102+
});
103+
104+
it('renders sandbox message when organization is a sandbox', () => {
105+
const sandboxOrg = {
106+
...mockOrganization,
107+
isSandbox: true,
108+
daysUntilSandboxCleaning: 7,
109+
};
110+
render(
111+
<MemoryRouter>
112+
<SiteListItem organization={sandboxOrg} site={mockSite} />
113+
</MemoryRouter>,
114+
);
115+
const sandboxMsg = screen.getByText((content) =>
116+
content.includes(
117+
// eslint-disable-next-line max-len
118+
`All site data for this sandbox organization will be removed in ${sandboxOrg.daysUntilSandboxCleaning} days`,
119+
),
120+
);
121+
expect(sandboxMsg).toBeInTheDocument();
122+
});
123+
124+
// eslint-disable-next-line max-len
125+
it('renders the site name as a link to the site’s build history if site and org are active', () => {
126+
render(
127+
<MemoryRouter>
128+
<SiteListItem organization={mockOrganization} site={mockSite} />
129+
</MemoryRouter>,
130+
);
131+
const link = screen.getByRole('link', {
132+
name: `${mockSite.owner}/${mockSite.repository}`,
133+
});
134+
expect(link).toHaveAttribute('href', `/sites/${mockSite.id}/builds`);
135+
expect(link).toHaveAttribute('title', `View site builds`);
136+
});
137+
138+
// eslint-disable-next-line max-len
139+
it('renders the site name as text with "(Inactive)" instead of a link if org is inactive', () => {
140+
const inactiveOrg = { ...mockOrganization, isActive: false };
141+
render(<SiteListItem organization={inactiveOrg} site={mockSite} />);
142+
const text = screen.getByText(`${mockSite.owner}/${mockSite.repository} (Inactive)`);
143+
const link = screen.queryByRole('link', {
144+
name: `${mockSite.owner}/${mockSite.repository}`,
145+
});
146+
expect(link).not.toBeInTheDocument();
147+
expect(text).toBeInTheDocument();
148+
});
149+
150+
// eslint-disable-next-line max-len
151+
it('renders the site name as text with "(Inactive)" instead of a link if site is inactive', () => {
152+
const inactiveSite = { ...mockSite, isActive: false };
153+
render(<SiteListItem organization={mockOrganization} site={inactiveSite} />);
154+
const link = screen.queryByRole('link', {
155+
name: `${mockSite.owner}/${mockSite.repository}`,
156+
});
157+
const text = screen.getByText(
158+
`${inactiveSite.owner}/${inactiveSite.repository} (Inactive)`,
159+
);
160+
expect(link).not.toBeInTheDocument();
161+
expect(text).toBeInTheDocument();
162+
});
163+
});

test/frontend/support/data/organizations.js

+6
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ export function createFixtureOrg({
55
id = randomUUID(),
66
name = 'an-organization',
77
updatedAt = Date.now().toString(),
8+
isActive = true,
9+
isSandbox = false,
10+
daysUnitSandboxCleaning = null,
811
} = {}) {
912
return {
1013
createdAt,
1114
id,
1215
name,
1316
updatedAt,
17+
isActive,
18+
isSandbox,
19+
daysUnitSandboxCleaning,
1420
};
1521
}

test/frontend/support/data/sites.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export function createFixtureSite({
2+
createdAt = new Date().toISOString(),
3+
publishedAt = new Date().toISOString(),
4+
repoLastVerified = new Date().toISOString(),
5+
id = Math.floor(Math.random() * 1000),
6+
name = 'test-site',
7+
repository = 'test-repo',
8+
owner = 'test-owner',
9+
isActive = true,
10+
} = {}) {
11+
return {
12+
createdAt,
13+
publishedAt,
14+
repoLastVerified,
15+
id,
16+
name,
17+
repository,
18+
owner,
19+
isActive,
20+
};
21+
}

0 commit comments

Comments
 (0)