diff --git a/static/app/components/lightWeightNoProjectMessage.tsx b/static/app/components/lightWeightNoProjectMessage.tsx deleted file mode 100644 index 1b9211e54d202b..00000000000000 --- a/static/app/components/lightWeightNoProjectMessage.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import NoProjectMessage from 'app/components/noProjectMessage'; -import {Organization, Project} from 'app/types'; -import withProjects from 'app/utils/withProjects'; - -type Props = { - organization: Organization; - projects: Project[]; - loadingProjects: boolean; -}; - -function LightWeightNoProjectMessage({ - organization, - projects, - loadingProjects, - ...props -}: Props) { - return ( - - ); -} - -export default withProjects(LightWeightNoProjectMessage); diff --git a/static/app/components/noProjectMessage.tsx b/static/app/components/noProjectMessage.tsx index 7a52c8fd26267b..1e4cf609032eae 100644 --- a/static/app/components/noProjectMessage.tsx +++ b/static/app/components/noProjectMessage.tsx @@ -11,15 +11,16 @@ import {t} from 'app/locale'; import ConfigStore from 'app/stores/configStore'; import space from 'app/styles/space'; import {Organization, Project} from 'app/types'; +import withProjects from 'app/utils/withProjects'; type Props = React.PropsWithChildren<{ organization: Organization; - projects?: Project[]; - loadingProjects?: boolean; + projects: Project[]; + loadingProjects: boolean; superuserNeedsToBeProjectMember?: boolean; }>; -export default function NoProjectMessage({ +function NoProjectMessage({ children, organization, projects, @@ -127,3 +128,5 @@ const Content = styled(Flex)` const Actions = styled(ButtonBar)` width: fit-content; `; + +export default withProjects(NoProjectMessage); diff --git a/static/app/views/dashboardsV2/detail.tsx b/static/app/views/dashboardsV2/detail.tsx index f9d046fa319a2b..a4f75d8b83b622 100644 --- a/static/app/views/dashboardsV2/detail.tsx +++ b/static/app/views/dashboardsV2/detail.tsx @@ -13,7 +13,7 @@ import {Client} from 'app/api'; import Breadcrumbs from 'app/components/breadcrumbs'; import HookOrDefault from 'app/components/hookOrDefault'; import * as Layout from 'app/components/layouts/thirds'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import {t} from 'app/locale'; import {PageContent} from 'app/styles/organization'; @@ -403,7 +403,7 @@ class DashboardDetail extends Component { }} > - + { router={router} location={location} /> - + ); @@ -455,7 +455,7 @@ class DashboardDetail extends Component { }, }} > - + { /> - + ); } diff --git a/static/app/views/dashboardsV2/manage/index.tsx b/static/app/views/dashboardsV2/manage/index.tsx index 2617e38e176584..2bfe5637247292 100644 --- a/static/app/views/dashboardsV2/manage/index.tsx +++ b/static/app/views/dashboardsV2/manage/index.tsx @@ -7,7 +7,7 @@ import Feature from 'app/components/acl/feature'; import Alert from 'app/components/alert'; import Button from 'app/components/button'; import DropdownControl, {DropdownItem} from 'app/components/dropdownControl'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import SearchBar from 'app/components/searchBar'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {IconAdd} from 'app/icons'; @@ -191,7 +191,7 @@ class ManageDashboards extends AsyncView { > - + {t('Dashboards')} @@ -210,7 +210,7 @@ class ManageDashboards extends AsyncView { {this.renderActions()} {this.renderDashboards()} - + diff --git a/static/app/views/eventsV2/eventDetails/index.tsx b/static/app/views/eventsV2/eventDetails/index.tsx index 1b30eab0383968..8acb2cdeb36cfb 100644 --- a/static/app/views/eventsV2/eventDetails/index.tsx +++ b/static/app/views/eventsV2/eventDetails/index.tsx @@ -2,7 +2,7 @@ import {Component} from 'react'; import {RouteComponentProps} from 'react-router'; import styled from '@emotion/styled'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {t} from 'app/locale'; import {PageContent} from 'app/styles/organization'; @@ -53,7 +53,7 @@ class EventDetails extends Component { projectSlug={projectSlug} > - + { router={router} route={route} /> - + ); diff --git a/static/app/views/eventsV2/landing.tsx b/static/app/views/eventsV2/landing.tsx index 15efa817d45e8d..44871739fda401 100644 --- a/static/app/views/eventsV2/landing.tsx +++ b/static/app/views/eventsV2/landing.tsx @@ -10,7 +10,7 @@ import GuideAnchor from 'app/components/assistant/guideAnchor'; import AsyncComponent from 'app/components/asyncComponent'; import Button from 'app/components/button'; import DropdownControl, {DropdownItem} from 'app/components/dropdownControl'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import SearchBar from 'app/components/searchBar'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import Switch from 'app/components/switchButton'; @@ -290,7 +290,7 @@ class DiscoverLanding extends AsyncComponent { > - + @@ -315,7 +315,7 @@ class DiscoverLanding extends AsyncComponent { {this.renderActions()} {this.renderComponent()} - + diff --git a/static/app/views/eventsV2/results.tsx b/static/app/views/eventsV2/results.tsx index 3f65e90aa577ef..552f1a5f72f65b 100644 --- a/static/app/views/eventsV2/results.tsx +++ b/static/app/views/eventsV2/results.tsx @@ -17,7 +17,7 @@ import Confirm from 'app/components/confirm'; import {CreateAlertFromViewButton} from 'app/components/createAlertButton'; import SearchBar from 'app/components/events/searchBar'; import * as Layout from 'app/components/layouts/thirds'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; @@ -461,7 +461,7 @@ class Results extends React.Component { return ( - + { {this.setOpenFunction} - + ); diff --git a/static/app/views/issueList/container.tsx b/static/app/views/issueList/container.tsx index 3f476d227675f4..c3fb2d4e32cb7c 100644 --- a/static/app/views/issueList/container.tsx +++ b/static/app/views/issueList/container.tsx @@ -1,7 +1,7 @@ import {Component} from 'react'; import DocumentTitle from 'react-document-title'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import {Organization} from 'app/types'; import withOrganization from 'app/utils/withOrganization'; @@ -21,9 +21,7 @@ class IssueListContainer extends Component { return ( - - {children} - + {children} ); diff --git a/static/app/views/performance/compare/index.tsx b/static/app/views/performance/compare/index.tsx index 3040172703c382..0366139a5c1a53 100644 --- a/static/app/views/performance/compare/index.tsx +++ b/static/app/views/performance/compare/index.tsx @@ -4,9 +4,9 @@ import styled from '@emotion/styled'; import * as Sentry from '@sentry/react'; import NotFound from 'app/components/errors/notFound'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; import LoadingError from 'app/components/loadingError'; import LoadingIndicator from 'app/components/loadingIndicator'; +import NoProjectMessage from 'app/components/noProjectMessage'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {t} from 'app/locale'; import {PageContent} from 'app/styles/organization'; @@ -125,9 +125,9 @@ class TransactionComparisonPage extends React.PureComponent { > - + {this.renderComparison({baselineEventSlug, regressionEventSlug})} - + diff --git a/static/app/views/performance/content.tsx b/static/app/views/performance/content.tsx index 939a47ceef1234..79be7520c0dfc5 100644 --- a/static/app/views/performance/content.tsx +++ b/static/app/views/performance/content.tsx @@ -9,7 +9,7 @@ import Feature from 'app/components/acl/feature'; import Alert from 'app/components/alert'; import Button from 'app/components/button'; import GlobalSdkUpdateAlert from 'app/components/globalSdkUpdateAlert'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import PageHeading from 'app/components/pageHeading'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; @@ -211,7 +211,7 @@ class PerformanceContent extends Component { return ( - + {t('Performance')} {!showOnboarding && ( @@ -248,7 +248,7 @@ class PerformanceContent extends Component { handleSearch={this.handleSearch} /> )} - + ); } diff --git a/static/app/views/performance/traceDetails/index.tsx b/static/app/views/performance/traceDetails/index.tsx index cef4e9e30b578c..905ee98f979b3a 100644 --- a/static/app/views/performance/traceDetails/index.tsx +++ b/static/app/views/performance/traceDetails/index.tsx @@ -3,7 +3,7 @@ import {RouteComponentProps} from 'react-router'; import styled from '@emotion/styled'; import {Client} from 'app/api'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {ALL_ACCESS_PROJECTS} from 'app/constants/globalSelectionHeader'; @@ -142,9 +142,9 @@ class TraceSummary extends Component { return ( - + {this.renderContent()} - + ); diff --git a/static/app/views/performance/transactionDetails/index.tsx b/static/app/views/performance/transactionDetails/index.tsx index 6f5c038f985b2c..be268e3daa0761 100644 --- a/static/app/views/performance/transactionDetails/index.tsx +++ b/static/app/views/performance/transactionDetails/index.tsx @@ -2,7 +2,7 @@ import {Component} from 'react'; import {RouteComponentProps} from 'react-router'; import styled from '@emotion/styled'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {t} from 'app/locale'; import {PageContent} from 'app/styles/organization'; @@ -36,7 +36,7 @@ class EventDetails extends Component { projectSlug={projectSlug} > - + {({projects}) => { if (projects.length === 0) { @@ -58,7 +58,7 @@ class EventDetails extends Component { router={router} route={route} /> - + ); diff --git a/static/app/views/performance/transactionSummary/pageLayout.tsx b/static/app/views/performance/transactionSummary/pageLayout.tsx index 1defe01c84fc06..45460e805dffa4 100644 --- a/static/app/views/performance/transactionSummary/pageLayout.tsx +++ b/static/app/views/performance/transactionSummary/pageLayout.tsx @@ -7,7 +7,7 @@ import Feature from 'app/components/acl/feature'; import Alert from 'app/components/alert'; import GlobalSdkUpdateAlert from 'app/components/globalSdkUpdateAlert'; import * as Layout from 'app/components/layouts/thirds'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {IconFlag} from 'app/icons'; @@ -112,7 +112,7 @@ function PageLayout(props: Props) { showProjectSettingsLink > - + - + diff --git a/static/app/views/performance/trends/index.tsx b/static/app/views/performance/trends/index.tsx index 887777c5d793f1..3f08681fe2b976 100644 --- a/static/app/views/performance/trends/index.tsx +++ b/static/app/views/performance/trends/index.tsx @@ -3,7 +3,7 @@ import {RouteComponentProps} from 'react-router'; import styled from '@emotion/styled'; import {Client} from 'app/api'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {t} from 'app/locale'; import {PageContent} from 'app/styles/organization'; @@ -79,9 +79,9 @@ class TrendsSummary extends React.Component { return ( - + {this.renderContent()} - + ); diff --git a/static/app/views/performance/vitalDetail/index.tsx b/static/app/views/performance/vitalDetail/index.tsx index c90b3b5fa456bd..5c7c75b567cafb 100644 --- a/static/app/views/performance/vitalDetail/index.tsx +++ b/static/app/views/performance/vitalDetail/index.tsx @@ -5,7 +5,7 @@ import isEqual from 'lodash/isEqual'; import {loadOrganizationTags} from 'app/actionCreators/tags'; import {Client} from 'app/api'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import SentryDocumentTitle from 'app/components/sentryDocumentTitle'; import {t} from 'app/locale'; @@ -109,7 +109,7 @@ class VitalDetail extends Component { - + { router={router} vitalName={vitalName || WebVital.LCP} /> - + diff --git a/static/app/views/projectDetail/projectDetail.tsx b/static/app/views/projectDetail/projectDetail.tsx index e7a1228ab28a1b..895d6e7037ec5c 100644 --- a/static/app/views/projectDetail/projectDetail.tsx +++ b/static/app/views/projectDetail/projectDetail.tsx @@ -15,8 +15,8 @@ import GlobalEventProcessingAlert from 'app/components/globalEventProcessingAler import GlobalSdkUpdateAlert from 'app/components/globalSdkUpdateAlert'; import IdBadge from 'app/components/idBadge'; import * as Layout from 'app/components/layouts/thirds'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; import LoadingError from 'app/components/loadingError'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import MissingProjectMembership from 'app/components/projects/missingProjectMembership'; import TextOverflow from 'app/components/textOverflow'; @@ -240,7 +240,7 @@ class ProjectDetail extends AsyncView { skipLoadLastUsed onUpdateProjects={this.handleProjectChange} > - + @@ -370,7 +370,7 @@ class ProjectDetail extends AsyncView { - + ); } diff --git a/static/app/views/projectsDashboard/index.tsx b/static/app/views/projectsDashboard/index.tsx index 5da791aa0aba25..050218614cd323 100644 --- a/static/app/views/projectsDashboard/index.tsx +++ b/static/app/views/projectsDashboard/index.tsx @@ -67,11 +67,7 @@ function Dashboard({teams, params, organization, loadingTeams, error}: Props) { if (showEmptyMessage) { return ( - + ); } diff --git a/static/app/views/releases/detail/index.tsx b/static/app/views/releases/detail/index.tsx index 4cc4ff1c1cd014..528aabfc05d7f5 100644 --- a/static/app/views/releases/detail/index.tsx +++ b/static/app/views/releases/detail/index.tsx @@ -6,8 +6,8 @@ import moment from 'moment'; import Alert from 'app/components/alert'; import AsyncComponent from 'app/components/asyncComponent'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; import LoadingIndicator from 'app/components/loadingIndicator'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams'; import PickProjectToContinue from 'app/components/pickProjectToContinue'; @@ -196,7 +196,7 @@ class ReleasesDetail extends AsyncView { } return ( - + { {this.props.children} - + ); } } diff --git a/static/app/views/releases/list/index.tsx b/static/app/views/releases/list/index.tsx index 087844462d9882..5304f36c5d0d44 100644 --- a/static/app/views/releases/list/index.tsx +++ b/static/app/views/releases/list/index.tsx @@ -9,9 +9,9 @@ import Feature from 'app/components/acl/feature'; import Alert from 'app/components/alert'; import GuideAnchor from 'app/components/assistant/guideAnchor'; import EmptyStateWarning from 'app/components/emptyStateWarning'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; import ExternalLink from 'app/components/links/externalLink'; import LoadingIndicator from 'app/components/loadingIndicator'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import {getRelativeSummary} from 'app/components/organizations/timeRangeSelector/utils'; import PageHeading from 'app/components/pageHeading'; @@ -544,7 +544,7 @@ class ReleasesList extends AsyncView { )} > - + {t('Releases')} @@ -608,7 +608,7 @@ class ReleasesList extends AsyncView { {error ? super.renderError(new Error('Unable to load all required endpoints')) : this.renderInnerBody(activeDisplay, showReleaseAdoptionStages)} - + ); diff --git a/static/app/views/userFeedback/index.tsx b/static/app/views/userFeedback/index.tsx index 8dda7de410d3b9..e12381405f8ea0 100644 --- a/static/app/views/userFeedback/index.tsx +++ b/static/app/views/userFeedback/index.tsx @@ -7,8 +7,8 @@ import Button from 'app/components/button'; import ButtonBar from 'app/components/buttonBar'; import EventUserFeedback from 'app/components/events/userFeedback'; import CompactIssue from 'app/components/issues/compactIssue'; -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; import LoadingIndicator from 'app/components/loadingIndicator'; +import NoProjectMessage from 'app/components/noProjectMessage'; import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader'; import PageHeading from 'app/components/pageHeading'; import Pagination from 'app/components/pagination'; @@ -119,7 +119,7 @@ class OrganizationUserFeedback extends AsyncView { return ( - +
{t('User Feedback')} @@ -139,7 +139,7 @@ class OrganizationUserFeedback extends AsyncView { {this.renderStreamBody()}
-
+
); diff --git a/tests/js/spec/views/projectsDashboard/lightWeightNoProjectMessage.spec.jsx b/tests/js/spec/views/projectsDashboard/lightWeightNoProjectMessage.spec.jsx deleted file mode 100644 index c02c175373a639..00000000000000 --- a/tests/js/spec/views/projectsDashboard/lightWeightNoProjectMessage.spec.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import {Component} from 'react'; - -import {mountWithTheme} from 'sentry-test/enzyme'; - -import LightWeightNoProjectMessage from 'app/components/lightWeightNoProjectMessage'; -import ProjectsStore from 'app/stores/projectsStore'; - -describe('LightWeightNoProjectMessage', function () { - beforeEach(function () { - ProjectsStore.reset(); - }); - - it('renders', async function () { - const project1 = TestStubs.Project(); - const project2 = TestStubs.Project(); - const organization = TestStubs.Organization({slug: 'org-slug'}); - delete organization.projects; - ProjectsStore.loadInitialData([project1, project2]); - const wrapper = mountWithTheme( - - {null} - , - TestStubs.routerContext() - ); - expect(wrapper.prop('children')).toBe(null); - expect(wrapper.find('NoProjectMessage').exists()).toBe(true); - }); - - it('does not remount when the projects store loads', async function () { - const mount = jest.fn(); - const unmount = jest.fn(); - class MockComponent extends Component { - componentWillMount() { - mount(); - } - componentWillUnmount() { - unmount(); - } - render() { - return
children
; - } - } - - const project1 = TestStubs.Project(); - const project2 = TestStubs.Project(); - const organization = TestStubs.Organization({slug: 'org-slug'}); - delete organization.projects; - const wrapper = mountWithTheme( - - - , - TestStubs.routerContext() - ); - - // verify MockComponent is mounted once - expect(mount).toHaveBeenCalledTimes(1); - expect(wrapper.find('NoProjectMessage')).toHaveLength(1); - expect(wrapper.find('NoProjectMessage').prop('loadingProjects')).toEqual(true); - ProjectsStore.loadInitialData([project1, project2]); - // await for trigger from projects store to resolve - await tick(); - wrapper.update(); - - // verify MockComponent is not unmounted and is still mounted once - expect(unmount).toHaveBeenCalledTimes(0); - expect(mount).toHaveBeenCalledTimes(1); - expect(wrapper.find('NoProjectMessage')).toHaveLength(1); - expect(wrapper.find('NoProjectMessage').prop('loadingProjects')).toEqual(false); - }); -}); diff --git a/tests/js/spec/views/projectsDashboard/noProjectMessage.spec.jsx b/tests/js/spec/views/projectsDashboard/noProjectMessage.spec.jsx index 9ad459869a63a8..2273d2206c3cbe 100644 --- a/tests/js/spec/views/projectsDashboard/noProjectMessage.spec.jsx +++ b/tests/js/spec/views/projectsDashboard/noProjectMessage.spec.jsx @@ -1,11 +1,34 @@ +import {Component} from 'react'; + import {mountWithTheme} from 'sentry-test/enzyme'; import NoProjectMessage from 'app/components/noProjectMessage'; import ConfigStore from 'app/stores/configStore'; +import ProjectsStore from 'app/stores/projectsStore'; describe('NoProjectMessage', function () { + beforeEach(function () { + ProjectsStore.reset(); + }); + const org = TestStubs.Organization(); + + it('renders', async function () { + const project1 = TestStubs.Project(); + const project2 = TestStubs.Project(); + const organization = TestStubs.Organization({slug: 'org-slug'}); + delete organization.projects; + ProjectsStore.loadInitialData([project1, project2]); + const wrapper = mountWithTheme( + {null}, + TestStubs.routerContext() + ); + expect(wrapper.prop('children')).toBe(null); + expect(wrapper.find('NoProjectMessage').exists()).toBe(true); + }); + it('shows "Create Project" button when there are no projects', function () { + ProjectsStore.loadInitialData([]); const wrapper = mountWithTheme( , TestStubs.routerContext() @@ -16,6 +39,7 @@ describe('NoProjectMessage', function () { }); it('"Create Project" is disabled when no access to `project:write`', function () { + ProjectsStore.loadInitialData([]); const wrapper = mountWithTheme( , TestStubs.routerContext() @@ -34,22 +58,18 @@ describe('NoProjectMessage', function () { }); it('has a "Join a Team" button when no projects but org has projects', function () { + ProjectsStore.loadInitialData([TestStubs.Project({hasAccess: false})]); const wrapper = mountWithTheme( - , + , TestStubs.routerContext() ); expect(wrapper.find('Button[to="/settings/org-slug/teams/"]')).toHaveLength(1); }); it('has a disabled "Join a Team" button if no access to `team:read`', function () { + ProjectsStore.loadInitialData([TestStubs.Project({hasAccess: false})]); const wrapper = mountWithTheme( - , + , TestStubs.routerContext() ); expect(wrapper.find('Button[to="/settings/org-slug/teams/"]').prop('disabled')).toBe( @@ -57,47 +77,59 @@ describe('NoProjectMessage', function () { ); }); - it('handles projects from props', function () { - const lightWeightOrg = TestStubs.Organization(); - delete lightWeightOrg.projects; - + it('shows empty message to superusers that are not members', function () { + ProjectsStore.loadInitialData([ + TestStubs.Project({hasAccess: true, isMember: false}), + ]); + ConfigStore.config.user = {isSuperuser: true}; const wrapper = mountWithTheme( - , + + {null} + , TestStubs.routerContext() ); - expect( - wrapper.find('Button[to="/organizations/org-slug/projects/new/"]') - ).toHaveLength(1); + expect(wrapper.find('HelpMessage')).toHaveLength(1); }); - it('handles loading projects from props', function () { - const lightWeightOrg = TestStubs.Organization(); - delete lightWeightOrg.projects; - - const child =
child
; + it('does not remount when the projects store loads', async function () { + const mount = jest.fn(); + const unmount = jest.fn(); + class MockComponent extends Component { + componentWillMount() { + mount(); + } + componentWillUnmount() { + unmount(); + } + render() { + return
children
; + } + } + const project1 = TestStubs.Project(); + const project2 = TestStubs.Project(); + const organization = TestStubs.Organization({slug: 'org-slug'}); + delete organization.projects; const wrapper = mountWithTheme( - - {child} + + , TestStubs.routerContext() ); - // ensure loading projects causes children to render - expect(wrapper.find('div')).toHaveLength(1); - }); - it('shows empty message to superusers that are not members', function () { - ConfigStore.config.user = {isSuperuser: true}; - const wrapper = mountWithTheme( - - {null} - , - TestStubs.routerContext() - ); - expect(wrapper.find('HelpMessage')).toHaveLength(1); + // verify MockComponent is mounted once + expect(mount).toHaveBeenCalledTimes(1); + expect(wrapper.find('NoProjectMessage')).toHaveLength(1); + expect(wrapper.find('NoProjectMessage').prop('loadingProjects')).toEqual(true); + ProjectsStore.loadInitialData([project1, project2]); + // await for trigger from projects store to resolve + await tick(); + wrapper.update(); + + // verify MockComponent is not unmounted and is still mounted once + expect(unmount).toHaveBeenCalledTimes(0); + expect(mount).toHaveBeenCalledTimes(1); + expect(wrapper.find('NoProjectMessage')).toHaveLength(1); + expect(wrapper.find('NoProjectMessage').prop('loadingProjects')).toEqual(false); }); });