Skip to content

✨ Add private, local only, API for usage in E2E tests #1429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Dec 2, 2024
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
4 changes: 4 additions & 0 deletions .github/workflows/generate-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ jobs:
- run: uv run bash scripts/generate-client.sh
env:
VIRTUAL_ENV: backend/.venv
ENVIRONMENT: production
SECRET_KEY: just-for-generating-client
POSTGRES_PASSWORD: just-for-generating-client
FIRST_SUPERUSER_PASSWORD: just-for-generating-client
- name: Add changes to git
run: |
git config --local user.email "[email protected]"
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "0.4.15"
enable-cache: true
- run: uv sync
working-directory: backend
- run: npm ci
working-directory: frontend
- run: uv run bash scripts/generate-client.sh
env:
VIRTUAL_ENV: backend/.venv
- run: docker compose build
- run: docker compose down -v --remove-orphans
- name: Run Playwright tests
Expand Down
7 changes: 6 additions & 1 deletion backend/app/api/main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from fastapi import APIRouter

from app.api.routes import items, login, users, utils
from app.api.routes import items, login, private, users, utils
from app.core.config import settings

api_router = APIRouter()
api_router.include_router(login.router, tags=["login"])
api_router.include_router(users.router, prefix="/users", tags=["users"])
api_router.include_router(utils.router, prefix="/utils", tags=["utils"])
api_router.include_router(items.router, prefix="/items", tags=["items"])


if settings.ENVIRONMENT == "local":
api_router.include_router(private.router)
38 changes: 38 additions & 0 deletions backend/app/api/routes/private.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Any

from fastapi import APIRouter
from pydantic import BaseModel

from app.api.deps import SessionDep
from app.core.security import get_password_hash
from app.models import (
User,
UserPublic,
)

router = APIRouter(tags=["private"], prefix="/private")


class PrivateUserCreate(BaseModel):
email: str
password: str
full_name: str
is_verified: bool = False


@router.post("/users/", response_model=UserPublic)
def create_user(user_in: PrivateUserCreate, session: SessionDep) -> Any:
"""
Create a new user.
"""

user = User(
email=user_in.email,
full_name=user_in.full_name,
hashed_password=get_password_hash(user_in.password),
)

session.add(user)
session.commit()

return user
26 changes: 26 additions & 0 deletions backend/app/tests/api/routes/test_private.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from fastapi.testclient import TestClient
from sqlmodel import Session, select

from app.core.config import settings
from app.models import User


def test_create_user(client: TestClient, db: Session) -> None:
r = client.post(
f"{settings.API_V1_STR}/private/users/",
json={
"email": "[email protected]",
"password": "password123",
"full_name": "Pollo Listo",
},
)

assert r.status_code == 200

data = r.json()

user = db.exec(select(User).where(User.id == data["id"])).first()

assert user
assert user.email == "[email protected]"
assert user.full_name == "Pollo Listo"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣 🐔

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"build": "tsc -p tsconfig.build.json && vite build",
"lint": "biome check --apply-unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./",
"preview": "vite preview",
"generate-client": "openapi-ts"
Expand Down
43 changes: 14 additions & 29 deletions frontend/tests/user-settings.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { expect, test } from "@playwright/test"
import { firstSuperuser, firstSuperuserPassword } from "./config.ts"
import { randomEmail, randomPassword } from "./utils/random"
import { logInUser, logOutUser, signUpNewUser } from "./utils/user"
import { logInUser, logOutUser } from "./utils/user"
import { createUser } from "./utils/privateApi.ts"

const tabs = ["My profile", "Password", "Appearance"]

Expand All @@ -26,13 +27,11 @@ test.describe("Edit user full name and email successfully", () => {
test.use({ storageState: { cookies: [], origins: [] } })

test("Edit user name with a valid name", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const updatedName = "Test User 2"
const password = randomPassword()

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -50,13 +49,11 @@ test.describe("Edit user full name and email successfully", () => {
})

test("Edit user email with a valid email", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const updatedEmail = randomEmail()
const password = randomPassword()

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -77,13 +74,11 @@ test.describe("Edit user with invalid data", () => {
test.use({ storageState: { cookies: [], origins: [] } })

test("Edit user email with an invalid email", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const password = randomPassword()
const invalidEmail = ""

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -97,13 +92,11 @@ test.describe("Edit user with invalid data", () => {
})

test("Cancel edit action restores original name", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const password = randomPassword()
const updatedName = "Test User"

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
const user = await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -114,18 +107,18 @@ test.describe("Edit user with invalid data", () => {
await page.getByLabel("Full name").fill(updatedName)
await page.getByRole("button", { name: "Cancel" }).first().click()
await expect(
page.getByLabel("My profile").getByText(fullName, { exact: true }),
page
.getByLabel("My profile")
.getByText(user.full_name as string, { exact: true }),
).toBeVisible()
})

test("Cancel edit action restores original email", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const password = randomPassword()
const updatedEmail = randomEmail()

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -147,13 +140,11 @@ test.describe("Change password successfully", () => {
test.use({ storageState: { cookies: [], origins: [] } })

test("Update password successfully", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const password = randomPassword()
const NewPassword = randomPassword()

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -177,13 +168,11 @@ test.describe("Change password with invalid data", () => {
test.use({ storageState: { cookies: [], origins: [] } })

test("Update password with weak passwords", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const password = randomPassword()
const weakPassword = "weak"

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -201,14 +190,12 @@ test.describe("Change password with invalid data", () => {
test("New password and confirmation password do not match", async ({
page,
}) => {
const fullName = "Test User"
const email = randomEmail()
const password = randomPassword()
const newPassword = randomPassword()
const confirmPassword = randomPassword()

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand All @@ -223,12 +210,10 @@ test.describe("Change password with invalid data", () => {
})

test("Current password and new password are the same", async ({ page }) => {
const fullName = "Test User"
const email = randomEmail()
const password = randomPassword()

// Sign up a new user
await signUpNewUser(page, fullName, email, password)
await createUser({ email, password })

// Log in the user
await logInUser(page, email, password)
Expand Down
22 changes: 22 additions & 0 deletions frontend/tests/utils/privateApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Note: the `PrivateService` is only available when generating the client
// for local environments
import { OpenAPI, PrivateService } from "../../src/client"

OpenAPI.BASE = `${process.env.VITE_API_URL}`

export const createUser = async ({
email,
password,
}: {
email: string
password: string
}) => {
return await PrivateService.createUser({
requestBody: {
email,
password,
is_verified: true,
full_name: "Test User",
},
})
}
4 changes: 4 additions & 0 deletions frontend/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["tests/**/*.ts"]
}