subscriptionHeader.spec.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. import moment from 'moment-timezone';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {BillingConfigFixture} from 'getsentry-test/fixtures/billingConfig';
  4. import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription';
  5. import {render, screen, within} from 'sentry-test/reactTestingLibrary';
  6. import {PendingChangesFixture} from 'getsentry/__fixtures__/pendingChanges';
  7. import {PlanFixture} from 'getsentry/__fixtures__/plan';
  8. import SubscriptionStore from 'getsentry/stores/subscriptionStore';
  9. import {PlanTier} from 'getsentry/types';
  10. import SubscriptionHeader from 'getsentry/views/subscriptionPage/subscriptionHeader';
  11. describe('SubscriptionHeader', function () {
  12. beforeEach(() => {
  13. MockApiClient.clearMockResponses();
  14. MockApiClient.addMockResponse({
  15. url: `/customers/org-slug/billing-config/`,
  16. method: 'GET',
  17. body: BillingConfigFixture(PlanTier.AM1),
  18. });
  19. MockApiClient.addMockResponse({
  20. url: `/subscriptions/org-slug/`,
  21. method: 'GET',
  22. body: [],
  23. });
  24. MockApiClient.addMockResponse({
  25. url: `/organizations/org-slug/promotions/trigger-check/`,
  26. method: 'POST',
  27. });
  28. MockApiClient.addMockResponse({
  29. url: `/customers/org-slug/plan-migrations/`,
  30. query: {scheduled: 1, applied: 0},
  31. method: 'GET',
  32. body: [],
  33. });
  34. MockApiClient.addMockResponse({
  35. url: `/organizations/org-slug/prompts-activity/`,
  36. body: {},
  37. });
  38. });
  39. it('does not render editable sections for YY partnership', async function () {
  40. const organization = OrganizationFixture({
  41. features: ['usage-log'],
  42. access: ['org:billing'],
  43. });
  44. const subscription = SubscriptionFixture({
  45. plan: 'am2_business',
  46. planTier: 'am2',
  47. partner: {
  48. externalId: 'x123x',
  49. name: 'YY Org',
  50. partnership: {
  51. id: 'YY',
  52. displayName: 'YY',
  53. supportNote: 'foo',
  54. },
  55. isActive: true,
  56. },
  57. organization,
  58. canSelfServe: false,
  59. });
  60. SubscriptionStore.set(organization.slug, subscription);
  61. render(
  62. <SubscriptionHeader organization={organization} subscription={subscription} />
  63. );
  64. expect(await screen.findByTestId('partnership-note')).toBeInTheDocument();
  65. expect(
  66. screen.queryByRole('button', {name: 'Manage subscription'})
  67. ).not.toBeInTheDocument();
  68. expect(screen.queryByText('Billing Details')).not.toBeInTheDocument();
  69. });
  70. it('renders partner plan ending banner for partner orgs with flag and ending contract', function () {
  71. const organization = OrganizationFixture({
  72. features: ['usage-log', 'partner-billing-migration'],
  73. access: ['org:billing'],
  74. });
  75. const now = moment();
  76. const subscription = SubscriptionFixture({
  77. plan: 'am2_sponsored_team_auf',
  78. planDetails: PlanFixture({}),
  79. planTier: 'am2',
  80. partner: {
  81. externalId: 'x123x',
  82. name: 'FOO Org',
  83. partnership: {
  84. id: 'FOO',
  85. displayName: 'FOO',
  86. supportNote: '',
  87. },
  88. isActive: true,
  89. },
  90. organization,
  91. canSelfServe: true,
  92. contractPeriodEnd: now.add(30, 'days').toString(),
  93. });
  94. SubscriptionStore.set(organization.slug, subscription);
  95. render(
  96. <SubscriptionHeader organization={organization} subscription={subscription} />
  97. );
  98. expect(screen.getByTestId('partner-plan-ending-banner')).toBeInTheDocument();
  99. });
  100. it('does not render partner plan ending banner for partner orgs with flag and ending contract greater than 30 days', function () {
  101. const organization = OrganizationFixture({
  102. features: ['usage-log', 'partner-billing-migration'],
  103. access: ['org:billing'],
  104. });
  105. const now = moment();
  106. const subscription = SubscriptionFixture({
  107. plan: 'am2_sponsored_team_auf',
  108. planDetails: PlanFixture({}),
  109. planTier: 'am2',
  110. partner: {
  111. externalId: 'x123x',
  112. name: 'FOO Org',
  113. partnership: {
  114. id: 'FOO',
  115. displayName: 'FOO',
  116. supportNote: '',
  117. },
  118. isActive: true,
  119. },
  120. organization,
  121. canSelfServe: true,
  122. contractPeriodEnd: now.add(50, 'days').toString(),
  123. });
  124. SubscriptionStore.set(organization.slug, subscription);
  125. render(
  126. <SubscriptionHeader organization={organization} subscription={subscription} />
  127. );
  128. expect(screen.queryByTestId('partner-plan-ending-banner')).not.toBeInTheDocument();
  129. });
  130. it('does not render partner plan ending banner for orgs with pending upgrade', function () {
  131. const organization = OrganizationFixture({
  132. features: ['usage-log', 'partner-billing-migration'],
  133. access: ['org:billing'],
  134. });
  135. const now = moment();
  136. const subscription = SubscriptionFixture({
  137. plan: 'am2_sponsored_team_auf',
  138. planDetails: PlanFixture({}),
  139. planTier: 'am2',
  140. partner: {
  141. externalId: 'x123x',
  142. name: 'FOO Org',
  143. partnership: {
  144. id: 'FOO',
  145. displayName: 'FOO',
  146. supportNote: '',
  147. },
  148. isActive: true,
  149. },
  150. pendingChanges: PendingChangesFixture({
  151. plan: 'am3_business',
  152. planDetails: PlanFixture({
  153. name: 'Business',
  154. price: 100,
  155. }),
  156. }),
  157. organization,
  158. canSelfServe: true,
  159. contractPeriodEnd: now.add(30, 'days').toString(),
  160. });
  161. SubscriptionStore.set(organization.slug, subscription);
  162. render(
  163. <SubscriptionHeader organization={organization} subscription={subscription} />
  164. );
  165. expect(screen.queryByTestId('partner-plan-ending-banner')).not.toBeInTheDocument();
  166. });
  167. it('renders partner plan ending banner for orgs with pending downgrade', function () {
  168. const organization = OrganizationFixture({
  169. features: ['usage-log', 'partner-billing-migration'],
  170. access: ['org:billing'],
  171. });
  172. const now = moment();
  173. const subscription = SubscriptionFixture({
  174. plan: 'am2_sponsored_team_auf',
  175. planDetails: PlanFixture({}),
  176. planTier: 'am2',
  177. partner: {
  178. externalId: 'x123x',
  179. name: 'FOO Org',
  180. partnership: {
  181. id: 'FOO',
  182. displayName: 'FOO',
  183. supportNote: '',
  184. },
  185. isActive: true,
  186. },
  187. pendingChanges: PendingChangesFixture({
  188. plan: 'am3_f',
  189. planDetails: PlanFixture({
  190. name: 'Developer',
  191. price: 0,
  192. }),
  193. }),
  194. organization,
  195. canSelfServe: true,
  196. contractPeriodEnd: now.add(30, 'days').toString(),
  197. });
  198. SubscriptionStore.set(organization.slug, subscription);
  199. render(
  200. <SubscriptionHeader organization={organization} subscription={subscription} />
  201. );
  202. expect(screen.getByTestId('partner-plan-ending-banner')).toBeInTheDocument();
  203. });
  204. it('renders usage log tab for owners and billing users', function () {
  205. const organization = OrganizationFixture({
  206. access: ['org:billing'],
  207. });
  208. const sub = SubscriptionFixture({organization});
  209. render(<SubscriptionHeader organization={organization} subscription={sub} />);
  210. expect(screen.getByText(/Usage Log/i)).toBeInTheDocument();
  211. });
  212. it('renders usage log tab for managers', function () {
  213. const organization = OrganizationFixture({
  214. access: ['org:write'],
  215. });
  216. const sub = SubscriptionFixture({organization});
  217. render(<SubscriptionHeader organization={organization} subscription={sub} />);
  218. expect(screen.getByText(/Usage Log/i)).toBeInTheDocument();
  219. });
  220. it('renders usage tab for admin and member users', function () {
  221. const organization = OrganizationFixture({access: ['org:read']});
  222. const sub = SubscriptionFixture({organization});
  223. render(<SubscriptionHeader organization={organization} subscription={sub} />);
  224. expect(screen.getByText(/Usage Log/i)).toBeInTheDocument();
  225. });
  226. it('renders notifications tab for owners and billing users with flag', function () {
  227. const organization = OrganizationFixture({
  228. access: ['org:billing'],
  229. });
  230. organization.features.push('spend-visibility-notifications');
  231. const sub = SubscriptionFixture({organization});
  232. render(<SubscriptionHeader organization={organization} subscription={sub} />);
  233. expect(screen.getByText(/Notifications/i)).toBeInTheDocument();
  234. });
  235. it('does not render notifications tab for owners and billing users without flag', function () {
  236. const organization = OrganizationFixture({
  237. access: ['org:billing'],
  238. });
  239. const sub = SubscriptionFixture({organization});
  240. render(<SubscriptionHeader organization={organization} subscription={sub} />);
  241. expect(screen.queryByText(/Notifications/i)).not.toBeInTheDocument();
  242. });
  243. it('does not render Billing Details tab for self serve partner', function () {
  244. const organization = OrganizationFixture({
  245. access: ['org:billing'],
  246. });
  247. const sub = SubscriptionFixture({
  248. organization,
  249. isSelfServePartner: true,
  250. });
  251. render(<SubscriptionHeader organization={organization} subscription={sub} />);
  252. expect(screen.queryByText(/Billing Details/i)).not.toBeInTheDocument();
  253. });
  254. it('renders managed note for non-self-serve subscriptions', function () {
  255. const organization = OrganizationFixture({
  256. access: ['org:billing'],
  257. });
  258. const subscription = SubscriptionFixture({
  259. organization,
  260. canSelfServe: false,
  261. });
  262. render(
  263. <SubscriptionHeader organization={organization} subscription={subscription} />
  264. );
  265. const managedNote = screen.getByTestId('managed-note');
  266. expect(managedNote).toBeInTheDocument();
  267. expect(within(managedNote).getByRole('link')).toHaveAttribute(
  268. 'href',
  269. 'mailto:support@sentry.io'
  270. );
  271. expect(managedNote).toHaveTextContent(
  272. 'Contact us at support@sentry.io to make changes to your subscription.'
  273. );
  274. });
  275. it('does not render managed note for self-serve subscriptions', function () {
  276. const organization = OrganizationFixture({
  277. access: ['org:billing'],
  278. });
  279. const subscription = SubscriptionFixture({
  280. organization,
  281. canSelfServe: true,
  282. });
  283. render(
  284. <SubscriptionHeader organization={organization} subscription={subscription} />
  285. );
  286. expect(screen.queryByTestId('managed-note')).not.toBeInTheDocument();
  287. });
  288. });