From 19c131bdfccb98c24bcf432c5e5968bc5d4ffd5c Mon Sep 17 00:00:00 2001
From: stanislavh <stanislav.honcharov@sonarsource.com>
Date: Mon, 6 Nov 2023 11:13:33 +0100
Subject: SONAR-20873 Create new education tour for accepting issues

---
 .../js/apps/issues/__tests__/IssuesAppGuide-it.tsx |  31 +++-
 .../IssuesNewStatusAndTransitionGuide-it.tsx       | 174 +++++++++++++++++++++
 2 files changed, 201 insertions(+), 4 deletions(-)
 create mode 100644 server/sonar-web/src/main/js/apps/issues/__tests__/IssuesNewStatusAndTransitionGuide-it.tsx

(limited to 'server/sonar-web/src/main/js/apps/issues/__tests__')

diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesAppGuide-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesAppGuide-it.tsx
index fe8b8d36bb2..93c5f8b24ea 100644
--- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesAppGuide-it.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesAppGuide-it.tsx
@@ -64,7 +64,12 @@ beforeEach(() => {
 
 it('should display guide', async () => {
   const user = userEvent.setup();
-  renderIssueApp(mockCurrentUser({ isLoggedIn: true }));
+  renderIssueApp(
+    mockCurrentUser({
+      isLoggedIn: true,
+      dismissedNotices: { [NoticeType.ISSUE_NEW_STATUS_AND_TRANSITION_GUIDE]: true },
+    }),
+  );
 
   expect(await ui.guidePopup.find()).toBeInTheDocument();
 
@@ -106,7 +111,13 @@ it('should display guide', async () => {
 
 it('should not show guide for those who dismissed it', async () => {
   renderIssueApp(
-    mockCurrentUser({ isLoggedIn: true, dismissedNotices: { [NoticeType.ISSUE_GUIDE]: true } }),
+    mockCurrentUser({
+      isLoggedIn: true,
+      dismissedNotices: {
+        [NoticeType.ISSUE_GUIDE]: true,
+        [NoticeType.ISSUE_NEW_STATUS_AND_TRANSITION_GUIDE]: true,
+      },
+    }),
   );
 
   expect((await ui.issueItems.findAll()).length).toBeGreaterThan(0);
@@ -115,7 +126,12 @@ it('should not show guide for those who dismissed it', async () => {
 
 it('should skip guide', async () => {
   const user = userEvent.setup();
-  renderIssueApp(mockCurrentUser({ isLoggedIn: true }));
+  renderIssueApp(
+    mockCurrentUser({
+      isLoggedIn: true,
+      dismissedNotices: { [NoticeType.ISSUE_NEW_STATUS_AND_TRANSITION_GUIDE]: true },
+    }),
+  );
 
   expect(await ui.guidePopup.find()).toBeInTheDocument();
   expect(ui.guidePopup.get()).toHaveTextContent('guiding.issue_list.1.title');
@@ -127,7 +143,14 @@ it('should skip guide', async () => {
 });
 
 it('should not show guide if issues need sync', async () => {
-  renderProjectIssuesApp(undefined, { needIssueSync: true }, mockCurrentUser({ isLoggedIn: true }));
+  renderProjectIssuesApp(
+    undefined,
+    { needIssueSync: true },
+    mockCurrentUser({
+      isLoggedIn: true,
+      dismissedNotices: { [NoticeType.ISSUE_NEW_STATUS_AND_TRANSITION_GUIDE]: true },
+    }),
+  );
 
   expect((await ui.issueItems.findAll()).length).toBeGreaterThan(0);
   expect(ui.guidePopup.query()).not.toBeInTheDocument();
diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesNewStatusAndTransitionGuide-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesNewStatusAndTransitionGuide-it.tsx
new file mode 100644
index 00000000000..43708b804d5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesNewStatusAndTransitionGuide-it.tsx
@@ -0,0 +1,174 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import { act } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import React from 'react';
+import IssuesServiceMock from '../../../api/mocks/IssuesServiceMock';
+import CurrentUserContextProvider from '../../../app/components/current-user/CurrentUserContextProvider';
+import IssueTransitionComponent from '../../../components/issue/components/IssueTransition';
+import { mockCurrentUser, mockIssue } from '../../../helpers/testMocks';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
+import { IssueTransition } from '../../../types/issues';
+import { Issue } from '../../../types/types';
+import { NoticeType } from '../../../types/users';
+import IssueNewStatusAndTransitionGuide from '../components/IssueNewStatusAndTransitionGuide';
+import { ui } from '../test-utils';
+
+const issuesHandler = new IssuesServiceMock();
+
+beforeEach(() => {
+  issuesHandler.reset();
+});
+
+it('should display status guide', async () => {
+  const user = userEvent.setup();
+  renderIssueNewStatusGuide();
+
+  expect(await ui.guidePopup.find()).toBeInTheDocument();
+  expect(ui.guidePopup.get()).toHaveTextContent('guiding.issue_accept.1.title');
+
+  await act(async () => {
+    await user.click(ui.guidePopup.byRole('button', { name: 'next' }).get());
+  });
+
+  expect(ui.guidePopup.get()).toHaveTextContent('guiding.issue_accept.2.title');
+
+  await act(async () => {
+    await user.click(ui.guidePopup.byRole('button', { name: 'go_back' }).get());
+  });
+  expect(ui.guidePopup.get()).toHaveTextContent('guiding.issue_accept.1.title');
+
+  await act(async () => {
+    await user.click(ui.guidePopup.byRole('button', { name: 'next' }).get());
+  });
+  await act(async () => {
+    await user.click(ui.guidePopup.byRole('button', { name: 'next' }).get());
+  });
+  expect(ui.guidePopup.get()).toHaveTextContent('guiding.issue_accept.3.title');
+  expect(ui.guidePopup.byRole('button', { name: 'Next' }).query()).not.toBeInTheDocument();
+
+  await act(async () => {
+    await user.click(ui.guidePopup.byRole('button', { name: 'close' }).get());
+  });
+
+  expect(ui.guidePopup.query()).not.toBeInTheDocument();
+});
+
+it('should not show guide for those who dismissed it', () => {
+  renderIssueNewStatusGuide(
+    mockCurrentUser({
+      isLoggedIn: true,
+      dismissedNotices: {
+        [NoticeType.ISSUE_GUIDE]: true,
+        [NoticeType.ISSUE_NEW_STATUS_AND_TRANSITION_GUIDE]: true,
+      },
+    }),
+  );
+
+  expect(ui.guidePopup.query()).not.toBeInTheDocument();
+});
+
+it('should skip guide', async () => {
+  const user = userEvent.setup();
+  renderIssueNewStatusGuide();
+
+  expect(await ui.guidePopup.find()).toBeInTheDocument();
+  expect(ui.guidePopup.get()).toHaveTextContent('guiding.issue_accept.1.title');
+
+  await user.click(ui.guidePopup.byRole('button', { name: 'skip' }).get());
+
+  expect(ui.guidePopup.query()).not.toBeInTheDocument();
+});
+
+it('should not show guide if user is not logged in', () => {
+  renderIssueNewStatusGuide(mockCurrentUser({ isLoggedIn: false }));
+
+  expect(ui.guidePopup.query()).not.toBeInTheDocument();
+});
+
+it('should not show guide if there are no issues', () => {
+  renderIssueNewStatusGuide(mockCurrentUser({ isLoggedIn: true }), []);
+
+  expect(ui.guidePopup.query()).not.toBeInTheDocument();
+});
+
+it('should not show guide if CCT guide is shown', () => {
+  renderIssueNewStatusGuide(
+    mockCurrentUser({ isLoggedIn: true, dismissedNotices: { [NoticeType.ISSUE_GUIDE]: false } }),
+    [],
+  );
+
+  expect(ui.guidePopup.query()).not.toBeInTheDocument();
+});
+
+function IssueNewStatusGuide({ issues }: { issues: Issue[] }) {
+  const [open, setOpen] = React.useState(false);
+  const issue = mockIssue(false, {
+    transitions: [
+      IssueTransition.Accept,
+      IssueTransition.Confirm,
+      IssueTransition.Resolve,
+      IssueTransition.FalsePositive,
+      IssueTransition.WontFix,
+    ],
+  });
+
+  return (
+    <div data-guiding-id={`issue-transition-${issue.key}`}>
+      <div data-guiding-id="issue-accept-transition">/</div>
+      <IssueTransitionComponent
+        isOpen={open}
+        togglePopup={() => setOpen(!open)}
+        issue={issue}
+        onChange={jest.fn()}
+      />
+      <IssueNewStatusAndTransitionGuide
+        togglePopup={(_, __, show) => setOpen(Boolean(show))}
+        run
+        issues={issues}
+      />
+    </div>
+  );
+}
+
+function renderIssueNewStatusGuide(
+  currentUser = mockCurrentUser({
+    isLoggedIn: true,
+    dismissedNotices: { [NoticeType.ISSUE_GUIDE]: true },
+  }),
+  issues = [
+    mockIssue(false, {
+      transitions: [
+        IssueTransition.Accept,
+        IssueTransition.Confirm,
+        IssueTransition.Resolve,
+        IssueTransition.FalsePositive,
+        IssueTransition.WontFix,
+      ],
+    }),
+  ],
+) {
+  return renderComponent(
+    <CurrentUserContextProvider currentUser={currentUser}>
+      <IssueNewStatusGuide issues={issues} />
+    </CurrentUserContextProvider>,
+  );
+}
-- 
cgit v1.2.3