Skip to content
Merged
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 .github/workflows/build_test_and_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
needs: setup
runs-on: ubuntu-latest
if: github.ref != 'refs/heads/main'
timeout-minutes: 30
timeout-minutes: 40
strategy:
matrix:
browser: ["Chrome", "Edge", "Firefox", "Safari"]
Expand Down
6 changes: 5 additions & 1 deletion e2e/pages/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ export class DashboardPage {
}

async createProjectFromTemplate(projectName: string) {
await this.page.goto("/");
await this.page.goto("/welcome");
await this.page.getByRole("button", { name: "Start from Template" }).hover();
await this.page.getByRole("button", { name: "Start from Template" }).click();

await this.page.getByLabel("Categories").click();
await this.page.getByRole("option", { name: "Samples" }).click();
await this.page.locator("body").click({ position: { x: 0, y: 0 } });
await this.page.getByRole("button", { name: "Create Project From Template: HTTP" }).scrollIntoViewIfNeeded();
await this.page.getByRole("button", { name: "Create Project From Template: HTTP" }).click();
await this.page.getByPlaceholder("Enter project name").fill(projectName);
await this.page.waitForTimeout(500);
await this.page.getByRole("button", { name: "Create", exact: true }).click();
await expect(this.page.getByRole("heading", { name: "Configuration" })).toBeVisible({ timeout: 1200 });
}
Expand Down
1 change: 1 addition & 0 deletions e2e/pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { DashboardPage } from "./dashboard";
export { ProjectPage } from "./project";
export { WebhookSessionPage } from "./webhookSession";
2 changes: 0 additions & 2 deletions e2e/pages/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ export class ProjectPage {
const loadersArray = await loaders;
await Promise.all(loadersArray.map((loader) => loader.waitFor({ state: "detached" })));

await this.page.getByRole("heading", { name: /^Welcome to .+$/, level: 1 }).isVisible();

await expect(successToast).not.toBeVisible({ timeout: 10000 });

const deletedProjectNameCell = this.page.getByRole("cell", { name: projectName });
Expand Down
106 changes: 106 additions & 0 deletions e2e/pages/webhookSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { expect, type APIRequestContext, type Page } from "@playwright/test";
import randomatic from "randomatic";

import { DashboardPage } from "./dashboard";
import { waitForToast } from "e2e/utils";
import { waitForLoadingOverlayGone } from "e2e/utils/waitForLoadingOverlayToDisappear";

export class WebhookSessionPage {
private readonly page: Page;
private readonly request: APIRequestContext;
private readonly dashboardPage: DashboardPage;
public projectName: string;

constructor(page: Page, request: APIRequestContext) {
this.page = page;
this.request = request;
this.dashboardPage = new DashboardPage(page);
this.projectName = `test_${randomatic("Aa", 4)}`;
}

async waitForFirstCompletedSession(timeoutMs = 120000) {
await expect(async () => {
const refreshButton = this.page.locator('button[aria-label="Refresh"]');
const isDisabled = await refreshButton.evaluate((element) => (element as HTMLButtonElement).disabled);

if (!isDisabled) {
await refreshButton.click();
}

const completedSession = await this.page.getByRole("button", { name: "1 Completed" }).isVisible();

expect(completedSession).toBe(true);

return true;
}).toPass({
timeout: timeoutMs,
intervals: [3000],
});
}

async setupProjectAndTriggerSession() {
await this.page.goto("/welcome");

try {
await this.page.locator('button[aria-label="Start from Template"]').hover();
await this.page.locator('button[aria-label="Start from Template"]').click();

await expect(this.page.locator('h2[aria-label="Start from Template"]')).toBeVisible();

await this.page.getByLabel("Categories").click();
await this.page.getByRole("option", { name: "Samples" }).click();
await this.page.locator("body").click({ position: { x: 0, y: 0 } });
await this.page.locator('button[aria-label="Create Project From Template: HTTP"]').scrollIntoViewIfNeeded();
await this.page.locator('button[aria-label="Create Project From Template: HTTP"]').click({ timeout: 2000 });

await this.page.getByPlaceholder("Enter project name").fill(this.projectName);
await this.page.waitForTimeout(500);

await this.page.locator('button[aria-label="Create"]').click();
await this.page.waitForURL(/\/explorer\/settings/);
await expect(this.page.getByRole("heading", { name: "Configuration" })).toBeVisible();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
await this.dashboardPage.createProjectFromTemplate(this.projectName);
}

await waitForLoadingOverlayGone(this.page);
await this.page.locator('button[aria-label="Open Triggers Section"]').click();
await expect(
this.page.locator(`button[aria-label='Trigger information for "receive_http_get_or_head"']`)
).toBeVisible();
await this.page.locator(`button[aria-label='Trigger information for "receive_http_get_or_head"']`).hover();

const copyButton = await this.page.waitForSelector('[data-testid="copy-receive_http_get_or_head-webhook-url"]');
const webhookUrl = await copyButton.getAttribute("value");

if (!webhookUrl) {
throw new Error("Failed to get webhook URL from button value attribute");
}

await this.page.locator('button[aria-label="Deploy project"]').click();

const toast = await waitForToast(this.page, "Project deployment completed successfully");
await expect(toast).toBeVisible();

const response = await this.request.get(webhookUrl, {
timeout: 5000,
});

if (!response.ok()) {
throw new Error(`Webhook request failed with status ${response.status()}`);
}

await this.page.locator('button[aria-label="Deployments"]').click();
await expect(this.page.getByText("Deployment History")).toBeVisible();

await expect(this.page.getByRole("heading", { name: "Configuration" })).toBeVisible();
await this.page.locator('button[aria-label="Close Project Settings"]').click();

await expect(this.page.getByText("Active").first()).toBeVisible();
const deploymentId = this.page.getByText(/bld_*/);
await expect(deploymentId).toBeVisible();

await this.waitForFirstCompletedSession();
}
}
182 changes: 182 additions & 0 deletions e2e/project/sessionsCompactMode.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import type { APIRequestContext, Page } from "@playwright/test";

import { expect, test } from "../fixtures";
import { WebhookSessionPage } from "e2e/pages";

interface SetupParams {
page: Page;
request: APIRequestContext;
}

test.describe("Sessions Table Compact Mode Suite", () => {
test.beforeEach(async ({ page, request }: SetupParams) => {
const webhookSessionPage = new WebhookSessionPage(page, request);
await webhookSessionPage.setupProjectAndTriggerSession();
});

test("Should display trigger icons when sessions table is in compact mode", async ({ page }) => {
test.setTimeout(5 * 60 * 1000);

const sessionsButton = page.locator('button[aria-label="Sessions"]');
await sessionsButton.click();

await page.waitForTimeout(2000);

const sessionsTableFrame = page.locator("#sessions-table");
await expect(sessionsTableFrame).toBeVisible();

const sessionTriggerTypeIconInSourceColumn = page.getByRole("img", {
name: "webhook receive_http_get_or_head trigger icon",
exact: true,
});
await expect(sessionTriggerTypeIconInSourceColumn).toBeVisible();
const sessionTriggerTypeIconInStartTimeColumn = page.getByRole("img", {
name: "webhook receive_http_get_or_head trigger",
exact: true,
});
await expect(sessionTriggerTypeIconInStartTimeColumn).not.toBeVisible();

const resizeButton = page.locator("#sessions-table-resize-button");
await expect(resizeButton).toBeVisible();

const resizeButtonBox = await resizeButton.boundingBox();
if (!resizeButtonBox) {
throw new Error("Resize button not found");
}

const viewportSize = page.viewportSize();
if (!viewportSize) {
throw new Error("Viewport size not available");
}

const targetX = viewportSize.width * 0.22;

await page.mouse.move(
resizeButtonBox.x + resizeButtonBox.width / 2,
resizeButtonBox.y + resizeButtonBox.height / 2
);
await page.mouse.down();
await page.mouse.move(targetX, resizeButtonBox.y + resizeButtonBox.height / 2);
await page.mouse.up();

await page.waitForTimeout(500);

const sessionTriggerNameCell = page.getByRole("cell", {
name: "receive_http_get_or_head",
exact: true,
});
await expect(sessionTriggerNameCell).not.toBeVisible();
const sessionTriggerTypeIconInStartColumn = page.getByRole("img", {
name: "webhook receive_http_get_or_head trigger",
exact: true,
});
await expect(sessionTriggerTypeIconInStartColumn).toBeVisible();
});

test("Should display text when sessions table is not in compact mode", async ({ page }) => {
test.setTimeout(5 * 60 * 1000);

await page.getByRole("button", { name: "Sessions" }).click();

await page.waitForTimeout(2000);

const sessionsTableFrame = page.locator("#sessions-table");
await expect(sessionsTableFrame).toBeVisible();

const resizeButton = page.locator("#sessions-table-resize-button");
await expect(resizeButton).toBeVisible();

const resizeButtonBox = await resizeButton.boundingBox();
if (!resizeButtonBox) {
throw new Error("Resize button not found");
}

const viewportSize = page.viewportSize();
if (!viewportSize) {
throw new Error("Viewport size not available");
}

const targetX = viewportSize.width * 0.5;

await page.mouse.move(
resizeButtonBox.x + resizeButtonBox.width / 2,
resizeButtonBox.y + resizeButtonBox.height / 2
);
await page.mouse.down();
await page.mouse.move(targetX, resizeButtonBox.y + resizeButtonBox.height / 2);
await page.mouse.up();

await page.waitForTimeout(500);

const sessionTriggerNameCell = page.getByRole("cell", { name: "receive_http_get_or_head", exact: true });
await expect(sessionTriggerNameCell).toBeVisible();
const sessionTriggerTypeIcon = page.getByRole("img", { name: "webhook receive_http_get_or_head trigger icon" });

await expect(sessionTriggerTypeIcon).toBeVisible();
});

test("Should toggle between icon and text when resizing", async ({ page }) => {
test.setTimeout(5 * 60 * 1000);

await page.getByRole("button", { name: "Sessions" }).click();

await page.waitForTimeout(2000);

const sessionsTableFrame = page.locator("#sessions-table");
await expect(sessionsTableFrame).toBeVisible();

const resizeButton = page.locator("#sessions-table-resize-button");
await expect(resizeButton).toBeVisible();

const resizeButtonBox = await resizeButton.boundingBox();
if (!resizeButtonBox) {
throw new Error("Resize button not found");
}

const viewportSize = page.viewportSize();
if (!viewportSize) {
throw new Error("Viewport size not available");
}

const wideTargetX = viewportSize.width * 0.5;
await page.mouse.move(
resizeButtonBox.x + resizeButtonBox.width / 2,
resizeButtonBox.y + resizeButtonBox.height / 2
);
await page.mouse.down();
await page.mouse.move(wideTargetX, resizeButtonBox.y + resizeButtonBox.height / 2);
await page.mouse.up();

await page.waitForTimeout(500);

const sessionTriggerNameCell = page.getByRole("cell", { name: "receive_http_get_or_head", exact: true });
await expect(sessionTriggerNameCell).toBeVisible();

const newResizeButtonBox = await resizeButton.boundingBox();
if (!newResizeButtonBox) {
throw new Error("Resize button not found after first resize");
}

const narrowTargetX = viewportSize.width * 0.2;
await page.mouse.move(
newResizeButtonBox.x + newResizeButtonBox.width / 2,
newResizeButtonBox.y + newResizeButtonBox.height / 2
);
await page.mouse.down();
await page.mouse.move(narrowTargetX, newResizeButtonBox.y + newResizeButtonBox.height / 2);
await page.mouse.up();

await page.waitForTimeout(500);

const sessionTriggerNameCellAfterResize = page.getByRole("img", {
name: "webhook receive_http_get_or_head trigger",
exact: true,
});
await expect(sessionTriggerNameCellAfterResize).toBeVisible();
const hasIconInStartTimeColumn = page.getByRole("img", {
name: "webhook receive_http_get_or_head trigger icon",
exact: true,
});
await expect(hasIconInStartTimeColumn).not.toBeVisible();
});
});
Loading
Loading