import React from 'react';
import {
act,
cleanup,
fireEvent,
render,
RenderResult,
} from '@testing-library/react';
import { axe } from 'jest-axe';
import { dataFixtures } from './data';
import MongoNav from './MongoNav';
describe('packages/mongo-nav', () => {
let queryByTestId: RenderResult['queryByTestId'];
let queryByText: RenderResult['queryByText'];
let getByTestId: RenderResult['getByTestId'];
let findByText: RenderResult['findByText'];
let container: RenderResult['container'];
let fetchMock: jest.Mock;
const originalFetch: typeof window.fetch = window.fetch;
beforeEach(() => {
fetchMock = jest.fn();
window.fetch = fetchMock;
});
afterEach(() => {
window.fetch = originalFetch;
jest.restoreAllMocks();
});
const renderComponent = async (props = {}) => {
await act(async () => {
({ findByText, getByTestId, queryByTestId, queryByText, container } =
render());
});
};
describe('by default', () => {
const responseObject: Pick = {
ok: true,
json: () => Promise.resolve(dataFixtures),
};
beforeEach(async () => {
fetchMock.mockResolvedValue(responseObject);
await renderComponent();
act(() => {
responseObject.json();
});
});
test('does not have basic accessibility issues', async () => {
const results = await axe(container);
expect(results).toHaveNoViolations();
});
test('fetch is called', () => {
expect(fetchMock.mock.calls.map(([uri]) => uri)).toEqual([
'https://cloud.mongodb.com/user/shared',
'https://cloud.mongodb.com/user/shared/alerts/project/fakeProjectId1',
]);
});
test('the organization and project navs are rendered', () => {
expect(queryByTestId('organization-nav')).toBeVisible();
expect(queryByTestId('project-nav')).toBeVisible();
});
test('current orgId is set based on data returned from fetch', () => {
expect((getByTestId('org-nav-billing') as HTMLAnchorElement).href).toBe(
`https://cloud.mongodb.com/v2#/org/${
dataFixtures!.currentOrganization!.orgId
}/billing/overview`,
);
});
test('current orgName is displayed inside the OrgSelect based on data returned from fetch', () => {
expect(
getByTestId('org-select-active-org').innerHTML.includes(
dataFixtures!.currentOrganization!.orgName,
),
).toBe(true);
});
test('current projectId is set based on data returned from fetch', () => {
expect(
(getByTestId('project-nav-activity-feed') as HTMLAnchorElement).href,
).toBe(
`https://cloud.mongodb.com/v2/${
dataFixtures!.currentProject!.projectId
}#activity`,
);
});
test('current projectName is displayed inside the ProjectSelect based on data returned from fetch', () => {
expect(
getByTestId('project-select-active-project').innerHTML.includes(
dataFixtures!.currentProject!.projectName,
),
).toBe(true);
});
test('projects displayed in ProjectSelect are only shown if they have the same orgId as the current project', () => {
const currentProject = getByTestId('project-select-active-project');
fireEvent.click(currentProject);
const projectList = getByTestId('project-select-project-list');
const projectOptions = projectList.querySelectorAll(
'[data-testid="project-option"]',
);
expect(projectOptions[0].innerHTML.includes('Demo Project 1')).toBe(true);
expect(projectOptions.length).toBe(1);
});
test('user is set based on data returned from fetch', () => {
expect(
getByTestId('user-menu-trigger').innerHTML.includes('DevMode'),
).toBe(true);
});
test('admin UI is not shown', () => {
expect(queryByText('Admin')).toBeNull();
});
});
describe('reloadData API is exposed to consumer through ref passed to MongoNav', () => {
let ref: React.RefObject;
const MongoNavWrapper = () => {
ref = React.useRef();
return ;
};
const renderWrapperComponent = async () => {
await act(async () => {
render();
});
};
let responseObject: Pick = {
ok: true,
json: () => Promise.resolve(dataFixtures),
};
beforeEach(async () => {
fetchMock.mockResolvedValue(responseObject);
await renderWrapperComponent();
act(() => {
responseObject.json();
});
});
afterEach(() => {
window.fetch = originalFetch;
jest.restoreAllMocks();
cleanup();
responseObject = {
ok: true,
json: () => Promise.resolve({}),
};
});
test('the RefObject contains a "reload" key', () => {
expect(Object.keys(ref.current)[0]).toBe('reloadData');
});
test('when the reloadData function is called, MongoNav refetches data', async () => {
// twice, once for main data and once for alerts polling
expect(fetchMock).toHaveBeenCalledTimes(1);
await act(async () => {
await ref.current.reloadData();
});
expect(fetchMock).toHaveBeenCalledTimes(2);
});
});
describe('when activeProjectId is supplied', () => {
const newActiveProject = dataFixtures.projects[0];
const activeProjectId = newActiveProject.projectId;
const expectedPostData = {
...dataFixtures,
currentProject: { ...newActiveProject },
};
const responseObject = {
ok: true,
json: () => Promise.resolve(expectedPostData),
};
beforeEach(async () => {
fetchMock.mockResolvedValue(responseObject);
await renderComponent({ activeProjectId });
act(() => {
responseObject.json();
});
});
test('fetch is called', () => {
expect(fetchMock.mock.calls.map(([uri]) => uri)).toEqual([
'https://cloud.mongodb.com/user/shared',
'https://cloud.mongodb.com/user/shared/alerts/project/fakeProjectId1',
]);
});
test('the organization and project navs are rendered', () => {
expect(queryByTestId('project-nav')).toBeVisible();
});
test('current orgId is set based on the new activeProjectId', () => {
expect((getByTestId('org-nav-billing') as HTMLAnchorElement).href).toBe(
`https://cloud.mongodb.com/v2#/org/${newActiveProject.orgId}/billing/overview`,
);
});
test('current projectId is set based on the new activeProjectId', () => {
expect(
(getByTestId('project-nav-activity-feed') as HTMLAnchorElement).href,
).toBe(
`https://cloud.mongodb.com/v2/${newActiveProject.projectId}#activity`,
);
});
test('current projectName is displayed inside the ProjectSelect based on the new activeProjectId', () => {
expect(
getByTestId('project-select-active-project').innerHTML.includes(
newActiveProject.projectName,
),
).toBe(true);
});
});
describe('when activeOrgId is supplied', () => {
const newActiveOrg = dataFixtures.organizations[1];
const activeOrgId = newActiveOrg.orgId;
const expectedPostData = {
...dataFixtures,
currentOrganization: { ...newActiveOrg },
};
const responseObject = {
ok: true,
json: () => Promise.resolve(expectedPostData),
};
beforeEach(async () => {
fetchMock.mockResolvedValue(responseObject);
await renderComponent({ activeOrgId });
act(() => {
responseObject.json();
});
});
test('fetch is called', () => {
expect(fetchMock.mock.calls.map(([uri]) => uri)).toEqual([
'https://cloud.mongodb.com/user/shared',
'https://cloud.mongodb.com/user/shared/alerts/project/fakeProjectId1',
]);
});
test('the organization and project navs are rendered', () => {
expect(queryByTestId('organization-nav')).toBeVisible();
expect(queryByTestId('project-nav')).toBeVisible();
});
test('current orgId is set based on the new activeOrgId', () => {
expect((getByTestId('org-nav-billing') as HTMLAnchorElement).href).toBe(
`https://cloud.mongodb.com/v2#/org/${newActiveOrg.orgId}/billing/overview`,
);
});
test('current orgName is displayed inside the OrgSelect based on the new activeOrgId', () => {
expect(
getByTestId('org-select-active-org').innerHTML.includes(
newActiveOrg.orgName,
),
).toBe(true);
});
});
describe('when activeProjectId and activeOrgID are both supplied', () => {
const newActiveOrg = dataFixtures.organizations[1];
const newActiveProject = dataFixtures.projects[0];
const activeProjectId = newActiveProject.projectId;
const activeOrgId = newActiveOrg.orgId;
const expectedPostData = {
...dataFixtures,
currentProject: { ...newActiveProject },
};
const responseObject = {
ok: true,
json: () => Promise.resolve(expectedPostData),
};
beforeEach(async () => {
fetchMock.mockResolvedValue(responseObject);
await renderComponent({
activeOrgId,
activeProjectId,
});
act(() => {
responseObject.json();
});
});
test('fetch is called', () => {
expect(fetchMock.mock.calls.map(([uri]) => uri)).toEqual([
'https://cloud.mongodb.com/user/shared',
'https://cloud.mongodb.com/user/shared/alerts/project/fakeProjectId1',
]);
});
test('current orgId is set based on the new activeProjectId', () => {
expect((getByTestId('org-nav-billing') as HTMLAnchorElement).href).toBe(
`https://cloud.mongodb.com/v2#/org/${newActiveProject.orgId}/billing/overview`,
);
});
test('current projectId is set based on the new activeProjectId', () => {
expect(
(getByTestId('project-nav-activity-feed') as HTMLAnchorElement).href,
).toBe(
`https://cloud.mongodb.com/v2/${newActiveProject.projectId}#activity`,
);
});
test('current projectName is displayed inside the ProjectSelect based on the new activeProjectId', () => {
expect(
getByTestId('project-select-active-project').innerHTML.includes(
newActiveProject.projectName,
),
).toBe(true);
});
});
describe('UserMenu behaves as expected when cloud is the active platform', () => {
beforeEach(
async () =>
await renderComponent({ mode: 'dev', activePlatform: 'cloud' }),
);
test('when the user menu opens, the cloud menu items are displayed', () => {
fireEvent.click(getByTestId('user-menu-trigger'));
expect(
queryByTestId('user-menuitem-cloud-user-preferences'),
).toBeInTheDocument();
});
});
describe('when user passes host override', () => {
const cloudHost = 'https://cloud-dev.mongodb.com';
beforeEach(
async () =>
await renderComponent({ mode: 'dev', hosts: { cloud: cloudHost } }),
);
test('link is properly constructured based on host override prop', () => {
expect((getByTestId('org-nav-billing') as HTMLAnchorElement).href).toBe(
`${cloudHost}/v2#/org/${
dataFixtures.currentOrganization!.orgId
}/billing/overview`,
);
});
});
describe('when user passes url override', () => {
const activityFeedHref = 'https://cloud.mongodb.com/activityfeed-test-url';
beforeEach(
async () =>
await renderComponent({
mode: 'dev',
urls: { projectNav: { activityFeed: activityFeedHref } },
}),
);
test('link is properly constructured based on url override prop', () => {
expect(
(getByTestId('project-nav-activity-feed') as HTMLAnchorElement).href,
).toBe(activityFeedHref);
});
});
describe('when user passes both host and url overrides', () => {
const cloudHost = 'https://cloud-dev.mongodb.com';
const activityFeedHref = 'https://cloud.mongodb.com/activityfeed-test-url';
beforeEach(
async () =>
await renderComponent({
mode: 'dev',
urls: { projectNav: { activityFeed: activityFeedHref } },
hosts: { cloud: cloudHost },
}),
);
test('link is properly constructured based on host override prop', () => {
expect((getByTestId('org-nav-billing') as HTMLAnchorElement).href).toBe(
`${cloudHost}/v2#/org/${
dataFixtures.currentOrganization!.orgId
}/billing/overview`,
);
});
test('link is properly constructured based on url override prop', () => {
expect(
(getByTestId('project-nav-activity-feed') as HTMLAnchorElement).href,
).toBe(activityFeedHref);
});
});
describe('when loadData is set to false', () => {
beforeEach(
async () =>
await renderComponent({
mode: 'dev',
loadData: false,
}),
);
test('the user name is not displayed', () => {
expect(
getByTestId('user-menu-trigger').innerHTML.includes('DevMode'),
).toBe(false);
});
test('the current project name is not displayed', () => {
expect(
getByTestId('project-select-active-project').innerHTML.includes(
dataFixtures!.currentProject!.projectName,
),
).toBe(false);
});
test('the current organization name is not displayed', () => {
expect(
getByTestId('org-select-active-org').innerHTML.includes(
dataFixtures!.currentOrganization!.orgName,
),
).toBe(false);
});
});
describe('when dataFixtures prop is set', () => {
const newOrgName = 'My Test Org';
beforeEach(
async () =>
await renderComponent({
mode: 'dev',
dataFixtures: {
currentOrganization: {
orgName: newOrgName,
},
},
}),
);
test('prop data overrides default data', () => {
expect(
getByTestId('org-select-active-org').innerHTML.includes(newOrgName),
).toBe(true);
});
});
describe('when dataFixtures prop sets admin as true', () => {
beforeEach(() =>
renderComponent({
mode: 'dev',
dataFixtures: {
account: {
admin: true,
},
},
}),
);
test('admin UI is shown', async () => {
expect(await findByText('Admin')).toBeVisible();
});
});
describe('when the environment is set to "government"', () => {
describe('cloud links default to cloud.mongodbgov.com', () => {
beforeEach(
async () =>
await renderComponent({
mode: 'dev',
environment: 'government',
}),
);
test('in the org nav', () => {
const billing = getByTestId('org-nav-billing') as HTMLAnchorElement;
expect(billing.href).toBe(
'https://cloud.mongodbgov.com/v2#/org/fakeOrgId1/billing/overview',
);
});
test('in the project nav', () => {
const support = getByTestId('project-nav-data-services').closest(
'a',
) as HTMLAnchorElement;
expect(support.href).toBe(
'https://cloud.mongodbgov.com/v2/fakeProjectId1#',
);
});
test('in the user menu', () => {
const userMenuTrigger = getByTestId('user-menu-trigger');
fireEvent.click(userMenuTrigger);
const submenuElement = getByTestId('user-submenu-cloud') as
| HTMLAnchorElement
| undefined;
expect(submenuElement).toHaveTextContent('Atlas for Government');
expect(submenuElement?.href).toBe('https://cloud.mongodbgov.com/');
});
});
});
});