import {Organization} from 'sentry-fixture/organization';
import {Team} from 'sentry-fixture/team';
import {initializeOrg} from 'sentry-test/initializeOrg';
import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
import * as OrganizationActionCreator from 'sentry/actionCreators/organization';
import * as openSudo from 'sentry/actionCreators/sudoModal';
import ConfigStore from 'sentry/stores/configStore';
import OrganizationStore from 'sentry/stores/organizationStore';
import ProjectsStore from 'sentry/stores/projectsStore';
import TeamStore from 'sentry/stores/teamStore';
import useOrganization from 'sentry/utils/useOrganization';
import {OrganizationLegacyContext} from 'sentry/views/organizationContextContainer';
jest.mock('sentry/stores/configStore', () => ({
get: jest.fn(),
}));
describe('OrganizationContextContainer', function () {
const {organization, projects, routerProps} = initializeOrg();
const teams = [Team()];
const api = new MockApiClient();
let getOrgMock: jest.Mock;
let getProjectsMock: jest.Mock;
let getTeamsMock: jest.Mock;
function DisplayOrg() {
const contextOrg = useOrganization();
return
{contextOrg.slug}
;
}
type Props = Partial>;
function makeComponent(props?: Props) {
return (
);
}
function renderComponent(props?: Props) {
return render(makeComponent(props));
}
beforeEach(function () {
MockApiClient.clearMockResponses();
getOrgMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/',
body: organization,
});
getProjectsMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/projects/',
body: projects,
});
getTeamsMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/teams/',
body: teams,
});
jest.spyOn(TeamStore, 'loadInitialData');
jest.spyOn(ProjectsStore, 'loadInitialData');
jest.spyOn(OrganizationActionCreator, 'fetchOrganizationDetails');
});
afterEach(function () {
OrganizationStore.reset();
jest.restoreAllMocks();
});
it('renders and fetches org, projects, and teams', async function () {
renderComponent();
await waitFor(() => expect(getOrgMock).toHaveBeenCalled());
expect(getProjectsMock).toHaveBeenCalled();
expect(getTeamsMock).toHaveBeenCalled();
expect(screen.queryByRole('loading-indicator')).not.toBeInTheDocument();
expect(screen.getByText(organization.slug)).toBeInTheDocument();
expect(
screen.queryByText('The organization you were looking for was not found.')
).not.toBeInTheDocument();
expect(TeamStore.loadInitialData).toHaveBeenCalledWith(teams);
expect(ProjectsStore.loadInitialData).toHaveBeenCalledWith(projects);
expect(OrganizationActionCreator.fetchOrganizationDetails).toHaveBeenCalledWith(
api,
'org-slug',
true,
true
);
});
it('fetches new org when router params change', async function () {
const newOrg = Organization({slug: 'new-slug'});
const {rerender} = renderComponent();
expect(await screen.findByText(organization.slug)).toBeInTheDocument();
const mock = MockApiClient.addMockResponse({
url: '/organizations/new-slug/',
body: newOrg,
});
const projectsMock = MockApiClient.addMockResponse({
url: '/organizations/new-slug/projects/',
body: projects,
});
const teamsMock = MockApiClient.addMockResponse({
url: '/organizations/new-slug/teams/',
body: teams,
});
// Re-render with new org slug
rerender(makeComponent({params: {orgId: newOrg.slug}}));
// Loads new org
expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
// Renders new org
expect(await screen.findByText(newOrg.slug)).toBeInTheDocument();
expect(mock).toHaveBeenLastCalledWith('/organizations/new-slug/', expect.anything());
expect(projectsMock).toHaveBeenCalled();
expect(teamsMock).toHaveBeenCalled();
});
it('shows loading error for non-superusers on 403s', async function () {
getOrgMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/',
statusCode: 403,
});
jest.spyOn(console, 'error').mockImplementation(jest.fn()); // eslint-disable-line no-console
renderComponent();
expect(
await screen.findByText('There was an error loading data.')
).toBeInTheDocument();
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalled();
});
it('opens sudo modal for superusers on 403s', async function () {
const openSudoSpy = jest.spyOn(openSudo, 'openSudo');
jest
.mocked(ConfigStore.get)
.mockImplementation(() => TestStubs.Config({isSuperuser: true}));
getOrgMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/',
statusCode: 403,
});
renderComponent();
await waitFor(() => expect(openSudoSpy).toHaveBeenCalled());
});
it('uses last organization from ConfigStore', function () {
getOrgMock = MockApiClient.addMockResponse({
url: '/organizations/last-org/',
body: organization,
});
MockApiClient.addMockResponse({
url: '/organizations/last-org/projects/',
body: projects,
});
MockApiClient.addMockResponse({
url: '/organizations/last-org/teams/',
body: teams,
});
// mocking `.get('lastOrganization')`
jest.mocked(ConfigStore.get).mockImplementation(() => 'last-org');
renderComponent({useLastOrganization: true, params: {orgId: ''}});
expect(getOrgMock).toHaveBeenLastCalledWith(
'/organizations/last-org/',
expect.anything()
);
});
it('uses last organization from `organizations` prop', async function () {
MockApiClient.addMockResponse({
url: '/organizations/foo/environments/',
body: TestStubs.Environments(),
});
getOrgMock = MockApiClient.addMockResponse({
url: '/organizations/foo/',
body: organization,
});
getProjectsMock = MockApiClient.addMockResponse({
url: '/organizations/foo/projects/',
body: projects,
});
getTeamsMock = MockApiClient.addMockResponse({
url: '/organizations/foo/teams/',
body: teams,
});
jest.mocked(ConfigStore.get).mockImplementation(() => '');
const {rerender} = renderComponent({
params: {orgId: ''},
useLastOrganization: true,
organizationsLoading: true,
organizations: [],
});
expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
rerender(
makeComponent({
params: {orgId: ''},
useLastOrganization: true,
organizationsLoading: false,
organizations: [Organization({slug: 'foo'}), Organization({slug: 'bar'})],
})
);
expect(await screen.findByText(organization.slug)).toBeInTheDocument();
expect(getOrgMock).toHaveBeenCalled();
expect(getProjectsMock).toHaveBeenCalled();
expect(getTeamsMock).toHaveBeenCalled();
});
it('uses last organization when no orgId in URL - and fetches org details once', async function () {
jest.mocked(ConfigStore.get).mockImplementation(() => 'my-last-org');
getOrgMock = MockApiClient.addMockResponse({
url: '/organizations/my-last-org/',
body: Organization({slug: 'my-last-org'}),
});
getProjectsMock = MockApiClient.addMockResponse({
url: '/organizations/my-last-org/projects/',
body: projects,
});
getTeamsMock = MockApiClient.addMockResponse({
url: '/organizations/my-last-org/teams/',
body: teams,
});
const {rerender} = renderComponent({
params: {orgId: ''},
useLastOrganization: true,
organizations: [],
});
expect(await screen.findByText('my-last-org')).toBeInTheDocument();
expect(getOrgMock).toHaveBeenCalledTimes(1);
// Simulate OrganizationsStore being loaded *after* `OrganizationContext` finishes
// org details fetch
rerender(
makeComponent({
params: {orgId: ''},
useLastOrganization: true,
organizationsLoading: false,
organizations: [Organization({slug: 'foo'}), Organization({slug: 'bar'})],
})
);
expect(getOrgMock).toHaveBeenCalledTimes(1);
expect(getProjectsMock).toHaveBeenCalledTimes(1);
expect(getTeamsMock).toHaveBeenCalledTimes(1);
});
it('fetches org details only once if organizations loading store changes', async function () {
const {rerender} = renderComponent({
params: {orgId: 'org-slug'},
organizationsLoading: true,
organizations: [],
});
expect(await screen.findByText(organization.slug)).toBeInTheDocument();
expect(getOrgMock).toHaveBeenCalledTimes(1);
// Simulate OrganizationsStore being loaded *after* `OrganizationContext` finishes
// org details fetch
rerender(
makeComponent({
params: {orgId: 'org-slug'},
organizationsLoading: false,
organizations: [Organization({slug: 'foo'}), Organization({slug: 'bar'})],
})
);
expect(getOrgMock).toHaveBeenCalledTimes(1);
expect(getProjectsMock).toHaveBeenCalledTimes(1);
expect(getTeamsMock).toHaveBeenCalledTimes(1);
});
});