withDomainRequired.spec.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import {RouteComponentProps} from 'react-router';
  2. import {Location, LocationDescriptor, LocationDescriptorObject} from 'history';
  3. import {initializeOrg} from 'sentry-test/initializeOrg';
  4. import {render, screen} from 'sentry-test/reactTestingLibrary';
  5. import withDomainRequired, {normalizeUrl} from 'sentry/utils/withDomainRequired';
  6. describe('normalizeUrl', function () {
  7. let result;
  8. beforeEach(function () {
  9. window.__initialData = {
  10. customerDomain: {
  11. subdomain: 'albertos-apples',
  12. organizationUrl: 'https://albertos-apples.sentry.io',
  13. sentryUrl: 'https://sentry.io',
  14. },
  15. } as any;
  16. });
  17. it('replaces paths in strings', function () {
  18. const location = TestStubs.location();
  19. const cases = [
  20. // input, expected
  21. ['/settings/organization', '/settings/organization'],
  22. ['/settings/sentry/members/', '/settings/members/'],
  23. ['/settings/sentry/members/3/', '/settings/members/3/'],
  24. ['/settings/sentry/teams/peeps/', '/settings/teams/peeps/'],
  25. ['/settings/account/security/', '/settings/account/security/'],
  26. ['/settings/account/details/', '/settings/account/details/'],
  27. ['/organizations/new', '/organizations/new'],
  28. ['/organizations/new/', '/organizations/new/'],
  29. ['/join-request/acme', '/join-request/'],
  30. ['/join-request/acme/', '/join-request/'],
  31. ['/onboarding/acme/', '/onboarding/'],
  32. ['/onboarding/acme/project/', '/onboarding/project/'],
  33. ['/organizations/albertos-apples/issues/', '/issues/'],
  34. ['/organizations/albertos-apples/issues/?_q=all#hash', '/issues/?_q=all#hash'],
  35. ['/acme/project-slug/getting-started/', '/getting-started/project-slug/'],
  36. [
  37. '/acme/project-slug/getting-started/python',
  38. '/getting-started/project-slug/python',
  39. ],
  40. ['/settings/projects/python/filters/', '/settings/projects/python/filters/'],
  41. [
  42. '/settings/projects/python/filters/discarded/',
  43. '/settings/projects/python/filters/discarded/',
  44. ],
  45. ];
  46. for (const scenario of cases) {
  47. result = normalizeUrl(scenario[0], location);
  48. expect(result).toEqual(scenario[1]);
  49. }
  50. });
  51. it('replaces pathname in objects', function () {
  52. const location = TestStubs.location();
  53. result = normalizeUrl({pathname: '/settings/organization'}, location);
  54. // @ts-ignore
  55. expect(result.pathname).toEqual('/settings/organization');
  56. result = normalizeUrl({pathname: '/settings/sentry/members'}, location);
  57. // @ts-ignore
  58. expect(result.pathname).toEqual('/settings/members');
  59. result = normalizeUrl({pathname: '/organizations/albertos-apples/issues'}, location);
  60. // @ts-ignore
  61. expect(result.pathname).toEqual('/issues');
  62. result = normalizeUrl(
  63. {
  64. pathname: '/organizations/albertos-apples/issues',
  65. query: {q: 'all'},
  66. },
  67. location
  68. );
  69. // @ts-ignore
  70. expect(result.pathname).toEqual('/issues');
  71. });
  72. it('replaces pathname in function callback', function () {
  73. const location = TestStubs.location();
  74. function objectCallback(_loc: Location): LocationDescriptorObject {
  75. return {pathname: '/settings/organization'};
  76. }
  77. result = normalizeUrl(objectCallback, location);
  78. // @ts-ignore
  79. expect(result.pathname).toEqual('/settings/organization');
  80. function stringCallback(_loc: Location): LocationDescriptor {
  81. return '/organizations/a-long-slug/discover/';
  82. }
  83. result = normalizeUrl(stringCallback, location);
  84. expect(result).toEqual('/discover/');
  85. });
  86. it('errors on functions without location', function () {
  87. function objectCallback(_loc: Location): LocationDescriptorObject {
  88. return {pathname: '/settings/organization'};
  89. }
  90. expect(() => normalizeUrl(objectCallback)).toThrow();
  91. });
  92. });
  93. describe('withDomainRequired', function () {
  94. type Props = RouteComponentProps<{orgId: string}, {}>;
  95. const MyComponent = (props: Props) => {
  96. const {params} = props;
  97. return <div>Org slug: {params.orgId ?? 'no org slug'}</div>;
  98. };
  99. beforeEach(function () {
  100. Object.defineProperty(window, 'location', {
  101. writable: true,
  102. value: {
  103. replace: jest.fn(),
  104. pathname: '/organizations/albertos-apples/issues/',
  105. search: '?q=123',
  106. hash: '#hash',
  107. },
  108. });
  109. window.__initialData = {
  110. customerDomain: {
  111. subdomain: 'albertos-apples',
  112. organizationUrl: 'https://albertos-apples.sentry.io',
  113. sentryUrl: 'https://sentry.io',
  114. },
  115. links: {
  116. organizationUrl: null,
  117. regionUrl: null,
  118. sentryUrl: 'https://sentry.io',
  119. },
  120. } as any;
  121. });
  122. it('redirects to sentryUrl in non-customer domain world', function () {
  123. window.__initialData = {
  124. customerDomain: null,
  125. features: ['organizations:customer-domains'],
  126. links: {
  127. organizationUrl: null,
  128. regionUrl: null,
  129. sentryUrl: 'https://sentry.io',
  130. },
  131. } as any;
  132. const organization = TestStubs.Organization({
  133. slug: 'albertos-apples',
  134. features: [],
  135. });
  136. const params = {
  137. orgId: 'albertos-apples',
  138. };
  139. const {router, route, routerContext} = initializeOrg({
  140. ...initializeOrg(),
  141. organization,
  142. router: {
  143. params,
  144. },
  145. });
  146. const WrappedComponent = withDomainRequired(MyComponent);
  147. const {container} = render(
  148. <WrappedComponent
  149. router={router}
  150. location={router.location}
  151. params={params}
  152. routes={router.routes}
  153. routeParams={router.params}
  154. route={route}
  155. />,
  156. {context: routerContext}
  157. );
  158. expect(container).toBeEmptyDOMElement();
  159. expect(window.location.replace).toHaveBeenCalledTimes(1);
  160. expect(window.location.replace).toHaveBeenCalledWith(
  161. 'https://sentry.io/organizations/albertos-apples/issues/?q=123#hash'
  162. );
  163. });
  164. it('redirects to sentryUrl if customer-domains is omitted', function () {
  165. window.__initialData = {
  166. customerDomain: {
  167. subdomain: 'albertos-apples',
  168. organizationUrl: 'https://albertos-apples.sentry.io',
  169. sentryUrl: 'https://sentry.io',
  170. },
  171. features: [],
  172. links: {
  173. organizationUrl: null,
  174. regionUrl: null,
  175. sentryUrl: 'https://sentry.io',
  176. },
  177. } as any;
  178. const organization = TestStubs.Organization({
  179. slug: 'albertos-apples',
  180. features: [],
  181. });
  182. const params = {
  183. orgId: 'albertos-apples',
  184. };
  185. const {router, route, routerContext} = initializeOrg({
  186. ...initializeOrg(),
  187. organization,
  188. router: {
  189. params,
  190. },
  191. });
  192. const WrappedComponent = withDomainRequired(MyComponent);
  193. const {container} = render(
  194. <WrappedComponent
  195. router={router}
  196. location={router.location}
  197. params={params}
  198. routes={router.routes}
  199. routeParams={router.params}
  200. route={route}
  201. />,
  202. {context: routerContext}
  203. );
  204. expect(container).toBeEmptyDOMElement();
  205. expect(window.location.replace).toHaveBeenCalledTimes(1);
  206. expect(window.location.replace).toHaveBeenCalledWith(
  207. 'https://sentry.io/organizations/albertos-apples/issues/?q=123#hash'
  208. );
  209. });
  210. it('renders when window.__initialData.customerDomain and customer-domains feature is present', function () {
  211. window.__initialData = {
  212. customerDomain: {
  213. subdomain: 'albertos-apples',
  214. organizationUrl: 'https://albertos-apples.sentry.io',
  215. sentryUrl: 'https://sentry.io',
  216. },
  217. features: ['organizations:customer-domains'],
  218. links: {
  219. organizationUrl: 'https://albertos-apples.sentry.io',
  220. regionUrl: 'https://eu.sentry.io',
  221. sentryUrl: 'https://sentry.io',
  222. },
  223. } as any;
  224. const organization = TestStubs.Organization({
  225. slug: 'albertos-apples',
  226. features: [],
  227. });
  228. const params = {
  229. orgId: 'albertos-apples',
  230. };
  231. const {router, route, routerContext} = initializeOrg({
  232. ...initializeOrg(),
  233. organization,
  234. router: {
  235. params,
  236. },
  237. });
  238. const WrappedComponent = withDomainRequired(MyComponent);
  239. render(
  240. <WrappedComponent
  241. router={router}
  242. location={router.location}
  243. params={params}
  244. routes={router.routes}
  245. routeParams={router.params}
  246. route={route}
  247. />,
  248. {context: routerContext}
  249. );
  250. expect(screen.getByText('Org slug: albertos-apples')).toBeInTheDocument();
  251. expect(window.location.replace).toHaveBeenCalledTimes(0);
  252. });
  253. });