mirror of
https://github.com/Significant-Gravitas/Auto-GPT.git
synced 2025-01-08 11:57:32 +08:00
feat(tests): add baseline utility for integration testing from frontend ui (#8765)
This commit is contained in:
parent
86fbbae65c
commit
5dd151b41e
2
.gitignore
vendored
2
.gitignore
vendored
@ -171,3 +171,5 @@ ig*
|
||||
.github_access_token
|
||||
LICENSE.rtf
|
||||
autogpt_platform/backend/settings.py
|
||||
/.auth
|
||||
/autogpt_platform/frontend/.auth
|
||||
|
@ -143,7 +143,9 @@ export default function PrivatePage() {
|
||||
return (
|
||||
<div className="mx-auto max-w-3xl md:py-8">
|
||||
<div className="flex items-center justify-between">
|
||||
<p>Hello {user.email}</p>
|
||||
<p>
|
||||
Hello <span data-testid="profile-email">{user.email}</span>
|
||||
</p>
|
||||
<Button onClick={() => supabase.auth.signOut()}>
|
||||
<LogOutIcon className="mr-1.5 size-4" />
|
||||
Log out
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { test, expect } from "./fixtures";
|
||||
// auth.spec.ts
|
||||
import { test } from "./fixtures";
|
||||
|
||||
test.describe("Authentication", () => {
|
||||
test("user can login successfully", async ({ page, loginPage, testUser }) => {
|
||||
await page.goto("/login"); // Make sure we're on the login page
|
||||
await page.goto("/login");
|
||||
await loginPage.login(testUser.email, testUser.password);
|
||||
// expect to be redirected to the home page
|
||||
await expect(page).toHaveURL("/");
|
||||
// expect to see the Monitor text
|
||||
await expect(page.getByText("Monitor")).toBeVisible();
|
||||
await test.expect(page).toHaveURL("/");
|
||||
await test.expect(page.getByText("Monitor")).toBeVisible();
|
||||
});
|
||||
|
||||
test("user can logout successfully", async ({
|
||||
@ -15,17 +14,17 @@ test.describe("Authentication", () => {
|
||||
loginPage,
|
||||
testUser,
|
||||
}) => {
|
||||
await page.goto("/login"); // Make sure we're on the login page
|
||||
await page.goto("/login");
|
||||
await loginPage.login(testUser.email, testUser.password);
|
||||
|
||||
// Expect to be on the home page
|
||||
await expect(page).toHaveURL("/");
|
||||
await test.expect(page).toHaveURL("/");
|
||||
|
||||
// Click on the user menu
|
||||
await page.getByRole("button", { name: "CN" }).click();
|
||||
// Click on the logout menu item
|
||||
await page.getByRole("menuitem", { name: "Log out" }).click();
|
||||
// Expect to be redirected to the login page
|
||||
await expect(page).toHaveURL("/login");
|
||||
|
||||
await test.expect(page).toHaveURL("/login");
|
||||
});
|
||||
|
||||
test("login in, then out, then in again", async ({
|
||||
@ -33,14 +32,14 @@ test.describe("Authentication", () => {
|
||||
loginPage,
|
||||
testUser,
|
||||
}) => {
|
||||
await page.goto("/login"); // Make sure we're on the login page
|
||||
await page.goto("/login");
|
||||
await loginPage.login(testUser.email, testUser.password);
|
||||
await page.goto("/");
|
||||
await page.getByRole("button", { name: "CN" }).click();
|
||||
await page.getByRole("menuitem", { name: "Log out" }).click();
|
||||
await expect(page).toHaveURL("/login");
|
||||
await test.expect(page).toHaveURL("/login");
|
||||
await loginPage.login(testUser.email, testUser.password);
|
||||
await expect(page).toHaveURL("/");
|
||||
await expect(page.getByText("Monitor")).toBeVisible();
|
||||
await test.expect(page).toHaveURL("/");
|
||||
await test.expect(page.getByText("Monitor")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
@ -1,18 +1,109 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { test as base } from "@playwright/test";
|
||||
import { createTestUserFixture } from "./test-user.fixture";
|
||||
import { createLoginPageFixture } from "./login-page.fixture";
|
||||
import type { TestUser } from "./test-user.fixture";
|
||||
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
||||
import { faker } from "@faker-js/faker";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { TestUser } from "./test-user.fixture";
|
||||
import { LoginPage } from "../pages/login.page";
|
||||
|
||||
type Fixtures = {
|
||||
// Extend both worker state and test-specific fixtures
|
||||
type WorkerFixtures = {
|
||||
workerAuth: TestUser;
|
||||
};
|
||||
|
||||
type TestFixtures = {
|
||||
testUser: TestUser;
|
||||
loginPage: LoginPage;
|
||||
};
|
||||
|
||||
// Combine fixtures
|
||||
export const test = base.extend<Fixtures>({
|
||||
testUser: createTestUserFixture,
|
||||
loginPage: createLoginPageFixture,
|
||||
let supabase: SupabaseClient;
|
||||
|
||||
function getSupabaseAdmin() {
|
||||
if (!supabase) {
|
||||
supabase = createClient(
|
||||
process.env.SUPABASE_URL!,
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
||||
{
|
||||
auth: {
|
||||
autoRefreshToken: false,
|
||||
persistSession: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
return supabase;
|
||||
}
|
||||
|
||||
export const test = base.extend<TestFixtures, WorkerFixtures>({
|
||||
// Define the worker-level fixture that creates and manages worker-specific auth
|
||||
workerAuth: [
|
||||
async ({}, use, workerInfo) => {
|
||||
const workerId = workerInfo.workerIndex;
|
||||
const fileName = path.resolve(
|
||||
process.cwd(),
|
||||
`.auth/worker-${workerId}.json`,
|
||||
);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
const dirPath = path.dirname(fileName);
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
|
||||
let auth: TestUser;
|
||||
if (fs.existsSync(fileName)) {
|
||||
auth = JSON.parse(fs.readFileSync(fileName, "utf-8"));
|
||||
} else {
|
||||
// Generate new worker-specific test user
|
||||
auth = {
|
||||
email: `test.worker.${workerId}.${Date.now()}@example.com`,
|
||||
password: faker.internet.password({ length: 12 }),
|
||||
};
|
||||
|
||||
const supabase = getSupabaseAdmin();
|
||||
const {
|
||||
data: { user },
|
||||
error: signUpError,
|
||||
} = await supabase.auth.signUp({
|
||||
email: auth.email,
|
||||
password: auth.password,
|
||||
});
|
||||
|
||||
if (signUpError) {
|
||||
throw signUpError;
|
||||
}
|
||||
|
||||
auth.id = user?.id;
|
||||
fs.writeFileSync(fileName, JSON.stringify(auth));
|
||||
}
|
||||
|
||||
await use(auth);
|
||||
|
||||
// Cleanup code is commented out to preserve test users during development
|
||||
/*
|
||||
if (workerInfo.project.metadata.teardown) {
|
||||
if (auth.id) {
|
||||
await deleteTestUser(auth.id);
|
||||
}
|
||||
if (fs.existsSync(fileName)) {
|
||||
fs.unlinkSync(fileName);
|
||||
}
|
||||
}
|
||||
*/
|
||||
},
|
||||
{ scope: "worker" },
|
||||
],
|
||||
|
||||
// Define the test-level fixture that provides access to the worker auth
|
||||
testUser: async ({ workerAuth }, use) => {
|
||||
await use(workerAuth);
|
||||
},
|
||||
|
||||
// Update login page fixture to use worker auth by default
|
||||
loginPage: async ({ page }, use) => {
|
||||
await use(new LoginPage(page));
|
||||
},
|
||||
});
|
||||
|
||||
export { expect } from "@playwright/test";
|
||||
|
15
autogpt_platform/frontend/src/tests/pages/base.page.ts
Normal file
15
autogpt_platform/frontend/src/tests/pages/base.page.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Page } from "@playwright/test";
|
||||
import { NavBar } from "./navbar.page";
|
||||
|
||||
export class BasePage {
|
||||
readonly navbar: NavBar;
|
||||
|
||||
constructor(protected page: Page) {
|
||||
this.navbar = new NavBar(page);
|
||||
}
|
||||
|
||||
async waitForPageLoad() {
|
||||
// Common page load waiting logic
|
||||
await this.page.waitForLoadState("networkidle", { timeout: 10000 });
|
||||
}
|
||||
}
|
51
autogpt_platform/frontend/src/tests/pages/navbar.page.ts
Normal file
51
autogpt_platform/frontend/src/tests/pages/navbar.page.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export class NavBar {
|
||||
constructor(private page: Page) {}
|
||||
|
||||
async clickProfileLink() {
|
||||
// await this.page.getByTestId("profile-link").click();
|
||||
|
||||
await this.page.getByRole("button", { name: "CN" }).click();
|
||||
await this.page.getByRole("menuitem", { name: "Profile" }).click();
|
||||
}
|
||||
|
||||
async clickMonitorLink() {
|
||||
await this.page.getByTestId("monitor-link").click();
|
||||
}
|
||||
|
||||
async clickBuildLink() {
|
||||
await this.page.getByTestId("build-link").click();
|
||||
}
|
||||
|
||||
async clickMarketplaceLink() {
|
||||
await this.page.getByTestId("marketplace-link").click();
|
||||
}
|
||||
|
||||
async getUserMenuButton() {
|
||||
return this.page.getByRole("button", { name: "CN" });
|
||||
}
|
||||
|
||||
async clickUserMenu() {
|
||||
await (await this.getUserMenuButton()).click();
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.clickUserMenu();
|
||||
await this.page.getByRole("menuitem", { name: "Log out" }).click();
|
||||
}
|
||||
|
||||
async isLoggedIn(): Promise<boolean> {
|
||||
try {
|
||||
await (
|
||||
await this.getUserMenuButton()
|
||||
).waitFor({
|
||||
state: "visible",
|
||||
timeout: 5000,
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
38
autogpt_platform/frontend/src/tests/pages/profile.page.ts
Normal file
38
autogpt_platform/frontend/src/tests/pages/profile.page.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Page } from "@playwright/test";
|
||||
import { BasePage } from "./base.page";
|
||||
|
||||
export class ProfilePage extends BasePage {
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
}
|
||||
|
||||
async getDisplayedEmail(): Promise<string> {
|
||||
await this.waitForPageToLoad();
|
||||
const email = await this.page.getByTestId("profile-email").textContent();
|
||||
if (!email) {
|
||||
throw new Error("Email not found");
|
||||
}
|
||||
return email;
|
||||
}
|
||||
|
||||
async isLoaded(): Promise<boolean> {
|
||||
try {
|
||||
await this.waitForPageToLoad();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error loading profile page", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async waitForPageToLoad(): Promise<void> {
|
||||
await this.page.waitForLoadState("networkidle", { timeout: 60_000 });
|
||||
|
||||
await this.page.getByTestId("profile-email").waitFor({
|
||||
state: "visible",
|
||||
timeout: 60_000,
|
||||
});
|
||||
|
||||
await this.page.waitForLoadState("networkidle", { timeout: 60_000 });
|
||||
}
|
||||
}
|
57
autogpt_platform/frontend/src/tests/profile.spec.ts
Normal file
57
autogpt_platform/frontend/src/tests/profile.spec.ts
Normal file
@ -0,0 +1,57 @@
|
||||
// profile.spec.ts
|
||||
import { test } from "./fixtures";
|
||||
import { ProfilePage } from "./pages/profile.page";
|
||||
|
||||
test.describe("Profile", () => {
|
||||
let profilePage: ProfilePage;
|
||||
|
||||
test.beforeEach(async ({ page, loginPage, testUser }) => {
|
||||
profilePage = new ProfilePage(page);
|
||||
|
||||
// Start each test with login using worker auth
|
||||
await page.goto("/login");
|
||||
await loginPage.login(testUser.email, testUser.password);
|
||||
await test.expect(page).toHaveURL("/");
|
||||
});
|
||||
|
||||
test("user can view their profile information", async ({
|
||||
page,
|
||||
testUser,
|
||||
}) => {
|
||||
await profilePage.navbar.clickProfileLink();
|
||||
// workaround for #8788
|
||||
// sleep for 10 seconds to allow page to load due to bug in our system
|
||||
await page.waitForTimeout(10000);
|
||||
await page.reload();
|
||||
await page.reload();
|
||||
await test.expect(profilePage.isLoaded()).resolves.toBeTruthy();
|
||||
await test.expect(page).toHaveURL(new RegExp("/profile"));
|
||||
|
||||
// Verify email matches test worker's email
|
||||
const displayedEmail = await profilePage.getDisplayedEmail();
|
||||
test.expect(displayedEmail).toBe(testUser.email);
|
||||
});
|
||||
|
||||
test("profile navigation is accessible from navbar", async ({ page }) => {
|
||||
await profilePage.navbar.clickProfileLink();
|
||||
await test.expect(page).toHaveURL(new RegExp("/profile"));
|
||||
// workaround for #8788
|
||||
await page.reload();
|
||||
await page.reload();
|
||||
await test.expect(profilePage.isLoaded()).resolves.toBeTruthy();
|
||||
});
|
||||
|
||||
test("profile displays user Credential providers", async ({ page }) => {
|
||||
await profilePage.navbar.clickProfileLink();
|
||||
|
||||
// await test
|
||||
// .expect(page.getByTestId("profile-section-personal"))
|
||||
// .toBeVisible();
|
||||
// await test
|
||||
// .expect(page.getByTestId("profile-section-settings"))
|
||||
// .toBeVisible();
|
||||
// await test
|
||||
// .expect(page.getByTestId("profile-section-security"))
|
||||
// .toBeVisible();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user