test(e2e): test login page

This commit is contained in:
Dominik 2025-05-13 09:04:37 +02:00
parent 99601eaeb7
commit 11d98e63ae
10 changed files with 60 additions and 5 deletions

5
.env.test Normal file
View file

@ -0,0 +1,5 @@
AUTH_SECRET="auth_secret"
AUTH_URL="http://localhost:3000"
DATABASE_URL="file:./dev.db"
AUTH_AUTHENTIK_ID="id"
AUTH_AUTHENTIK_ISSUER="issuer"

View file

@ -23,5 +23,5 @@ jobs:
uses: https://github.com/cypress-io/github-action@v6
with:
build: yarn run build
start: yarn start
start: yarn cypress:start_server
component: true

1
.gitignore vendored
View file

@ -33,6 +33,7 @@ yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
!.env.example
!.env.test
# vercel
.vercel

View file

@ -7,4 +7,10 @@ export default defineConfig({
bundler: 'webpack',
},
},
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});

31
cypress/e2e/login.cy.ts Normal file
View file

@ -0,0 +1,31 @@
describe('login', () => {
it('loads', () => {
cy.visit('http://localhost:3000/');
cy.getBySel('login-header').should('exist');
});
it('shows login form', () => {
cy.visit('http://localhost:3000/');
cy.getBySel('login-form').should('exist');
cy.getBySel('email-input').should('exist');
cy.getBySel('password-input').should('exist');
cy.getBySel('login-button').should('exist');
});
it('shows sso button', () => {
cy.visit('http://localhost:3000/');
cy.getBySel('sso-login-button_authentik').should('exist');
});
it('allows login', () => {
cy.visit('http://localhost:3000/');
cy.getBySel('email-input').type('test@example.com');
cy.getBySel('password-input').type('password');
cy.getBySel('login-button').click();
cy.url().should('include', '/home');
});
});

View file

@ -8,6 +8,7 @@
"start": "node .next/standalone/server.js",
"lint": "next lint",
"format": "prettier --write .",
"cypress:start_server": "cp .next/static/ .next/standalone/.next/ -r && dotenv -e .env.test -- node .next/standalone/server.js",
"cypress:open": "cypress open",
"cypress:run": "cypress run",
"prisma:migrate": "dotenv -e .env.local -- prisma migrate dev",

View file

@ -30,7 +30,9 @@ export default async function LoginPage() {
<div>
<Card className='w-[350px] max-w-screen'>
<CardHeader>
<CardTitle className='text-lg text-center'>Login</CardTitle>
<CardTitle className='text-lg text-center' data-cy='login-header'>
Login
</CardTitle>
</CardHeader>
<CardContent className='gap-6 flex flex-col'>
<LoginForm />
@ -42,6 +44,7 @@ export default async function LoginPage() {
key={provider.id}
provider={provider.id}
providerDisplayName={provider.name}
data-cy={'sso-login-button_' + provider.name.toLowerCase()}
/>
))}
</CardContent>

View file

@ -7,13 +7,14 @@ export default function LabeledInput({
placeholder,
value,
name,
...props
}: {
type: 'text' | 'email' | 'password';
label: string;
placeholder?: string;
name: string;
value?: string;
name?: string;
}) {
} & React.InputHTMLAttributes<HTMLInputElement>) {
return (
<div className='flex flex-col gap-1'>
<Label htmlFor={name}>{label}</Label>
@ -24,6 +25,7 @@ export default function LabeledInput({
defaultValue={value}
id={name}
name={name}
{...props}
/>
</div>
);

View file

@ -10,6 +10,7 @@ export default function LoginForm() {
return (
<form
className='flex flex-col gap-5 w-full'
data-cy='login-form'
action={async (formData) => {
'use server';
try {
@ -27,17 +28,20 @@ export default function LoginForm() {
label='E-Mail or Username'
placeholder='What you are known as.'
name='email'
data-cy='email-input'
/>
<LabeledInput
type='password'
label='Password'
placeholder="Let's hope you remember it."
name='password'
data-cy='password-input'
/>
<Button
className='hover:bg-blue-600 hover:text-white'
type='submit'
variant='secondary'
data-cy='login-button'
>
Login
</Button>

View file

@ -5,10 +5,11 @@ import { faOpenid } from '@fortawesome/free-brands-svg-icons';
export default function SSOLogin({
provider,
providerDisplayName,
...props
}: {
provider: string;
providerDisplayName: string;
}) {
} & React.HTMLProps<HTMLFormElement>) {
return (
<form
className='flex flex-col items-center gap-4 w-full'
@ -16,6 +17,7 @@ export default function SSOLogin({
'use server';
await signIn(provider);
}}
{...props}
>
<IconButton
className='w-full'