Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .woodpecker.star
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ def e2eTests(ctx):
for browser_name in browsers_for_suite:
environment = {
"HEADLESS": True,
"RETRY": "1",
"RETRY": "0",
"REPORT_TRACING": params["reportTracing"],
"OC_BASE_URL": "opencloud:9200",
"OC_SHOW_USER_EMAIL_IN_RESULTS": True,
Expand Down
33 changes: 16 additions & 17 deletions tests/e2e/cucumber/features/search/search.feature
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,21 @@ Feature: Search
| resource |
| folder |
| FolDer |
| PARENT |
| new-lorem-big.txt |

# subfolder search
And "Alice" searches "child" using the global search and the "all files" filter
Then following resources should be displayed in the search list for user "Alice"
| resource |
| child-one |
| child-two |
| resource |
| FolDer/child-one |
| FolDer/child-one/child-two |
But following resources should not be displayed in the search list for user "Alice"
| resource |
| folder |
| FolDer |
| folder_from_brian |
| .hidden-file.txt |
| new-lorem-big.txt |
| resource |
| folder |
| FolDer |
| new_share_from_brian |
| .hidden-file.txt |
| new-lorem-big.txt |

# received shares search
And "Alice" searches "NEW" using the global search and the "all files" filter
Expand Down Expand Up @@ -136,16 +135,16 @@ Feature: Search
When "Alice" opens folder "mainFolder"
And "Alice" searches "example" using the global search and the "all files" filter
Then following resources should be displayed in the search list for user "Alice"
| resource |
| exampleInsideThePersonalSpace.txt |
| exampleInsideTheMainFolder.txt |
| exampleInsideTheSubFolder.txt |
| resource |
| exampleInsideThePersonalSpace.txt |
| mainFolder/exampleInsideTheMainFolder.txt |
| mainFolder/subFolder/exampleInsideTheSubFolder.txt |

When "Alice" searches "example" using the global search and the "current folder" filter
Then following resources should be displayed in the search list for user "Alice"
| resource |
| exampleInsideTheMainFolder.txt |
| exampleInsideTheSubFolder.txt |
| resource |
| mainFolder/exampleInsideTheMainFolder.txt |
| mainFolder/subFolder/exampleInsideTheSubFolder.txt |
But following resources should not be displayed in the search list for user "Alice"
| resource |
| exampleInsideThePersonalSpace.txt |
Expand Down
8 changes: 4 additions & 4 deletions tests/e2e/cucumber/features/search/searchProjectSpace.feature
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ Feature: Search in the project space
# search for project space objects
When "Alice" searches "-'s" using the global search and the "all files" filter
Then following resources should be displayed in the search list for user "Alice"
| resource |
| new-'single'quotes.txt |
| resource |
| folder(WithSymbols:!;_+-&)/new-'single'quotes.txt |
But following resources should not be displayed in the search list for user "Alice"
| resource |
| folder(WithSymbols:!;_+-&) |
Expand All @@ -33,6 +33,6 @@ Feature: Search in the project space
| resource |
| folder(WithSymbols:!;_+-&) |
But following resources should not be displayed in the search list for user "Alice"
| resource |
| new-'single'quotes.txt |
| resource |
| folder(WithSymbols:!;_+-&)/new-'single'quotes.txt |
And "Alice" logs out
9 changes: 8 additions & 1 deletion tests/e2e/cucumber/features/smoke/trashbinDelete.feature
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,24 @@ Feature: Trashbin delete
Given "Alice" creates the following folders in personal space using API
| name |
| folderToShare |
| empty-folder |
| empty-folder |
And "Alice" creates the following files into personal space using API
| pathToFile | content |
| folderToShare/lorem.txt | lorem ipsum |
| sample.txt | sample |
And "Alice" opens the "files" app
And following resources should be displayed in the files list for user "Alice"
| resource |
| sample.txt |
And "Alice" shares the following resource using the sidebar panel
| resource | recipient | type | role | resourceType |
| folderToShare | Brian | user | Can edit | folder |
And "Brian" logs in
And "Brian" navigates to the shared with me page
And "Brian" opens folder "folderToShare"
And following resources should be displayed in the files list for user "Brian"
| resource |
| lorem.txt |
When "Brian" deletes the following resources using the sidebar panel
| resource |
| lorem.txt |
Expand Down
37 changes: 30 additions & 7 deletions tests/e2e/cucumber/steps/ui/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { expect } from '@playwright/test'
import { config } from '../../../config'
import {
createResourceTypes,
displayedResourceType,
shortcutType,
ActionViaType,
PanelType
Expand All @@ -16,6 +15,7 @@ import { Resource } from '../../../support/objects/app-files'
import * as runtimeFs from '../../../support/utils/runtimeFs'
import { searchFilter } from '../../../support/objects/app-files/resource/actions'
import { File } from '../../../support/types'
import { waitProcessingToFinish } from '../../../support/objects/app-files/fileEvents'

When(
'{string} creates the following resource(s)',
Expand Down Expand Up @@ -365,21 +365,44 @@ When(
)

Then(
/^following resources (should|should not) be displayed in the (search list|files list|Shares|trashbin) for user "([^"]*)"$/,
/^following resources (should|should not) be displayed in the (?:files list|Shares|trashbin) for user "([^"]*)"$/,
async function (
this: World,
actionType: string,
stepUser: string,
stepTable: DataTable
): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const resourceObject = new objects.applicationFiles.Resource({ page })
for (const info of stepTable.hashes()) {
if (actionType === 'should') {
await expect(resourceObject.getResourceLocator(info.resource)).toBeVisible({
timeout: config.timeout * 1000
})
await waitProcessingToFinish(page, info.resource)
return
}
await expect(resourceObject.getResourceLocator(info.resource)).not.toBeVisible()
}
}
)

Then(
/^following resources (should|should not) be displayed in the search list for user "([^"]*)"$/,
async function (
this: World,
actionType: string,
listType: string,
stepUser: string,
stepTable: DataTable
): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const resourceObject = new objects.applicationFiles.Resource({ page })
const actualList = await resourceObject.getDisplayedResources({
keyword: listType as displayedResourceType
})
for (const info of stepTable.hashes()) {
expect(actualList.includes(info.resource)).toBe(actionType === 'should')
if (actionType === 'should') {
await expect(resourceObject.getResourceSearchItemLocator(info.resource)).toBeVisible()
return
}
await expect(resourceObject.getResourceSearchItemLocator(info.resource)).not.toBeVisible()
}
}
)
Expand Down
68 changes: 68 additions & 0 deletions tests/e2e/support/objects/app-files/fileEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Page, Locator } from '@playwright/test'
import util from 'util'
import { config } from '../../../config'

const resourceNameSelector =
':is(#files-files-table, .oc-tiles-item, #files-shared-with-me-accepted-section, .files-table) [data-test-resource-name="%s"]'
const resourceProcessingIcon =
'//*[@data-test-resource-name="%s"]/ancestor::*[self::li or self::tr]//span[@data-test-indicator-type="resource-processing"]'
const resourceLockIcon =
'//*[@data-test-resource-name="%s"]/ancestor::*[self::li or self::tr]//span[@data-test-indicator-type="resource-locked"]'

const getResourceLocator = (page: Page, resource: string): Locator => {
return page.locator(util.format(resourceNameSelector, resource))
}

const getProcessingLocator = (page: Page, resource: string): Locator => {
return page.locator(util.format(resourceProcessingIcon, resource))
}

export const getLockLocator = (page: Page, resource: string): Locator => {
return page.locator(util.format(resourceLockIcon, resource))
}

export const waitProcessingToFinish = async (page: Page, resource: string): Promise<void> => {
await waitNotToBeVisible(
page,
getProcessingLocator(page, resource),
'Waiting for file processing to finish',
() => getResourceLocator(page, resource).waitFor()
)
}

export const waitForLockToDisappear = async (page: Page, resource: string): Promise<void> => {
await waitNotToBeVisible(
page,
getLockLocator(page, resource),
'Waiting for file lock to be removed',
() => getResourceLocator(page, resource).waitFor()
)
}

const waitNotToBeVisible = async (
page: Page,
locator: Locator,
message: string,
fn?: () => Promise<void>
) => {
const timeout = (config.timeout / 2) * 1000
const startTime = Date.now()
let elapsedTime = 0

while (elapsedTime < timeout) {
if (!(await locator.isVisible())) {
return
}
console.info(`[INFO] ${message}...`)
await page.waitForTimeout(config.minTimeout * 1000)
await page.reload()

if (fn) {
await fn()
}

elapsedTime = Date.now() - startTime
}

await Promise.reject(new Error(`${timeout}ms timeout exceeded:`))
}
1 change: 1 addition & 0 deletions tests/e2e/support/objects/app-files/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { Share } from './share'
export { Spaces } from './spaces'
export { Trashbin } from './trashbin'
export { Search } from './search'
export * as fileEvents from './fileEvents'
60 changes: 46 additions & 14 deletions tests/e2e/support/objects/app-files/resource/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { editor, sidebar } from '../utils'
import { environment, utils } from '../../../../support'
import { config } from '../../../../config'
import { File, Space } from '../../../types'
import { waitProcessingToFinish } from '../fileEvents'

const appLoadingSpinner = '#app-loading-spinner'
const topbarFilenameSelector = '#app-top-bar-resource .oc-resource-name'
const downloadFileButtonSingleShareView = '.oc-files-actions-download-file-trigger'
const downloadFolderButtonSingleShareView = '.oc-files-actions-download-archive-trigger'
Expand Down Expand Up @@ -41,9 +43,7 @@ const createNewOfficeDocumentFileBUtton = '//div[@id="new-file-menu-drop"]//span
const createNewShortcutButton = '#new-shortcut-btn'
const shortcutResorceInput = '#create-shortcut-modal-url-input'
const saveTextFileInEditorButton = '#app-save-action:visible'
const textEditor = '#text-editor #text-editor-container'
const textEditorPlainTextInput = '#text-editor #text-editor-container .cm-content'
const textEditorMarkdownInput = '#text-editor #text-editor-container .cm-content'
const resourceNameInput = '.oc-modal input'
const resourceUploadButton = '#upload-menu-btn'
const fileUploadInput = '#files-file-upload-input'
Expand Down Expand Up @@ -76,6 +76,7 @@ const searchList =
'//div[@id="files-global-search-options"]//li[contains(@class,"preview")]//span[contains(@class,"oc-resource-name")]'
const globalSearchOptions = '#files-global-search-options'
const loadingSpinner = '#files-global-search-options .loading'
const searchListItem = '#files-global-search span[data-test-resource-name="%s"]'
const filesViewOptionButton = '#files-view-options-btn'
const hiddenFilesToggleButton = '//*[@data-testid="files-switch-hidden-files"]//button'
const previewImage = '//main[@id="preview"]//div[contains(@class,"stage_media")]//img'
Expand Down Expand Up @@ -128,6 +129,10 @@ const userAvatarInActivitypanelSelector = '[data-test-user-name="%s"]'
const mobileViewmodeSwitchBtn = '#mobile-viewmode-switch-toggle'
const mobileViewmodeSwitchDropdown = '#mobile-viewmode-switch-drop'

// file viewer
const pdfViewerContainer = '#pdf-viewer object.pdf-viewer'
const textEditorContainer = '#text-editor-container div.md-editor-content'

// online office locators
// Collabora
const collaboraDocPermissionModeSelector = '#permissionmode-container'
Expand All @@ -152,6 +157,26 @@ const openWithButton = '//*[@id="oc-files-context-actions-context"]//span[text()
const tilesSlider = '#tiles-size-slider'
const undoBtn = 'action-handler'

export const getResourceLocator = ({
page,
resource
}: {
page: Page
resource: string
}): Locator => {
return page.locator(util.format(resourceNameSelector, resource))
}

export const getResourceSearchItemLocator = ({
page,
resource
}: {
page: Page
resource: string
}): Locator => {
return page.locator(util.format(searchListItem, resource))
}

export const clickResource = async ({
page,
path
Expand Down Expand Up @@ -449,13 +474,11 @@ const createDocumentFile = async (
"Editor should be either 'Collabora' or 'OnlyOffice' but found " + editorToOpen
)
}
const respPromise = Promise.all([
page.waitForResponse((res) => res.status() === 207 && res.request().method() === 'PROPFIND')
await Promise.all([
page.waitForResponse((res) => res.status() === 207 && res.request().method() === 'PROPFIND'),
editor.close(page)
])
await editor.close(page)
await respPromise

await page.reload()
await page.locator(util.format(resourceNameSelector, name)).waitFor()
// wait for lock to be removed
expect(getLockLocator({ page, resource: name })).not.toBeVisible()
Expand Down Expand Up @@ -569,11 +592,7 @@ export const editTextDocument = async ({
name: string
content: string
}): Promise<void> => {
const isMarkdownMode = await page.locator(textEditor).getAttribute('data-markdown-mode')
const inputLocator =
isMarkdownMode === 'true' ? textEditorMarkdownInput : textEditorPlainTextInput

await page.locator(inputLocator).fill(content)
await page.locator(textEditorPlainTextInput).fill(content)
await Promise.all([
page.waitForResponse((resp) => resp.status() === 204 && resp.request().method() === 'PUT'),
page.waitForResponse((resp) => resp.status() === 207 && resp.request().method() === 'PROPFIND'),
Expand Down Expand Up @@ -684,7 +703,7 @@ export const dropUploadFiles = async (args: uploadResourceArgs): Promise<void> =
const { page, resources } = args

// waiting to files view
await page.locator(addNewResourceButton).waitFor()
await expect(page.locator(addNewResourceButton)).not.toHaveAttribute('disabled')
await utils.dragDropFiles(page, resources, filesView)

await page.locator(uploadInfoCloseButton).click()
Expand Down Expand Up @@ -1561,6 +1580,7 @@ export const searchResourceGlobalSearch = async (
}

await page.locator(globalSearchBarFilter).click()
await page.locator(appLoadingSpinner).waitFor({ state: 'detached' })

if (!keyword) {
await page.locator(globalSearchInput).click()
Expand Down Expand Up @@ -1788,6 +1808,8 @@ export interface openFileInViewerArgs {

export const openFileInViewer = async (args: openFileInViewerArgs): Promise<void> => {
const { page, name, actionType } = args
await waitProcessingToFinish(page, name)

switch (actionType) {
case 'OnlyOffice':
await Promise.all([
Expand Down Expand Up @@ -1850,14 +1872,24 @@ export const openFileInViewer = async (args: openFileInViewerArgs): Promise<void
}
break
}
case 'pdfviewer':
case 'pdfviewer': {
await Promise.all([
page.waitForResponse(
(resp) => resp.status() === 207 && resp.request().method() === 'PROPFIND'
),
page.locator(util.format(resourceNameSelector, name)).click()
])
await page.locator(pdfViewerContainer).waitFor()
break
}
case 'texteditor': {
await Promise.all([
page.waitForResponse(
(resp) => resp.status() === 207 && resp.request().method() === 'PROPFIND'
),
page.locator(util.format(resourceNameSelector, name)).click()
])
await page.locator(textEditorContainer).waitFor()
break
}
}
Expand Down
Loading