No more flake, only fail, still need to fix 20+

This commit is contained in:
Dustin Brett
2023-07-19 00:09:29 -07:00
parent f4b5b93562
commit 2a2f95b758
12 changed files with 480 additions and 224 deletions

View File

@@ -88,7 +88,7 @@
"import/no-duplicates": "error",
"import/no-extraneous-dependencies": [
"error",
{ "devDependencies": ["**/*.config.ts", "**/*.spec.ts", "**/*.spec.tsx"] }
{ "devDependencies": ["*.config.ts", "e2e/**", "__tests__/**"] }
],
"import/prefer-default-export": "off",
"jsx-a11y/label-has-associated-control": [
@@ -113,6 +113,7 @@
"no-undef-init": "error",
"no-underscore-dangle": "off",
"no-unsafe-optional-chaining": "error",
"playwright/expect-expect": "off",
"react/function-component-definition": [
"error",
{ "namedComponents": "arrow-function" }

View File

@@ -210,8 +210,8 @@ export const getEventData = (
(event as InputChangeEvent).target?.files || dataTransfer?.items || [];
const text = dataTransfer?.getData("application/json");
if (files instanceof DataTransferItemList) {
files = [...files].filter(
if (Array.isArray(files)) {
files = [...(files as unknown as DataTransferItemList)].filter(
(item) => !("kind" in item) || item.kind === "file"
) as unknown as DataTransferItemList;
}

View File

@@ -1,23 +1,45 @@
import { expect, test } from "@playwright/test";
import {
BASE_APP_FAVICON,
BASE_APP_TITLE,
CONTEXT_MENU_SELECTOR,
DESKTOP_FILE_ENTRY_SELECTOR,
FAVICON_SELECTOR,
FILE_MENU_ITEMS,
RIGHT_CLICK,
SELECTION_SELECTOR,
TEST_APP_ICON,
TEST_APP_TITLE,
TEST_ROOT_FILE,
TEST_ROOT_FILE_TEXT,
TEST_ROOT_FILE_TOOLTIP,
TEST_SEARCH,
TEST_SEARCH_RESULT,
WINDOW_SELECTOR,
} from "e2e/constants";
import {
clickFirstDesktopFileEntry,
contextMenuIsVisible,
desktopFileEntriesAreVisible,
desktopIsVisible,
fileExplorerFileEntriesAreVisible,
fileExplorerFileIsHidden,
fileExplorerFileIsVisible,
focusOnWindow,
windowIsVisible,
} from "e2e/functions";
test.beforeEach(async ({ page }) => page.goto("/?app=FileExplorer"));
test.beforeEach(async ({ page }) => {
await page.goto("/?app=FileExplorer");
await desktopIsVisible({ page });
await desktopFileEntriesAreVisible({ page });
await windowIsVisible({ page });
await fileExplorerFileEntriesAreVisible({ page });
await fileExplorerFileIsVisible(TEST_ROOT_FILE, { page });
});
test("has address bar", async ({ page }) => {
await focusOnWindow({ page });
const addressBar = page.locator(WINDOW_SELECTOR).getByLabel(/^Address$/);
await expect(addressBar).toHaveValue(TEST_APP_TITLE);
@@ -28,6 +50,8 @@ test("has address bar", async ({ page }) => {
await addressBar.click(RIGHT_CLICK);
await contextMenuIsVisible({ page });
await expect(
page.locator(CONTEXT_MENU_SELECTOR).getByLabel(/^Copy address$/)
).toBeVisible();
@@ -38,22 +62,76 @@ test("has search box", async ({ page }) => {
.locator(WINDOW_SELECTOR)
.getByLabel(/^Search box$/)
.type(TEST_SEARCH, {
delay: 25,
delay: 50,
});
await contextMenuIsVisible({ page });
await expect(
page.locator(CONTEXT_MENU_SELECTOR).getByLabel(TEST_SEARCH_RESULT)
).toBeVisible();
});
test("has status bar", async ({ page }) => {
const windowElement = page.locator(WINDOW_SELECTOR);
await windowElement.getByLabel(TEST_ROOT_FILE).click();
await expect(windowElement.getByLabel(/^Total item count$/)).toContainText(
/^\d items$/
);
await expect(
windowElement.getByLabel(/^Selected item count and size$/)
).toContainText(/^1 item selected|\d{3} bytes$/);
});
test("changes title", async ({ page }) => {
const titleWithApp = `${TEST_APP_TITLE} - ${BASE_APP_TITLE}`;
const isUsingAppTitle = async (): Promise<void> =>
expect(page).toHaveTitle(titleWithApp);
await isUsingAppTitle();
await desktopFileEntriesAreVisible({ page });
await clickFirstDesktopFileEntry({ page });
await expect.poll(() => page.title()).toEqual(BASE_APP_TITLE);
await focusOnWindow({ page });
await isUsingAppTitle();
});
test("changes icon", async ({ page }) => {
const favIcon = page.locator(FAVICON_SELECTOR);
const isUsingAppIcon = async (): Promise<void> =>
expect(page.locator(FAVICON_SELECTOR)).toHaveAttribute(
"href",
TEST_APP_ICON
);
await isUsingAppIcon();
await desktopFileEntriesAreVisible({ page });
await clickFirstDesktopFileEntry({ page });
await expect
.poll(() => favIcon.getAttribute("href"))
.toMatch(BASE_APP_FAVICON);
await focusOnWindow({ page });
await isUsingAppIcon();
});
test.describe("has file", () => {
test.describe("has context menu", () => {
test.beforeEach(async ({ page }) =>
test.beforeEach(async ({ page }) => {
page
.locator(WINDOW_SELECTOR)
.getByLabel(TEST_ROOT_FILE)
.click(RIGHT_CLICK)
);
.click(RIGHT_CLICK);
await contextMenuIsVisible({ page });
});
test("with items", async ({ page }) => {
const menu = page.locator(CONTEXT_MENU_SELECTOR);
@@ -76,17 +154,15 @@ test.describe("has file", () => {
});
test("can delete", async ({ page }) => {
const rootFile = page.locator(WINDOW_SELECTOR).getByLabel(TEST_ROOT_FILE);
await expect(rootFile).toBeVisible();
await page.getByLabel(/^Delete$/).click();
await expect(rootFile).toBeHidden();
await fileExplorerFileIsHidden(TEST_ROOT_FILE, { page });
await page.reload();
await expect(rootFile).toBeHidden();
await windowIsVisible({ page });
await fileExplorerFileIsHidden(TEST_ROOT_FILE, { page });
});
// TODO: can cut/copy->paste (to Desktop)
@@ -94,88 +170,26 @@ test.describe("has file", () => {
// TODO: can create shortcut (expect prepended name & icon)
});
test("with tooltip", async ({ page }) => {
test("with tooltip", async ({ page, request }) => {
const testFile = page.locator(WINDOW_SELECTOR).getByLabel(TEST_ROOT_FILE);
// Q: Why both?
await testFile.hover();
await testFile.click();
expect(await testFile.getAttribute("title")).toMatch(
TEST_ROOT_FILE_TOOLTIP
);
});
const statsRequest = await request.head(TEST_ROOT_FILE_TEXT);
test.describe("with selection", () => {
test("effect", async ({ page }) => {
const viewport = page.viewportSize();
// eslint-disable-next-line playwright/no-conditional-in-test
const x = (viewport?.width || 0) / 2;
// eslint-disable-next-line playwright/no-conditional-in-test
const y = (viewport?.height || 0) / 2;
const SELECTION_OFFSET = 25;
await expect(statsRequest).toBeOK();
await page.mouse.move(x, y);
await page.mouse.down({
button: "left",
});
await page.mouse.move(x + SELECTION_OFFSET, y + SELECTION_OFFSET);
const selection = page.locator(SELECTION_SELECTOR);
await expect(selection).toBeVisible();
const boundingBox = await selection.boundingBox();
expect(boundingBox?.width).toEqual(SELECTION_OFFSET);
expect(boundingBox?.height).toEqual(SELECTION_OFFSET);
expect(boundingBox?.x).toEqual(x);
expect(boundingBox?.y).toEqual(y);
});
// TODO: file entry (single/multi)
await expect
.poll(() => testFile.getAttribute("title"))
.toMatch(TEST_ROOT_FILE_TOOLTIP);
});
// TODO: can drag (to Desktop)
// TODO: can drop (from Desktop)
});
test("has status bar", async ({ page }) => {
const windowElement = page.locator(WINDOW_SELECTOR);
await windowElement.getByLabel(TEST_ROOT_FILE).click();
await expect(windowElement.getByLabel(/^Total item count$/)).toContainText(
/^\d items$/
);
await expect(
windowElement.getByLabel(/^Selected item count and size$/)
).toContainText(/^1 item selected|\d{3} bytes$/);
});
test("changes title", async ({ page }) => {
await expect(page).toHaveTitle(BASE_APP_TITLE);
await page.locator(WINDOW_SELECTOR).click();
await expect(page).toHaveTitle(`${TEST_APP_TITLE} - ${BASE_APP_TITLE}`);
});
test("changes icon", async ({ page }) => {
const favIcon = page.locator("link[rel=icon]");
await expect(favIcon).toBeHidden();
await page.locator(WINDOW_SELECTOR).click();
await expect(page.locator("link[rel=icon]")).toHaveAttribute(
"href",
TEST_APP_ICON
);
await page.locator(DESKTOP_FILE_ENTRY_SELECTOR).first().click();
await expect(favIcon).toHaveAttribute("href", /^\/favicon.ico$/);
});
// TODO: has context menu (FOLDER_MENU_ITEMS)
// TODO: has back, forward, recent & up
// TODO: has keyboard shortcuts (Paste, Ctrl: C, X, V)

View File

@@ -1,11 +1,17 @@
import { expect, test } from "@playwright/test";
import { test } from "@playwright/test";
import {
DESKTOP_FILE_ENTRY_SELECTOR,
FILE_DRAG_NOT_WORKING_BROWSERS,
FORCE,
TEST_APP_CONTAINER_APP,
WINDOW_SELECTOR,
WINDOW_TITLEBAR_SELECTOR,
} from "e2e/constants";
import {
desktopFileEntriesAreVisible,
windowIsVisible,
windowTitlebarEqualsText,
windowTitlebarIsVisible,
} from "e2e/functions";
test.describe("app container", () => {
test("has drop", async ({ browserName, page }) => {
@@ -16,18 +22,23 @@ test.describe("app container", () => {
await page.goto(`/?app=${TEST_APP_CONTAINER_APP}`);
await expect(page.locator(WINDOW_TITLEBAR_SELECTOR)).toContainText(
TEST_APP_CONTAINER_APP
);
await windowIsVisible({ page });
await windowTitlebarIsVisible({ page });
await windowTitlebarEqualsText(TEST_APP_CONTAINER_APP, { page });
await desktopFileEntriesAreVisible({ page });
const firstFile = page.locator(DESKTOP_FILE_ENTRY_SELECTOR).first();
await expect(firstFile).toBeVisible();
await firstFile.dragTo(page.locator(WINDOW_SELECTOR), FORCE);
await firstFile.dragTo(page.locator(WINDOW_SELECTOR));
await expect(page.locator(WINDOW_TITLEBAR_SELECTOR)).toContainText(
`${(await firstFile.textContent()) || ""}.url - ${TEST_APP_CONTAINER_APP}`
await windowTitlebarEqualsText(
`${
(await firstFile.textContent()) || ""
}.url - ${TEST_APP_CONTAINER_APP}`,
{ page }
);
});

View File

@@ -2,19 +2,35 @@ import AxeBuilder from "@axe-core/playwright";
import { expect, test } from "@playwright/test";
import {
ACCESSIBILITY_EXCEPTION_IDS,
BACKGROUND_CANVAS_SELECTOR,
CONTEXT_MENU_SELECTOR,
DESKTOP_FILE_ENTRY_SELECTOR,
DESKTOP_ELEMENT,
DESKTOP_MENU_ITEMS,
EXACT,
NEW_FILE_LABEL,
NEW_FILE_LABEL_TEXT,
NEW_FOLDER_LABEL,
RIGHT_CLICK,
SELECTION_SELECTOR,
TASKBAR_ENTRY_SELECTOR,
} from "e2e/constants";
import {
backgroundIsUrl,
canvasBackgroundIsHidden,
canvasBackgroundIsVisible,
contextMenuIsVisible,
desktopEntryIsHidden,
desktopEntryIsVisible,
desktopFileEntriesAreVisible,
desktopIsVisible,
loadApp,
taskbarEntryIsVisible,
} from "e2e/functions";
test.beforeEach(async ({ page }) => page.goto("/"));
test.beforeEach(async ({ page }) => {
await loadApp({ page });
await desktopIsVisible({ page });
});
test("pass accessibility scan", async ({ page }) =>
expect(
@@ -25,19 +41,49 @@ test("pass accessibility scan", async ({ page }) =>
).violations
).toEqual([]));
test("has background", async ({ page }) => {
await expect(page.locator(BACKGROUND_CANVAS_SELECTOR)).toBeVisible();
});
test("has background", canvasBackgroundIsVisible);
test("has file entry", async ({ page }) => {
await expect(page.locator(DESKTOP_FILE_ENTRY_SELECTOR).first()).toBeVisible();
});
test("has file entry", desktopFileEntriesAreVisible);
// TODO: has grid (move file on grid)
test.describe("has selection", () => {
test("with effect", async ({ page }) => {
const { width = 0, height = 0 } =
// eslint-disable-next-line playwright/no-conditional-in-test
(await page.getByRole(DESKTOP_ELEMENT).boundingBox()) || {};
const x = width / 2;
const y = height / 2;
const SELECTION_OFFSET = 25;
await page.mouse.move(x, y);
await page.mouse.down({
button: "left",
});
await page.mouse.move(x + SELECTION_OFFSET, y + SELECTION_OFFSET);
await page.waitForSelector(SELECTION_SELECTOR);
const selection = page.locator(SELECTION_SELECTOR);
await expect(selection).toBeVisible();
const boundingBox = await selection.boundingBox();
expect(boundingBox?.width).toEqual(SELECTION_OFFSET);
expect(boundingBox?.height).toEqual(SELECTION_OFFSET);
expect(boundingBox?.x).toEqual(x);
expect(boundingBox?.y).toEqual(y);
});
// TODO: file entry (single/multi)
});
test.describe("has context menu", () => {
test.beforeEach(async ({ page }) => {
await page.getByRole("main").click(RIGHT_CLICK);
await page.getByRole(DESKTOP_ELEMENT).click(RIGHT_CLICK);
await contextMenuIsVisible({ page });
});
test("with items", async ({ browserName, page }) => {
@@ -57,79 +103,71 @@ test.describe("has context menu", () => {
}
});
test("can change background", async ({ page }) => {
await expect(page.locator(BACKGROUND_CANVAS_SELECTOR)).toBeVisible();
test.describe("with file functions", () => {
test.beforeEach(desktopFileEntriesAreVisible);
await page.getByLabel(/^Background$/).click();
await page.getByLabel(/^APOD$/).click();
test("can create folder", async ({ page }) => {
await desktopEntryIsHidden(NEW_FOLDER_LABEL, { page });
await expect(page.locator(BACKGROUND_CANVAS_SELECTOR)).toBeHidden();
await page.getByLabel(/^New$/).click();
await page.getByLabel(/^Folder$/).click();
expect(
await page.waitForFunction(
([selectorProperty, selectorValue]) =>
window
.getComputedStyle(document.documentElement)
.getPropertyValue(selectorProperty)
.match(selectorValue),
["background-image", /^url\(.*?\)$/] as [string, RegExp]
)
).toBeTruthy();
await desktopEntryIsVisible(NEW_FOLDER_LABEL, { page });
await page.reload();
await page.reload();
await expect(page.locator(BACKGROUND_CANVAS_SELECTOR)).toBeHidden();
});
test("can create folder", async ({ page }) => {
await expect(page.getByLabel(NEW_FOLDER_LABEL)).toBeHidden();
await page.getByLabel(/^New$/).click();
await page.getByLabel(/^Folder$/).click();
await page.getByRole("main").click();
await expect(page.getByLabel(NEW_FOLDER_LABEL)).toBeVisible();
await page.reload();
await expect(page.getByLabel(NEW_FOLDER_LABEL)).toBeVisible();
});
test("can create file", async ({ page }) => {
await expect(page.getByLabel(NEW_FILE_LABEL)).toBeHidden();
await page.getByLabel(/^New$/).click();
await page.getByLabel(/^Text Document$/).click();
await page.getByRole("main").click();
await expect(page.getByLabel(NEW_FILE_LABEL)).toBeVisible();
await page.reload();
await expect(page.getByLabel(NEW_FILE_LABEL)).toBeVisible();
});
test("can add file", async ({ page }) => {
const uploadPromise = page.waitForEvent("filechooser");
await page.getByLabel(/^Add file\(s\)$/).click();
await (
await uploadPromise
).setFiles({
buffer: Buffer.from(""),
mimeType: "text/plain",
name: NEW_FILE_LABEL_TEXT,
await desktopEntryIsVisible(NEW_FOLDER_LABEL, { page });
});
await expect(page.getByLabel(NEW_FILE_LABEL)).toBeVisible();
test("can create file", async ({ page }) => {
await desktopEntryIsHidden(NEW_FILE_LABEL, { page });
await page.getByLabel(/^New$/).click();
await page.getByLabel(/^Text Document$/).click();
await desktopEntryIsVisible(NEW_FILE_LABEL, { page });
await page.reload();
await desktopEntryIsVisible(NEW_FILE_LABEL, { page });
});
test("can add file", async ({ page }) => {
const uploadPromise = page.waitForEvent("filechooser");
await page.getByLabel(/^Add file\(s\)$/).click();
await (
await uploadPromise
).setFiles({
buffer: Buffer.from(""),
mimeType: "text/plain",
name: NEW_FILE_LABEL_TEXT,
});
await desktopEntryIsVisible(NEW_FILE_LABEL, { page });
});
});
test("can change background", async ({ page }) => {
await canvasBackgroundIsVisible({ page });
await page.getByLabel(/^Background$/).click();
await page.getByLabel(/^Picture Slideshow$/).click();
await canvasBackgroundIsHidden({ page });
await backgroundIsUrl({ page });
await page.reload();
await canvasBackgroundIsHidden({ page });
});
test("can inspect", async ({ page }) => {
await page.getByLabel(/^Inspect$/).click();
await taskbarEntryIsVisible({ page });
await expect(
page.locator(TASKBAR_ENTRY_SELECTOR).getByLabel(/^DevTools$/)
).toBeVisible();
@@ -138,6 +176,8 @@ test.describe("has context menu", () => {
test("can view page source", async ({ page }) => {
await page.getByLabel(/^View page source$/).click();
await taskbarEntryIsVisible({ page });
await expect(
page
.locator(TASKBAR_ENTRY_SELECTOR)
@@ -148,6 +188,8 @@ test.describe("has context menu", () => {
test("can open terminal", async ({ page }) => {
await page.getByLabel(/^Open Terminal here$/).click();
await taskbarEntryIsVisible({ page });
await expect(
page.locator(TASKBAR_ENTRY_SELECTOR).getByLabel(/^Terminal$/)
).toBeVisible();
@@ -156,7 +198,9 @@ test.describe("has context menu", () => {
test.describe("has keyboard shortcuts", () => {
test("ctrl + shift + r (open run dialog)", async ({ page }) => {
await page.getByRole("main").press("Control+Shift+KeyR");
await page.getByRole(DESKTOP_ELEMENT).press("Control+Shift+KeyR");
await taskbarEntryIsVisible({ page });
await expect(
page.locator(TASKBAR_ENTRY_SELECTOR).getByLabel(/^Run$/)
@@ -164,7 +208,9 @@ test.describe("has keyboard shortcuts", () => {
});
test("ctrl + shift + e (open file explorer)", async ({ page }) => {
await page.getByRole("main").press("Control+Shift+KeyE");
await page.getByRole(DESKTOP_ELEMENT).press("Control+Shift+KeyE");
await taskbarEntryIsVisible({ page });
await expect(
page.locator(TASKBAR_ENTRY_SELECTOR).getByLabel(/^File Explorer$/)

View File

@@ -1,10 +1,11 @@
import { expect, test } from "@playwright/test";
import { START_MENU_SELECTOR } from "e2e/constants";
import { DESKTOP_ELEMENT, START_MENU_SELECTOR } from "e2e/constants";
import { clickStartButton, loadApp } from "e2e/functions";
test.beforeEach(async ({ page }) => {
await page.goto("/");
await loadApp({ page });
await page.getByLabel(/^Start$/).click();
await clickStartButton({ page });
});
test.describe("has sidebar", () => {
@@ -27,7 +28,7 @@ test.describe("can close", () => {
test("via click", async ({ page }) => {
await expect(page.locator(START_MENU_SELECTOR)).toBeVisible();
await page.getByLabel(/^Start$/).click();
await clickStartButton({ page });
await expect(page.locator(START_MENU_SELECTOR)).toBeHidden();
});
@@ -35,7 +36,7 @@ test.describe("can close", () => {
test("via blur", async ({ page }) => {
await expect(page.locator(START_MENU_SELECTOR)).toBeVisible();
page.locator("main").click();
page.getByRole(DESKTOP_ELEMENT).click();
await expect(page.locator(START_MENU_SELECTOR)).toBeHidden();
});

View File

@@ -2,27 +2,32 @@ import { expect, test } from "@playwright/test";
import {
CLOCK_REGEX,
OFFSCREEN_CANVAS_NOT_SUPPORTED_BROWSERS,
SHEEP_SELECTOR,
TASKBAR_ENTRY_SELECTOR,
TEST_APP,
TEST_APP_ICON,
TEST_APP_TITLE,
} from "e2e/constants";
import {
clickStartButton,
disableOffscreenCanvas,
loadApp,
sheepIsVisible,
startButtonIsVisible,
taskbarEntryIsVisible,
} from "e2e/functions";
test.describe("elements", () => {
test.beforeEach(async ({ page }) => page.goto("/"));
test.beforeEach(loadApp);
test.describe("has start button", () => {
test("is visible", async ({ page }) => {
await expect(page.getByLabel(/^Start$/)).toBeVisible();
});
test("is visible", startButtonIsVisible);
test("with sheep", async ({ page }) => {
page.keyboard.down("Control");
page.keyboard.down("Shift");
page.getByLabel(/^Start$/).click();
await page.keyboard.down("Control");
await page.keyboard.down("Shift");
await expect(page.locator(SHEEP_SELECTOR)).toBeVisible();
await clickStartButton({ page });
await sheepIsVisible({ page });
});
// TODO: has context menu
@@ -41,9 +46,7 @@ test.describe("elements", () => {
});
test("via text", async ({ page }) => {
await page.addInitScript(() => {
delete (window as Partial<Window & typeof globalThis>).OffscreenCanvas;
});
await page.addInitScript(disableOffscreenCanvas);
await page.reload();
@@ -51,11 +54,13 @@ test.describe("elements", () => {
});
test("with sheep", async ({ page }) => {
test.setTimeout(10000);
const clock = page.getByLabel(/^Clock$/);
clock.click({ clickCount: 7 });
await expect(page.locator(SHEEP_SELECTOR)).toBeVisible();
await sheepIsVisible({ page });
});
// TODO: has context menu
@@ -72,9 +77,7 @@ test.describe("entries", () => {
test.beforeEach(async ({ page }) => page.goto(`/?app=${TEST_APP}`));
test.describe("has entry", () => {
test.beforeEach(async ({ page }) =>
expect(page.locator(TASKBAR_ENTRY_SELECTOR)).toBeVisible()
);
test.beforeEach(taskbarEntryIsVisible);
test("with title", async ({ page }) =>
expect(

View File

@@ -1,24 +1,28 @@
import { expect, test } from "@playwright/test";
import {
DESKTOP_ELEMENT,
TASKBAR_SELECTOR,
WINDOW_SELECTOR,
WINDOW_TITLEBAR_SELECTOR,
} from "e2e/constants";
import {
isMaximized,
waitForAnimation,
windowIsHidden,
windowTitlebarEqualsText,
windowTitlebarIsVisible,
} from "e2e/functions";
const isMaximized = ([windowSelector, taskbarSelector]: string[]): boolean =>
window.innerWidth ===
(document.querySelector(windowSelector) as HTMLElement)?.clientWidth &&
window.innerHeight -
((document.querySelector(taskbarSelector) as HTMLElement)?.clientHeight ||
0) ===
(document.querySelector(windowSelector) as HTMLElement)?.clientHeight;
test.beforeEach(async ({ page }) => {
await page.goto("/?app=FileExplorer");
test.beforeEach(async ({ page }) => page.goto("/?app=FileExplorer"));
test("has title", async ({ page }) => {
await expect(page.locator(WINDOW_TITLEBAR_SELECTOR)).toContainText(/^My PC$/);
await waitForAnimation(WINDOW_SELECTOR, { page });
await windowTitlebarIsVisible({ page });
});
test("has title", async ({ page }) =>
windowTitlebarEqualsText("My PC", { page }));
test("has minimize", async ({ page }) => {
const windowElement = page.locator(WINDOW_SELECTOR);
@@ -60,17 +64,13 @@ test.describe("has maximize", () => {
});
test.describe("has close", () => {
test.beforeEach(async ({ page }) =>
expect(page.locator(WINDOW_SELECTOR)).toBeVisible()
);
test("on click button", async ({ page }) => {
await page
.locator(WINDOW_TITLEBAR_SELECTOR)
.getByLabel(/^Close$/)
.click();
await expect(page.locator(WINDOW_SELECTOR)).toBeHidden();
await windowIsHidden({ page });
});
test("on double click icon", async ({ page }) => {
@@ -78,23 +78,15 @@ test.describe("has close", () => {
.locator(`${WINDOW_TITLEBAR_SELECTOR}>button>figure>picture`)
.dblclick();
await expect(page.locator(WINDOW_SELECTOR)).toBeHidden();
await windowIsHidden({ page });
});
});
test("has drag", async ({ page }) => {
await page
.locator(WINDOW_SELECTOR)
.evaluate((element) =>
Promise.all(
element.getAnimations().map((animation) => animation.finished)
)
);
const titlebarElement = page.locator(WINDOW_TITLEBAR_SELECTOR);
const initialBoundingBox = await titlebarElement.boundingBox();
await titlebarElement.dragTo(page.getByRole("main"), {
await titlebarElement.dragTo(page.getByRole(DESKTOP_ELEMENT), {
targetPosition: {
x: (initialBoundingBox?.width || 0) / 2,
y: (initialBoundingBox?.height || 0) / 2,
@@ -106,7 +98,7 @@ test("has drag", async ({ page }) => {
expect(initialBoundingBox?.x).not.toEqual(finalBoundingBox?.x);
expect(initialBoundingBox?.y).not.toEqual(finalBoundingBox?.y);
const mainBoundingBox = await page.getByRole("main").boundingBox();
const mainBoundingBox = await page.getByRole(DESKTOP_ELEMENT).boundingBox();
expect(finalBoundingBox?.y).toEqual(mainBoundingBox?.y);
expect(finalBoundingBox?.x).toEqual(mainBoundingBox?.x);

View File

@@ -6,23 +6,34 @@ export const RIGHT_CLICK = { button: "right" } as Parameters<
Locator["click"]
>[0];
export const EXACT = { exact: true };
export const VISIBLE = { state: "visible" } as Parameters<
Locator["waitFor"]
>[0];
export const FORCE = { force: true };
const DESKTOP_ELEMENT = "main";
const NEXT_JS_CONTAINER = "#__next";
const ENTRY_SELECTOR = "ol>li";
export const DESKTOP_ELEMENT = "main";
export const FAVICON_SELECTOR = "link[rel=icon]";
export const BACKGROUND_CANVAS_SELECTOR = `${DESKTOP_ELEMENT}>canvas`;
export const DESKTOP_FILE_ENTRY_SELECTOR = `${DESKTOP_ELEMENT}>${ENTRY_SELECTOR}`;
export const WINDOW_SELECTOR = `${DESKTOP_ELEMENT}>.react-draggable>section`;
export const WINDOW_TITLEBAR_SELECTOR = `${WINDOW_SELECTOR}>div>header`;
export const FILE_EXPLORER_FILE_ENTRY_SELECTOR = `${WINDOW_SELECTOR}>div>div>${ENTRY_SELECTOR}`;
export const TASKBAR_SELECTOR = `${DESKTOP_ELEMENT}>nav:not([style])`;
export const START_MENU_SELECTOR = `${DESKTOP_ELEMENT}>nav[style]`;
export const TASKBAR_ENTRY_SELECTOR = `${TASKBAR_SELECTOR}>${ENTRY_SELECTOR}`;
export const CONTEXT_MENU_SELECTOR = `${NEXT_JS_CONTAINER}>nav`;
export const SELECTION_SELECTOR = `${DESKTOP_ELEMENT}>ol>span`;
export const SHEEP_SELECTOR = `${DESKTOP_ELEMENT}>div>img[src^=data]`;
export const START_BUTTON_LABEL = /^Start$/;
export const ACCESSIBILITY_EXCEPTION_IDS = ["meta-viewport"];
export const ACCESSIBILITY_EXCEPTION_IDS = [
"aria-allowed-role",
"image-redundant-alt",
"meta-viewport",
];
export const DIRECTORY_PICKER_NOT_SUPPORTED_BROWSERS = new Set([
"webkit",
@@ -72,9 +83,10 @@ export const TEST_APP_CONTAINER_APP = "Marked";
export const TEST_APP = "FileExplorer";
export const TEST_APP_TITLE = "My PC";
export const TEST_APP_ICON = /\/pc\.(webp|png)$/;
export const TEST_ROOT_FILE = /^session.json$/;
export const TEST_ROOT_FILE = /^CREDITS.md$/;
export const TEST_ROOT_FILE_TEXT = "CREDITS.md";
export const TEST_ROOT_FILE_TOOLTIP =
/^Type: JSON File\nSize: \d{3} bytes\nDate modified: \b\d{4}-\d{2}-\d{2} \d{1,2}:\d{2} (?:AM|PM)$/;
/^Type: Markdown File\nSize: \d\.\d\d KB\nDate modified: \b\d{4}-\d{2}-\d{2} \d{1,2}:\d{2} (?:AM|PM)$/;
export const TEST_SEARCH = "CREDITS";
export const TEST_SEARCH_RESULT = /^CREDITS.md$/;
@@ -85,3 +97,4 @@ export const NEW_FILE_LABEL_TEXT = "New Text Document.txt";
export const CLOCK_REGEX = /^(1[0-2]|0?[1-9])(?::[0-5]\d){2}\s?(AM|PM)$/;
export const BASE_APP_TITLE = "daedalOS";
export const BASE_APP_FAVICON = /^\/favicon.ico$/;

177
e2e/functions.ts Normal file
View File

@@ -0,0 +1,177 @@
import type { Page, Response } from "@playwright/test";
import { expect } from "@playwright/test";
import {
BACKGROUND_CANVAS_SELECTOR,
CONTEXT_MENU_SELECTOR,
DESKTOP_ELEMENT,
DESKTOP_FILE_ENTRY_SELECTOR,
FILE_EXPLORER_FILE_ENTRY_SELECTOR,
SHEEP_SELECTOR,
START_BUTTON_LABEL,
TASKBAR_ENTRY_SELECTOR,
WINDOW_SELECTOR,
WINDOW_TITLEBAR_SELECTOR,
} from "e2e/constants";
type TestProps = {
page: Page;
};
export const waitForAnimation = async (
selector: string,
{ page }: TestProps
): Promise<Animation[]> =>
page
.locator(selector)
.evaluate((element) =>
Promise.all(
element.getAnimations().map((animation) => animation.finished)
)
);
export const canvasBackgroundIsHidden = async ({
page,
}: TestProps): Promise<void> =>
expect(page.locator(BACKGROUND_CANVAS_SELECTOR)).toBeHidden();
export const canvasBackgroundIsVisible = async ({
page,
}: TestProps): Promise<void> =>
expect(page.locator(BACKGROUND_CANVAS_SELECTOR)).toBeVisible();
export const contextMenuIsVisible = async ({
page,
}: TestProps): Promise<void> =>
expect(page.locator(CONTEXT_MENU_SELECTOR)).toBeVisible();
export const desktopIsVisible = async ({ page }: TestProps): Promise<void> =>
expect(page.getByRole(DESKTOP_ELEMENT)).toBeVisible();
export const desktopEntryIsHidden = async (
entry: RegExp,
{ page }: TestProps
): Promise<void> => {
await expect
.poll(() => page.locator(DESKTOP_FILE_ENTRY_SELECTOR).count())
.toBeGreaterThan(0);
await expect(page.getByLabel(entry)).toBeHidden();
};
export const desktopEntryIsVisible = async (
entry: RegExp,
{ page }: TestProps
): Promise<void> => {
await expect
.poll(() => page.locator(DESKTOP_FILE_ENTRY_SELECTOR).count())
.toBeGreaterThan(1);
await expect(page.getByLabel(entry)).toBeVisible();
};
export const desktopFileEntriesAreVisible = async ({
page,
}: TestProps): Promise<void> => {
const desktopFileEntry = page.locator(DESKTOP_FILE_ENTRY_SELECTOR);
await expect.poll(() => desktopFileEntry.count()).toBeGreaterThan(0);
await expect.poll(() => desktopFileEntry.first().isVisible()).toBeTruthy();
};
export const taskbarEntryIsVisible = async ({
page,
}: TestProps): Promise<void> =>
expect(page.locator(TASKBAR_ENTRY_SELECTOR)).toBeVisible();
export const isMaximized = ([
windowSelector,
taskbarSelector,
]: string[]): boolean =>
window.innerWidth ===
(document.querySelector(windowSelector) as HTMLElement)?.clientWidth &&
window.innerHeight -
((document.querySelector(taskbarSelector) as HTMLElement)?.clientHeight ||
0) ===
(document.querySelector(windowSelector) as HTMLElement)?.clientHeight;
export const sheepIsVisible = async ({ page }: TestProps): Promise<void> =>
expect(page.locator(SHEEP_SELECTOR)).toBeVisible();
export const startButtonIsVisible = async ({
page,
}: TestProps): Promise<void> =>
expect(page.getByLabel(START_BUTTON_LABEL)).toBeVisible();
export const windowIsHidden = async ({ page }: TestProps): Promise<void> =>
expect(page.locator(WINDOW_SELECTOR)).toBeHidden();
export const windowIsVisible = async ({ page }: TestProps): Promise<void> =>
expect.poll(() => page.locator(WINDOW_SELECTOR).isVisible()).toBeTruthy();
export const windowTitlebarIsVisible = async ({
page,
}: TestProps): Promise<void> =>
expect(page.locator(WINDOW_TITLEBAR_SELECTOR)).toBeVisible();
export const windowTitlebarEqualsText = async (
text: string,
{ page }: TestProps
): Promise<void> =>
expect
.poll(() => page.locator(WINDOW_TITLEBAR_SELECTOR).textContent())
.toEqual(text);
export const fileExplorerFileIsHidden = async (
file: RegExp,
{ page }: TestProps
): Promise<void> =>
expect(page.locator(WINDOW_SELECTOR).getByLabel(file)).toBeHidden();
export const fileExplorerFileEntriesAreVisible = async ({
page,
}: TestProps): Promise<void> => {
await page.waitForSelector(FILE_EXPLORER_FILE_ENTRY_SELECTOR);
const fileExplorerFileEntry = page.locator(FILE_EXPLORER_FILE_ENTRY_SELECTOR);
await expect.poll(() => fileExplorerFileEntry.count()).toBeGreaterThan(0);
await expect
.poll(() => fileExplorerFileEntry.first().isVisible())
.toBeTruthy();
};
export const fileExplorerFileIsVisible = async (
file: RegExp,
{ page }: TestProps
): Promise<void> =>
expect
.poll(() => page.locator(WINDOW_SELECTOR).getByLabel(file).isVisible())
.toBeTruthy();
export const clickFirstDesktopFileEntry = async ({
page,
}: TestProps): Promise<void> =>
page.locator(DESKTOP_FILE_ENTRY_SELECTOR).first().click();
export const focusOnWindow = async ({ page }: TestProps): Promise<void> =>
page.locator(WINDOW_SELECTOR).focus();
export const clickStartButton = async ({ page }: TestProps): Promise<void> =>
page.getByLabel(START_BUTTON_LABEL).click();
export const backgroundIsUrl = async ({ page }: TestProps): Promise<void> =>
expect(
await page.waitForFunction(
([selectorProperty, selectorValue]) =>
window
.getComputedStyle(document.documentElement)
.getPropertyValue(selectorProperty)
.match(selectorValue),
["background-image", /^url\(.*?\)$/] as [string, RegExp]
)
).toBeTruthy();
export const loadApp = async ({ page }: TestProps): Promise<Response | null> =>
page.goto("/");
export const disableOffscreenCanvas = (): void => {
delete (window as Partial<Window & typeof globalThis>).OffscreenCanvas;
};

View File

@@ -25,6 +25,7 @@
"docker:run": "docker run -dp 3000:3000 --rm --name daedalos daedalos",
"dev": "next dev",
"e2e": "playwright test",
"e2e:debug": "playwright test --debug",
"eslint": "eslint --report-unused-disable-directives .",
"export": "next export",
"prepare": "husky install",
@@ -32,7 +33,6 @@
"start": "next start",
"stylelint": "stylelint --formatter=verbose **/*.ts*",
"test": "jest",
"test:watch": "jest --watch",
"unused-exports": "ts-prune --project tsconfig.json --ignore \"\\pages|\\e2e\" --error"
},
"lint-staged": {

View File

@@ -5,7 +5,6 @@ const PORT = process.env.PORT || 3000;
const baseURL = `http://localhost:${PORT}`;
const config: PlaywrightTestConfig = {
expect: { timeout: 30000 },
fullyParallel: true,
projects: [
{
@@ -24,7 +23,6 @@ const config: PlaywrightTestConfig = {
},
],
reporter: process.env.CI ? "dot" : [["html", { open: "always" }]],
retries: 3,
testDir: "e2e",
use: {
baseURL,