diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..4e566a0 --- /dev/null +++ b/.env.test @@ -0,0 +1,4 @@ +AUTH_SECRET="auth_secret" +AUTH_URL="http://localhost:3000" +DATABASE_URL="file:./dev.db" +AUTH_AUTHENTIK_ISSUER="auth_issuer" \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 23c73d1..5a12acc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -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 diff --git a/.gitignore b/.gitignore index 7b8da95..15ad52f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* !.env.example +!.env.test # vercel .vercel diff --git a/cypress.config.ts b/cypress.config.ts index d2013c5..bebdaa5 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -7,4 +7,10 @@ export default defineConfig({ bundler: 'webpack', }, }, + + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + }, }); diff --git a/cypress/e2e/login.cy.ts b/cypress/e2e/login.cy.ts new file mode 100644 index 0000000..6045354 --- /dev/null +++ b/cypress/e2e/login.cy.ts @@ -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'); + }); +}); diff --git a/package.json b/package.json index a9fa0b1..b6b9548 100644 --- a/package.json +++ b/package.json @@ -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" }, @@ -43,6 +44,7 @@ "@types/react": "19.1.3", "@types/react-dom": "19.1.4", "cypress": "14.3.3", + "dotenv-cli": "^8.0.0", "eslint": "9.26.0", "eslint-config-next": "15.3.2", "eslint-config-prettier": "10.1.5", diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 714e996..6b74310 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -30,7 +30,9 @@ export default async function LoginPage() {
- Login + + Login + @@ -38,7 +40,11 @@ export default async function LoginPage() {
{process.env.AUTH_AUTHENTIK_ISSUER && ( - + )}
diff --git a/src/components/labeled-input.tsx b/src/components/labeled-input.tsx index 7b4768a..1f715a1 100644 --- a/src/components/labeled-input.tsx +++ b/src/components/labeled-input.tsx @@ -6,12 +6,13 @@ export default function LabeledInput({ label, placeholder, value, + ...props }: { type: 'text' | 'email' | 'password'; label: string; placeholder?: string; value?: string; -}) { +} & React.InputHTMLAttributes) { const elementId = Math.random().toString(36).substring(2, 15); return ( @@ -23,6 +24,7 @@ export default function LabeledInput({ placeholder={placeholder} defaultValue={value} id={elementId} + {...props} />
); diff --git a/src/components/user/login-form.tsx b/src/components/user/login-form.tsx index 20438e8..e25cec2 100644 --- a/src/components/user/login-form.tsx +++ b/src/components/user/login-form.tsx @@ -3,21 +3,24 @@ import { Button } from '@/components/ui/button'; export default function LoginForm() { return ( -
+ diff --git a/src/components/user/sso-login-button.tsx b/src/components/user/sso-login-button.tsx index 064e2eb..ca3b6ce 100644 --- a/src/components/user/sso-login-button.tsx +++ b/src/components/user/sso-login-button.tsx @@ -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) { return (