Added Settings UI tests, UI fix (#6317)

This commit is contained in:
Alexander Platov 2024-08-12 13:30:17 +07:00 committed by GitHub
parent d9640ccaa0
commit 840d26dd01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 255 additions and 15 deletions

View File

@ -41,6 +41,7 @@
export let numberOfBlocks: number = 0
export let thinHeader: boolean = false
export let accentHeader: boolean = false
export let headerNoPadding: boolean = false
export let hideSubheader: boolean = false
export let hideContent: boolean = false
export let hideAttachments: boolean = false
@ -108,6 +109,7 @@
class="antiCard-header"
class:withSub={$$slots.subheader && !hideSubheader}
class:thinHeader
class:noPadding={headerNoPadding}
class:border-bottom-popup-divider={headerDivide}
>
<div class="antiCard-header__title-wrap">

View File

@ -44,6 +44,10 @@
&.withSub.thinHeader { padding: 1rem 1.5rem 0; }
&.thinHeader:not(.withSub) { padding: 1rem 1.5rem; }
&:not(.withSub, .thinHeader) { padding: 1.5rem; }
&.noPadding {
padding: 0 1.5rem;
min-height: 4.75rem;
}
&__title-wrap {
display: flex;

View File

@ -33,6 +33,7 @@
export let allowDeselect: boolean = false
export let showDropdownIcon: boolean = false
export let dataId: string | undefined = undefined
export let kind: ButtonKind = 'no-border'
export let size: ButtonSize = 'small'
export let justify: 'left' | 'center' = 'center'
@ -68,6 +69,7 @@
{justify}
{disabled}
pressed={opened}
{dataId}
showTooltip={{ label, direction: labelDirection }}
on:click={() => {
if (!opened) {

View File

@ -157,6 +157,7 @@
kind={'primary'}
icon={IconAdd}
size={'small'}
dataId={'btnAdd'}
{disabled}
on:click={(ev) => {
createAttribute(ev)

View File

@ -109,6 +109,7 @@
searchField="name"
showNavigate={false}
focusIndex={20000}
id={'selectSpaceType'}
/>
</svelte:fragment>
<div class="flex-col flex-gap-2">

View File

@ -85,7 +85,7 @@
<div class="hulyComponent-content__column-group">
<div class="hulyComponent-content__header">
<div class="flex gap-1">
<ButtonIcon icon={descriptor.icon} size={'large'} kind={'secondary'} />
<ButtonIcon icon={descriptor.icon} size={'large'} kind={'secondary'} dataId={'btnSelectIcon'} />
<ModernEditbox
kind="ghost"
size="large"

View File

@ -63,6 +63,7 @@
kind="primary"
icon={IconAdd}
size="small"
dataId={'btnAdd'}
{disabled}
on:click={(ev) => {
$settingsStore = { id: 'createRole', component: CreateRole, props: { type, descriptor } }

View File

@ -26,6 +26,6 @@
<div class="hulyTableAttr-header font-medium-12">
<IconFolder size="small" />
<span><Label label={task.string.Collections} /></span>
<ButtonIcon kind="primary" icon={IconAdd} size="small" {disabled} on:click={() => {}} />
<ButtonIcon kind="primary" icon={IconAdd} size="small" dataId={'btnAdd'} {disabled} on:click={() => {}} />
</div>
{/if}

View File

@ -63,6 +63,7 @@
kind="primary"
icon={IconAdd}
size="small"
dataId={'btnAdd'}
{disabled}
on:click={(ev) => {
if (disabled) {

View File

@ -2,7 +2,7 @@
import { Class, Doc, Ref, toIdMap } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import task, { ProjectType, TaskType } from '@hcengineering/task'
import { ButtonKind, DropdownLabels } from '@hcengineering/ui'
import { ButtonKind, ButtonSize, DropdownLabels } from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import { selectedTaskTypeStore, taskTypeStore } from '../..'
@ -11,7 +11,9 @@
export let focusIndex: number = -1
export let baseClass: Ref<Class<Doc>> | undefined = undefined
export let kind: ButtonKind = 'regular'
export let size: ButtonSize = 'medium'
export let allTypes = false
const client = getClient()
$: taskTypeDescriptors = toIdMap(client.getModel().findAllSync(task.class.TaskTypeDescriptor, {}))
@ -48,8 +50,9 @@
<DropdownLabels
{focusIndex}
{kind}
size={'medium'}
{size}
{items}
dataId={'btnSelectTaskType'}
bind:selected={value}
enableSearch={false}
on:selected={change}

View File

@ -147,6 +147,7 @@
iconProps={{ value: taskType }}
size={'large'}
kind={'secondary'}
dataId={'btnSelectIcon'}
disabled={readonly}
on:click={selectIcon}
/>

View File

@ -741,6 +741,7 @@
onCancel={showConfirmationDialog}
hideAttachments={attachments.size === 0}
hideSubheader={parentIssue == null}
headerNoPadding
noFade={true}
on:changeContent
>
@ -778,11 +779,16 @@
<DocCreateExtComponent manager={docCreateManager} kind={'header'} space={currentProject} props={extraProps} />
</svelte:fragment>
<svelte:fragment slot="title" let:label>
<div class="flex-row-center gap-2">
<div>
<div class="flex-row-center gap-2 pt-1 pb-1 pr-1">
<span class="overflow-label">
<Label {label} />
</div>
<TaskKindSelector projectType={currentProject?.type} bind:value={kind} baseClass={tracker.class.Issue} />
</span>
<TaskKindSelector
projectType={currentProject?.type}
bind:value={kind}
baseClass={tracker.class.Issue}
size={'small'}
/>
{#if relatedTo}
<div class="lower mr-2">
<Label label={tracker.string.RelatedTo} />

View File

@ -40,6 +40,7 @@
class:disabled
class:selected={selected === color.name}
style="background-color: {col}"
data-id={color.name}
on:click={() => {
if (disabled) return
dispatch('close', i)
@ -59,6 +60,7 @@
class:disabled
class:selected={selected === color.name}
style="background-color: {col}"
data-id={color.name}
on:click={() => {
if (disabled) return
dispatch('close', i)

View File

@ -101,6 +101,7 @@ test.describe('contact tests', () => {
await contractPage.checkIfPersonIsDeleted(first, last, 1)
await contractPage.personRightClickOption(first, last, ButtonAction.NewApplication)
await contractPage.addNewApplication('Test Application', 'CR Chen Rosamund')
await page.waitForTimeout(1000)
await contractPage.clickOnEmployee(first, last)
await contractPage.checkStateApplication('HR Interview')
})

View File

@ -68,6 +68,7 @@ export class ContractPage {
readonly stateApplication = (role: string): Locator => this.page.getByRole('cell', { name: role })
readonly commentApplication = (): Locator => this.page.getByRole('button', { name: '1', exact: true })
readonly commentDescription = (): Locator => this.page.getByText('Test Application')
readonly buttonClosePanel = (): Locator => this.page.locator('button#btnPClose')
// ACTIONS
@ -249,4 +250,8 @@ export class ContractPage {
await this.commentApplication().hover()
await expect(this.commentDescription()).toBeVisible()
}
async closePanel (): Promise<void> {
await this.buttonClosePanel().click()
}
}

View File

@ -0,0 +1,131 @@
import { expect, type Locator } from '@playwright/test'
import { CommonPage } from './common-page'
import { SpaceTypes, TaskTypes } from './types'
export class SettingsPage extends CommonPage {
profileButton = (): Locator => this.page.locator('#profile-button')
settingsButton = (): Locator => this.page.locator('button:has-text("Settings")')
spaceTypesHeader = (): Locator => this.page.locator('button.hulyNavGroup-header', { hasText: 'Space types' })
newSpaceTypeButton = (): Locator => this.page.locator('#new-space-type')
popupSelectSpaceTypeButton = (): Locator =>
this.page.locator('form#setting\\:string\\:NewSpaceType div#selectSpaceType button')
popupSpaceTypeNameInput = (): Locator =>
this.page.locator('form#setting\\:string\\:NewSpaceType').getByPlaceholder('Space type', { exact: true })
popupCreateButton = (): Locator =>
this.page.locator('form#setting\\:string\\:NewSpaceType div.antiCard-footer button', { hasText: 'Create' })
spaceTypeButton = (name: string, category?: string): Locator =>
this.page.locator('div#navGroup-spaceTypes button.hulyTaskNavLink-container', {
hasText: category !== undefined ? `${name} ${category}` : name
})
breadcrumbButton = (hasText: string): Locator => this.page.locator('button.hulyBreadcrumb-container', { hasText })
stateButton = (name: string): Locator =>
this.page
.locator('div.hulyTableAttr-header', { hasText: 'Process states' })
.locator('xpath=..')
.locator('button.hulyTableAttr-content__row', { hasText: name })
statusNameInput = (): Locator =>
this.page.locator('div.hulyModal-container.type-aside', { hasText: 'Edit state' }).getByPlaceholder('Status name')
selectColorButton = (name: string): Locator =>
this.page.locator('div.hulyTableAttr-container', { hasText: 'Color' }).locator(`div.color[data-id="${name}"]`)
selectIconButton = (): Locator => this.page.locator('button[data-id="btnSelectIcon"]')
emojiSectionButton = (): Locator => this.page.locator('div.popup div.tab', { hasText: 'Emoji' })
emojiIconButton = (hasText: string): Locator => this.page.locator('div.popup div.element', { hasText })
taskTypeRow = (value: string): Locator =>
this.page
.locator('div.hulyTableAttr-header', { hasText: 'Task types' })
.locator('xpath=..')
.locator('div.hulyTableAttr-content button.hulyTableAttr-content__row', { hasText: value })
addTaskTypeButton = (): Locator =>
this.page.locator('div.hulyTableAttr-header', { hasText: 'Task types' }).locator('button[data-id="btnAdd"]')
taskNameInput = (): Locator => this.page.getByPlaceholder('Task name *')
taskTypeButton = (): Locator =>
this.page.locator('div.hulyModal-content__settingsSet-line', { hasText: 'Task type' }).locator('button')
asideFooterButton = (hasText: string): Locator =>
this.page.locator('div.hulyModal-container.type-aside div.hulyModal-footer button', { hasText })
async navigateToWorkspace (workspaceUrl: string): Promise<void> {
const response = await this.page.goto(workspaceUrl)
if (response === null || response === undefined) {
throw new Error(`Failed to navigate to ${workspaceUrl}`)
}
await response.finished()
}
async openProfileMenu (): Promise<void> {
await this.profileButton().click()
}
async openSettings (): Promise<void> {
await this.settingsButton().click()
}
async clickAddSpaceType (): Promise<void> {
await this.newSpaceTypeButton().click()
}
async createSpaceType (name: string, spaceType?: SpaceTypes): Promise<void> {
await this.spaceTypesHeader().hover()
await this.newSpaceTypeButton().click()
if (spaceType !== undefined) {
await this.popupSelectSpaceTypeButton().click()
await this.selectPopupMenu(spaceType).click()
}
await this.popupSpaceTypeNameInput().fill(name)
await this.popupCreateButton().click()
}
async selectSpaceType (name: string, category?: string): Promise<void> {
await this.spaceTypeButton(name, category).click()
}
async addTaskType (name: string, taskType?: TaskTypes): Promise<void> {
await this.addTaskTypeButton().click()
await this.taskNameInput().fill(name)
if (taskType !== undefined) {
await this.taskTypeButton().click()
await this.selectPopupItem(taskType)
}
await this.asideFooterButton('Create').click()
}
async checkTaskType (name: string, taskType?: TaskTypes): Promise<void> {
await expect(this.taskTypeRow(`${name} ${taskType ?? TaskTypes.Task}`)).toBeVisible()
}
async openTaskType (name: string, taskType?: TaskTypes): Promise<void> {
await this.taskTypeRow(`${name} ${taskType ?? TaskTypes.Task}`).click()
}
async checkOpened (breadcrumbOne: string, breadcrumbTwo?: string): Promise<void> {
if (breadcrumbTwo !== undefined) await expect(this.breadcrumbButton(breadcrumbTwo)).toBeVisible()
await expect(this.breadcrumbButton(breadcrumbOne)).toBeVisible()
}
async checkState (name: string): Promise<void> {
await expect(this.stateButton(name)).toBeVisible()
}
async changeState (name: string, newName: string, color?: string): Promise<void> {
await this.stateButton(name).click()
expect(await this.statusNameInput().inputValue()).toContain(name)
await this.statusNameInput().fill(newName)
if (color !== undefined) await this.selectColorButton(color).click()
await this.asideFooterButton('Save').click()
}
async changeIcon (): Promise<void> {
await this.selectIconButton().click()
await this.emojiSectionButton().click()
await this.emojiIconButton('❗').click()
}
}

View File

@ -170,10 +170,10 @@ export class IssuesPage extends CommonTrackerPage {
await this.page.keyboard.press('Escape')
}
async createAndOpenIssue (name: string, assignee: string, status: string): Promise<void> {
async createAndOpenIssue (name: string, assignee: string, status: string, taskType?: string): Promise<void> {
try {
await this.notificationTimeoutSetting('5000')
await createIssue(this.page, { name, assignee, status })
await createIssue(this.page, { name, assignee, status, taskType })
await this.page.waitForSelector(`text="${name}"`)
await this.viewIssueButton().click()
} finally {

View File

@ -40,7 +40,14 @@ export class TemplatePage extends CommonTrackerPage {
proseMirrorEditor = (): Locator => this.page.locator('.ProseMirror')
saveTemplateButton = (): Locator => this.page.locator('text=Save template')
editTemplateButton = (): Locator => this.page.locator('text=Edit template')
vacanciesLink = (): Locator => this.page.locator('#new-space-type')
newSpaceTypeButton = (): Locator => this.page.locator('#new-space-type')
spaceTypeButton = (name: string, category?: string): Locator =>
this.page.locator('div#navGroup-spaceTypes button.hulyTaskNavLink-container', {
hasText: category !== undefined ? `${name} ${category}` : name
})
addTaskTypeButton = (): Locator =>
this.page.locator('div.hulyTableAttr-header', { hasText: 'Task types' }).locator('button[data-id="btnAdd"]')
async createNewTemplate (data: NewIssue): Promise<void> {
await this.buttonNewTemplate().click()
@ -114,7 +121,16 @@ export class TemplatePage extends CommonTrackerPage {
}
async selectVacancies (): Promise<void> {
await this.vacanciesLink().click()
await this.newSpaceTypeButton().click()
}
async selectSpaceType (name: string, category?: string): Promise<void> {
await this.spaceTypeButton(name, category).click()
}
async addTaskType (): Promise<void> {
console.log('[!!!] ', await this.page.locator('div.hulyTableAttr-header', { hasText: 'Task types' }).isVisible())
await this.addTaskTypeButton().click()
}
async createTemplate (templateName: string, templateContent: string): Promise<void> {

View File

@ -3,3 +3,21 @@ export interface DateDivided {
month: string
year: string
}
export type SpaceTypes =
| 'Documents'
| 'Drive'
| 'Trainings'
| 'Controlled Documents'
| 'Products'
| 'Recruiting'
| 'Leads'
| 'Tracker'
| 'Boards'
| 'Github'
export enum TaskTypes {
Task = 'Task',
Subtask = 'Sub-task',
TaskAndSubtask = 'Task & Sub-task'
}

View File

@ -1,20 +1,25 @@
import { test } from '@playwright/test'
import { PlatformSetting, PlatformURI } from './utils'
import { PlatformSetting, PlatformURI, generateId } from './utils'
import { UserProfilePage } from './model/profile/user-profile-page'
import { TemplatePage } from './model/tracker/templates-page'
import { SettingsPage } from './model/settings-page'
import { IssuesPage } from './model/tracker/issues-page'
import { TaskTypes } from './model/types'
test.use({
storageState: PlatformSetting
})
test.describe('contact tests', () => {
test.describe('settings tests', () => {
let userProfilePage: UserProfilePage
let templatePage: TemplatePage
let settingsPage: SettingsPage
const platformUri = `${PlatformURI}/workbench/sanity-ws`
const expectedProfileUrl = `${PlatformURI}/workbench/sanity-ws/setting/profile`
test.beforeEach(async ({ page }) => {
userProfilePage = new UserProfilePage(page)
templatePage = new TemplatePage(page)
settingsPage = new SettingsPage(page)
await (await page.goto(`${PlatformURI}/workbench/sanity-ws`))?.finished()
})
@ -38,6 +43,39 @@ test.describe('contact tests', () => {
await templatePage.editTemplate('some more2 value')
})
test('add-task-types', async () => {
const spaceName = `TT-${generateId(4)}`
await settingsPage.navigateToWorkspace(platformUri)
await settingsPage.openProfileMenu()
await settingsPage.openSettings()
await settingsPage.createSpaceType(spaceName, 'Tracker')
await settingsPage.selectSpaceType(spaceName, 'Tracker')
await settingsPage.addTaskType('Issue', TaskTypes.TaskAndSubtask)
await settingsPage.checkTaskType('Issue', TaskTypes.TaskAndSubtask)
await settingsPage.openTaskType('Issue', TaskTypes.TaskAndSubtask)
await settingsPage.checkOpened(spaceName, 'Issue')
})
test('customize-task-types', async ({ page }) => {
const taskTypeName = `Bug-${generateId(4)}`
await settingsPage.navigateToWorkspace(platformUri)
await settingsPage.openProfileMenu()
await settingsPage.openSettings()
await settingsPage.selectSpaceType('Default', 'Tracker')
await settingsPage.addTaskType(taskTypeName, TaskTypes.TaskAndSubtask)
await settingsPage.checkTaskType(taskTypeName, TaskTypes.TaskAndSubtask)
await settingsPage.openTaskType(taskTypeName, TaskTypes.TaskAndSubtask)
await settingsPage.checkOpened('Default', taskTypeName)
await settingsPage.changeIcon()
await settingsPage.checkState('Todo')
await settingsPage.changeState('Todo', 'Needs Attention', 'Firework')
await settingsPage.checkState('New state')
await settingsPage.changeState('New state', 'Under Review', 'Sunshine')
const issuesPage = new IssuesPage(page)
await issuesPage.clickOnApplicationButton()
await issuesPage.createAndOpenIssue('Minor bug', 'Appleseed John', 'Needs Attention', taskTypeName)
})
// TODO: Need rework.
test.skip('manage-templates', async () => {
await templatePage.navigateToWorkspace(platformUri)

View File

@ -13,6 +13,7 @@ export interface IssueProps {
milestone?: string
estimation?: string
dueDate?: string
taskType?: string
}
export enum ViewletSelectors {
@ -56,8 +57,14 @@ export async function setViewOrder (page: Page, orderName: string): Promise<void
}
export async function fillIssueForm (page: Page, props: IssueProps): Promise<void> {
const { name, description, status, assignee, labels, priority, component, milestone } = props
const { name, description, status, assignee, labels, priority, component, milestone, taskType } = props
const af = 'form '
if (taskType !== undefined) {
await page.click(af + 'button[data-id="btnSelectTaskType"]')
await page.click(`.menu-item:has-text("${taskType}")`)
}
const issueTitle = page.locator(af + '[placeholder="Issue\\ title"]')
await issueTitle.fill(name)
await issueTitle.evaluate((e) => {