Skip to content

Commit

Permalink
feat: generate simple auth tests (#8709)
Browse files Browse the repository at this point in the history
  • Loading branch information
ntindle authored Nov 20, 2024
1 parent cf43248 commit 92bfbfa
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/platform-frontend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions autogpt_platform/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 4 additions & 4 deletions autogpt_platform/frontend/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
46 changes: 46 additions & 0 deletions autogpt_platform/frontend/src/tests/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions autogpt_platform/frontend/src/tests/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 14 additions & 0 deletions autogpt_platform/frontend/src/tests/fixtures/login-page.fixture.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 83 additions & 0 deletions autogpt_platform/frontend/src/tests/fixtures/test-user.fixture.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 51 additions & 0 deletions autogpt_platform/frontend/src/tests/pages/login.page.ts
Original file line number Diff line number Diff line change
@@ -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("[email protected]");
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("[email protected]");
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
}
}
2 changes: 1 addition & 1 deletion autogpt_platform/frontend/src/tests/title.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures";

test("has title", async ({ page }) => {
await page.goto("/");
Expand Down
2 changes: 1 addition & 1 deletion autogpt_platform/frontend/src/tests/util.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from "@playwright/test";
import { test, expect } from "./fixtures";
import { setNestedProperty } from "../lib/utils";

const testCases = [
Expand Down
9 changes: 9 additions & 0 deletions autogpt_platform/frontend/src/tests/utils/user-generator.ts
Original file line number Diff line number Diff line change
@@ -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(),
};
}
5 changes: 5 additions & 0 deletions autogpt_platform/frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 92bfbfa

Please sign in to comment.