onboarding.spec.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. import {Project as ProjectFixture} from 'sentry-fixture/project';
  2. import {ProjectKeys} from 'sentry-fixture/projectKeys';
  3. import {initializeOrg} from 'sentry-test/initializeOrg';
  4. import {
  5. render,
  6. renderGlobalModal,
  7. screen,
  8. userEvent,
  9. } from 'sentry-test/reactTestingLibrary';
  10. import {OnboardingContextProvider} from 'sentry/components/onboarding/onboardingContext';
  11. import * as useRecentCreatedProjectHook from 'sentry/components/onboarding/useRecentCreatedProject';
  12. import type {PlatformKey} from 'sentry/types';
  13. import {OnboardingProjectStatus, Project} from 'sentry/types';
  14. import Onboarding from 'sentry/views/onboarding/onboarding';
  15. describe('Onboarding', function () {
  16. afterEach(function () {
  17. MockApiClient.clearMockResponses();
  18. });
  19. it('renders the welcome page', function () {
  20. const routeParams = {
  21. step: 'welcome',
  22. };
  23. const {routerProps, routerContext, organization} = initializeOrg({
  24. router: {
  25. params: routeParams,
  26. },
  27. });
  28. render(
  29. <OnboardingContextProvider>
  30. <Onboarding {...routerProps} />
  31. </OnboardingContextProvider>,
  32. {
  33. context: routerContext,
  34. organization,
  35. }
  36. );
  37. expect(screen.getByLabelText('Start')).toBeInTheDocument();
  38. expect(screen.getByLabelText('Invite Team')).toBeInTheDocument();
  39. });
  40. it('renders the select platform step', async function () {
  41. const routeParams = {
  42. step: 'select-platform',
  43. };
  44. const {routerProps, routerContext, organization} = initializeOrg({
  45. router: {
  46. params: routeParams,
  47. },
  48. });
  49. render(
  50. <OnboardingContextProvider>
  51. <Onboarding {...routerProps} />
  52. </OnboardingContextProvider>,
  53. {
  54. context: routerContext,
  55. organization,
  56. }
  57. );
  58. expect(
  59. await screen.findByText('Select the platform you want to monitor')
  60. ).toBeInTheDocument();
  61. });
  62. it('renders the setup docs step', async function () {
  63. const nextJsProject: Project = ProjectFixture({
  64. platform: 'javascript-nextjs',
  65. id: '2',
  66. slug: 'javascript-nextjs-slug',
  67. });
  68. const routeParams = {
  69. step: 'setup-docs',
  70. };
  71. const {routerProps, routerContext, organization} = initializeOrg({
  72. router: {
  73. params: routeParams,
  74. },
  75. });
  76. MockApiClient.addMockResponse({
  77. url: `/organizations/${organization.slug}/sdks/`,
  78. body: {},
  79. });
  80. MockApiClient.addMockResponse({
  81. url: `/projects/${organization.slug}/${nextJsProject.slug}/docs/javascript-nextjs-with-error-monitoring/`,
  82. body: null,
  83. });
  84. MockApiClient.addMockResponse({
  85. url: `/projects/org-slug/${nextJsProject.slug}/`,
  86. body: [nextJsProject],
  87. });
  88. MockApiClient.addMockResponse({
  89. url: `/projects/${organization.slug}/${nextJsProject.slug}/issues/`,
  90. body: [],
  91. });
  92. MockApiClient.addMockResponse({
  93. url: `/projects/org-slug/${nextJsProject.slug}/keys/`,
  94. method: 'GET',
  95. body: [ProjectKeys()[0]],
  96. });
  97. jest
  98. .spyOn(useRecentCreatedProjectHook, 'useRecentCreatedProject')
  99. .mockImplementation(() => {
  100. return {
  101. ...nextJsProject,
  102. firstError: false,
  103. firstTransaction: false,
  104. hasReplays: false,
  105. hasSessions: false,
  106. olderThanOneHour: false,
  107. firstIssue: undefined,
  108. };
  109. });
  110. render(
  111. <OnboardingContextProvider
  112. value={{
  113. selectedSDK: {
  114. key: nextJsProject.slug as PlatformKey,
  115. type: 'framework',
  116. language: 'javascript',
  117. category: 'browser',
  118. },
  119. projects: {
  120. [nextJsProject.id]: {
  121. slug: nextJsProject.slug,
  122. status: OnboardingProjectStatus.WAITING,
  123. firstIssueId: undefined,
  124. },
  125. },
  126. }}
  127. >
  128. <Onboarding {...routerProps} />
  129. </OnboardingContextProvider>,
  130. {
  131. context: routerContext,
  132. organization,
  133. }
  134. );
  135. expect(await screen.findByText('Configure Next.js SDK')).toBeInTheDocument();
  136. });
  137. it('renders SDK data removal modal when going back', async function () {
  138. const reactProject: Project = ProjectFixture({
  139. platform: 'javascript-react',
  140. id: '2',
  141. slug: 'javascript-react-slug',
  142. firstTransactionEvent: false,
  143. firstEvent: null,
  144. hasReplays: false,
  145. hasSessions: false,
  146. });
  147. const routeParams = {
  148. step: 'setup-docs',
  149. };
  150. const {routerProps, routerContext, organization} = initializeOrg({
  151. router: {
  152. params: routeParams,
  153. },
  154. });
  155. MockApiClient.addMockResponse({
  156. url: `/organizations/${organization.slug}/sdks/`,
  157. body: {},
  158. });
  159. MockApiClient.addMockResponse({
  160. url: `/projects/org-slug/${reactProject.slug}/`,
  161. body: [reactProject],
  162. });
  163. MockApiClient.addMockResponse({
  164. url: `/projects/org-slug/${reactProject.slug}/keys/`,
  165. method: 'GET',
  166. body: [ProjectKeys()[0]],
  167. });
  168. MockApiClient.addMockResponse({
  169. url: `/projects/${organization.slug}/${reactProject.slug}/issues/`,
  170. body: [],
  171. });
  172. jest
  173. .spyOn(useRecentCreatedProjectHook, 'useRecentCreatedProject')
  174. .mockImplementation(() => {
  175. return {
  176. ...reactProject,
  177. firstError: false,
  178. firstTransaction: false,
  179. hasReplays: false,
  180. hasSessions: false,
  181. olderThanOneHour: false,
  182. firstIssue: undefined,
  183. };
  184. });
  185. render(
  186. <OnboardingContextProvider
  187. value={{
  188. selectedSDK: {
  189. key: reactProject.slug as PlatformKey,
  190. type: 'framework',
  191. language: 'javascript',
  192. category: 'browser',
  193. },
  194. projects: {
  195. [reactProject.id]: {
  196. slug: reactProject.slug,
  197. status: OnboardingProjectStatus.WAITING,
  198. firstIssueId: undefined,
  199. },
  200. },
  201. }}
  202. >
  203. <Onboarding {...routerProps} />
  204. </OnboardingContextProvider>,
  205. {
  206. context: routerContext,
  207. organization,
  208. }
  209. );
  210. // Await for the docs to be loaded
  211. await screen.findByText('Configure React SDK');
  212. renderGlobalModal();
  213. // Click on back button
  214. await userEvent.click(screen.getByRole('button', {name: 'Back'}));
  215. // Await for the modal to be open
  216. expect(
  217. await screen.findByText(/Are you sure you want to head back?/)
  218. ).toBeInTheDocument();
  219. // Close modal
  220. await userEvent.click(screen.getByRole('button', {name: 'Cancel'}));
  221. });
  222. it('does not render SDK data removal modal when going back', async function () {
  223. const reactProject: Project = ProjectFixture({
  224. platform: 'javascript-react',
  225. id: '2',
  226. slug: 'javascript-react-slug',
  227. });
  228. const routeParams = {
  229. step: 'setup-docs',
  230. };
  231. const {routerProps, routerContext, organization} = initializeOrg({
  232. router: {
  233. params: routeParams,
  234. },
  235. });
  236. MockApiClient.addMockResponse({
  237. url: `/organizations/${organization.slug}/sdks/`,
  238. body: {},
  239. });
  240. MockApiClient.addMockResponse({
  241. url: `/projects/org-slug/${reactProject.slug}/`,
  242. body: [reactProject],
  243. });
  244. MockApiClient.addMockResponse({
  245. url: `/projects/org-slug/${reactProject.slug}/keys/`,
  246. method: 'GET',
  247. body: [ProjectKeys()[0]],
  248. });
  249. MockApiClient.addMockResponse({
  250. url: `/projects/${organization.slug}/${reactProject.slug}/issues/`,
  251. body: [],
  252. });
  253. jest
  254. .spyOn(useRecentCreatedProjectHook, 'useRecentCreatedProject')
  255. .mockImplementation(() => {
  256. return {
  257. ...reactProject,
  258. firstError: false,
  259. firstTransaction: false,
  260. hasReplays: false,
  261. hasSessions: true,
  262. olderThanOneHour: false,
  263. firstIssue: undefined,
  264. };
  265. });
  266. render(
  267. <OnboardingContextProvider
  268. value={{
  269. selectedSDK: {
  270. key: reactProject.slug as PlatformKey,
  271. type: 'framework',
  272. language: 'javascript',
  273. category: 'browser',
  274. },
  275. projects: {
  276. [reactProject.id]: {
  277. slug: reactProject.slug,
  278. status: OnboardingProjectStatus.WAITING,
  279. firstIssueId: undefined,
  280. },
  281. },
  282. }}
  283. >
  284. <Onboarding {...routerProps} />
  285. </OnboardingContextProvider>,
  286. {
  287. context: routerContext,
  288. organization,
  289. }
  290. );
  291. // Await for the docs to be loaded
  292. await screen.findByText('Configure React SDK');
  293. renderGlobalModal();
  294. // Click on back button
  295. await userEvent.click(screen.getByRole('button', {name: 'Back'}));
  296. // Await for the modal to be open
  297. expect(
  298. screen.queryByText(/Are you sure you want to head back?/)
  299. ).not.toBeInTheDocument();
  300. });
  301. it('renders framework selection modal if vanilla js is selected', async function () {
  302. const routeParams = {
  303. step: 'select-platform',
  304. };
  305. const {routerProps, routerContext, organization} = initializeOrg({
  306. organization: {
  307. features: ['onboarding-sdk-selection'],
  308. },
  309. router: {
  310. params: routeParams,
  311. },
  312. });
  313. render(
  314. <OnboardingContextProvider>
  315. <Onboarding {...routerProps} />
  316. </OnboardingContextProvider>,
  317. {
  318. context: routerContext,
  319. organization,
  320. }
  321. );
  322. renderGlobalModal();
  323. // Select the JavaScript platform
  324. await userEvent.click(screen.getByTestId('platform-javascript'));
  325. // Click on 'configure SDK' button
  326. await userEvent.click(screen.getByRole('button', {name: 'Configure SDK'}));
  327. // Modal is open
  328. await screen.findByText('Do you use a framework?');
  329. // Close modal
  330. await userEvent.click(screen.getByRole('button', {name: 'Skip'}));
  331. });
  332. it('does not render framework selection modal if vanilla js is NOT selected', async function () {
  333. const routeParams = {
  334. step: 'select-platform',
  335. };
  336. const {routerProps, routerContext, organization} = initializeOrg({
  337. organization: {
  338. features: ['onboarding-sdk-selection'],
  339. },
  340. router: {
  341. params: routeParams,
  342. },
  343. });
  344. render(
  345. <OnboardingContextProvider>
  346. <Onboarding {...routerProps} />
  347. </OnboardingContextProvider>,
  348. {
  349. context: routerContext,
  350. organization,
  351. }
  352. );
  353. // Select the React platform
  354. await userEvent.click(screen.getByTestId('platform-javascript-vue'));
  355. // Click on 'configure SDK' button
  356. await userEvent.click(screen.getByRole('button', {name: 'Configure SDK'}));
  357. // Modal shall not be open
  358. expect(screen.queryByText('Do you use a framework?')).not.toBeInTheDocument();
  359. });
  360. });