setupDocs.spec.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. import {Location} from 'history';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {render, screen} from 'sentry-test/reactTestingLibrary';
  4. import {OnboardingContextProvider} from 'sentry/components/onboarding/onboardingContext';
  5. import {PRODUCT} from 'sentry/components/onboarding/productSelection';
  6. import ProjectsStore from 'sentry/stores/projectsStore';
  7. import {Organization, Project} from 'sentry/types';
  8. import SetupDocs from 'sentry/views/onboarding/setupDocs';
  9. const PROJECT_KEY = TestStubs.ProjectKeys()[0];
  10. function renderMockRequests({
  11. project,
  12. orgSlug,
  13. location,
  14. }: {
  15. orgSlug: Organization['slug'];
  16. project: Project;
  17. location?: Location;
  18. }) {
  19. MockApiClient.addMockResponse({
  20. url: `/projects/${orgSlug}/${project.slug}/`,
  21. body: project,
  22. });
  23. if (project.slug === 'javascript') {
  24. MockApiClient.addMockResponse({
  25. url: `/projects/${orgSlug}/${project.slug}/keys/`,
  26. body: [PROJECT_KEY],
  27. });
  28. }
  29. MockApiClient.addMockResponse({
  30. url: `/projects/${orgSlug}/${project.slug}/issues/`,
  31. body: [],
  32. });
  33. if (project.slug === 'javascript-react') {
  34. const products = location?.query.product ?? [];
  35. if (
  36. products.includes(PRODUCT.PERFORMANCE_MONITORING) &&
  37. products.includes(PRODUCT.SESSION_REPLAY)
  38. ) {
  39. MockApiClient.addMockResponse({
  40. url: `/projects/${orgSlug}/${project.slug}/docs/javascript-react-with-error-monitoring-performance-and-replay/`,
  41. body: {html: 'javascript-react-with-error-monitoring-performance-and-replay'},
  42. });
  43. } else if (products.includes(PRODUCT.PERFORMANCE_MONITORING)) {
  44. MockApiClient.addMockResponse({
  45. url: `/projects/${orgSlug}/${project.slug}/docs/javascript-react-with-error-monitoring-and-performance/`,
  46. body: {html: 'javascript-react-with-error-monitoring-and-performance'},
  47. });
  48. } else if (products.includes(PRODUCT.SESSION_REPLAY)) {
  49. MockApiClient.addMockResponse({
  50. url: `/projects/${orgSlug}/${project.slug}/docs/javascript-react-with-error-monitoring-and-replay/`,
  51. body: {html: 'javascript-react-with-error-monitoring-and-replay'},
  52. });
  53. } else {
  54. MockApiClient.addMockResponse({
  55. url: `/projects/${orgSlug}/${project.slug}/docs/javascript-react-with-error-monitoring/`,
  56. body: {html: 'javascript-react-with-error-monitoring'},
  57. });
  58. }
  59. } else {
  60. MockApiClient.addMockResponse({
  61. url: `/projects/${orgSlug}/${project.slug}/docs/${project.platform}/`,
  62. body: {html: ''},
  63. });
  64. }
  65. }
  66. describe('Onboarding Setup Docs', function () {
  67. it('does not render Product Selection', async function () {
  68. const {router, route, routerContext, organization, project} = initializeOrg({
  69. projects: [
  70. {
  71. ...initializeOrg().project,
  72. slug: 'python',
  73. platform: 'python',
  74. },
  75. ],
  76. });
  77. ProjectsStore.init();
  78. ProjectsStore.loadInitialData([project]);
  79. renderMockRequests({project, orgSlug: organization.slug});
  80. render(
  81. <OnboardingContextProvider>
  82. <SetupDocs
  83. active
  84. onComplete={() => {}}
  85. stepIndex={2}
  86. router={router}
  87. route={route}
  88. location={router.location}
  89. genSkipOnboardingLink={() => ''}
  90. orgId={organization.slug}
  91. search=""
  92. recentCreatedProject={project}
  93. />
  94. </OnboardingContextProvider>,
  95. {
  96. context: routerContext,
  97. organization,
  98. }
  99. );
  100. expect(
  101. await screen.findByRole('heading', {name: 'Configure Python SDK'})
  102. ).toBeInTheDocument();
  103. expect(
  104. screen.queryByTestId(
  105. `product-${PRODUCT.ERROR_MONITORING}-${PRODUCT.PERFORMANCE_MONITORING}-${PRODUCT.SESSION_REPLAY}`
  106. )
  107. ).not.toBeInTheDocument();
  108. });
  109. describe('renders Product Selection', function () {
  110. it('all products checked', async function () {
  111. const {router, route, routerContext, organization, project} = initializeOrg({
  112. router: {
  113. location: {
  114. query: {product: [PRODUCT.PERFORMANCE_MONITORING, PRODUCT.SESSION_REPLAY]},
  115. },
  116. },
  117. projects: [
  118. {
  119. ...initializeOrg().project,
  120. slug: 'javascript-react',
  121. platform: 'javascript-react',
  122. },
  123. ],
  124. });
  125. ProjectsStore.init();
  126. ProjectsStore.loadInitialData([project]);
  127. renderMockRequests({
  128. project,
  129. orgSlug: organization.slug,
  130. location: router.location,
  131. });
  132. render(
  133. <OnboardingContextProvider>
  134. <SetupDocs
  135. active
  136. onComplete={() => {}}
  137. stepIndex={2}
  138. router={router}
  139. route={route}
  140. location={router.location}
  141. genSkipOnboardingLink={() => ''}
  142. orgId={organization.slug}
  143. search=""
  144. recentCreatedProject={project}
  145. />
  146. </OnboardingContextProvider>,
  147. {
  148. context: routerContext,
  149. organization,
  150. }
  151. );
  152. expect(
  153. await screen.findByRole('heading', {name: 'Configure React SDK'})
  154. ).toBeInTheDocument();
  155. // Render variation of docs - default (all checked)
  156. expect(
  157. await screen.findByText(
  158. 'javascript-react-with-error-monitoring-performance-and-replay'
  159. )
  160. ).toBeInTheDocument();
  161. });
  162. it('only performance checked', async function () {
  163. const {router, route, routerContext, organization, project} = initializeOrg({
  164. router: {
  165. location: {
  166. query: {product: [PRODUCT.PERFORMANCE_MONITORING]},
  167. },
  168. },
  169. projects: [
  170. {
  171. ...initializeOrg().project,
  172. slug: 'javascript-react',
  173. platform: 'javascript-react',
  174. },
  175. ],
  176. });
  177. ProjectsStore.init();
  178. ProjectsStore.loadInitialData([project]);
  179. renderMockRequests({
  180. project,
  181. orgSlug: organization.slug,
  182. location: router.location,
  183. });
  184. render(
  185. <OnboardingContextProvider>
  186. <SetupDocs
  187. active
  188. onComplete={() => {}}
  189. stepIndex={2}
  190. router={router}
  191. route={route}
  192. location={router.location}
  193. genSkipOnboardingLink={() => ''}
  194. orgId={organization.slug}
  195. search=""
  196. recentCreatedProject={project}
  197. />
  198. </OnboardingContextProvider>,
  199. {
  200. context: routerContext,
  201. organization,
  202. }
  203. );
  204. // Render variation of docs - error monitoring and performance doc
  205. expect(
  206. await screen.findByText('javascript-react-with-error-monitoring-and-performance')
  207. ).toBeInTheDocument();
  208. });
  209. it('only session replay checked', async function () {
  210. const {router, route, routerContext, organization, project} = initializeOrg({
  211. router: {
  212. location: {
  213. query: {product: [PRODUCT.SESSION_REPLAY]},
  214. },
  215. },
  216. projects: [
  217. {
  218. ...initializeOrg().project,
  219. slug: 'javascript-react',
  220. platform: 'javascript-react',
  221. },
  222. ],
  223. });
  224. ProjectsStore.init();
  225. ProjectsStore.loadInitialData([project]);
  226. renderMockRequests({
  227. project,
  228. orgSlug: organization.slug,
  229. location: router.location,
  230. });
  231. render(
  232. <OnboardingContextProvider>
  233. <SetupDocs
  234. active
  235. onComplete={() => {}}
  236. stepIndex={2}
  237. router={router}
  238. route={route}
  239. location={router.location}
  240. genSkipOnboardingLink={() => ''}
  241. orgId={organization.slug}
  242. search=""
  243. recentCreatedProject={project}
  244. />
  245. </OnboardingContextProvider>,
  246. {
  247. context: routerContext,
  248. organization,
  249. }
  250. );
  251. // Render variation of docs - error monitoring and replay doc
  252. expect(
  253. await screen.findByText('javascript-react-with-error-monitoring-and-replay')
  254. ).toBeInTheDocument();
  255. });
  256. it('only error monitoring checked', async function () {
  257. const {router, route, routerContext, organization, project} = initializeOrg({
  258. router: {
  259. location: {
  260. query: {product: []},
  261. },
  262. },
  263. projects: [
  264. {
  265. ...initializeOrg().project,
  266. slug: 'javascript-react',
  267. platform: 'javascript-react',
  268. },
  269. ],
  270. });
  271. ProjectsStore.init();
  272. ProjectsStore.loadInitialData([project]);
  273. renderMockRequests({
  274. project,
  275. orgSlug: organization.slug,
  276. location: router.location,
  277. });
  278. render(
  279. <OnboardingContextProvider>
  280. <SetupDocs
  281. active
  282. onComplete={() => {}}
  283. stepIndex={2}
  284. router={router}
  285. route={route}
  286. location={router.location}
  287. genSkipOnboardingLink={() => ''}
  288. orgId={organization.slug}
  289. search=""
  290. recentCreatedProject={project}
  291. />
  292. </OnboardingContextProvider>,
  293. {
  294. context: routerContext,
  295. organization,
  296. }
  297. );
  298. // Render variation of docs - error monitoring doc
  299. expect(
  300. await screen.findByText('javascript-react-with-error-monitoring')
  301. ).toBeInTheDocument();
  302. });
  303. });
  304. describe('JS Loader Script', function () {
  305. it('renders Loader Script setup', async function () {
  306. const {router, route, routerContext, organization, project} = initializeOrg({
  307. router: {
  308. location: {
  309. query: {product: [PRODUCT.PERFORMANCE_MONITORING, PRODUCT.SESSION_REPLAY]},
  310. },
  311. },
  312. projects: [
  313. {
  314. ...initializeOrg().project,
  315. slug: 'javascript',
  316. platform: 'javascript',
  317. },
  318. ],
  319. });
  320. const updateLoaderMock = MockApiClient.addMockResponse({
  321. url: `/projects/${organization.slug}/${project.slug}/keys/${PROJECT_KEY.id}/`,
  322. method: 'PUT',
  323. body: PROJECT_KEY,
  324. });
  325. ProjectsStore.init();
  326. ProjectsStore.loadInitialData([project]);
  327. renderMockRequests({
  328. project,
  329. orgSlug: organization.slug,
  330. location: router.location,
  331. });
  332. const {rerender} = render(
  333. <OnboardingContextProvider>
  334. <SetupDocs
  335. active
  336. onComplete={() => {}}
  337. stepIndex={2}
  338. router={router}
  339. route={route}
  340. location={router.location}
  341. genSkipOnboardingLink={() => ''}
  342. orgId={organization.slug}
  343. search=""
  344. recentCreatedProject={project}
  345. />
  346. </OnboardingContextProvider>,
  347. {
  348. context: routerContext,
  349. organization,
  350. }
  351. );
  352. expect(
  353. await screen.findByRole('heading', {name: 'Configure JavaScript SDK'})
  354. ).toBeInTheDocument();
  355. expect(updateLoaderMock).toHaveBeenCalledTimes(1);
  356. expect(updateLoaderMock).toHaveBeenCalledWith(
  357. expect.any(String), // The URL
  358. {
  359. data: {
  360. dynamicSdkLoaderOptions: {
  361. hasDebug: false,
  362. hasPerformance: true,
  363. hasReplay: true,
  364. },
  365. },
  366. error: expect.any(Function),
  367. method: 'PUT',
  368. success: expect.any(Function),
  369. }
  370. );
  371. // update query in URL
  372. router.location.query = {
  373. product: [PRODUCT.SESSION_REPLAY],
  374. };
  375. rerender(
  376. <OnboardingContextProvider>
  377. <SetupDocs
  378. active
  379. onComplete={() => {}}
  380. stepIndex={2}
  381. router={router}
  382. route={route}
  383. location={router.location}
  384. genSkipOnboardingLink={() => ''}
  385. orgId={organization.slug}
  386. search=""
  387. recentCreatedProject={project}
  388. />
  389. </OnboardingContextProvider>
  390. );
  391. expect(updateLoaderMock).toHaveBeenCalledTimes(2);
  392. expect(updateLoaderMock).toHaveBeenLastCalledWith(
  393. expect.any(String), // The URL
  394. {
  395. data: {
  396. dynamicSdkLoaderOptions: {
  397. hasDebug: false,
  398. hasPerformance: false,
  399. hasReplay: true,
  400. },
  401. },
  402. error: expect.any(Function),
  403. method: 'PUT',
  404. success: expect.any(Function),
  405. }
  406. );
  407. });
  408. });
  409. });