withDomainRequired.spec.tsx 8.5 KB

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