mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-09 15:17:59 -05:00
feat: generate simple auth tests (#8709)
This commit is contained in:
4
.github/workflows/platform-frontend-ci.yml
vendored
4
.github/workflows/platform-frontend-ci.yml
vendored
@@ -69,6 +69,10 @@ jobs:
|
||||
run: |
|
||||
cp ../supabase/docker/.env.example ../.env
|
||||
|
||||
- name: Copy backend .env
|
||||
run: |
|
||||
cp ../backend/.env.example ../backend/.env
|
||||
|
||||
- name: Run docker compose
|
||||
run: |
|
||||
docker compose -f ../docker-compose.yml up -d
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"defaults"
|
||||
],
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^9.2.0",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@next/third-parties": "^15.0.3",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
|
||||
@@ -4,10 +4,10 @@ import { defineConfig, devices } from "@playwright/test";
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// import dotenv from 'dotenv';
|
||||
// import path from 'path';
|
||||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
||||
|
||||
import dotenv from "dotenv";
|
||||
import path from "path";
|
||||
dotenv.config({ path: path.resolve(__dirname, ".env") });
|
||||
dotenv.config({ path: path.resolve(__dirname, "../backend/.env") });
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
|
||||
46
autogpt_platform/frontend/src/tests/auth.spec.ts
Normal file
46
autogpt_platform/frontend/src/tests/auth.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { test, expect } 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 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();
|
||||
});
|
||||
|
||||
test("user can logout successfully", async ({
|
||||
page,
|
||||
loginPage,
|
||||
testUser,
|
||||
}) => {
|
||||
await page.goto("/login"); // Make sure we're on the login page
|
||||
await loginPage.login(testUser.email, testUser.password);
|
||||
|
||||
// Expect to be on the home page
|
||||
await 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");
|
||||
});
|
||||
|
||||
test("login in, then out, then in again", async ({
|
||||
page,
|
||||
loginPage,
|
||||
testUser,
|
||||
}) => {
|
||||
await page.goto("/login"); // Make sure we're on the login page
|
||||
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 loginPage.login(testUser.email, testUser.password);
|
||||
await expect(page).toHaveURL("/");
|
||||
await expect(page.getByText("Monitor")).toBeVisible();
|
||||
});
|
||||
});
|
||||
18
autogpt_platform/frontend/src/tests/fixtures/index.ts
vendored
Normal file
18
autogpt_platform/frontend/src/tests/fixtures/index.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
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 { LoginPage } from "../pages/login.page";
|
||||
|
||||
type Fixtures = {
|
||||
testUser: TestUser;
|
||||
loginPage: LoginPage;
|
||||
};
|
||||
|
||||
// Combine fixtures
|
||||
export const test = base.extend<Fixtures>({
|
||||
testUser: createTestUserFixture,
|
||||
loginPage: createLoginPageFixture,
|
||||
});
|
||||
|
||||
export { expect } from "@playwright/test";
|
||||
14
autogpt_platform/frontend/src/tests/fixtures/login-page.fixture.ts
vendored
Normal file
14
autogpt_platform/frontend/src/tests/fixtures/login-page.fixture.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { test as base } from "@playwright/test";
|
||||
import { LoginPage } from "../pages/login.page";
|
||||
|
||||
export const loginPageFixture = base.extend<{ loginPage: LoginPage }>({
|
||||
loginPage: async ({ page }, use) => {
|
||||
await use(new LoginPage(page));
|
||||
},
|
||||
});
|
||||
|
||||
// Export just the fixture function
|
||||
export const createLoginPageFixture = async ({ page }, use) => {
|
||||
await use(new LoginPage(page));
|
||||
};
|
||||
83
autogpt_platform/frontend/src/tests/fixtures/test-user.fixture.ts
vendored
Normal file
83
autogpt_platform/frontend/src/tests/fixtures/test-user.fixture.ts
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
||||
import { faker } from "@faker-js/faker";
|
||||
|
||||
export type TestUser = {
|
||||
email: string;
|
||||
password: string;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
async function createTestUser(userData: TestUser): Promise<TestUser> {
|
||||
const supabase = getSupabaseAdmin();
|
||||
|
||||
const { data: authUser, error: authError } = await supabase.auth.signUp({
|
||||
email: userData.email,
|
||||
password: userData.password,
|
||||
});
|
||||
|
||||
if (authError) {
|
||||
throw new Error(`Failed to create test user: ${authError.message}`);
|
||||
}
|
||||
|
||||
return {
|
||||
...userData,
|
||||
id: authUser.user?.id,
|
||||
};
|
||||
}
|
||||
|
||||
async function deleteTestUser(userId: string) {
|
||||
const supabase = getSupabaseAdmin();
|
||||
|
||||
try {
|
||||
const { error } = await supabase.auth.admin.deleteUser(userId);
|
||||
|
||||
if (error) {
|
||||
console.warn(`Warning: Failed to delete test user: ${error.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Warning: Error during user cleanup: ${(error as Error).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function generateUserData(): TestUser {
|
||||
return {
|
||||
email: `test.${faker.string.uuid()}@example.com`,
|
||||
password: faker.internet.password({ length: 12 }),
|
||||
};
|
||||
}
|
||||
|
||||
// Export just the fixture function
|
||||
export const createTestUserFixture = async ({}, use) => {
|
||||
let user: TestUser | null = null;
|
||||
|
||||
try {
|
||||
const userData = generateUserData();
|
||||
user = await createTestUser(userData);
|
||||
await use(user);
|
||||
} finally {
|
||||
if (user?.id) {
|
||||
await deleteTestUser(user.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
51
autogpt_platform/frontend/src/tests/pages/login.page.ts
Normal file
51
autogpt_platform/frontend/src/tests/pages/login.page.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export class LoginPage {
|
||||
constructor(private page: Page) {}
|
||||
|
||||
async login(email: string, password: string) {
|
||||
console.log("Attempting login with:", { email, password }); // Debug log
|
||||
|
||||
// Fill email
|
||||
const emailInput = this.page.getByPlaceholder("user@email.com");
|
||||
await emailInput.waitFor({ state: "visible" });
|
||||
await emailInput.fill(email);
|
||||
|
||||
// Fill password
|
||||
const passwordInput = this.page.getByPlaceholder("password");
|
||||
await passwordInput.waitFor({ state: "visible" });
|
||||
await passwordInput.fill(password);
|
||||
|
||||
// Check terms
|
||||
const termsCheckbox = this.page.getByLabel("I agree to the Terms of Use");
|
||||
await termsCheckbox.waitFor({ state: "visible" });
|
||||
await termsCheckbox.click();
|
||||
|
||||
// TODO: This is a workaround to wait for the page to load after filling the email and password
|
||||
const emailInput2 = this.page.getByPlaceholder("user@email.com");
|
||||
await emailInput2.waitFor({ state: "visible" });
|
||||
await emailInput2.fill(email);
|
||||
|
||||
// Fill password
|
||||
const passwordInput2 = this.page.getByPlaceholder("password");
|
||||
await passwordInput2.waitFor({ state: "visible" });
|
||||
await passwordInput2.fill(password);
|
||||
|
||||
// Wait for the button to be ready
|
||||
const loginButton = this.page.getByRole("button", { name: "Log in" });
|
||||
await loginButton.waitFor({ state: "visible" });
|
||||
|
||||
// Start waiting for navigation before clicking
|
||||
const navigationPromise = this.page.waitForURL("/", { timeout: 60000 });
|
||||
|
||||
console.log("About to click login button"); // Debug log
|
||||
await loginButton.click();
|
||||
|
||||
console.log("Waiting for navigation"); // Debug log
|
||||
await navigationPromise;
|
||||
|
||||
console.log("Navigation complete, waiting for network idle"); // Debug log
|
||||
await this.page.waitForLoadState("networkidle", { timeout: 60000 });
|
||||
console.log("Login process complete"); // Debug log
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { test, expect } from "./fixtures";
|
||||
|
||||
test("has title", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { test, expect } from "./fixtures";
|
||||
import { setNestedProperty } from "../lib/utils";
|
||||
|
||||
const testCases = [
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
|
||||
export function generateUser() {
|
||||
return {
|
||||
email: faker.internet.email(),
|
||||
password: faker.internet.password(),
|
||||
name: faker.person.fullName(),
|
||||
};
|
||||
}
|
||||
@@ -1202,6 +1202,11 @@
|
||||
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz"
|
||||
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
|
||||
|
||||
"@faker-js/faker@^9.2.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.2.0.tgz#269ee3a5d2442e88e10d984e106028422bcb9551"
|
||||
integrity sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==
|
||||
|
||||
"@floating-ui/core@^1.6.0":
|
||||
version "1.6.7"
|
||||
resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz"
|
||||
|
||||
Reference in New Issue
Block a user