2024-06-20 19:19:01 +03:00
|
|
|
/** @file Actions for the "drive" page. */
|
|
|
|
import * as test from 'playwright/test'
|
|
|
|
|
2024-08-13 14:50:07 +03:00
|
|
|
import {
|
|
|
|
locateAssetPanel,
|
|
|
|
locateAssetsTable,
|
|
|
|
locateContextMenus,
|
|
|
|
locateCreateButton,
|
|
|
|
locateDriveView,
|
|
|
|
locateNewSecretIcon,
|
|
|
|
locateNonAssetRows,
|
|
|
|
locateSecretNameInput,
|
|
|
|
locateSecretValueInput,
|
|
|
|
TEXT,
|
|
|
|
} from '../actions'
|
2024-06-20 19:19:01 +03:00
|
|
|
import type * as baseActions from './BaseActions'
|
|
|
|
import * as contextMenuActions from './contextMenuActions'
|
|
|
|
import EditorPageActions from './EditorPageActions'
|
|
|
|
import * as goToPageActions from './goToPageActions'
|
|
|
|
import NewDataLinkModalActions from './NewDataLinkModalActions'
|
2024-06-24 19:02:22 +03:00
|
|
|
import PageActions from './PageActions'
|
2024-06-20 19:19:01 +03:00
|
|
|
import StartModalActions from './StartModalActions'
|
|
|
|
|
2024-07-16 12:55:45 +03:00
|
|
|
// =================
|
|
|
|
// === Constants ===
|
|
|
|
// =================
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
|
|
const ASSET_ROW_SAFE_POSITION = { x: 300, y: 16 }
|
|
|
|
|
2024-08-13 14:50:07 +03:00
|
|
|
// =======================
|
|
|
|
// === locateAssetRows ===
|
|
|
|
// =======================
|
|
|
|
|
|
|
|
/** Find all assets table rows (if any). */
|
|
|
|
function locateAssetRows(page: test.Page) {
|
|
|
|
return locateAssetsTable(page).getByTestId('asset-row')
|
|
|
|
}
|
|
|
|
|
2024-06-20 19:19:01 +03:00
|
|
|
// ========================
|
|
|
|
// === DrivePageActions ===
|
|
|
|
// ========================
|
|
|
|
|
|
|
|
/** Actions for the "drive" page. */
|
2024-06-24 19:02:22 +03:00
|
|
|
export default class DrivePageActions extends PageActions {
|
2024-06-20 19:19:01 +03:00
|
|
|
/** Actions for navigating to another page. */
|
|
|
|
get goToPage(): Omit<goToPageActions.GoToPageActions, 'drive'> {
|
|
|
|
return goToPageActions.goToPageActions(this.step.bind(this))
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Actions related to context menus. */
|
|
|
|
get contextMenu() {
|
|
|
|
return contextMenuActions.contextMenuActions(this.step.bind(this))
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Switch to a different category. */
|
|
|
|
get goToCategory() {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
|
|
const self: DrivePageActions = this
|
|
|
|
return {
|
|
|
|
/** Switch to the "cloud" category. */
|
|
|
|
cloud() {
|
|
|
|
return self.step('Go to "Cloud" category', (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
page
|
2024-09-13 15:51:35 +03:00
|
|
|
.getByRole('button', { name: TEXT.cloudCategory, exact: true })
|
2024-08-13 14:50:07 +03:00
|
|
|
.getByText(TEXT.cloudCategory)
|
|
|
|
.click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Switch to the "local" category. */
|
|
|
|
local() {
|
|
|
|
return self.step('Go to "Local" category', (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
page
|
2024-09-13 15:51:35 +03:00
|
|
|
.getByRole('button', { name: TEXT.localCategory, exact: true })
|
2024-08-13 14:50:07 +03:00
|
|
|
.getByText(TEXT.localCategory)
|
|
|
|
.click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Switch to the "recent" category. */
|
|
|
|
recent() {
|
|
|
|
return self.step('Go to "Recent" category', (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
page
|
2024-09-13 15:51:35 +03:00
|
|
|
.getByRole('button', { name: TEXT.recentCategory, exact: true })
|
2024-08-13 14:50:07 +03:00
|
|
|
.getByText(TEXT.recentCategory)
|
|
|
|
.click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Switch to the "trash" category. */
|
|
|
|
trash() {
|
|
|
|
return self.step('Go to "Trash" category', (page) =>
|
2024-09-13 15:51:35 +03:00
|
|
|
page.getByRole('button', { name: TEXT.trashCategory, exact: true }).click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Actions specific to the Drive table. */
|
|
|
|
get driveTable() {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
|
|
const self: DrivePageActions = this
|
|
|
|
return {
|
|
|
|
/** Click the column heading for the "name" column to change its sort order. */
|
|
|
|
clickNameColumnHeading() {
|
|
|
|
return self.step('Click "name" column heading', (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
page.getByLabel(TEXT.sortByName).or(page.getByLabel(TEXT.stopSortingByName)).click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Click the column heading for the "modified" column to change its sort order. */
|
|
|
|
clickModifiedColumnHeading() {
|
|
|
|
return self.step('Click "modified" column heading', (page) =>
|
|
|
|
page
|
2024-08-13 14:50:07 +03:00
|
|
|
.getByLabel(TEXT.sortByModificationDate)
|
|
|
|
.or(page.getByLabel(TEXT.stopSortingByModificationDate))
|
2024-06-20 19:19:01 +03:00
|
|
|
.click(),
|
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Click to select a specific row. */
|
|
|
|
clickRow(index: number) {
|
2024-07-16 12:55:45 +03:00
|
|
|
return self.step(`Click drive table row #${index}`, (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
locateAssetRows(page).nth(index).click({ position: ASSET_ROW_SAFE_POSITION }),
|
2024-06-20 19:19:01 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Right click a specific row to bring up its context menu, or the context menu for multiple
|
|
|
|
* assets when right clicking on a selected asset when multiple assets are selected. */
|
|
|
|
rightClickRow(index: number) {
|
2024-07-16 12:55:45 +03:00
|
|
|
return self.step(`Right click drive table row #${index}`, (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
locateAssetRows(page)
|
2024-06-20 19:19:01 +03:00
|
|
|
.nth(index)
|
2024-08-13 14:50:07 +03:00
|
|
|
.click({ button: 'right', position: ASSET_ROW_SAFE_POSITION }),
|
2024-06-20 19:19:01 +03:00
|
|
|
)
|
|
|
|
},
|
2024-07-16 12:55:45 +03:00
|
|
|
/** Double click a row. */
|
|
|
|
doubleClickRow(index: number) {
|
|
|
|
return self.step(`Double dlick drive table row #${index}`, (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
locateAssetRows(page).nth(index).dblclick({ position: ASSET_ROW_SAFE_POSITION }),
|
2024-07-16 12:55:45 +03:00
|
|
|
)
|
|
|
|
},
|
2024-06-20 19:19:01 +03:00
|
|
|
/** Interact with the set of all rows in the Drive table. */
|
2024-08-12 13:25:18 +03:00
|
|
|
withRows(
|
|
|
|
callback: (assetRows: test.Locator, nonAssetRows: test.Locator) => Promise<void> | void,
|
|
|
|
) {
|
2024-06-20 19:19:01 +03:00
|
|
|
return self.step('Interact with drive table rows', async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
await callback(locateAssetRows(page), locateNonAssetRows(page))
|
2024-06-20 19:19:01 +03:00
|
|
|
})
|
|
|
|
},
|
2024-07-16 12:55:45 +03:00
|
|
|
/** Drag a row onto another row. */
|
|
|
|
dragRowToRow(from: number, to: number) {
|
|
|
|
return self.step(`Drag drive table row #${from} to row #${to}`, async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
const rows = locateAssetRows(page)
|
2024-07-16 12:55:45 +03:00
|
|
|
await rows.nth(from).dragTo(rows.nth(to), {
|
|
|
|
sourcePosition: ASSET_ROW_SAFE_POSITION,
|
|
|
|
targetPosition: ASSET_ROW_SAFE_POSITION,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
},
|
|
|
|
/** Drag a row onto another row. */
|
|
|
|
dragRow(from: number, to: test.Locator, force?: boolean) {
|
|
|
|
return self.step(`Drag drive table row #${from} to custom locator`, (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
locateAssetRows(page)
|
2024-07-16 12:55:45 +03:00
|
|
|
.nth(from)
|
|
|
|
.dragTo(to, {
|
|
|
|
sourcePosition: ASSET_ROW_SAFE_POSITION,
|
|
|
|
...(force == null ? {} : { force }),
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
},
|
2024-06-20 19:19:01 +03:00
|
|
|
/** A test assertion to confirm that there is only one row visible, and that row is the
|
|
|
|
* placeholder row displayed when there are no assets to show. */
|
|
|
|
expectPlaceholderRow() {
|
|
|
|
return self.step('Expect placeholder row', async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
await test.expect(locateAssetRows(page)).toHaveCount(0)
|
|
|
|
const nonAssetRows = locateNonAssetRows(page)
|
2024-08-12 13:25:18 +03:00
|
|
|
await test.expect(nonAssetRows).toHaveCount(1)
|
2024-08-21 21:10:56 +03:00
|
|
|
await test.expect(nonAssetRows).toHaveText(/This folder is empty/)
|
2024-06-20 19:19:01 +03:00
|
|
|
})
|
|
|
|
},
|
|
|
|
/** A test assertion to confirm that there is only one row visible, and that row is the
|
|
|
|
* placeholder row displayed when there are no assets in Trash. */
|
|
|
|
expectTrashPlaceholderRow() {
|
|
|
|
return self.step('Expect trash placeholder row', async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
await test.expect(locateAssetRows(page)).toHaveCount(0)
|
|
|
|
const nonAssetRows = locateNonAssetRows(page)
|
2024-08-12 13:25:18 +03:00
|
|
|
await test.expect(nonAssetRows).toHaveCount(1)
|
|
|
|
await test.expect(nonAssetRows).toHaveText(/Your trash is empty/)
|
2024-06-20 19:19:01 +03:00
|
|
|
})
|
|
|
|
},
|
2024-07-16 12:55:45 +03:00
|
|
|
/** Toggle a column's visibility. */
|
|
|
|
get toggleColumn() {
|
|
|
|
return {
|
|
|
|
/** Toggle visibility for the "modified" column. */
|
|
|
|
modified() {
|
2024-08-13 14:50:07 +03:00
|
|
|
return self.step('Toggle "modified" column', (page) =>
|
|
|
|
page.getByAltText(TEXT.modifiedColumnName).click(),
|
2024-07-16 12:55:45 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Toggle visibility for the "shared with" column. */
|
|
|
|
sharedWith() {
|
2024-08-13 14:50:07 +03:00
|
|
|
return self.step('Toggle "shared with" column', (page) =>
|
|
|
|
page.getByAltText(TEXT.sharedWithColumnName).click(),
|
2024-07-16 12:55:45 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Toggle visibility for the "labels" column. */
|
|
|
|
labels() {
|
2024-08-13 14:50:07 +03:00
|
|
|
return self.step('Toggle "labels" column', (page) =>
|
|
|
|
page.getByAltText(TEXT.labelsColumnName).click(),
|
2024-07-16 12:55:45 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Toggle visibility for the "accessed by projects" column. */
|
|
|
|
accessedByProjects() {
|
2024-08-13 14:50:07 +03:00
|
|
|
return self.step('Toggle "accessed by projects" column', (page) =>
|
|
|
|
page.getByAltText(TEXT.accessedByProjectsColumnName).click(),
|
2024-07-16 12:55:45 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Toggle visibility for the "accessed data" column. */
|
|
|
|
accessedData() {
|
2024-08-13 14:50:07 +03:00
|
|
|
return self.step('Toggle "accessed data" column', (page) =>
|
|
|
|
page.getByAltText(TEXT.accessedDataColumnName).click(),
|
2024-07-16 12:55:45 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
/** Toggle visibility for the "docs" column. */
|
|
|
|
docs() {
|
2024-08-13 14:50:07 +03:00
|
|
|
return self.step('Toggle "docs" column', (page) =>
|
|
|
|
page.getByAltText(TEXT.docsColumnName).click(),
|
2024-07-16 12:55:45 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
2024-06-20 19:19:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Open the "start" modal. */
|
|
|
|
openStartModal() {
|
|
|
|
return this.step('Open "start" modal', (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
page.getByText(TEXT.startWithATemplate).click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
).into(StartModalActions)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Create a new empty project. */
|
|
|
|
newEmptyProject() {
|
|
|
|
return this.step('Create empty project', (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
page.getByText(TEXT.newEmptyProject).click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
).into(EditorPageActions)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Interact with the drive view (the main container of this page). */
|
|
|
|
withDriveView(callback: baseActions.LocatorCallback) {
|
2024-08-13 14:50:07 +03:00
|
|
|
return this.step('Interact with drive view', (page) => callback(locateDriveView(page)))
|
2024-06-20 19:19:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Create a new folder using the icon in the Drive Bar. */
|
|
|
|
createFolder() {
|
2024-09-13 19:49:14 +03:00
|
|
|
return this.step('Create folder', async (page) => {
|
|
|
|
await page.getByRole('button', { name: TEXT.newFolder, exact: true }).click()
|
|
|
|
// eslint-disable-next-line no-restricted-properties
|
|
|
|
await test.expect(page.locator('input:focus')).toBeVisible()
|
|
|
|
await page.keyboard.press('Escape')
|
|
|
|
})
|
2024-06-20 19:19:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Upload a file using the icon in the Drive Bar. */
|
|
|
|
uploadFile(
|
|
|
|
name: string,
|
|
|
|
contents: WithImplicitCoercion<Uint8Array | string | readonly number[]>,
|
|
|
|
mimeType = 'text/plain',
|
|
|
|
) {
|
|
|
|
return this.step(`Upload file '${name}'`, async (page) => {
|
|
|
|
const fileChooserPromise = page.waitForEvent('filechooser')
|
2024-08-13 14:50:07 +03:00
|
|
|
await page.getByRole('button', { name: TEXT.uploadFiles }).click()
|
2024-06-20 19:19:01 +03:00
|
|
|
const fileChooser = await fileChooserPromise
|
|
|
|
await fileChooser.setFiles([{ name, buffer: Buffer.from(contents), mimeType }])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Create a new secret using the icon in the Drive Bar. */
|
|
|
|
createSecret(name: string, value: string) {
|
|
|
|
return this.step(`Create secret '${name}' = '${value}'`, async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
await locateNewSecretIcon(page).click()
|
|
|
|
await locateSecretNameInput(page).fill(name)
|
|
|
|
await locateSecretValueInput(page).fill(value)
|
|
|
|
await locateCreateButton(page).click()
|
2024-06-20 19:19:01 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Toggle the Asset Panel open or closed. */
|
|
|
|
toggleAssetPanel() {
|
|
|
|
return this.step('Toggle asset panel', (page) =>
|
|
|
|
page.getByLabel('Asset Panel').locator('visible=true').click(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-07-16 12:55:45 +03:00
|
|
|
/** Interact with the container element of the assets table. */
|
|
|
|
withAssetsTable(callback: baseActions.LocatorCallback) {
|
|
|
|
return this.step('Interact with drive table', async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
await callback(locateAssetsTable(page))
|
2024-07-16 12:55:45 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-06-20 19:19:01 +03:00
|
|
|
/** Interact with the Asset Panel. */
|
|
|
|
withAssetPanel(callback: baseActions.LocatorCallback) {
|
|
|
|
return this.step('Interact with asset panel', async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
await callback(locateAssetPanel(page))
|
2024-06-20 19:19:01 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Open the Data Link creation modal by clicking on the Data Link icon. */
|
|
|
|
openDataLinkModal() {
|
|
|
|
return this.step('Open "new data link" modal', (page) =>
|
2024-08-13 14:50:07 +03:00
|
|
|
page.getByRole('button', { name: TEXT.newDatalink }).click(),
|
2024-06-20 19:19:01 +03:00
|
|
|
).into(NewDataLinkModalActions)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Interact with the context menus (the context menus MUST be visible). */
|
|
|
|
withContextMenus(callback: baseActions.LocatorCallback) {
|
|
|
|
return this.step('Interact with context menus', async (page) => {
|
2024-08-13 14:50:07 +03:00
|
|
|
await callback(locateContextMenus(page))
|
2024-06-20 19:19:01 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|