UBERF-6306 Add UI tests for indexer (#5165)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov 2024-04-03 20:45:05 +07:00 committed by GitHub
parent c69252404a
commit dc7512a844
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 512 additions and 0 deletions

View File

@ -139,6 +139,7 @@
<div class="flex-row-center h-full content-color gap-3"> <div class="flex-row-center h-full content-color gap-3">
<div class="history-box flex-row-center gap-3" class:embedded={embeddedPlatform}> <div class="history-box flex-row-center gap-3" class:embedded={embeddedPlatform}>
<button <button
id="statusbar-back"
class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square" class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square"
style:color={'var(--theme-dark-color)'} style:color={'var(--theme-dark-color)'}
on:click={() => { on:click={() => {
@ -148,6 +149,7 @@
<IconArrowLeft size={'small'} /> <IconArrowLeft size={'small'} />
</button> </button>
<button <button
id="statusbar-forward"
class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square" class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square"
style:color={'var(--theme-dark-color)'} style:color={'var(--theme-dark-color)'}
on:click={() => { on:click={() => {

View File

@ -9,6 +9,7 @@
</script> </script>
<button <button
id="statusbar-search"
class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square" class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square"
on:click={openPopup} on:click={openPopup}
style:color={'var(--theme-dark-color)'} style:color={'var(--theme-dark-color)'}

View File

@ -0,0 +1,431 @@
import { test, expect } from '@playwright/test'
import { NewDocument } from './model/documents/types'
import { LeftSideMenuPage } from './model/left-side-menu-page'
import { DocumentsPage } from './model/documents/documents-page'
import { DocumentContentPage } from './model/documents/document-content-page'
import { IssuesPage } from './model/tracker/issues-page'
import { IssuesDetailsPage } from './model/tracker/issues-details-page'
import { SpotlightPopup } from './model/spotlight-popup'
import { generateId, PlatformSetting, PlatformURI } from './utils'
import { NewIssue } from './model/tracker/types'
import { SignUpData } from './model/common-types'
import { LoginPage } from './model/login-page'
import { SignUpPage } from './model/signup-page'
import { SelectWorkspacePage } from './model/select-workspace-page'
test.use({
storageState: PlatformSetting
})
const retryOptions = { intervals: [1000, 1500, 2500], timeout: 60000 }
test.describe('Fulltext index', () => {
test.beforeEach(async ({ page }) => {
await (await page.goto(`${PlatformURI}/workbench/sanity-ws`))?.finished()
})
test.describe('Documents', () => {
test.beforeEach(async ({ page }) => {
const leftSideMenuPage = new LeftSideMenuPage(page)
await leftSideMenuPage.buttonDocuments.click()
})
test('Search created document', async ({ page }) => {
const titleId = generateId()
const contentId = generateId()
const newDocument: NewDocument = {
title: `Indexable Document ${titleId}`,
space: 'Default'
}
const content = `Indexable document content ${contentId}`
const leftSideMenuPage = new LeftSideMenuPage(page)
const documentsPage = new DocumentsPage(page)
const documentContentPage = new DocumentContentPage(page)
const spotlight = new SpotlightPopup(page)
await test.step('create document', async () => {
await documentsPage.buttonCreateDocument.click()
await documentsPage.createDocument(newDocument)
await documentsPage.openDocument(newDocument.title)
await documentContentPage.checkDocumentTitle(newDocument.title)
await documentContentPage.addContentToTheNewLine(content)
await documentContentPage.checkContent(content)
})
await test.step('close document', async () => {
// Go to inbox to close the document and trigger indexation
await leftSideMenuPage.buttonNotification.click()
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newDocument.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('search by content', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(contentId)
await spotlight.checkSearchResult(newDocument.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
})
test('Search updated document', async ({ page }) => {
const titleId = generateId()
const contentId = generateId()
const newDocument: NewDocument = {
title: `Indexable Document ${titleId}`,
space: 'Default'
}
const content = `Indexable document content ${contentId}`
const updatedTitleId = generateId()
const updatedTitle = `Indexable Document ${updatedTitleId}`
const updatedContentId = generateId()
const updatedContent = `Indexable document content ${updatedContentId}`
const leftSideMenuPage = new LeftSideMenuPage(page)
const documentsPage = new DocumentsPage(page)
const documentContentPage = new DocumentContentPage(page)
const spotlight = new SpotlightPopup(page)
await test.step('create document', async () => {
await documentsPage.buttonCreateDocument.click()
await documentsPage.createDocument(newDocument)
await documentsPage.openDocument(newDocument.title)
await documentContentPage.checkDocumentTitle(newDocument.title)
await documentContentPage.addContentToTheNewLine(content)
await documentContentPage.checkContent(content)
})
await test.step('update document', async () => {
await documentContentPage.updateDocumentTitle(updatedTitle)
await documentContentPage.checkDocumentTitle(updatedTitle)
await documentContentPage.addContentToTheNewLine(updatedContent)
})
await test.step('close document', async () => {
// Go to inbox to close the document and trigger indexation
await leftSideMenuPage.buttonNotification.click()
})
await test.step('search by old title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.checkSearchResult(newDocument.title, 0)
await spotlight.checkSearchResult(updatedTitle, 0)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(updatedTitleId)
await spotlight.checkSearchResult(updatedTitle, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('search by content', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(updatedContentId)
await spotlight.checkSearchResult(updatedTitle, 1)
await spotlight.close()
}).toPass(retryOptions)
})
})
test('Search removed document', async ({ page }) => {
const titleId = generateId()
const contentId = generateId()
const newDocument: NewDocument = {
title: `Indexable Document ${titleId}`,
space: 'Default'
}
const content = `Indexable document content ${contentId}`
const leftSideMenuPage = new LeftSideMenuPage(page)
const documentsPage = new DocumentsPage(page)
const documentContentPage = new DocumentContentPage(page)
const spotlight = new SpotlightPopup(page)
await test.step('create document', async () => {
await documentsPage.buttonCreateDocument.click()
await documentsPage.createDocument(newDocument)
await documentsPage.openDocument(newDocument.title)
await documentContentPage.checkDocumentTitle(newDocument.title)
await documentContentPage.addContentToTheNewLine(content)
await documentContentPage.checkContent(content)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newDocument.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('remove document', async () => {
await documentContentPage.executeMoreAction('Delete')
await documentContentPage.pressYesForPopup(page)
// Go to inbox to close the document and trigger indexation
await leftSideMenuPage.buttonNotification.click()
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newDocument.title, 0)
await spotlight.close()
}).toPass(retryOptions)
})
})
})
test.describe('Issues', () => {
test.beforeEach(async ({ page }) => {
const leftSideMenuPage = new LeftSideMenuPage(page)
await leftSideMenuPage.buttonTracker.click()
})
test('Search created issue', async ({ page }) => {
const titleId = generateId()
const contentId = generateId()
const newIssue: NewIssue = {
title: `Indexable issue ${titleId}`,
description: `Indexable issue content ${contentId}`,
status: 'Backlog'
}
const issuesPage = new IssuesPage(page)
const spotlight = new SpotlightPopup(page)
await test.step('create issue', async () => {
await issuesPage.createNewIssue(newIssue)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newIssue.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('search by content', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(contentId)
await spotlight.checkSearchResult(newIssue.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
})
test('Search updated issue', async ({ page }) => {
const titleId = generateId()
const contentId = generateId()
const updatedTitleId = generateId()
const updatedContentId = generateId()
const updatedTitle = `Indexable issue ${updatedTitleId}`
const updatedContent = `Indexable issue ${updatedContentId}`
const newIssue: NewIssue = {
title: `Indexable issue ${titleId}`,
description: `Indexable issue content ${contentId}`,
status: 'Backlog'
}
const issuesPage = new IssuesPage(page)
const issuesDetailsPage = new IssuesDetailsPage(page)
const spotlight = new SpotlightPopup(page)
await test.step('create issue', async () => {
await issuesPage.createNewIssue(newIssue)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newIssue.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('update issue', async () => {
await issuesPage.linkSidebarAll.click()
await issuesPage.modelSelectorAll.click()
await issuesPage.searchIssueByName(newIssue.title)
await issuesPage.openIssueByName(newIssue.title)
await issuesDetailsPage.editIssue({ title: updatedTitle })
await issuesDetailsPage.addToDescription(updatedContent)
await issuesDetailsPage.buttonCloseIssue.click()
})
await test.step('search by old title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newIssue.title, 0)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(updatedTitleId)
await spotlight.checkSearchResult(updatedTitle, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('search by content', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(updatedContentId)
await spotlight.checkSearchResult(updatedTitle, 1)
await spotlight.close()
}).toPass(retryOptions)
})
})
test('Search removed issue', async ({ page }) => {
const titleId = generateId()
const contentId = generateId()
const newIssue: NewIssue = {
title: `Indexable issue ${titleId}`,
description: `Indexable issue content ${contentId}`,
status: 'Backlog'
}
const issuesPage = new IssuesPage(page)
const issuesDetailsPage = new IssuesDetailsPage(page)
const spotlight = new SpotlightPopup(page)
await test.step('create issue', async () => {
await issuesPage.createNewIssue(newIssue)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newIssue.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('remove issue', async () => {
await issuesPage.linkSidebarAll.click()
await issuesPage.modelSelectorAll.click()
await issuesPage.searchIssueByName(newIssue.title)
await issuesPage.openIssueByName(newIssue.title)
await issuesDetailsPage.moreActionOnIssue('Delete')
await issuesDetailsPage.pressYesForPopup(page)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newIssue.title, 0)
await spotlight.close()
}).toPass(retryOptions)
})
})
})
test.describe('Indexed data does not leak cross workspace', () => {
test('Search issue from another workspace', async ({ page }) => {
const titleId = generateId()
const contentId = generateId()
const newIssue: NewIssue = {
title: `Indexable issue ${titleId}`,
description: `Indexable issue content ${contentId}`,
status: 'Backlog'
}
const loginPage = new LoginPage(page)
const signUpPage = new SignUpPage(page)
const selectWorkspacePage = new SelectWorkspacePage(page)
const leftSideMenuPage = new LeftSideMenuPage(page)
const issuesPage = new IssuesPage(page)
const spotlight = new SpotlightPopup(page)
await test.step('create issue', async () => {
await leftSideMenuPage.buttonTracker.click()
await issuesPage.createNewIssue(newIssue)
})
await test.step('search by title', async () => {
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newIssue.title, 1)
await spotlight.close()
}).toPass(retryOptions)
})
await test.step('create workspace', async () => {
const newUser: SignUpData = {
firstName: `FirstName-${generateId()}`,
lastName: `LastName-${generateId()}`,
email: `email+${generateId()}@gmail.com`,
password: '1234'
}
const newWorkspaceName = `New Workspace Name - ${generateId(2)}`
await loginPage.goto()
await loginPage.linkSignUp.click()
await signUpPage.signUp(newUser)
await selectWorkspacePage.createWorkspace(newWorkspaceName)
})
await test.step('search by title', async () => {
await leftSideMenuPage.buttonTracker.click()
await expect(async () => {
await spotlight.open()
await spotlight.fillSearchInput(titleId)
await spotlight.checkSearchResult(newIssue.title, 0)
await spotlight.close()
}).toPass(retryOptions)
})
})
})
})

View File

@ -26,6 +26,7 @@ export class DocumentContentPage extends CommonPage {
} }
async addContentToTheNewLine (newContent: string): Promise<string> { async addContentToTheNewLine (newContent: string): Promise<string> {
await expect(this.inputContent).toBeVisible()
await this.inputContent.pressSequentially(`\n${newContent}`) await this.inputContent.pressSequentially(`\n${newContent}`)
const endContent = await this.inputContent.textContent() const endContent = await this.inputContent.textContent()
if (endContent == null) { if (endContent == null) {

View File

@ -0,0 +1,44 @@
import { type Locator, type Page, expect } from '@playwright/test'
import { CommonPage } from './common-page'
import { StatusBar } from './statusbar'
export class SpotlightPopup extends CommonPage {
readonly page: Page
readonly popup: Locator
readonly input: Locator
readonly statusbar: StatusBar
constructor (page: Page) {
super()
this.page = page
this.popup = page.locator('div.popup')
this.input = this.popup.locator('input')
this.statusbar = new StatusBar(page)
}
async open (): Promise<void> {
const visible = await this.popup.isVisible()
if (visible) {
await this.close()
}
await this.statusbar.clickButtonSearch()
await expect(this.popup).toBeVisible()
}
async close (): Promise<void> {
await this.page.keyboard.press('Escape')
await expect(this.popup).not.toBeVisible()
}
async fillSearchInput (search: string): Promise<void> {
await this.input.fill(search)
await expect(this.input).toHaveValue(search)
await this.page.waitForTimeout(500)
}
async checkSearchResult (search: string, count: number): Promise<void> {
const result = this.popup.locator('div.list-item', { hasText: search })
await expect(result).toHaveCount(count)
}
}

View File

@ -0,0 +1,29 @@
import { Locator, Page } from '@playwright/test'
export class StatusBar {
readonly page: Page
readonly statusbar: Locator
readonly buttonBack: Locator
readonly buttonForward: Locator
readonly buttonSearch: Locator
constructor (page: Page) {
this.page = page
this.statusbar = page.locator('div.antiStatusBar')
this.buttonBack = this.statusbar.locator('button[id="statusbar-back"]')
this.buttonForward = this.statusbar.locator('button[id="statusbar-forward"]')
this.buttonSearch = this.statusbar.locator('button[id="statusbar-search"]')
}
async clickButtonBack (): Promise<void> {
await this.buttonBack.click()
}
async clickButtonForward (): Promise<void> {
await this.buttonForward.click()
}
async clickButtonSearch (): Promise<void> {
await this.buttonSearch.click()
}
}

View File

@ -55,6 +55,9 @@ export class IssuesDetailsPage extends CommonTrackerPage {
} }
async editIssue (data: Issue): Promise<void> { async editIssue (data: Issue): Promise<void> {
if (data.title != null) {
await this.inputTitle.fill(data.title)
}
if (data.status != null) { if (data.status != null) {
await this.buttonStatus.click() await this.buttonStatus.click()
await this.selectFromDropdown(this.page, data.status) await this.selectFromDropdown(this.page, data.status)

View File

@ -4,6 +4,7 @@ export interface NewIssue extends Issue {
} }
export interface Issue { export interface Issue {
title?: string
status?: string status?: string
priority?: string priority?: string
assignee?: string assignee?: string