setupDocs.spec.tsx 14 KB

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