setupDocs.spec.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {ProjectKeysFixture} from 'sentry-fixture/projectKeys';
  3. import {initializeOrg} from 'sentry-test/initializeOrg';
  4. import {
  5. render,
  6. screen,
  7. userEvent,
  8. waitForElementToBeRemoved,
  9. } from 'sentry-test/reactTestingLibrary';
  10. import {OnboardingContextProvider} from 'sentry/components/onboarding/onboardingContext';
  11. import {ProductSolution} from 'sentry/components/onboarding/productSelection';
  12. import ProjectsStore from 'sentry/stores/projectsStore';
  13. import type {OnboardingRecentCreatedProject} from 'sentry/types/onboarding';
  14. import type {Organization} from 'sentry/types/organization';
  15. import type {Project} from 'sentry/types/project';
  16. import SetupDocs from 'sentry/views/onboarding/setupDocs';
  17. const PROJECT_KEY = ProjectKeysFixture()[0];
  18. function renderMockRequests({
  19. project,
  20. orgSlug,
  21. }: {
  22. orgSlug: Organization['slug'];
  23. project: Project;
  24. }) {
  25. MockApiClient.addMockResponse({
  26. url: `/projects/${orgSlug}/${project.slug}/`,
  27. body: project,
  28. });
  29. MockApiClient.addMockResponse({
  30. url: `/projects/${orgSlug}/${project.slug}/keys/`,
  31. body: [PROJECT_KEY],
  32. });
  33. MockApiClient.addMockResponse({
  34. url: `/projects/${orgSlug}/${project.slug}/issues/`,
  35. body: [],
  36. });
  37. MockApiClient.addMockResponse({
  38. url: `/organizations/${orgSlug}/sdks/`,
  39. body: {
  40. 'sentry.java.android.gradle-plugin': {
  41. canonical: 'maven:io.sentry:sentry',
  42. main_docs_url: 'https://docs.sentry.io/platforms/java',
  43. name: 'io.sentry:sentry',
  44. package_url: 'https://search.maven.org/artifact/io.sentry/sentry',
  45. repo_url: 'https://github.com/getsentry/sentry-java',
  46. version: '3.12.0',
  47. },
  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. installationMode: 'auto',
  333. },
  334. },
  335. },
  336. projects: [
  337. {
  338. ...initializeOrg().project,
  339. slug: 'javascript',
  340. platform: 'javascript',
  341. },
  342. ],
  343. organization: OrganizationFixture({
  344. features: ['session-replay', 'performance-view'],
  345. }),
  346. });
  347. const updateLoaderMock = MockApiClient.addMockResponse({
  348. url: `/projects/${organization.slug}/${project.slug}/keys/${PROJECT_KEY.id}/`,
  349. method: 'PUT',
  350. body: PROJECT_KEY,
  351. });
  352. ProjectsStore.init();
  353. ProjectsStore.loadInitialData([project]);
  354. renderMockRequests({
  355. project,
  356. orgSlug: organization.slug,
  357. });
  358. render(
  359. <OnboardingContextProvider>
  360. <SetupDocs
  361. active
  362. onComplete={() => {}}
  363. stepIndex={2}
  364. router={router}
  365. route={{}}
  366. location={router.location}
  367. genSkipOnboardingLink={() => ''}
  368. orgId={organization.slug}
  369. search=""
  370. recentCreatedProject={project as OnboardingRecentCreatedProject}
  371. />
  372. </OnboardingContextProvider>,
  373. {
  374. router,
  375. organization,
  376. }
  377. );
  378. expect(
  379. await screen.findByRole('radio', {name: 'Loader Script'})
  380. ).toBeInTheDocument();
  381. expect(updateLoaderMock).toHaveBeenCalledTimes(1);
  382. expect(updateLoaderMock).toHaveBeenCalledWith(
  383. expect.any(String), // The URL
  384. {
  385. data: {
  386. dynamicSdkLoaderOptions: {
  387. hasDebug: false,
  388. hasPerformance: true,
  389. hasReplay: true,
  390. },
  391. },
  392. error: expect.any(Function),
  393. method: 'PUT',
  394. success: expect.any(Function),
  395. }
  396. );
  397. expect(
  398. await screen.findByRole('radio', {name: 'Loader Script'})
  399. ).toBeInTheDocument();
  400. await userEvent.click(screen.getByRole('checkbox', {name: 'Session Replay'}));
  401. expect(updateLoaderMock).toHaveBeenCalledTimes(2);
  402. expect(updateLoaderMock).toHaveBeenLastCalledWith(
  403. expect.any(String), // The URL
  404. {
  405. data: {
  406. dynamicSdkLoaderOptions: {
  407. hasDebug: false,
  408. hasPerformance: true,
  409. hasReplay: false,
  410. },
  411. },
  412. error: expect.any(Function),
  413. method: 'PUT',
  414. success: expect.any(Function),
  415. }
  416. );
  417. });
  418. });
  419. describe('special platforms', () => {
  420. it('renders platform other', async function () {
  421. const {router, organization, project} = initializeOrg({
  422. projects: [
  423. {
  424. ...initializeOrg().project,
  425. slug: 'other',
  426. platform: 'other',
  427. },
  428. ],
  429. });
  430. ProjectsStore.init();
  431. ProjectsStore.loadInitialData([project]);
  432. renderMockRequests({project, orgSlug: organization.slug});
  433. render(
  434. <OnboardingContextProvider>
  435. <SetupDocs
  436. active
  437. onComplete={() => {}}
  438. stepIndex={2}
  439. router={router}
  440. route={{}}
  441. location={router.location}
  442. genSkipOnboardingLink={() => ''}
  443. orgId={organization.slug}
  444. search=""
  445. recentCreatedProject={project as OnboardingRecentCreatedProject}
  446. />
  447. </OnboardingContextProvider>,
  448. {
  449. router,
  450. organization,
  451. }
  452. );
  453. expect(
  454. await screen.findByRole('heading', {name: 'Configure Other SDK'})
  455. ).toBeInTheDocument();
  456. });
  457. });
  458. });