Browse Source

test(js): Convert OrganizationContextContainer (#40569)

Evan Purkhiser 2 years ago
parent
commit
ee791dbe2d

+ 93 - 107
static/app/views/organizationContextContainer.spec.jsx

@@ -1,4 +1,4 @@
-import {mountWithTheme} from 'sentry-test/enzyme';
+import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
 
 import {openSudo} from 'sentry/actionCreators/modal';
 import * as OrganizationActionCreator from 'sentry/actionCreators/organization';
@@ -6,6 +6,7 @@ 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', () => ({
@@ -16,7 +17,6 @@ jest.mock('sentry/actionCreators/modal', () => ({
 }));
 
 describe('OrganizationContextContainer', function () {
-  let wrapper;
   const org = TestStubs.Organization();
   const teams = [TestStubs.Team()];
   const projects = [TestStubs.Project()];
@@ -26,8 +26,14 @@ describe('OrganizationContextContainer', function () {
   let getProjectsMock;
   let getTeamsMock;
 
-  const createWrapper = props => {
-    wrapper = mountWithTheme(
+  function DisplayOrg() {
+    const contextOrg = useOrganization();
+
+    return <div>{contextOrg.slug}</div>;
+  }
+
+  function makeComponent(props) {
+    return (
       <OrganizationLegacyContext
         api={api}
         params={{orgId: 'org-slug'}}
@@ -35,11 +41,14 @@ describe('OrganizationContextContainer', function () {
         routes={[]}
         {...props}
       >
-        <div />
+        <DisplayOrg />
       </OrganizationLegacyContext>
     );
-    return wrapper;
-  };
+  }
+
+  function renderComponent(props) {
+    return render(makeComponent(props));
+  }
 
   beforeEach(function () {
     MockApiClient.clearMockResponses();
@@ -62,7 +71,6 @@ describe('OrganizationContextContainer', function () {
   });
 
   afterEach(function () {
-    wrapper.unmount();
     OrganizationStore.reset();
 
     TeamStore.loadInitialData.mockRestore();
@@ -72,18 +80,17 @@ describe('OrganizationContextContainer', function () {
   });
 
   it('renders and fetches org, projects, and teams', async function () {
-    wrapper = createWrapper();
-    // await dispatching the action to org store
-    await tick();
-    // await resolving the api promise from action creator and updating component
-    await tick();
-    expect(getOrgMock).toHaveBeenCalled();
+    renderComponent();
+
+    await waitFor(() => expect(getOrgMock).toHaveBeenCalled());
     expect(getProjectsMock).toHaveBeenCalled();
     expect(getTeamsMock).toHaveBeenCalled();
 
-    expect(wrapper.state('loading')).toBe(false);
-    expect(wrapper.state('error')).toBe(null);
-    expect(wrapper.state('organization')).toEqual(org);
+    expect(screen.queryByRole('loading-indicator')).not.toBeInTheDocument();
+    expect(screen.getByText(org.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);
@@ -98,15 +105,8 @@ describe('OrganizationContextContainer', function () {
   it('fetches new org when router params change', async function () {
     const newOrg = TestStubs.Organization({slug: 'new-slug'});
 
-    wrapper = createWrapper();
-    const instance = wrapper.instance();
-    jest.spyOn(instance, 'render');
-
-    // initial render
-    await tick();
-    await tick();
-    expect(instance.state.organization).toEqual(org);
-    expect(instance.render).toHaveBeenCalledTimes(1);
+    const {rerender} = renderComponent();
+    expect(await screen.findByText(org.slug)).toBeInTheDocument();
 
     const mock = MockApiClient.addMockResponse({
       url: '/organizations/new-slug/',
@@ -121,24 +121,18 @@ describe('OrganizationContextContainer', function () {
       body: teams,
     });
 
-    wrapper.setProps({params: {orgId: newOrg.slug}}, () => {
-      // state should be reset based on props
-      expect(instance.state.organization).toEqual(null);
-      expect(instance.render).toHaveBeenCalledTimes(2);
-    });
+    // Re-render with new org slug
+    rerender(makeComponent({params: {orgId: newOrg.slug}}));
 
-    // await fetching new org
-    await tick();
-    await tick();
-    wrapper.update();
+    // 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();
-
-    expect(wrapper.state('loading')).toBe(false);
-    expect(wrapper.state('error')).toBe(null);
-    expect(wrapper.state('organization')).toEqual(newOrg);
   });
 
   it('shows loading error for non-superusers on 403s', async function () {
@@ -147,16 +141,16 @@ describe('OrganizationContextContainer', function () {
       statusCode: 403,
     });
     jest.spyOn(console, 'error').mockImplementation(jest.fn()); // eslint-disable-line no-console
-    wrapper = createWrapper();
-
-    // await dispatching action
-    await tick();
-    // await resolving api, and updating component
-    await tick();
-    await tick();
-    wrapper.update();
-    expect(wrapper.find('LoadingError')).toHaveLength(1);
-    console.error.mockRestore(); // 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();
+    // eslint-disable-next-line no-console
+    console.error.mockRestore();
   });
 
   it('opens sudo modal for superusers on 403s', async function () {
@@ -167,18 +161,13 @@ describe('OrganizationContextContainer', function () {
       url: '/organizations/org-slug/',
       statusCode: 403,
     });
-    wrapper = createWrapper();
-    // await dispatching action
-    await tick();
-    // await resolving api, and updating component
-    await tick();
-    await tick();
-    wrapper.update();
-
-    expect(openSudo).toHaveBeenCalled();
+
+    renderComponent();
+
+    await waitFor(() => expect(openSudo).toHaveBeenCalled());
   });
 
-  it('uses last organization from ConfigStore', async function () {
+  it('uses last organization from ConfigStore', function () {
     getOrgMock = MockApiClient.addMockResponse({
       url: '/organizations/last-org/',
       body: org,
@@ -194,11 +183,8 @@ describe('OrganizationContextContainer', function () {
 
     // mocking `.get('lastOrganization')`
     ConfigStore.get.mockImplementation(() => 'last-org');
-    wrapper = createWrapper({useLastOrganization: true, params: {}});
-    // await dispatching action
-    await tick();
-    // await dispatching the action to org store
-    await tick();
+    renderComponent({useLastOrganization: true, params: {}});
+
     expect(getOrgMock).toHaveBeenLastCalledWith(
       '/organizations/last-org/',
       expect.anything()
@@ -225,27 +211,28 @@ describe('OrganizationContextContainer', function () {
 
     ConfigStore.get.mockImplementation(() => '');
 
-    wrapper = createWrapper({
-      useLastOrganization: true,
+    const {rerender} = renderComponent({
       params: {orgId: ''},
+      useLastOrganization: true,
       organizationsLoading: true,
       organizations: [],
     });
 
-    expect(wrapper.find('LoadingTriangle')).toHaveLength(1);
-
-    wrapper.setProps({
-      organizationsLoading: false,
-      organizations: [
-        TestStubs.Organization({slug: 'foo'}),
-        TestStubs.Organization({slug: 'bar'}),
-      ],
-    });
+    expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
+
+    rerender(
+      makeComponent({
+        params: {orgId: ''},
+        useLastOrganization: true,
+        organizationsLoading: false,
+        organizations: [
+          TestStubs.Organization({slug: 'foo'}),
+          TestStubs.Organization({slug: 'bar'}),
+        ],
+      })
+    );
 
-    await tick(); // action to start fetch org
-    await tick(); // action after successfully fetching org
-    wrapper.update();
-    expect(wrapper.find('LoadingTriangle')).toHaveLength(0);
+    expect(await screen.findByText(org.slug)).toBeInTheDocument();
 
     expect(getOrgMock).toHaveBeenCalled();
     expect(getProjectsMock).toHaveBeenCalled();
@@ -267,28 +254,28 @@ describe('OrganizationContextContainer', function () {
       body: teams,
     });
 
-    wrapper = createWrapper({
+    const {rerender} = renderComponent({
       params: {},
       useLastOrganization: true,
       organizations: [],
     });
-    // await dispatching action
-    await tick();
-    // await resolving api, and updating component
-    await tick();
-    wrapper.update();
-    expect(wrapper.find('LoadingTriangle')).toHaveLength(0);
+
+    expect(await screen.findByText('my-last-org')).toBeInTheDocument();
     expect(getOrgMock).toHaveBeenCalledTimes(1);
 
     // Simulate OrganizationsStore being loaded *after* `OrganizationContext` finishes
     // org details fetch
-    wrapper.setProps({
-      organizationsLoading: false,
-      organizations: [
-        TestStubs.Organization({slug: 'foo'}),
-        TestStubs.Organization({slug: 'bar'}),
-      ],
-    });
+    rerender(
+      makeComponent({
+        params: {},
+        useLastOrganization: true,
+        organizationsLoading: false,
+        organizations: [
+          TestStubs.Organization({slug: 'foo'}),
+          TestStubs.Organization({slug: 'bar'}),
+        ],
+      })
+    );
 
     expect(getOrgMock).toHaveBeenCalledTimes(1);
     expect(getProjectsMock).toHaveBeenCalledTimes(1);
@@ -296,28 +283,27 @@ describe('OrganizationContextContainer', function () {
   });
 
   it('fetches org details only once if organizations loading store changes', async function () {
-    wrapper = createWrapper({
+    const {rerender} = renderComponent({
       params: {orgId: 'org-slug'},
       organizationsLoading: true,
       organizations: [],
     });
-    // await dispatching action
-    await tick();
-    // await resolving api, and updating component
-    await tick();
-    wrapper.update();
-    expect(wrapper.find('LoadingTriangle')).toHaveLength(0);
+
+    expect(await screen.findByText(org.slug)).toBeInTheDocument();
     expect(getOrgMock).toHaveBeenCalledTimes(1);
 
     // Simulate OrganizationsStore being loaded *after* `OrganizationContext` finishes
     // org details fetch
-    wrapper.setProps({
-      organizationsLoading: false,
-      organizations: [
-        TestStubs.Organization({slug: 'foo'}),
-        TestStubs.Organization({slug: 'bar'}),
-      ],
-    });
+    rerender(
+      makeComponent({
+        params: {orgId: 'org-slug'},
+        organizationsLoading: false,
+        organizations: [
+          TestStubs.Organization({slug: 'foo'}),
+          TestStubs.Organization({slug: 'bar'}),
+        ],
+      })
+    );
 
     expect(getOrgMock).toHaveBeenCalledTimes(1);
     expect(getProjectsMock).toHaveBeenCalledTimes(1);

+ 6 - 3
static/app/views/organizationContextContainer.tsx

@@ -202,9 +202,12 @@ class OrganizationContextContainer extends Component<Props, State> {
   }
 
   fetchData(isInitialFetch = false) {
-    if (!OrganizationContextContainer.getOrganizationSlug(this.props)) {
+    const orgSlug = OrganizationContextContainer.getOrganizationSlug(this.props);
+
+    if (!orgSlug) {
       return;
     }
+
     // fetch from the store, then fetch from the API if necessary
     if (OrganizationContextContainer.isOrgStorePopulatedCorrectly(this.props)) {
       return;
@@ -213,7 +216,7 @@ class OrganizationContextContainer extends Component<Props, State> {
     metric.mark({name: 'organization-details-fetch-start'});
     fetchOrganizationDetails(
       this.props.api,
-      OrganizationContextContainer.getOrganizationSlug(this.props),
+      orgSlug,
       !OrganizationContextContainer.isOrgChanging(this.props), // if true, will preserve a lightweight org that was fetched,
       isInitialFetch
     );
@@ -291,7 +294,7 @@ class OrganizationContextContainer extends Component<Props, State> {
         return this.renderBody();
       case ORGANIZATION_FETCH_ERROR_TYPES.ORG_NOT_FOUND:
         errorComponent = (
-          <Alert type="error">
+          <Alert type="error" data-test-id="org-loading-error">
             {t('The organization you were looking for was not found.')}
           </Alert>
         );