setupDocs.spec.tsx 14 KB

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