Improve My issues and fix few UI test instabilities (#4009)

This commit is contained in:
Andrey Sobolev 2023-11-18 15:20:19 +07:00 committed by GitHub
parent ce10be3877
commit a4ed8c3a10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 107 additions and 27 deletions

View File

@ -613,6 +613,12 @@ export const taskOperation: MigrateOperation = {
{
state: 'migrateProjectTypes',
func: migrateProjectTypes
},
{
state: 'projectTypeSpace',
func: async (client) => {
await client.update(DOMAIN_SPACE, { space: core.space.Model }, { space: core.space.Space })
}
}
])
},

View File

@ -330,6 +330,8 @@ function defineApplication (
component: tracker.component.MyIssues,
componentProps: {
config: [
['active', tracker.string.Active, {}],
['backlog', tracker.string.Backlog, {}],
['assigned', view.string.Assigned, {}],
['created', view.string.Created, {}],
['subscribed', view.string.Subscribed, {}]
@ -346,9 +348,9 @@ function defineApplication (
space: undefined,
title: tracker.string.AllIssues,
config: [
['all', tracker.string.All, {}],
['active', tracker.string.Active, {}],
['backlog', tracker.string.Backlog, {}]
['backlog', tracker.string.Backlog, {}],
['all', tracker.string.All, {}]
]
}
},
@ -385,9 +387,9 @@ function defineApplication (
componentProps: {
title: tracker.string.Issues,
config: [
['all', tracker.string.All, {}],
['active', tracker.string.Active, {}],
['backlog', tracker.string.Backlog, {}]
['backlog', tracker.string.Backlog, {}],
['all', tracker.string.All, {}]
]
}
},

View File

@ -10,6 +10,9 @@
border: 1px solid transparent;
transition-property: border, background-color, color, box-shadow;
transition-duration: .15s;
&.testing {
transition-duration: 0;
}
&.inline {
height: 1.375rem;

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import { deviceOptionsStore as deviceInfo, resizeObserver } from '..'
import { deviceOptionsStore as deviceInfo, resizeObserver, testing } from '..'
import { fitPopupElement } from '../popups'
import type { AnySvelteComponent, PopupAlignment, PopupOptions, PopupPositionElement, DeviceOptions } from '../types'
@ -144,8 +144,9 @@
/>
<div
class="popup {showing === undefined ? 'endShow' : showing === false ? 'preShow' : 'startShow'}"
class:anim={element === 'float' || element === 'centered'}
class="popup {testing ? 'endShow' : showing === undefined ? 'endShow' : showing === false ? 'preShow' : 'startShow'}"
class:testing
class:anim={(element === 'float' || element === 'centered') && !testing}
bind:this={modalHTML}
style={`z-index: ${zIndex + 1};`}
style:top={options?.props?.top}
@ -187,6 +188,7 @@
{#if overlay}
<div
class="modal-overlay"
class:testing
class:antiOverlay={options?.showOverlay}
style={`z-index: ${zIndex};`}
on:click={handleOverlayClick}
@ -235,5 +237,8 @@
height: 100vh;
transition: background-color 0.5s ease;
touch-action: none;
&.testing {
transition: background-color 0 ease;
}
}
</style>

View File

@ -256,3 +256,5 @@ export class DelayedCaller {
}
}
}
export const testing = (localStorage.getItem('#platform.testing.enabled') ?? 'false') === 'true'

View File

@ -79,7 +79,7 @@
kind={'primary'}
size={'medium'}
{items}
selected={account.role.toString()}
selected={account.role?.toString()}
on:selected={(e) => {
change(account, Number(e.detail))
}}

View File

@ -27,7 +27,7 @@
}
</script>
{#if value}
{#if value && Array.isArray(value.parents)}
<div class="root" style:max-width={maxWidth}>
<span class="names">
{#each value.parents as parentInfo}

View File

@ -17,7 +17,7 @@
import { Doc, DocumentQuery, getCurrentAccount, Ref } from '@hcengineering/core'
import type { IntlString } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import type { Issue, Project } from '@hcengineering/tracker'
import type { Issue, IssueStatus, Project } from '@hcengineering/tracker'
import { resolvedLocationStore } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
@ -36,6 +36,34 @@
let modeSelectorProps: IModeSelector | undefined = undefined
let mode: string | undefined = undefined
const activeStatusQuery = createQuery()
let activeStatuses: Ref<IssueStatus>[] = []
$: activeStatusQuery.query(
tracker.class.IssueStatus,
{ category: { $in: [tracker.issueStatusCategory.Unstarted, tracker.issueStatusCategory.Started] } },
(result) => {
activeStatuses = result.map(({ _id }) => _id)
}
)
let active: DocumentQuery<Issue>
$: active = { status: { $in: activeStatuses }, ...assigned }
const backlogStatusQuery = createQuery()
let backlogStatuses: Ref<IssueStatus>[] = []
let backlog: DocumentQuery<Issue> = {}
$: backlogStatusQuery.query(
tracker.class.IssueStatus,
{ category: tracker.issueStatusCategory.Backlog },
(result) => {
backlogStatuses = result.map(({ _id }) => _id)
}
)
$: backlog = { status: { $in: backlogStatuses }, ...assigned }
const subscribedQuery = createQuery()
$: subscribedQuery.query(
tracker.class.Issue,
@ -62,7 +90,7 @@
{ projection: { _id: 1 } }
)
$: queries = { assigned, created, subscribed }
$: queries = { assigned, active, backlog, created, subscribed }
$: mode = $resolvedLocationStore.query?.mode ?? undefined
$: if (mode === undefined || (queries as any)[mode] === undefined) {
;[[mode]] = config

View File

@ -20,6 +20,10 @@
"name": "#platform.notification.timeout",
"value": "0"
},
{
"name": "#platform.testing.enabled",
"value": "true"
},
{
"name": "#platform.notification.logging",
"value": "false"

View File

@ -5,7 +5,7 @@ export class CommonPage {
if (name !== 'first') {
await page.locator('div.selectPopup input').fill(name.split(' ')[0])
}
await page.locator('div.selectPopup div.list-item:first-child').click({ delay: 500 })
await page.locator('div.selectPopup div.list-item:first-child').click()
}
async pressCreateButtonSelectPopup (page: Page): Promise<void> {
@ -50,6 +50,6 @@ export class CommonPage {
if (name !== 'first') {
await page.locator('div.selectPopup input').fill(name.split(' ')[0])
}
await page.locator('div.selectPopup div.list-item').click({ delay: 500 })
await page.locator('div.selectPopup div.list-item').click()
}
}

View File

@ -6,6 +6,9 @@ import { CommonTrackerPage } from './common-tracker-page'
export class IssuesPage extends CommonTrackerPage {
readonly page: Page
readonly pageHeader: Locator
readonly modelSelectorAll: Locator
readonly modelSelectorActive: Locator
readonly modelSelectorBacklog: Locator
readonly buttonCreateNewIssue: Locator
readonly inputPopupCreateNewIssueTitle: Locator
readonly inputPopupCreateNewIssueDescription: Locator
@ -25,7 +28,10 @@ export class IssuesPage extends CommonTrackerPage {
constructor (page: Page) {
super(page)
this.page = page
this.pageHeader = page.locator('span[class*="header"]', { hasText: 'Issues' })
this.pageHeader = page.locator('div[class*="header"]', { hasText: 'Issues' })
this.modelSelectorAll = this.pageHeader.locator('text=All')
this.modelSelectorActive = this.pageHeader.locator('text=Active')
this.modelSelectorBacklog = this.pageHeader.locator('text=Backlog')
this.buttonCreateNewIssue = page.locator('button > span', { hasText: 'New issue' })
this.inputPopupCreateNewIssueTitle = page.locator('form[id="tracker:string:NewIssue"] input[type="text"]')
this.inputPopupCreateNewIssueDescription = page.locator('form[id="tracker:string:NewIssue"] div.tiptap')
@ -86,7 +92,7 @@ export class IssuesPage extends CommonTrackerPage {
await this.selectMenuItem(this.page, data.component)
}
if (data.estimation != null) {
await this.buttonPopupCreateNewIssueEstimation.click({ delay: 100 })
await this.buttonPopupCreateNewIssueEstimation.click()
await this.fillToSelectPopup(this.page, data.estimation)
}
if (data.milestone != null) {

View File

@ -34,6 +34,7 @@ test.describe('tracker issue tests', () => {
await leftSideMenuPage.buttonTracker.click()
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
await issuesPage.createNewIssue(newIssue)
await issuesPage.searchIssueByName(newIssue.title)
await issuesPage.openIssueByName(newIssue.title)
@ -67,6 +68,7 @@ test.describe('tracker issue tests', () => {
await leftSideMenuPage.buttonTracker.click()
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
await issuesPage.createNewIssue(newIssue)
await issuesPage.searchIssueByName(newIssue.title)
await issuesPage.openIssueByName(newIssue.title)

View File

@ -14,6 +14,7 @@ import {
ViewletSelectors
} from './tracker.utils'
import { fillSearch, generateId, PlatformSetting } from '../utils'
import { IssuesPage } from '../model/tracker/issues-page'
test.use({
storageState: PlatformSetting
})
@ -81,18 +82,25 @@ async function initIssues (prefix: string, page: Page): Promise<IssueProps[]> {
const issuesProps = await createIssues(prefix, page, components, milestones)
await page.click('text="Issues"')
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
return issuesProps
}
test.describe('tracker layout tests', () => {
const id = generateId(4)
let issuesPropsP: Promise<IssueProps[]>
let issuesProps: IssueProps[] = []
test.beforeEach(async ({ page }) => {
test.setTimeout(120000)
test.setTimeout(60000)
await navigate(page)
issuesProps = await initIssues(id, page)
if (issuesPropsP === undefined) {
issuesPropsP = initIssues(id, page)
}
issuesProps = await issuesPropsP
})
let issuesProps: IssueProps[] = []
const orders = ['Status', 'Modified', 'Priority'] as const
const groups = ['Status', 'Assignee', 'Priority', 'Component', 'Milestone', 'No grouping'] as const
const groupsLabels: { [key in (typeof groups)[number]]?: string[] } = {
@ -115,6 +123,8 @@ test.describe('tracker layout tests', () => {
}
const issueNames = issuesProps.map((props) => props.name)
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
await page.click(ViewletSelectors.Table)
await expect(locator).toContainText(groupLabels)
@ -130,7 +140,7 @@ test.describe('tracker layout tests', () => {
let orderedIssueNames: string[]
if (order === 'Priority') {
orderedIssueNames = issuesProps
orderedIssueNames = [...issuesProps]
.sort((propsLeft, propsRight) => {
if (propsLeft.priority === undefined || propsRight.priority === undefined) {
return -1
@ -149,7 +159,7 @@ test.describe('tracker layout tests', () => {
})
.map((p) => p.name)
} else if (order === 'Status') {
orderedIssueNames = issuesProps
orderedIssueNames = [...issuesProps]
.sort((propsLeft, propsRight) => {
if (propsLeft.status !== undefined && propsRight.status !== undefined) {
if (propsLeft.status === propsRight.status) {
@ -169,6 +179,8 @@ test.describe('tracker layout tests', () => {
} else {
orderedIssueNames = issuesProps.map((props) => props.name).reverse()
}
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
await page.click(ViewletSelectors.Board)
await setViewGroup(page, 'No grouping')
await setViewOrder(page, order)
@ -176,7 +188,7 @@ test.describe('tracker layout tests', () => {
await fillSearch(page, id)
await expect(locator).toContainText(orderedIssueNames, {
timeout: 15000
timeout: 5000
})
})
}

View File

@ -1,4 +1,5 @@
import { test, expect } from '@playwright/test'
import { test } from '@playwright/test'
import { IssuesPage } from '../model/tracker/issues-page'
import { PlatformSetting, PlatformURI } from '../utils'
test.use({
storageState: PlatformSetting
@ -9,9 +10,13 @@ test('check-status-loading', async ({ page }) => {
await page.goto(`${PlatformURI}/workbench/sanity-ws/tracker/tracker%3Aproject%3ADefaultProject/issues`)
)?.finished()
await expect(page.locator('.categoryHeader :text-is("In Progress")').first()).toBeVisible()
await expect(page.locator('.categoryHeader :text-is("Backlog")').first()).toBeVisible()
await expect(page.locator('.categoryHeader :text-is("Todo")').first()).toBeVisible()
await expect(page.locator('.categoryHeader :text-is("Done")').first()).toBeVisible()
await expect(page.locator('.categoryHeader :text-is("Canceled")').first()).toBeVisible()
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
// TODO: Test should create issues before checking for status loading
// await expect(page.locator('.categoryHeader :text-is("In Progress")').first()).toBeVisible()
// await expect(page.locator('.categoryHeader :text-is("Backlog")').first()).toBeVisible()
// await expect(page.locator('.categoryHeader :text-is("Todo")').first()).toBeVisible()
// await expect(page.locator('.categoryHeader :text-is("Done")').first()).toBeVisible()
// await expect(page.locator('.categoryHeader :text-is("Canceled")').first()).toBeVisible()
})

View File

@ -12,6 +12,7 @@ import {
toTime
} from './tracker.utils'
import { PlatformSetting, fillSearch, generateId } from '../utils'
import { IssuesPage } from '../model/tracker/issues-page'
test.use({
storageState: PlatformSetting
})
@ -198,6 +199,8 @@ test('report-time-from-main-view', async ({ page }) => {
await navigate(page)
await page.click('text="Issues"')
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
await page.click('button:has-text("View")')
await page.click('.ordering >> nth=0')
await page.click('text="Modified date"')
@ -313,6 +316,8 @@ test('sub-issue-draft', async ({ page }) => {
await navigate(page)
await createIssue(page, props)
await page.click('text="Issues"')
const issuesPage = new IssuesPage(page)
await issuesPage.modelSelectorAll.click()
await fillSearch(page, props.name)