daedalOS/e2e/components/apps/FileExplorer.spec.ts
Dustin Brett c98d8d823d
Some checks failed
Tests / tests (push) Has been cancelled
Package upgrades
2025-08-11 10:35:36 -07:00

522 lines
17 KiB
TypeScript

import { dirname, extname } from "path";
import { expect, test } from "@playwright/test";
import {
BASE_APP_FAVICON,
BASE_APP_TITLE,
CLIPBOARD_WRITE_HEADLESS_NOT_SUPPORTED_BROWSERS,
DESKTOP_SELECTOR,
DRAG_HEADLESS_NOT_SUPPORTED_BROWSERS,
FILE_EXPLORER_COLUMN_HEIGHT,
FILE_EXPLORER_ENTRIES_FOCUSED_SELECTOR,
FILE_EXPLORER_ENTRIES_SELECTOR,
FILE_EXPLORER_SELECTION_SELECTOR,
FILE_EXPLORER_SELECTOR,
FILE_EXPLORER_STATUS_BAR_SELECTOR,
FILE_MENU_ITEMS,
FOLDER_MENU_ITEMS,
ROOT_FOLDER_VIEW,
TEST_APP_ICON,
TEST_APP_TITLE,
TEST_APP_TITLE_TEXT,
TEST_DESKTOP_FILE,
TEST_IMAGE_NAME,
TEST_ROOT_ARCHIVE,
TEST_ROOT_FILE,
TEST_ROOT_FILE_2,
TEST_ROOT_FILE_ALT_APP,
TEST_ROOT_FILE_COPY,
TEST_ROOT_FILE_DEFAULT_APP,
TEST_ROOT_FILE_TEXT,
TEST_ROOT_FILE_TOOLTIP,
TEST_SEARCH,
TEST_SEARCH_RESULT,
WINDOW_RESIZE_HANDLE_WIDTH,
} from "e2e/constants";
import {
appIsOpen,
captureConsoleLogs,
clickContextMenuEntry,
clickDesktop,
clickFileExplorer,
clickFileExplorerAddressBar,
clickFileExplorerEntry,
clickFileExplorerNavButton,
clickFileExplorerSearchBox,
clickFirstDesktopEntry,
contextMenuEntryIsHidden,
contextMenuEntryIsVisible,
contextMenuHasCount,
contextMenuIsHidden,
contextMenuIsVisible,
desktopEntryIsHidden,
desktopEntryIsVisible,
disableWallpaper,
dragDesktopEntryToFileExplorer,
dragFileExplorerEntryToDesktop,
fileExplorerAddressBarHasValue,
fileExplorerEntriesAreVisible,
fileExplorerEntryHasShortcutIcon,
fileExplorerEntryHasTooltip,
fileExplorerEntryIsHidden,
fileExplorerEntryIsVisible,
fileExplorerNavButtonIsVisible,
fileExplorerRenameEntry,
filterMenuItems,
focusOnWindow,
loadApp,
mockSaveFilePicker,
pageHasIcon,
pageHasTitle,
pressFileExplorerAddressBarKeys,
pressFileExplorerEntryKeys,
selectArea,
typeInFileExplorerAddressBar,
typeInFileExplorerSearchBox,
windowAnimationIsFinished,
windowTitlebarTextIsVisible,
windowsAreVisible,
} from "e2e/functions";
import { UNKNOWN_ICON } from "components/system/Files/FileManager/icons";
test.beforeEach(captureConsoleLogs());
test.beforeEach(disableWallpaper);
test.beforeEach(async ({ page }) => loadApp({ app: "FileExplorer" })({ page }));
test.beforeEach(windowsAreVisible);
test.beforeEach(fileExplorerEntriesAreVisible);
test("has address bar", async ({ page }) => {
await fileExplorerAddressBarHasValue(TEST_APP_TITLE, { page });
await clickFileExplorerAddressBar({ page }, false, 2);
await fileExplorerAddressBarHasValue("/", { page });
await clickFileExplorerAddressBar({ page }, true);
await contextMenuIsVisible({ page });
await clickContextMenuEntry(/^Copy address$/, { page });
await contextMenuIsHidden({ page });
await fileExplorerAddressBarHasValue(TEST_APP_TITLE, { page });
await typeInFileExplorerAddressBar("/System", { page });
await pressFileExplorerAddressBarKeys("Enter", { page });
await fileExplorerAddressBarHasValue("System", { page });
await fileExplorerEntryIsVisible("Icons", { page });
});
test("can search", async ({ page }) => {
await clickFileExplorerSearchBox({ page });
await typeInFileExplorerSearchBox(TEST_SEARCH, { page });
await expect(() => contextMenuIsVisible({ page })).toPass();
await contextMenuEntryIsVisible(TEST_SEARCH_RESULT, { page });
});
test.describe("has files & folders", () => {
test.describe("has context menu", () => {
test.beforeEach(async ({ page }) => {
await clickFileExplorerEntry(TEST_ROOT_FILE, { page }, true);
await contextMenuIsVisible({ page });
});
test("has items", async ({ page }) => {
await contextMenuHasCount(FILE_MENU_ITEMS.length, { page });
for (const label of FILE_MENU_ITEMS) {
// eslint-disable-next-line no-await-in-loop
await contextMenuEntryIsVisible(label, { page });
}
});
test("can open", async ({ page }) => {
await clickContextMenuEntry(/^Open$/, { page });
await windowTitlebarTextIsVisible(
`${TEST_ROOT_FILE_TEXT} - ${TEST_ROOT_FILE_DEFAULT_APP}`,
{ page }
);
});
test("can open with", async ({ page }) => {
await clickContextMenuEntry(/^Open with$/, { page });
await clickContextMenuEntry(TEST_ROOT_FILE_ALT_APP, { page });
await windowTitlebarTextIsVisible(
`${TEST_ROOT_FILE_TEXT} - ${TEST_ROOT_FILE_ALT_APP}`,
{ page }
);
});
test("can download", async ({ page }) => {
const downloadPromise = page.waitForEvent("download");
const supportsSaveFilePicker = await page.evaluate(
() => typeof window.showSaveFilePicker === "function"
);
if (supportsSaveFilePicker) {
await mockSaveFilePicker({ page }, TEST_ROOT_FILE_TEXT);
}
await clickContextMenuEntry(/^Download$/, { page });
const download = await downloadPromise;
expect(await download.path()).toBeTruthy();
expect(download.suggestedFilename()).toMatch(TEST_ROOT_FILE);
});
test("can cut", async ({ page }) => {
await clickContextMenuEntry(/^Cut$/, { page });
const { width = 0 } =
(await page.locator(DESKTOP_SELECTOR).boundingBox()) || {};
await clickDesktop({ page }, true, width - 25, 25);
await contextMenuIsVisible({ page });
await clickContextMenuEntry(/^Paste$/, { page });
await desktopEntryIsVisible(TEST_ROOT_FILE, { page });
await fileExplorerEntryIsHidden(TEST_ROOT_FILE, { page });
});
test("can copy", async ({ page }) => {
await clickContextMenuEntry(/^Copy$/, { page });
const { width = 0 } =
(await page.locator(DESKTOP_SELECTOR).boundingBox()) || {};
await clickDesktop({ page }, true, width - 25, 25);
await contextMenuIsVisible({ page });
await clickContextMenuEntry(/^Paste$/, { page });
await desktopEntryIsVisible(TEST_ROOT_FILE, { page });
await fileExplorerEntryIsVisible(TEST_ROOT_FILE, { page });
});
test("can delete file", async ({ page }) => {
await clickContextMenuEntry(/^Delete$/, { page });
await fileExplorerEntryIsHidden(TEST_ROOT_FILE, { page });
await fileExplorerEntriesAreVisible({ page });
await page.reload();
await windowsAreVisible({ page });
await fileExplorerEntriesAreVisible({ page });
await fileExplorerEntryIsHidden(TEST_ROOT_FILE, { page });
});
test("can rename", async ({ page }) => {
await clickContextMenuEntry(/^Rename$/, { page });
const flippedName = [...dirname(TEST_ROOT_FILE_TEXT)].reverse().join("");
await fileExplorerRenameEntry(flippedName, { page });
await fileExplorerEntryIsVisible(
`${flippedName}${extname(TEST_ROOT_FILE_TEXT)}`,
{ page }
);
});
test("can archive", async ({ page }) => {
await clickContextMenuEntry(/^Add to archive...$/, { page });
await fileExplorerEntryIsVisible(TEST_ROOT_ARCHIVE, { page });
});
test("can create shortcut", async ({ page }) => {
const shortcutFile = `${TEST_ROOT_FILE_TEXT} - Shortcut`;
await fileExplorerEntryIsHidden(shortcutFile, { page });
await clickContextMenuEntry(/^Create shortcut$/, { page });
await fileExplorerEntryIsVisible(shortcutFile, { page });
await fileExplorerEntryHasShortcutIcon(shortcutFile, { page });
await page.reload();
await windowsAreVisible({ page });
await fileExplorerEntriesAreVisible({ page });
await fileExplorerEntryIsVisible(shortcutFile, { page });
});
test("has properties", async ({ page }) => {
await clickContextMenuEntry(/^Properties$/, { page });
await appIsOpen(`${TEST_ROOT_FILE_TEXT} Properties`, page);
});
});
test("can paste from filesystem", async ({ page }) => {
await page.keyboard.press("Control+KeyV");
await fileExplorerEntryIsHidden(TEST_ROOT_FILE_COPY, { page });
await clickFileExplorerEntry(TEST_ROOT_FILE, { page });
await page.keyboard.press("Control+KeyC");
await fileExplorerEntryIsHidden(TEST_ROOT_FILE_COPY, { page });
await clickFileExplorer({ page }, false, WINDOW_RESIZE_HANDLE_WIDTH / 2, 0);
await page.keyboard.press("Control+KeyV");
await fileExplorerEntryIsVisible(TEST_ROOT_FILE_COPY, { page });
});
test("can paste from clipboard", async ({
browserName,
context,
headless,
page,
}) => {
test.skip(
headless &&
CLIPBOARD_WRITE_HEADLESS_NOT_SUPPORTED_BROWSERS.has(browserName),
"no headless clipboard write support"
);
await page.keyboard.press("Control+KeyV");
await fileExplorerEntryIsHidden(TEST_IMAGE_NAME, { page });
await context.grantPermissions(["clipboard-write"]);
await page.evaluate(
async ([icon]) =>
navigator.clipboard.write([
new ClipboardItem({
"image/png": await (await fetch(icon)).blob(),
}),
]),
[UNKNOWN_ICON]
);
await fileExplorerEntryIsHidden(TEST_IMAGE_NAME, { page });
await page.keyboard.press("Control+KeyV");
await fileExplorerEntryIsVisible(TEST_IMAGE_NAME, { page });
});
test("can rename via F2", async ({ page }) => {
await clickFileExplorerEntry(TEST_ROOT_FILE, { page });
await pressFileExplorerEntryKeys(TEST_ROOT_FILE, "F2", { page });
const flippedName = [...dirname(TEST_ROOT_FILE_TEXT)].reverse().join("");
await fileExplorerRenameEntry(flippedName, { page });
await fileExplorerEntryIsVisible(
`${flippedName}${extname(TEST_ROOT_FILE_TEXT)}`,
{ page }
);
});
test("has status bar", async ({ page }) => {
await clickFileExplorerEntry(TEST_ROOT_FILE, { page });
const statusBar = page.locator(FILE_EXPLORER_STATUS_BAR_SELECTOR);
const entryInfo = statusBar.getByLabel(/^Total item count$/);
const selectedInfo = statusBar.getByLabel(/^Selected item count and size$/);
await expect(entryInfo).toContainText(/^\d+ items$/);
await expect(selectedInfo).toContainText(/^1 item selected|\d{3} bytes$/);
expect(
await page.locator(FILE_EXPLORER_ENTRIES_FOCUSED_SELECTOR).count()
).toEqual(1);
await page.keyboard.down("Control");
await clickFileExplorerEntry(TEST_ROOT_FILE_2, { page });
await expect(selectedInfo).toContainText(/^2 items selected|\d{3} KB$/);
expect(
await page.locator(FILE_EXPLORER_ENTRIES_FOCUSED_SELECTOR).count()
).toEqual(2);
});
test("has tooltip", async ({ page }) => {
const responsePromise = page.waitForResponse(TEST_ROOT_FILE_TEXT);
await clickFileExplorerEntry(TEST_ROOT_FILE, { page });
expect((await responsePromise).ok()).toBeTruthy();
await fileExplorerEntryHasTooltip(TEST_ROOT_FILE, TEST_ROOT_FILE_TOOLTIP, {
page,
});
});
test.describe("can open", () => {
test.beforeEach(({ page }) =>
fileExplorerEntryIsVisible(TEST_ROOT_FILE, { page })
);
test("via double click", async ({ page }) => {
await clickFileExplorerEntry(TEST_ROOT_FILE, { page }, false, 2);
await windowTitlebarTextIsVisible(
`${TEST_ROOT_FILE_TEXT} - ${TEST_ROOT_FILE_DEFAULT_APP}`,
{ page }
);
});
test("via enter", async ({ page }) => {
await clickFileExplorerEntry(TEST_ROOT_FILE, { page });
await pressFileExplorerEntryKeys(TEST_ROOT_FILE, "Enter", { page });
await windowTitlebarTextIsVisible(
`${TEST_ROOT_FILE_TEXT} - ${TEST_ROOT_FILE_DEFAULT_APP}`,
{ page }
);
});
});
test("can drop on desktop", async ({ browserName, headless, page }) => {
test.skip(
headless && DRAG_HEADLESS_NOT_SUPPORTED_BROWSERS.has(browserName),
"no headless drag support"
);
await desktopEntryIsHidden(TEST_ROOT_FILE, { page });
await fileExplorerEntryIsVisible(TEST_ROOT_FILE, { page });
await dragFileExplorerEntryToDesktop(TEST_ROOT_FILE, { page });
await fileExplorerEntryIsHidden(TEST_ROOT_FILE, { page });
await desktopEntryIsVisible(TEST_ROOT_FILE, { page });
});
test("can select multiple entries", async ({ page }) => {
await fileExplorerEntryIsVisible(TEST_ROOT_FILE, { page });
await windowAnimationIsFinished({ page });
const { x = 0, y = 0 } =
(await page.locator(FILE_EXPLORER_SELECTOR).boundingBox()) || {};
const { height = 0, width = 0 } =
(await page
.locator(FILE_EXPLORER_ENTRIES_SELECTOR)
.first()
.boundingBox()) || {};
await selectArea({
container: FILE_EXPLORER_SELECTION_SELECTOR,
page,
selection: {
height: Math.round(height) * 2,
up: true,
width: Math.round(width),
x: x + WINDOW_RESIZE_HANDLE_WIDTH / 2,
y:
y +
(ROOT_FOLDER_VIEW === "details" ? FILE_EXPLORER_COLUMN_HEIGHT : 0),
},
});
await expect(page.locator(".focus-within")).toHaveCount(2);
});
test("can drop from desktop", async ({ browserName, headless, page }) => {
test.skip(
headless && DRAG_HEADLESS_NOT_SUPPORTED_BROWSERS.has(browserName),
"no headless drag support"
);
await fileExplorerEntryIsHidden(TEST_DESKTOP_FILE, { page });
await desktopEntryIsVisible(TEST_DESKTOP_FILE, { page });
await dragDesktopEntryToFileExplorer(TEST_DESKTOP_FILE, { page });
await fileExplorerEntryIsVisible(TEST_DESKTOP_FILE, { page });
await desktopEntryIsHidden(TEST_DESKTOP_FILE, { page });
});
});
test("can change page title", async ({ page }) => {
const focusedAppPageTitle = `${TEST_APP_TITLE_TEXT} - ${BASE_APP_TITLE}`;
await pageHasTitle(focusedAppPageTitle, { page });
await clickFirstDesktopEntry({ page });
await pageHasTitle(BASE_APP_TITLE, { page });
await focusOnWindow({ page });
await pageHasTitle(focusedAppPageTitle, { page });
});
test("can change page icon", async ({ page }) => {
await pageHasIcon(TEST_APP_ICON, { page });
await clickFirstDesktopEntry({ page });
await pageHasIcon(BASE_APP_FAVICON, { page });
await focusOnWindow({ page });
await pageHasIcon(TEST_APP_ICON, { page });
});
test.describe("has context menu", () => {
test.beforeEach(async ({ page }) => {
await clickFileExplorer(
{ page },
true,
WINDOW_RESIZE_HANDLE_WIDTH / 2,
ROOT_FOLDER_VIEW === "details" ? FILE_EXPLORER_COLUMN_HEIGHT : 0
);
await contextMenuIsVisible({ page });
});
test("has items", async ({ browserName, page }) => {
const MENU_ITEMS = filterMenuItems(FOLDER_MENU_ITEMS, browserName);
const shownCount = MENU_ITEMS.filter(([, shown]) => shown).length;
await contextMenuHasCount(shownCount, { page });
for (const [label, shown] of MENU_ITEMS) {
// eslint-disable-next-line no-await-in-loop
await (shown
? contextMenuEntryIsVisible(label, { page })
: contextMenuEntryIsHidden(label, { page }));
}
});
test("has properties", async ({ page }) => {
await clickContextMenuEntry(/^Properties$/, { page });
await appIsOpen(/^Properties$/, page);
});
});
test.describe("has navigation", () => {
test.beforeEach(async ({ page }) =>
expect(async () => {
await clickFileExplorerEntry(/^System$/, { page }, false, 2);
await windowTitlebarTextIsVisible(/^System$/, { page });
}).toPass()
);
test("can go back & forward", async ({ page }) => {
await fileExplorerEntriesAreVisible({ page });
await typeInFileExplorerAddressBar("/Users", { page });
await pressFileExplorerAddressBarKeys("Enter", { page });
await windowTitlebarTextIsVisible(/^Users$/, { page });
await fileExplorerNavButtonIsVisible(/^Back to System$/, { page });
await fileExplorerNavButtonIsVisible(/^Up to "My PC"$/, { page });
await clickFileExplorerNavButton(/^Back to System$/, { page });
await windowTitlebarTextIsVisible(/^System$/, { page });
await fileExplorerNavButtonIsVisible(/^Back to My PC$/, { page });
await fileExplorerNavButtonIsVisible(/^Forward to Users$/, { page });
await fileExplorerNavButtonIsVisible(/^Up to "My PC"$/, { page });
});
test("can go up", async ({ page }) => {
await clickFileExplorerEntry(/^Icons$/, { page }, false, 2);
await windowTitlebarTextIsVisible(/^Icons$/, { page });
await fileExplorerNavButtonIsVisible(/^Back to System$/, { page });
await fileExplorerNavButtonIsVisible(/^Up to "System"$/, { page });
await clickFileExplorerNavButton(/^Up to "System"$/, { page });
await windowTitlebarTextIsVisible(/^System$/, { page });
await fileExplorerNavButtonIsVisible(/^Back to Icons$/, { page });
await fileExplorerNavButtonIsVisible(/^Up to "My PC"$/, { page });
});
test("has recent locations", async ({ page }) => {
await fileExplorerEntriesAreVisible({ page });
await clickFileExplorerNavButton(/^Recent locations$/, { page });
await contextMenuEntryIsVisible(/^My PC$/, { page });
await contextMenuEntryIsVisible(/^System$/, { page });
await contextMenuHasCount(2, { page });
await clickContextMenuEntry(/^My PC$/, { page });
await windowTitlebarTextIsVisible(/^My PC$/, { page });
});
});