Fix TSK-101 navigation (#2068)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-06-14 23:54:25 +07:00 committed by GitHub
parent 5d33062176
commit 665ef78199
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 244 additions and 151 deletions

View File

@ -17,13 +17,9 @@
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import { getPlatformColor, ScrollBox, Scroller } from '@anticrm/ui' import { getPlatformColor, ScrollBox, Scroller } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { slide } from 'svelte/transition' import { CardDragEvent, ExtItem, Item, StateType, TypeState } from '../types'
import { DocWithRank, StateType, TypeState } from '../types'
import { calcRank } from '../utils' import { calcRank } from '../utils'
import KanbanRow from './KanbanRow.svelte'
type Item = DocWithRank & { state: StateType; doneState: StateType | null }
type ExtItem = { prev?: Item; it: Item; next?: Item; pos: number }
type CardDragEvent = DragEvent & { currentTarget: EventTarget & HTMLDivElement }
export let _class: Ref<Class<Item>> export let _class: Ref<Class<Item>>
export let space: Ref<Space> export let space: Ref<Space>
@ -144,7 +140,6 @@
return calcRank(object.it, object.next) return calcRank(object.it, object.next)
} }
} }
const slideD = (node: any, args: any) => (args.isDragging ? slide(node, args) : {})
function panelDragOver (event: Event, state: TypeState): void { function panelDragOver (event: Event, state: TypeState): void {
event.preventDefault() event.preventDefault()
@ -184,11 +179,14 @@
let stateObjects: ExtItem[] let stateObjects: ExtItem[]
const stateRefs: HTMLElement[] = [] const stateRefs: HTMLElement[] = []
const stateRows: KanbanRow[] = []
$: stateRefs.length = states.length $: stateRefs.length = states.length
$: stateRows.length = states.length
function scrollInto (statePos: number): void { function scrollInto (statePos: number, obj: Item): void {
stateRefs[statePos]?.scrollIntoView({ behavior: 'auto', block: 'nearest' }) stateRefs[statePos]?.scrollIntoView({ behavior: 'auto', block: 'nearest' })
stateRows[statePos]?.scroll(obj)
} }
export function select (offset: 1 | -1 | 0, of?: Doc, dir?: 'vertical' | 'horizontal'): void { export function select (offset: 1 | -1 | 0, of?: Doc, dir?: 'vertical' | 'horizontal'): void {
@ -227,16 +225,18 @@
if (offset === -1) { if (offset === -1) {
if (dir === undefined || dir === 'vertical') { if (dir === undefined || dir === 'vertical') {
scrollInto(objState) const obj = (stateObjs[statePos - 1] ?? stateObjs[0]).it
dispatch('obj-focus', (stateObjs[statePos - 1] ?? stateObjs[0]).it) scrollInto(objState, obj)
dispatch('obj-focus', obj)
return return
} else { } else {
while (objState > 0) { while (objState > 0) {
objState-- objState--
const nstateObjs = getStateObjects(objects, states[objState]) const nstateObjs = getStateObjects(objects, states[objState])
if (nstateObjs.length > 0) { if (nstateObjs.length > 0) {
scrollInto(objState) const obj = (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it
dispatch('obj-focus', (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it) scrollInto(objState, obj)
dispatch('obj-focus', obj)
break break
} }
} }
@ -244,23 +244,25 @@
} }
if (offset === 1) { if (offset === 1) {
if (dir === undefined || dir === 'vertical') { if (dir === undefined || dir === 'vertical') {
scrollInto(objState) const obj = (stateObjs[statePos + 1] ?? stateObjs[stateObjs.length - 1]).it
dispatch('obj-focus', (stateObjs[statePos + 1] ?? stateObjs[stateObjs.length - 1]).it) scrollInto(objState, obj)
dispatch('obj-focus', obj)
return return
} else { } else {
while (objState < states.length - 1) { while (objState < states.length - 1) {
objState++ objState++
const nstateObjs = getStateObjects(objects, states[objState]) const nstateObjs = getStateObjects(objects, states[objState])
if (nstateObjs.length > 0) { if (nstateObjs.length > 0) {
scrollInto(objState) const obj = (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it
dispatch('obj-focus', (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it) scrollInto(objState, obj)
dispatch('obj-focus', obj)
break break
} }
} }
} }
} }
if (offset === 0) { if (offset === 0) {
scrollInto(objState) scrollInto(objState, obj)
dispatch('obj-focus', obj) dispatch('obj-focus', obj)
} }
} }
@ -309,35 +311,25 @@
{/if} {/if}
<Scroller padding={'.5rem 0'} on:dragover on:drop> <Scroller padding={'.5rem 0'} on:dragover on:drop>
<slot name="beforeCard" {state} /> <slot name="beforeCard" {state} />
{#each stateObjects as object} <KanbanRow
{@const dragged = isDragging && object.it._id === dragCard?._id} bind:this={stateRows[si]}
<div {stateObjects}
transition:slideD|local={{ isDragging }} {isDragging}
class="step-tb75" {dragCard}
on:dragover|preventDefault={(evt) => cardDragOver(evt, object)} {objects}
on:drop|preventDefault={(evt) => cardDrop(evt, object)} {selection}
> {checkedSet}
<div {state}
class="card-container" {cardDragOver}
class:selection={selection !== undefined ? objects[selection]?._id === object.it._id : false} {cardDrop}
class:checked={checkedSet.has(object.it._id)} {onDragStart}
on:mouseover={() => dispatch('obj-focus', object.it)} {showMenu}
on:focus={() => {}} >
on:contextmenu={(evt) => showMenu(evt, object)} <svelte:fragment slot="card" let:object let:dragged>
draggable={true} <slot name="card" {object} {dragged} />
class:draggable={true} </svelte:fragment>
on:dragstart </KanbanRow>
on:dragend
class:dragged
on:dragstart={() => onDragStart(object, state)}
on:dragend={() => {
isDragging = false
}}
>
<slot name="card" object={toAny(object.it)} {dragged} />
</div>
</div>
{/each}
<slot name="afterCard" {space} {state} /> <slot name="afterCard" {space} {state} />
</Scroller> </Scroller>
</div> </div>
@ -361,42 +353,6 @@
padding: 1.5rem 2rem 0; padding: 1.5rem 2rem 0;
} }
.card-container {
background-color: var(--board-card-bg-color);
border-radius: 0.25rem;
// transition: box-shadow .15s ease-in-out;
&:hover {
background-color: var(--board-card-bg-hover);
}
&.checked {
background-color: var(--highlight-select);
box-shadow: inset 0 0 1px 1px var(--highlight-select-border);
&:hover {
background-color: var(--highlight-select-hover);
}
}
&.selection,
&.checked.selection {
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
animation: anim-border 1s ease-in-out;
&:hover {
background-color: var(--highlight-hover);
}
}
&.checked.selection:hover {
background-color: var(--highlight-select-hover);
}
&.draggable {
cursor: grab;
}
&.dragged {
background-color: var(--board-bg-color);
}
}
@keyframes anim-border { @keyframes anim-border {
from { from {
box-shadow: inset 0 0 1px 1px var(--primary-edit-border-color); box-shadow: inset 0 0 1px 1px var(--primary-edit-border-color);

View File

@ -0,0 +1,129 @@
<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Doc, Ref } from '@anticrm/core'
import { createEventDispatcher } from 'svelte'
import { slide } from 'svelte/transition'
import { CardDragEvent, ExtItem, Item, TypeState } from '../types'
export let stateObjects: ExtItem[]
export let isDragging: boolean
export let dragCard: Item | undefined
export let objects: Item[]
export let selection: number | undefined = undefined
export let checkedSet: Set<Ref<Doc>>
export let state: TypeState
export let cardDragOver: (evt: CardDragEvent, object: ExtItem) => void
export let cardDrop: (evt: CardDragEvent, object: ExtItem) => void
export let onDragStart: (object: ExtItem, state: TypeState) => void
export let showMenu: (evt: MouseEvent, object: ExtItem) => void
const dispatch = createEventDispatcher()
function toAny (object: any): any {
return object
}
const slideD = (node: any, args: any) => (args.isDragging ? slide(node, args) : {})
const stateRefs: HTMLElement[] = []
$: stateRefs.length = stateObjects.length
export function scroll (item: Item): void {
const pos = stateObjects.findIndex((it) => it.it._id === item._id)
if (pos >= 0) {
stateRefs[pos]?.scrollIntoView({ behavior: 'auto', block: 'nearest' })
}
}
</script>
{#each stateObjects as object, i}
{@const dragged = isDragging && object.it._id === dragCard?._id}
<div
bind:this={stateRefs[i]}
transition:slideD|local={{ isDragging }}
class="step-tb75"
on:dragover|preventDefault={(evt) => cardDragOver(evt, object)}
on:drop|preventDefault={(evt) => cardDrop(evt, object)}
>
<div
class="card-container"
class:selection={selection !== undefined ? objects[selection]?._id === object.it._id : false}
class:checked={checkedSet.has(object.it._id)}
on:mouseover={() => dispatch('obj-focus', object.it)}
on:focus={() => {}}
on:contextmenu={(evt) => showMenu(evt, object)}
draggable={true}
class:draggable={true}
on:dragstart
on:dragend
class:dragged
on:dragstart={() => onDragStart(object, state)}
on:dragend={() => {
isDragging = false
}}
>
<slot name="card" object={toAny(object.it)} {dragged} />
</div>
</div>
{/each}
<style lang="scss">
.card-container {
background-color: var(--board-card-bg-color);
border-radius: 0.25rem;
// transition: box-shadow .15s ease-in-out;
&:hover {
background-color: var(--board-card-bg-hover);
}
&.checked {
background-color: var(--highlight-select);
box-shadow: inset 0 0 1px 1px var(--highlight-select-border);
&:hover {
background-color: var(--highlight-select-hover);
}
}
&.selection,
&.checked.selection {
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
animation: anim-border 1s ease-in-out;
&:hover {
background-color: var(--highlight-hover);
}
}
&.checked.selection:hover {
background-color: var(--highlight-select-hover);
}
&.draggable {
cursor: grab;
}
&.dragged {
background-color: var(--board-bg-color);
}
}
@keyframes anim-border {
from {
box-shadow: inset 0 0 1px 1px var(--primary-edit-border-color);
}
to {
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
}
}
</style>

View File

@ -19,3 +19,20 @@ export interface TypeState {
color: number color: number
icon?: Asset icon?: Asset
} }
/**
* @public
*/
export type Item = DocWithRank & { state: StateType, doneState: StateType | null }
/**
* @public
*/
export interface ExtItem {
prev?: Item
it: Item
next?: Item
pos: number
}
/**
* @public
*/
export type CardDragEvent = DragEvent & { currentTarget: EventTarget & HTMLDivElement }

View File

@ -45,7 +45,6 @@
<Avatar size={'x-small'} avatar={employee.avatar} /> <Avatar size={'x-small'} avatar={employee.avatar} />
<div class="overflow-label user">{formatName(employee.name)}</div> <div class="overflow-label user">{formatName(employee.name)}</div>
{:else} {:else}
{JSON.stringify(value)}
<div class="overflow-label user">{value.email}</div> <div class="overflow-label user">{value.email}</div>
{/if} {/if}
</div> </div>

View File

@ -14,11 +14,11 @@
--> -->
<script lang="ts"> <script lang="ts">
import { Ref, SortingOrder } from '@anticrm/core' import { Ref, SortingOrder } from '@anticrm/core'
import { Project } from '@anticrm/tracker'
import { IntlString, translate } from '@anticrm/platform' import { IntlString, translate } from '@anticrm/platform'
import { createQuery, getClient } from '@anticrm/presentation' import { getClient } from '@anticrm/presentation'
import { Button, showPopup, SelectPopup, eventToHTMLElement, ButtonShape } from '@anticrm/ui' import { Project } from '@anticrm/tracker'
import type { ButtonKind, ButtonSize } from '@anticrm/ui' import type { ButtonKind, ButtonSize } from '@anticrm/ui'
import { Button, ButtonShape, eventToHTMLElement, SelectPopup, showPopup } from '@anticrm/ui'
import tracker from '../plugin' import tracker from '../plugin'
export let value: Ref<Project> | null | undefined export let value: Ref<Project> | null | undefined
@ -33,23 +33,10 @@
export let width: string | undefined = 'min-content' export let width: string | undefined = 'min-content'
const client = getClient() const client = getClient()
const projectsQuery = createQuery()
let projects: Project[] = []
let selectedProject: Project | undefined let selectedProject: Project | undefined
let defaultProjectLabel = '' let defaultProjectLabel = ''
$: projectsQuery.query(
tracker.class.Project,
{},
(currentProjects) => {
projects = currentProjects
},
{
sort: { modifiedOn: SortingOrder.Ascending }
}
)
$: if (value !== undefined) { $: if (value !== undefined) {
handleSelectedProjectIdUpdated(value) handleSelectedProjectIdUpdated(value)
} }
@ -58,15 +45,6 @@
$: projectIcon = selectedProject?.icon ?? tracker.icon.Projects $: projectIcon = selectedProject?.icon ?? tracker.icon.Projects
$: projectText = shouldShowLabel ? selectedProject?.label ?? defaultProjectLabel : undefined $: projectText = shouldShowLabel ? selectedProject?.label ?? defaultProjectLabel : undefined
$: projectsInfo = [
{ id: null, icon: tracker.icon.Projects, label: tracker.string.NoProject },
...projects.map((p) => ({
id: p._id,
icon: p.icon,
text: p.label
}))
]
const handleSelectedProjectIdUpdated = async (newProjectId: Ref<Project> | null) => { const handleSelectedProjectIdUpdated = async (newProjectId: Ref<Project> | null) => {
if (newProjectId === null) { if (newProjectId === null) {
selectedProject = undefined selectedProject = undefined
@ -77,12 +55,28 @@
selectedProject = await client.findOne(tracker.class.Project, { _id: newProjectId }) selectedProject = await client.findOne(tracker.class.Project, { _id: newProjectId })
} }
const handleProjectEditorOpened = (event: MouseEvent) => { const handleProjectEditorOpened = async (event: MouseEvent): Promise<void> => {
event.stopPropagation() event.stopPropagation()
if (!isEditable) { if (!isEditable) {
return return
} }
const projects = await client.findAll(
tracker.class.Project,
{},
{
sort: { modifiedOn: SortingOrder.Ascending }
}
)
const projectsInfo = [
{ id: null, icon: tracker.icon.Projects, label: tracker.string.NoProject },
...projects.map((p) => ({
id: p._id,
icon: p.icon,
text: p.label
}))
]
showPopup( showPopup(
SelectPopup, SelectPopup,
{ value: projectsInfo, placeholder: popupPlaceholder, searchable: true }, { value: projectsInfo, placeholder: popupPlaceholder, searchable: true },

View File

@ -69,7 +69,11 @@
const options: FindOptions<Issue> = { const options: FindOptions<Issue> = {
lookup: { lookup: {
assignee: contact.class.Employee assignee: contact.class.Employee,
space: tracker.class.Team,
_id: {
subIssues: tracker.class.Issue
}
} }
} }
@ -157,7 +161,7 @@
}} }}
> >
<div class="flex-col mr-6"> <div class="flex-col mr-6">
<IssuePresenter value={object} {currentTeam} /> <IssuePresenter value={object} />
<span class="fs-bold caption-color mt-1 lines-limit-2"> <span class="fs-bold caption-color mt-1 lines-limit-2">
{object.title} {object.title}
</span> </span>

View File

@ -13,40 +13,29 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { createQuery, getClient } from '@anticrm/presentation' import { WithLookup } from '@anticrm/core'
import type { Issue, Team } from '@anticrm/tracker' import type { Issue, Team } from '@anticrm/tracker'
import { Icon, showPanel } from '@anticrm/ui' import { Icon, showPanel } from '@anticrm/ui'
import tracker from '../../plugin' import tracker from '../../plugin'
import { getIssueId } from '../../utils'
export let value: Issue export let value: WithLookup<Issue>
export let currentTeam: Team | undefined
export let inline: boolean = false export let inline: boolean = false
const client = getClient()
const spaceQuery = createQuery()
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
function handleIssueEditorOpened () { function handleIssueEditorOpened () {
showPanel(tracker.component.EditIssue, value._id, value._class, 'content') showPanel(tracker.component.EditIssue, value._id, value._class, 'content')
} }
$: if (!currentTeam) { $: title = `${(value?.$lookup?.space as Team)?.identifier}-${value.number}`
spaceQuery.query(tracker.class.Team, { _id: value.space }, (res) => ([currentTeam] = res))
} else {
spaceQuery.unsubscribe()
}
$: issueName = currentTeam && getIssueId(currentTeam, value)
</script> </script>
{#if value && shortLabel} {#if value}
<div class="flex-presenter issuePresenterRoot" class:inline-presenter={inline} on:click={handleIssueEditorOpened}> <div class="flex-presenter issuePresenterRoot" class:inline-presenter={inline} on:click={handleIssueEditorOpened}>
<div class="icon"> <div class="icon">
<Icon icon={tracker.icon.Issue} size={'small'} /> <Icon icon={tracker.icon.Issue} size={'small'} />
</div> </div>
{#if issueName !== undefined} <span title="title" class="label nowrap issueLabel">
<span title={issueName} class="label nowrap issueLabel">{issueName}</span> {title}
{/if} </span>
</div> </div>
{/if} {/if}

View File

@ -91,7 +91,11 @@
const options: FindOptions<Issue> = { const options: FindOptions<Issue> = {
sort: { [issuesOrderKeyMap[orderingKey]]: issuesSortOrderMap[issuesOrderKeyMap[orderingKey]] }, sort: { [issuesOrderKeyMap[orderingKey]]: issuesSortOrderMap[issuesOrderKeyMap[orderingKey]] },
limit: ENTRIES_LIMIT, limit: ENTRIES_LIMIT,
lookup: { assignee: contact.class.Employee, status: tracker.class.IssueStatus } lookup: {
assignee: contact.class.Employee,
status: tracker.class.IssueStatus,
space: tracker.class.Team
}
} }
$: baseQuery = { $: baseQuery = {

View File

@ -57,7 +57,8 @@
const baseOptions: FindOptions<Issue> = { const baseOptions: FindOptions<Issue> = {
lookup: { lookup: {
assignee: contact.class.Employee, assignee: contact.class.Employee,
status: tracker.class.IssueStatus status: tracker.class.IssueStatus,
space: tracker.class.Team
} }
} }

View File

@ -54,7 +54,11 @@
{ {
sort: { [orderByKey]: issuesSortOrderMap[orderByKey] }, sort: { [orderByKey]: issuesSortOrderMap[orderByKey] },
limit: 200, limit: 200,
lookup: { assignee: contact.class.Employee, status: tracker.class.IssueStatus } lookup: {
assignee: contact.class.Employee,
status: tracker.class.IssueStatus,
space: tracker.class.Team
}
} }
) )
</script> </script>

View File

@ -13,15 +13,14 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { SortingOrder, WithLookup, Ref, Doc } from '@anticrm/core' import { Doc, Ref, WithLookup } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
import { Issue, IssueStatus, Team } from '@anticrm/tracker' import { Issue, IssueStatus, Team } from '@anticrm/tracker'
import { Button, ProgressCircle, showPopup, SelectPopup, closeTooltip, showPanel } from '@anticrm/ui'
import type { ButtonKind, ButtonSize } from '@anticrm/ui' import type { ButtonKind, ButtonSize } from '@anticrm/ui'
import { Button, closeTooltip, ProgressCircle, SelectPopup, showPanel, showPopup } from '@anticrm/ui'
import tracker from '../../../plugin' import tracker from '../../../plugin'
import { getIssueId } from '../../../utils' import { getIssueId } from '../../../utils'
export let issue: Issue export let issue: WithLookup<Issue>
export let currentTeam: Team | undefined export let currentTeam: Team | undefined
export let issueStatuses: WithLookup<IssueStatus>[] | undefined export let issueStatuses: WithLookup<IssueStatus>[] | undefined
@ -30,7 +29,6 @@
export let justify: 'left' | 'center' = 'left' export let justify: 'left' | 'center' = 'left'
export let width: string | undefined = 'min-contet' export let width: string | undefined = 'min-contet'
const subIssuesQuery = createQuery()
let btn: HTMLElement let btn: HTMLElement
let subIssues: Issue[] | undefined let subIssues: Issue[] | undefined
@ -38,9 +36,10 @@
let countComplate: number = 0 let countComplate: number = 0
$: hasSubIssues = issue.subIssues > 0 $: hasSubIssues = issue.subIssues > 0
$: subIssuesQuery.query(tracker.class.Issue, { attachedTo: issue._id }, async (result) => (subIssues = result), { $: if (issue.$lookup?.subIssues !== undefined) {
sort: { rank: SortingOrder.Ascending } subIssues = issue.$lookup.subIssues as Issue[]
}) subIssues.sort((a, b) => a.rank.localeCompare(b.rank))
}
$: if (issueStatuses && subIssues) { $: if (issueStatuses && subIssues) {
doneStatus = issueStatuses.find((s) => s.category === tracker.issueStatusCategory.Completed)?._id ?? undefined doneStatus = issueStatuses.find((s) => s.category === tracker.issueStatusCategory.Completed)?._id ?? undefined
if (doneStatus) countComplate = subIssues.filter((si) => si.status === doneStatus).length if (doneStatus) countComplate = subIssues.filter((si) => si.status === doneStatus).length

View File

@ -54,8 +54,6 @@
} }
</script> </script>
{projectId}
{JSON.stringify(project)}
{#if project} {#if project}
<EditProject {project} /> <EditProject {project} />
{:else} {:else}

View File

@ -69,13 +69,12 @@ test.describe('recruit tests', () => {
await page.click('button:has-text("Vacancy")') await page.click('button:has-text("Vacancy")')
await page.fill('[placeholder="Software\\ Engineer"]', vacancyId) await page.fill('[placeholder="Software\\ Engineer"]', vacancyId)
await page.click('button:has-text("Create")') await page.click('button:has-text("Create")')
await page.locator(`text=${vacancyId}`).click() await page.click(`tr > :has-text("${vacancyId}")`)
await page.click('text=Talents') await page.click('text=Talents')
await page.click('text=Talents') await page.click('text=Talents')
await page.click('text=Andrey P.') await page.click('text=Andrey P.')
// await page.locator('.mixin-selector').locator('text="Candidate"').click()
// Click on Add button // Click on Add button
await page.click('.applications-container .flex-row-center .flex-center') await page.click('.applications-container .flex-row-center .flex-center')
@ -87,7 +86,7 @@ test.describe('recruit tests', () => {
await page.click('button:has-text("Create")') await page.click('button:has-text("Create")')
await page.locator(`tr:has-text("${vacancyId}") >> text=APP-`).click() await page.click(`tr:has-text("${vacancyId}") >> text=APP-`)
await page.click('button:has-text("Assigned recruiter")') await page.click('button:has-text("Assigned recruiter")')
await page.click('button:has-text("Rosamund Chen")') await page.click('button:has-text("Rosamund Chen")')
}) })
@ -100,9 +99,9 @@ test.describe('recruit tests', () => {
await page.locator('text=Vacancies').click() await page.locator('text=Vacancies').click()
await page.click('button:has-text("Vacancy")') await page.click('button:has-text("Vacancy")')
await page.fill('[placeholder="Software\\ Engineer"]', vacancyId) await page.fill('form [placeholder="Software\\ Engineer"]', vacancyId)
await page.click('button:has-text("Create")') await page.click('form button:has-text("Create")')
await page.locator(`text=${vacancyId}`).click() await page.click(`tr > :has-text("${vacancyId}")`)
// Create Applicatio n1 // Create Applicatio n1
await page.click('button:has-text("Application")') await page.click('button:has-text("Application")')

View File

@ -90,17 +90,17 @@ test.describe('recruit tests', () => {
// Click [placeholder="John"] // Click [placeholder="John"]
await page.click('[placeholder="John"]') await page.click('[placeholder="John"]')
// Fill [placeholder="John"] // Fill [placeholder="John"]
const first = 'first-' + generateId().slice(0, 4) const first = 'first-' + generateId(4)
await page.fill('[placeholder="John"]', first) await page.fill('[placeholder="John"]', first)
// Click [placeholder="Appleseed"] // Click [placeholder="Appleseed"]
await page.click('[placeholder="Appleseed"]') await page.click('[placeholder="Appleseed"]')
// Fill [placeholder="Appleseed"] // Fill [placeholder="Appleseed"]
const last = 'last-' + generateId().slice(0, 4) const last = 'last-' + generateId(4)
await page.fill('[placeholder="Appleseed"]', last) await page.fill('[placeholder="Appleseed"]', last)
// Click button:has-text("Create") // Click button:has-text("Create")
await page.click('button:has-text("Create")') await page.click('button:has-text("Create")')
// Click text=q w // Click text=q w
await page.click(`text=${first} ${last}`) await page.click(`tr > :has-text("${first} ${last}")`)
// Click text=java // Click text=java
await expect(page.locator('text=java').first()).toBeVisible() await expect(page.locator('text=java').first()).toBeVisible()
}) })