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
3 changes: 2 additions & 1 deletion backend/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export async function connectToDatabase() {

console.log("Connected to MongoDB");

return client.db("artex");
// Use the normal Artex database unless another database is explicitly configured.
return client.db(process.env.MONGODB_DB ?? "artex");
}
62 changes: 62 additions & 0 deletions backend/src/seedE2eDatabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import "dotenv/config";
import { MongoClient } from "mongodb";

const mongoUri = process.env.MONGODB_URI ?? "mongodb://127.0.0.1:27017";

const sourceDatabaseName = process.env.E2E_SOURCE_DB ?? "artex";
const targetDatabaseName = process.env.MONGODB_DB ?? "artex_e2e_test";

const ARTWORK_LIMIT = 50;

const client = new MongoClient(mongoUri);

async function seedE2eDatabase() {
if (sourceDatabaseName === targetDatabaseName) {
throw new Error(
"E2E source and target database cannot be the same database.",
);
}

try {
await client.connect();

const sourceDatabase = client.db(sourceDatabaseName);
const targetDatabase = client.db(targetDatabaseName);

const sourceArtworks = sourceDatabase.collection("met_artworks");
const targetArtworks = targetDatabase.collection("met_artworks");

// Start every E2E run without data created by previous test runs.
await Promise.all([
targetDatabase.collection("users").deleteMany({}),
targetDatabase.collection("galleries").deleteMany({}),
targetArtworks.deleteMany({}),
]);

// Copy a small deterministic subset of real artwork data for the E2E tests.
const artworks = await sourceArtworks
.find({}, { projection: { _id: 0 } })
.sort({ id: 1 })
.limit(ARTWORK_LIMIT)
.toArray();

if (artworks.length === 0) {
throw new Error(
`No artworks found in source database "${sourceDatabaseName}".`,
);
}

await targetArtworks.insertMany(artworks);

console.log(
`Seeded "${targetDatabaseName}" with ${artworks.length} artworks from "${sourceDatabaseName}".`,
);
} finally {
await client.close();
}
}

seedE2eDatabase().catch((error: unknown) => {
console.error("Failed to seed E2E database:", error);
process.exitCode = 1;
});
69 changes: 69 additions & 0 deletions e2e/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { test, expect } from "@playwright/test";

test.describe("authentication", () => {
test.beforeEach(async ({ page }) => {
// Start each auth test without a persisted session.
await page.goto("/");

await page.evaluate(() => {
sessionStorage.clear();
});
});

test("redirects unauthenticated users from profile to login", async ({
page,
}) => {
await page.goto("/profile");

// Protected routes should redirect users without an auth token.
await expect(page).toHaveURL(/\/login$/);

await expect(
page.getByRole("heading", {
name: "Login",
level: 1,
}),
).toBeVisible();

await expect(page.getByLabel("Username")).toBeVisible();
await expect(page.getByLabel("Password")).toBeVisible();
});

test("shows an error when login credentials are invalid", async ({
page,
}) => {
await page.goto("/login");

const loginButton = page.getByRole("button", {
name: "Login",
exact: true,
});

const loginForm = loginButton.locator("xpath=ancestor::form");

const usernameInput = loginForm.getByLabel("Username");
const passwordInput = loginForm.getByLabel("Password");

// Use credentials that should not belong to a real account.
await usernameInput.fill(`invalid-user-${Date.now()}`);
await passwordInput.fill("DefinitelyWrong123!");

await expect(usernameInput).not.toHaveValue("");
await expect(passwordInput).not.toHaveValue("");

await loginButton.click();

// The login attempt should fail without navigating away.
await expect(page).toHaveURL(/\/login$/);

await expect(page.getByRole("alert")).toBeVisible();

// The form should become usable again after the failed request.
await expect(
page.getByRole("button", {
name: "Login",
exact: true,
}),
).toBeEnabled();
});
});
Loading