From 4f842eaa8c582f6e1b0a6f0f1d04efbaabac42f2 Mon Sep 17 00:00:00 2001
From: mrsadman99 <60566490+mrsadman99@users.noreply.github.com>
Date: Wed, 21 Dec 2022 13:00:44 +0700
Subject: [PATCH] TSK-473: Added tracker layout sanity tests (#2452)

Signed-off-by: Anton Brechka <anton.brechka@xored.com>
---
 tests/sanity/tests/tracker.layout.spec.ts   | 169 ++++++++++++++++++++
 tests/sanity/tests/tracker.projects.spec.ts |   2 +
 tests/sanity/tests/tracker.spec.ts          | 142 +++-------------
 tests/sanity/tests/tracker.utils.ts         | 168 +++++++++++++++++++
 4 files changed, 361 insertions(+), 120 deletions(-)
 create mode 100644 tests/sanity/tests/tracker.layout.spec.ts
 create mode 100644 tests/sanity/tests/tracker.utils.ts

diff --git a/tests/sanity/tests/tracker.layout.spec.ts b/tests/sanity/tests/tracker.layout.spec.ts
new file mode 100644
index 0000000000..d50050182c
--- /dev/null
+++ b/tests/sanity/tests/tracker.layout.spec.ts
@@ -0,0 +1,169 @@
+import { test, expect, Page } from '@playwright/test'
+import {
+  checkIssueFromList,
+  createIssue,
+  createProject,
+  createSprint,
+  DEFAULT_STATUSES,
+  DEFAULT_USER,
+  IssueProps,
+  navigate,
+  PRIORITIES,
+  setViewGroup,
+  setViewOrder,
+  ViewletSelectors
+} from './tracker.utils'
+import { generateId, PlatformSetting } from './utils'
+test.use({
+  storageState: PlatformSetting
+})
+
+const getIssueName = (postfix: string = generateId(5)): string => `issue-${postfix}`
+
+async function createIssues (page: Page, projects?: string[], sprints?: string[]): Promise<IssueProps[]> {
+  const issuesProps = []
+  for (let index = 0; index < 5; index++) {
+    const shiftedIndex = 4 - index
+    const issueProps = {
+      name: getIssueName(`layout-${shiftedIndex}`),
+      status: DEFAULT_STATUSES[shiftedIndex],
+      assignee: shiftedIndex % 2 === 0 ? DEFAULT_USER : 'Chen Rosamund',
+      priority: PRIORITIES[shiftedIndex],
+      project: projects !== undefined ? projects[index % projects.length] : undefined,
+      sprint: sprints !== undefined ? sprints[index % sprints.length] : undefined
+    }
+    issuesProps.push(issueProps)
+
+    await createIssue(page, issueProps)
+  }
+
+  return issuesProps
+}
+
+async function createProjects (page: Page): Promise<string[]> {
+  const projects = []
+
+  for (let index = 0; index < 5; index++) {
+    const prjId = `project-${generateId()}-${index}`
+    projects.push(prjId)
+
+    await createProject(page, prjId)
+  }
+
+  return projects
+}
+
+async function createSprints (page: Page): Promise<string[]> {
+  const sprints = []
+
+  for (let index = 0; index < 5; index++) {
+    const sprintId = `sprint-${generateId()}-${index}`
+    sprints.push(sprintId)
+
+    await createSprint(page, sprintId)
+  }
+
+  return sprints
+}
+
+async function initIssues (page: Page): Promise<IssueProps[]> {
+  const projects = await createProjects(page)
+  const sprints = await createSprints(page)
+  const issuesProps = await createIssues(page, projects, sprints)
+  await page.click('text="Issues"')
+
+  return issuesProps
+}
+
+test.describe('tracker layout tests', () => {
+  test.beforeEach(async ({ page }) => {
+    test.setTimeout(60000)
+    await navigate(page)
+    issuesProps = await initIssues(page)
+  })
+
+  let issuesProps: IssueProps[] = []
+  const orders = ['Status', 'Last updated', 'Priority'] as const
+  const groups = ['Status', 'Assignee', 'Priority', 'Project', 'Sprint', 'No grouping'] as const
+  const groupsLabels: { [key in typeof groups[number]]?: string[] } = {
+    Status: DEFAULT_STATUSES,
+    Assignee: [DEFAULT_USER, 'Chen Rosamund'],
+    Priority: PRIORITIES,
+    'No grouping': ['No grouping']
+  }
+
+  for (const group of groups) {
+    test(`issues-${group.toLowerCase()}-grouping-layout`, async ({ page }) => {
+      const locator = page.locator('.issueslist-container')
+      await setViewGroup(page, group)
+
+      let groupLabels: any[]
+      if (group === 'Sprint') {
+        groupLabels = issuesProps.map((props) => props.sprint)
+      } else if (group === 'Project') {
+        groupLabels = issuesProps.map((props) => props.project)
+      } else {
+        groupLabels = groupsLabels[group] ?? []
+      }
+      const issueNames = issuesProps.map((props) => props.name)
+
+      await page.click(ViewletSelectors.Table)
+      await expect(locator).toContainText(groupLabels)
+
+      for (const issueName of issueNames) {
+        await checkIssueFromList(page, issueName)
+      }
+    })
+  }
+
+  for (const order of orders) {
+    test(`issues-${order.toLowerCase()}-ordering-layout`, async ({ page }) => {
+      const locator = page.locator('.panel-container')
+      let orderedIssueNames: string[]
+
+      if (order === 'Priority') {
+        orderedIssueNames = issuesProps
+          .sort((propsLeft, propsRight) => {
+            if (propsLeft.priority === undefined || propsRight.priority === undefined) {
+              return -1
+            }
+
+            if (propsLeft.priority === propsRight.priority) {
+              return 0
+            } else if (
+              PRIORITIES.findIndex((p) => p === propsLeft.priority) -
+                PRIORITIES.findIndex((p) => p === propsRight.priority) >
+              0
+            ) {
+              return 1
+            }
+            return -1
+          })
+          .map((p) => p.name)
+      } else if (order === 'Status') {
+        orderedIssueNames = issuesProps
+          .sort((propsLeft, propsRight) => {
+            if (propsLeft.status !== undefined && propsRight.status !== undefined) {
+              if (propsLeft.status === propsRight.status) {
+                return 0
+              } else if (
+                DEFAULT_STATUSES.findIndex((s) => s === propsLeft.status) -
+                  DEFAULT_STATUSES.findIndex((s) => s === propsRight.status) >
+                0
+              ) {
+                return 1
+              }
+            }
+
+            return -1
+          })
+          .map((p) => p.name)
+      } else {
+        orderedIssueNames = issuesProps.map((props) => props.name).reverse()
+      }
+      await setViewOrder(page, order)
+      await page.click(ViewletSelectors.Board)
+      await expect(locator).toContainText(orderedIssueNames)
+    })
+  }
+})
diff --git a/tests/sanity/tests/tracker.projects.spec.ts b/tests/sanity/tests/tracker.projects.spec.ts
index 9471497341..34ecd87048 100644
--- a/tests/sanity/tests/tracker.projects.spec.ts
+++ b/tests/sanity/tests/tracker.projects.spec.ts
@@ -1,4 +1,5 @@
 import { expect, test } from '@playwright/test'
+import { navigate } from './tracker.utils'
 import { generateId, PlatformSetting, PlatformURI } from './utils'
 
 test.use({
@@ -13,6 +14,7 @@ test.describe('project tests', () => {
   test('create-project-issue', async ({ page }) => {
     await page.click('[id="app-tracker\\:string\\:TrackerApplication"]')
 
+    await navigate(page)
     // Click text=Projects
     await page.click('text=Projects')
     await expect(page).toHaveURL(
diff --git a/tests/sanity/tests/tracker.spec.ts b/tests/sanity/tests/tracker.spec.ts
index 50981d17b0..830f69ca9b 100644
--- a/tests/sanity/tests/tracker.spec.ts
+++ b/tests/sanity/tests/tracker.spec.ts
@@ -1,130 +1,32 @@
-import { test, expect, Page } from '@playwright/test'
-import { generateId, PlatformSetting, PlatformURI } from './utils'
+import { test, expect } from '@playwright/test'
+import {
+  checkIssue,
+  createIssue,
+  createLabel,
+  createSubissue,
+  DEFAULT_STATUSES,
+  DEFAULT_USER,
+  navigate,
+  openIssue,
+  ViewletSelectors
+} from './tracker.utils'
+import { generateId, PlatformSetting } from './utils'
 test.use({
   storageState: PlatformSetting
 })
 
-async function navigate (page: Page): Promise<void> {
-  await page.goto(`${PlatformURI}/workbench%3Acomponent%3AWorkbenchApp/sanity-ws`)
-  await page.click('[id="app-tracker\\:string\\:TrackerApplication"]')
-  await expect(page).toHaveURL(`${PlatformURI}/workbench%3Acomponent%3AWorkbenchApp/sanity-ws/tracker`)
-}
-
-interface IssueProps {
-  name: string
-  description?: string
-  status?: string
-  labels?: string[]
-  priority?: string
-  assignee?: string
-}
-
-async function fillIssueForm (
-  page: Page,
-  { name, description, status, assignee, labels, priority }: IssueProps
-): Promise<void> {
-  await page.fill('[placeholder="Issue\\ title"]', name)
-  if (description !== undefined) {
-    await page.fill('.ProseMirror', description)
-  }
-  if (status !== undefined) {
-    await page.click('#status-editor')
-    await page.click(`.menu-item:has-text("${status}")`)
-  }
-  if (priority !== undefined) {
-    await page.click('button:has-text("No priority")')
-    await page.click(`.selectPopup button:has-text("${priority}")`)
-  }
-  if (labels !== undefined) {
-    await page.click('.button:has-text("Labels")')
-    for (const label of labels) {
-      await page.click(`.selectPopup button:has-text("${label}") >> nth=0`)
-    }
-    await page.keyboard.press('Escape')
-  }
-  if (assignee !== undefined) {
-    await page.click('.button:has-text("Assignee")')
-    await page.click(`.selectPopup button:has-text("${assignee}")`)
-  }
-}
-
-async function createIssue (page: Page, props: IssueProps): Promise<void> {
-  await page.waitForSelector('span:has-text("Default")')
-  await page.click('button:has-text("New issue")')
-  await fillIssueForm(page, props)
-  await page.click('button:has-text("Save issue")')
-  await page.waitForSelector('form.antiCard', { state: 'detached' })
-}
-
-async function createSubissue (page: Page, props: IssueProps): Promise<void> {
-  await page.click('button:has-text("Add sub-issue")')
-  await fillIssueForm(page, props)
-  await page.click('button:has-text("Save")')
-}
-
-interface LabelProps {
-  label: string
-}
-async function createLabel (page: Page, { label }: LabelProps): Promise<void> {
-  await page.click('button:has-text("New issue")')
-  await page.click('button:has-text("Labels")')
-  await page.click('.buttons-group >> button >> nth=-1')
-  await page.fill('[id="tags:string:AddTag"] >> input >> nth=0', label)
-  await page.click('[id="tags:string:AddTag"] >> button:has-text("Create")')
-  await page.waitForSelector('form.antiCard[id="tags:string:AddTag"]', { state: 'detached' })
-  await page.keyboard.press('Escape')
-  await page.waitForTimeout(100)
-  await page.keyboard.press('Escape')
-}
-
-async function checkIssue (
-  page: Page,
-  { name, description, status, assignee, labels, priority }: IssueProps
-): Promise<void> {
-  if (name !== undefined) {
-    await expect(page.locator('.popupPanel')).toContainText(name)
-  }
-  if (description !== undefined) {
-    await expect(page.locator('.popupPanel')).toContainText(description)
-  }
-  const asideLocator = page.locator('.popupPanel-body__aside')
-  if (status !== undefined) {
-    await expect(asideLocator).toContainText(status)
-  }
-  if (labels !== undefined) {
-    await expect(asideLocator).toContainText(labels)
-  }
-  if (priority !== undefined) {
-    await expect(asideLocator).toContainText(priority)
-  }
-  if (assignee !== undefined) {
-    await expect(asideLocator).toContainText(assignee)
-  }
-}
-
-async function openIssue (page: Page, name: string): Promise<void> {
-  await page.click(`.antiList__row:has-text("${name}") .issuePresenterRoot`)
-}
-
-const defaultStatuses = ['Backlog', 'Todo', 'In Progress', 'Done', 'Canceled']
-const defaultUser = 'Appleseed John'
-enum viewletSelectors {
-  Table = '.tablist-container >> div.button:nth-child(1)',
-  Board = '.tablist-container >> div.button:nth-child(2)'
-}
-
 test('create-issue-and-sub-issue', async ({ page }) => {
   const props = {
     name: getIssueName(),
     description: 'description',
     labels: ['label', 'another-label'],
-    status: defaultStatuses[0],
+    status: DEFAULT_STATUSES[0],
     priority: 'Urgent',
-    assignee: defaultUser
+    assignee: DEFAULT_USER
   }
   await navigate(page)
   for (const label of props.labels) {
-    await createLabel(page, { label })
+    await createLabel(page, label)
   }
   await createIssue(page, props)
   await page.click('text="Issues"')
@@ -140,22 +42,22 @@ const getIssueName = (postfix: string = generateId(5)): string => `issue-${postf
 
 test('issues-status-display', async ({ page }) => {
   const panelStatusMap = new Map([
-    ['Issues', defaultStatuses],
+    ['Issues', DEFAULT_STATUSES],
     ['Active', ['Todo', 'In Progress']],
     ['Backlog', ['Backlog']]
   ])
   const locator = page.locator('.issueslist-container')
   await navigate(page)
-  for (const status of defaultStatuses) {
+  for (const status of DEFAULT_STATUSES) {
     await createIssue(page, { name: getIssueName(status), status })
   }
   for (const [panel, statuses] of panelStatusMap) {
-    const excluded = defaultStatuses.filter((status) => !statuses.includes(status))
+    const excluded = DEFAULT_STATUSES.filter((status) => !statuses.includes(status))
     await page.locator(`text="${panel}"`).click()
-    await page.click(viewletSelectors.Table)
+    await page.click(ViewletSelectors.Table)
     await expect(locator).toContainText(statuses)
     if (excluded.length > 0) await expect(locator).not.toContainText(excluded)
-    await page.click(viewletSelectors.Board)
+    await page.click(ViewletSelectors.Board)
     if (excluded.length > 0) await expect(locator).not.toContainText(excluded)
     for (const status of statuses) {
       await expect(page.locator(`.panel-container:has-text("${status}")`)).toContainText(getIssueName(status))
@@ -166,7 +68,7 @@ test('issues-status-display', async ({ page }) => {
 test('save-view-options', async ({ page }) => {
   const panels = ['Issues', 'Active', 'Backlog']
   await navigate(page)
-  for (const viewletSelector of [viewletSelectors.Board, viewletSelectors.Table]) {
+  for (const viewletSelector of [ViewletSelectors.Board, ViewletSelectors.Table]) {
     for (const panel of panels) {
       await page.click(`text="${panel}"`)
       await page.click(viewletSelector)
diff --git a/tests/sanity/tests/tracker.utils.ts b/tests/sanity/tests/tracker.utils.ts
new file mode 100644
index 0000000000..8c13af90f6
--- /dev/null
+++ b/tests/sanity/tests/tracker.utils.ts
@@ -0,0 +1,168 @@
+import { Page, expect } from '@playwright/test'
+import { PlatformURI } from './utils'
+
+export interface IssueProps {
+  name: string
+  description?: string
+  status?: string
+  labels?: string[]
+  priority?: string
+  assignee?: string
+  project?: string
+  sprint?: string
+}
+
+export enum ViewletSelectors {
+  Table = '.tablist-container >> div.button:nth-child(1)',
+  Board = '.tablist-container >> div.button:nth-child(2)'
+}
+
+export const PRIORITIES = ['No priority', 'Urgent', 'High', 'Medium', 'Low']
+export const DEFAULT_STATUSES = ['Backlog', 'Todo', 'In Progress', 'Done', 'Canceled']
+export const DEFAULT_USER = 'Appleseed John'
+
+export async function navigate (page: Page): Promise<void> {
+  await page.goto(`${PlatformURI}/workbench%3Acomponent%3AWorkbenchApp/sanity-ws`)
+  await page.click('[id="app-tracker\\:string\\:TrackerApplication"]')
+  await expect(page).toHaveURL(`${PlatformURI}/workbench%3Acomponent%3AWorkbenchApp/sanity-ws/tracker`)
+}
+
+export async function setViewGroup (page: Page, groupName: string): Promise<void> {
+  await page.click('button:has-text("View")')
+  await page.click('.antiCard >> button >> nth=0')
+  await page.click(`.menu-item:has-text("${groupName}")`)
+  await expect(page.locator('.antiCard >> button >> nth=0')).toContainText(groupName)
+
+  await page.keyboard.press('Escape')
+}
+
+export async function setViewOrder (page: Page, orderName: string): Promise<void> {
+  await page.click('button:has-text("View")')
+  await page.click('.antiCard >> button >> nth=1')
+  await page.click(`.menu-item:has-text("${orderName}")`)
+  await expect(page.locator('.antiCard >> button >> nth=1')).toContainText(orderName)
+
+  await page.keyboard.press('Escape')
+}
+
+export async function fillIssueForm (page: Page, props: IssueProps): Promise<void> {
+  const { name, description, status, assignee, labels, priority, project, sprint } = props
+  await page.fill('[placeholder="Issue\\ title"]', name)
+  if (description !== undefined) {
+    await page.fill('.ProseMirror', description)
+  }
+  if (status !== undefined) {
+    await page.click('#status-editor')
+    await page.click(`.menu-item:has-text("${status}")`)
+  }
+  if (priority !== undefined) {
+    await page.click('button:has-text("No priority")')
+    await page.click(`.selectPopup button:has-text("${priority}")`)
+  }
+  if (labels !== undefined) {
+    await page.click('.button:has-text("Labels")')
+    for (const label of labels) {
+      await page.click(`.selectPopup button:has-text("${label}") >> nth=0`)
+    }
+    await page.keyboard.press('Escape')
+  }
+  if (assignee !== undefined) {
+    await page.click('.button:has-text("Assignee")')
+    await page.click(`.selectPopup button:has-text("${assignee}")`)
+  }
+  if (project !== undefined) {
+    await page.click('form button:has-text("Project")')
+    await page.click(`.selectPopup button:has-text("${project}")`)
+  }
+  if (sprint !== undefined) {
+    await page.click('.button:has-text("No Sprint")')
+    await page.click(`.selectPopup button:has-text("${sprint}")`)
+  }
+}
+
+export async function createIssue (page: Page, props: IssueProps): Promise<void> {
+  await page.waitForSelector('span:has-text("Default")')
+  await page.click('button:has-text("New issue")')
+  await fillIssueForm(page, props)
+  await page.click('button:has-text("Save issue")')
+  await page.waitForSelector('form.antiCard', { state: 'detached' })
+}
+
+export async function createProject (page: Page, projectName: string): Promise<void> {
+  await page.click('text=Projects')
+  await expect(page).toHaveURL(
+    `${PlatformURI}/workbench%3Acomponent%3AWorkbenchApp/sanity-ws/tracker/tracker%3Ateam%3ADefaultTeam/projects`
+  )
+  await page.click('button:has-text("Project")')
+  await page.click('[placeholder="Project\\ name"]')
+  await page.fill('[placeholder="Project\\ name"]', projectName)
+  await page.click('button:has-text("Create project")')
+}
+
+export async function createSprint (page: Page, sprintName: string): Promise<void> {
+  await page.click('text=Sprints')
+  await expect(page).toHaveURL(
+    `${PlatformURI}/workbench%3Acomponent%3AWorkbenchApp/sanity-ws/tracker/tracker%3Ateam%3ADefaultTeam/sprints`
+  )
+  await page.click('button:has-text("Sprint")')
+  await page.click('[placeholder="Sprint\\ name"]')
+  await page.fill('[placeholder="Sprint\\ name"]', sprintName)
+  await page.click('button:has-text("Create")')
+}
+
+export async function createSubissue (page: Page, props: IssueProps): Promise<void> {
+  await page.click('button:has-text("Add sub-issue")')
+  await fillIssueForm(page, props)
+  await page.click('button:has-text("Save")')
+}
+
+export async function createLabel (page: Page, label: string): Promise<void> {
+  await page.click('button:has-text("New issue")')
+  await page.click('button:has-text("Labels")')
+  await page.click('.buttons-group >> button >> nth=-1')
+  await page.fill('[id="tags:string:AddTag"] >> input >> nth=0', label)
+  await page.click('[id="tags:string:AddTag"] >> button:has-text("Create")')
+  await page.waitForSelector('form.antiCard[id="tags:string:AddTag"]', { state: 'detached' })
+  await page.keyboard.press('Escape')
+  await page.waitForTimeout(100)
+  await page.keyboard.press('Escape')
+}
+
+export async function checkIssue (page: Page, props: IssueProps): Promise<void> {
+  const { name, description, status, assignee, labels, priority, project, sprint } = props
+
+  if (name !== undefined) {
+    await expect(page.locator('.popupPanel')).toContainText(name)
+  }
+  if (description !== undefined) {
+    await expect(page.locator('.popupPanel')).toContainText(description)
+  }
+  const asideLocator = page.locator('.popupPanel-body__aside')
+  if (status !== undefined) {
+    await expect(asideLocator).toContainText(status)
+  }
+  if (labels !== undefined) {
+    await expect(asideLocator).toContainText(labels)
+  }
+  if (priority !== undefined) {
+    await expect(asideLocator).toContainText(priority)
+  }
+  if (assignee !== undefined) {
+    await expect(asideLocator).toContainText(assignee)
+  }
+  if (project !== undefined) {
+    await expect(asideLocator).toContainText(project)
+  }
+  if (sprint !== undefined) {
+    await expect(asideLocator).toContainText(sprint)
+  }
+}
+
+export async function checkIssueFromList (page: Page, issueName: string): Promise<void> {
+  await page.click(ViewletSelectors.Board)
+  await expect(page.locator(`.panel-container:has-text("${issueName}")`)).toContainText(issueName)
+}
+
+export async function openIssue (page: Page, name: string): Promise<void> {
+  await page.click(`.antiList__row:has-text("${name}") .issuePresenterRoot`)
+}