withDomainRedirect.spec.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import {RouteComponentProps} from 'react-router';
  2. import * as Sentry from '@sentry/react';
  3. import {initializeOrg} from 'sentry-test/initializeOrg';
  4. import {render, screen} from 'sentry-test/reactTestingLibrary';
  5. import withDomainRedirect from 'sentry/utils/withDomainRedirect';
  6. import {OrganizationContext} from 'sentry/views/organizationContext';
  7. jest.unmock('sentry/utils/recreateRoute');
  8. const originalLocation = window.location;
  9. // /settings/:orgId/:projectId/(searches/:searchId/)alerts/
  10. const projectRoutes = [
  11. {path: '/', childRoutes: []},
  12. {childRoutes: []},
  13. {path: '/settings/', name: 'Settings', indexRoute: {}, childRoutes: []},
  14. {name: 'Organizations', path: ':orgId/', childRoutes: []},
  15. {name: 'Projects', path: ':projectId/(searches/:searchId/)', childRoutes: []},
  16. {name: 'Alerts', path: 'alerts/'},
  17. ];
  18. describe('withDomainRedirect', function () {
  19. type Props = RouteComponentProps<{orgId: string}, {}>;
  20. const MyComponent = (props: Props) => {
  21. const {params} = props;
  22. return <div>Org slug: {params.orgId ?? 'no org slug'}</div>;
  23. };
  24. let spyWithScope;
  25. beforeEach(function () {
  26. spyWithScope = jest.spyOn(Sentry, 'withScope').mockImplementation(() => {});
  27. Object.defineProperty(window, 'location', {
  28. writable: true,
  29. value: {
  30. replace: jest.fn(),
  31. pathname: '/organizations/albertos-apples/issues/',
  32. search: '?q=123',
  33. hash: '#hash',
  34. },
  35. });
  36. window.__initialData = {
  37. customerDomain: {
  38. subdomain: 'albertos-apples',
  39. organizationUrl: 'https://albertos-apples.sentry.io',
  40. sentryUrl: 'https://sentry.io',
  41. },
  42. links: {
  43. organizationUrl: null,
  44. regionUrl: null,
  45. sentryUrl: 'https://sentry.io',
  46. },
  47. } as any;
  48. });
  49. afterEach(function () {
  50. jest.resetAllMocks();
  51. window.location = originalLocation;
  52. });
  53. it('renders MyComponent in non-customer domain world', function () {
  54. window.__initialData = {
  55. customerDomain: null,
  56. links: {
  57. organizationUrl: null,
  58. regionUrl: null,
  59. sentryUrl: 'https://sentry.io',
  60. },
  61. } as any;
  62. const params = {
  63. orgId: 'albertos-apples',
  64. };
  65. const {router, route, routerContext} = initializeOrg({
  66. ...initializeOrg(),
  67. router: {
  68. params,
  69. },
  70. });
  71. const WrappedComponent = withDomainRedirect(MyComponent);
  72. render(
  73. <WrappedComponent
  74. router={router}
  75. location={router.location}
  76. params={params}
  77. routes={router.routes}
  78. routeParams={router.params}
  79. route={route}
  80. />,
  81. {context: routerContext}
  82. );
  83. expect(screen.getByText('Org slug: albertos-apples')).toBeInTheDocument();
  84. });
  85. it('redirects to sentryUrl on org slug mistmatch', function () {
  86. const organization = TestStubs.Organization({
  87. slug: 'bobs-bagels',
  88. features: ['customer-domains'],
  89. });
  90. const params = {
  91. orgId: 'albertos-apples',
  92. };
  93. const {router, route, routerContext} = initializeOrg({
  94. ...initializeOrg(),
  95. organization,
  96. router: {
  97. params,
  98. },
  99. });
  100. const WrappedComponent = withDomainRedirect(MyComponent);
  101. const {container} = render(
  102. <OrganizationContext.Provider value={organization}>
  103. <WrappedComponent
  104. router={router}
  105. location={router.location}
  106. params={params}
  107. routes={router.routes}
  108. routeParams={router.params}
  109. route={route}
  110. />
  111. </OrganizationContext.Provider>,
  112. {context: routerContext}
  113. );
  114. expect(container).toBeEmptyDOMElement();
  115. expect(window.location.replace).toHaveBeenCalledTimes(1);
  116. expect(window.location.replace).toHaveBeenCalledWith(
  117. 'https://sentry.io/organizations/albertos-apples/issues/?q=123#hash'
  118. );
  119. expect(spyWithScope).not.toHaveBeenCalled();
  120. });
  121. it('redirects to sentryUrl on missing customer domain feature', function () {
  122. const organization = TestStubs.Organization({slug: 'albertos-apples', features: []});
  123. const params = {
  124. orgId: organization.slug,
  125. };
  126. const {router, route, routerContext} = initializeOrg({
  127. ...initializeOrg(),
  128. organization,
  129. router: {
  130. params,
  131. },
  132. });
  133. const WrappedComponent = withDomainRedirect(MyComponent);
  134. const {container} = render(
  135. <OrganizationContext.Provider value={organization}>
  136. <WrappedComponent
  137. router={router}
  138. location={router.location}
  139. params={params}
  140. routes={router.routes}
  141. routeParams={router.params}
  142. route={route}
  143. />
  144. </OrganizationContext.Provider>,
  145. {context: routerContext}
  146. );
  147. expect(container).toBeEmptyDOMElement();
  148. expect(window.location.replace).toHaveBeenCalledTimes(1);
  149. expect(window.location.replace).toHaveBeenCalledWith(
  150. 'https://sentry.io/organizations/albertos-apples/issues/?q=123#hash'
  151. );
  152. expect(spyWithScope).not.toHaveBeenCalled();
  153. });
  154. it('redirect when :orgId is present in the routes', function () {
  155. const organization = TestStubs.Organization({
  156. slug: 'albertos-apples',
  157. features: ['customer-domains'],
  158. });
  159. const params = {
  160. orgId: organization.slug,
  161. projectId: 'react',
  162. };
  163. const {router, route, routerContext} = initializeOrg({
  164. ...initializeOrg(),
  165. organization,
  166. router: {
  167. params,
  168. routes: projectRoutes,
  169. },
  170. });
  171. const WrappedComponent = withDomainRedirect(MyComponent);
  172. const {container} = render(
  173. <OrganizationContext.Provider value={organization}>
  174. <WrappedComponent
  175. router={router}
  176. location={router.location}
  177. params={params}
  178. routes={router.routes}
  179. routeParams={router.params}
  180. route={route}
  181. />
  182. </OrganizationContext.Provider>,
  183. {context: routerContext}
  184. );
  185. expect(container).toBeEmptyDOMElement();
  186. expect(router.replace).toHaveBeenCalledTimes(1);
  187. expect(router.replace).toHaveBeenCalledWith('/settings/react/alerts/?q=123#hash');
  188. expect(spyWithScope).toHaveBeenCalledTimes(1);
  189. });
  190. it('does not redirect when :orgId is not present in the routes', function () {
  191. const organization = TestStubs.Organization({
  192. slug: 'albertos-apples',
  193. features: ['customer-domains'],
  194. });
  195. const params = {};
  196. const {router, route, routerContext} = initializeOrg({
  197. ...initializeOrg(),
  198. organization,
  199. router: {
  200. params,
  201. // /settings/account/notifications/reports/
  202. routes: [
  203. {path: '/', childRoutes: []},
  204. {childRoutes: []},
  205. {path: '/settings/', name: 'Settings', indexRoute: {}, childRoutes: []},
  206. {name: 'Account', path: 'account/', childRoutes: []},
  207. {name: 'Notifications', path: 'notifications/', childRoutes: []},
  208. {name: 'Reports', path: 'reports/'},
  209. ],
  210. },
  211. });
  212. const WrappedComponent = withDomainRedirect(MyComponent);
  213. render(
  214. <OrganizationContext.Provider value={organization}>
  215. <WrappedComponent
  216. router={router}
  217. location={router.location}
  218. params={params}
  219. routes={router.routes}
  220. routeParams={router.params}
  221. route={route}
  222. />
  223. </OrganizationContext.Provider>,
  224. {context: routerContext}
  225. );
  226. expect(screen.getByText('Org slug: no org slug')).toBeInTheDocument();
  227. expect(router.replace).not.toHaveBeenCalled();
  228. expect(spyWithScope).not.toHaveBeenCalled();
  229. });
  230. });