diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..b7c0f63 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[run] +omit = + project/cli/database.py \ No newline at end of file diff --git a/cypress/integration/login_spec.js b/cypress/integration/login_spec.js new file mode 100644 index 0000000..4eb596c --- /dev/null +++ b/cypress/integration/login_spec.js @@ -0,0 +1,28 @@ +describe('Login', () => { + it('user log in', () => { + cy.visit('/login') + + // Blank + cy.get('#submit').click() + cy.assertRequired('email') + cy.assertRequired('password') + + // Email + cy.get('#email').type("invalidmail") + cy.assertInvalid('email', 'Geben Sie bitte eine gültige E-Mail-Adresse ein.') + + cy.get('#email').clear().type("test@test.de") + cy.assertValid('email') + + // Password + cy.get('#password').type("password") + cy.assertValid('password') + + // Submit + cy.get('#submit').click() + + cy.url().should('include', '/manage') + cy.get('h1').should('contain', 'Organisationen') + cy.getCookie('session').should('exist') + }) + }) \ No newline at end of file diff --git a/cypress/integration/profile_spec.js b/cypress/integration/profile_spec.js new file mode 100644 index 0000000..ba7f9b5 --- /dev/null +++ b/cypress/integration/profile_spec.js @@ -0,0 +1,7 @@ +describe('Profile', () => { + it('profile', () => { + cy.login() + cy.visit('/profile') + cy.get('h1').should('contain', 'test@test.de') + }) + }) \ No newline at end of file diff --git a/cypress/integration/register_spec.js b/cypress/integration/register_spec.js index 480548c..ea13710 100644 --- a/cypress/integration/register_spec.js +++ b/cypress/integration/register_spec.js @@ -1,15 +1,41 @@ describe('Register', () => { - beforeEach(() => { - cy.logexec('flask database reset --seed') - }) - it('registers user', () => { cy.visit('/register') - cy.get('input[name=email]').type("firstname.lastname@gmail.com") - cy.get('input[name=password]').type("iloveoveda{enter}") - cy.get('input[name=password_confirm]').type("iloveoveda{enter}") - cy.get('input[name=accept_tos]').check() - cy.get('input[name=submit]').click() + + // Blank + cy.get('#submit').click() + cy.assertRequired('email') + cy.assertRequired('password') + cy.assertRequired('password_confirm') + cy.assertRequired('accept_tos') + + // Email + cy.get('#email').type("invalidmail") + cy.assertInvalid('email', 'Geben Sie bitte eine gültige E-Mail-Adresse ein.') + + cy.get('#email').clear().type("test@test.de") + cy.assertInvalid('email', 'Mit dieser E-Mail existiert bereits ein Account.') + + cy.get('#email').clear().type("firstname.lastname@gmail.com") + cy.assertValid('email') + + // Password + cy.get('#password').type("short") + cy.assertInvalid('password', 'Geben Sie bitte mindestens 8 Zeichen ein.') + + cy.get('#password').clear().type("iloveoveda") + cy.assertValid('password') + + // Confirm password + cy.get('#password_confirm').type("different") + cy.assertInvalid('password_confirm', 'Wiederholen Sie bitte denselben Wert.') + + cy.get('#password_confirm').clear().type("iloveoveda") + cy.assertValid('password_confirm') + + // Submit + cy.get('#accept_tos').check() + cy.get('#submit').click() cy.url().should('eq', Cypress.config().baseUrl + '/') cy.get('div.alert').should('contain', 'Bestätigungsanleitung') diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 5bad9d4..cfcc692 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,28 +1,3 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) Cypress.Commands.add('logexec', (command) => { cy.exec(command, { failOnNonZeroExit: false }).then(result => { if (result.code) { @@ -32,4 +7,33 @@ Cypress.Commands.add('logexec', (command) => { Stderr:\n${result.stderr}`); } }) -}) \ No newline at end of file +}) + +Cypress.Commands.add('setup', () => { + cy.logexec('flask database reset --seed') + cy.logexec('flask user create test@test.de password --confirm') +}) + +Cypress.Commands.add('assertValid', (fieldId) => { + cy.get('#' + fieldId).should('have.class', 'is-valid') + cy.get('#' + fieldId + '-error').should('be.empty') +}) + + +Cypress.Commands.add('assertInvalid', (fieldId, msg) => { + cy.get('#' + fieldId).should('have.class', 'is-invalid') + cy.get('#' + fieldId + '-error').should('contain', msg) +}) + +Cypress.Commands.add('assertRequired', (fieldId) => { + cy.assertInvalid(fieldId, 'Pflichtfeld') +}) + +Cypress.Commands.add('login', (email = "test@test.de", password = "password") => { + cy.visit('/login') + cy.get('#email').type(email) + cy.get('#password').type(password) + cy.get('#submit').click() + cy.url().should('include', '/manage') + cy.getCookie('session').should('exist') +}) diff --git a/cypress/support/index.js b/cypress/support/index.js index d68db96..0aa354e 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,20 +1,5 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: import './commands' -// Alternatively you can use CommonJS syntax: -// require('./commands') +beforeEach(() => { + cy.setup() +}) \ No newline at end of file diff --git a/project/cli/database.py b/project/cli/database.py index d56875a..ec09cdc 100644 --- a/project/cli/database.py +++ b/project/cli/database.py @@ -1,4 +1,3 @@ -# pragma: no cover import click from flask.cli import AppGroup from sqlalchemy import MetaData diff --git a/project/cli/user.py b/project/cli/user.py index b496ce5..0ced9fe 100644 --- a/project/cli/user.py +++ b/project/cli/user.py @@ -1,8 +1,13 @@ import click from flask.cli import AppGroup +from flask_security.confirmable import confirm_user from project import app, db -from project.services.user import add_admin_roles_to_user +from project.services.user import ( + add_admin_roles_to_user, + create_user, + find_user_by_email, +) user_cli = AppGroup("user") @@ -15,4 +20,27 @@ def add_admin_roles(email): click.echo(f"Admin roles were added to {email}.") +@user_cli.command("create") +@click.argument("email") +@click.argument("password") +@click.option("--confirm/--no-confirm", default=False) +def create(email, password, confirm): + user = create_user(email, password) + + if confirm: + confirm_user(user) + + db.session.commit() + click.echo(f"Created user {email}.") + + +@user_cli.command("confirm") +@click.argument("email") +def confirm(email): + user = find_user_by_email(email) + confirm_user(user) + db.session.commit() + click.echo(f"Confirmed user {email}.") + + app.cli.add_command(user_cli) diff --git a/project/templates/security/login_user.html b/project/templates/security/login_user.html index 6e9e01a..7549772 100644 --- a/project/templates/security/login_user.html +++ b/project/templates/security/login_user.html @@ -1,11 +1,23 @@ {% extends "layout.html" %} -{% from "_macros.html" import render_field_with_errors, render_field, render_field_errors %} +{% from "_macros.html" import render_jquery_steps_header, render_field_with_errors, render_field, render_field_errors %} + +{% block header %} +{{ render_jquery_steps_header() }} + + +{% endblock %} {% block content %}

{{ _fsdomain('Login') }}

{% set next = request.args['next'] if 'next' in request.args and 'authorize' in request.args['next'] else 'manage_after_login' %} -
+ {{ login_user_form.hidden_tag() }} {{ render_field_with_errors(login_user_form.email) }} {{ render_field_with_errors(login_user_form.password) }} diff --git a/project/templates/security/register_user.html b/project/templates/security/register_user.html index d0dffcd..7dcb5b9 100644 --- a/project/templates/security/register_user.html +++ b/project/templates/security/register_user.html @@ -26,7 +26,8 @@ required: true, minlength: 8, equalTo: "#password" - } + }, + accept_tos: "required", } }); }); @@ -45,8 +46,10 @@ {% endif %}
- {{ register_user_form.accept_tos(class="form-check-input")|safe }} - {{ register_user_form.accept_tos.label(class="form-check-label") }} +
+ {{ register_user_form.accept_tos(class="form-check-input")|safe }} + {{ register_user_form.accept_tos.label(class="form-check-label") }} +
{{ render_field(register_user_form.submit) }} diff --git a/tests/cli/test_user.py b/tests/cli/test_user.py index dac64c8..0ce89e2 100644 --- a/tests/cli/test_user.py +++ b/tests/cli/test_user.py @@ -26,3 +26,43 @@ def test_add_admin_roles_UserDoesNotExist(client, seeder, app): result = runner.invoke(args=["user", "add-admin-roles", "unknown@test.de"]) assert result.exit_code == 1 assert "User with given email does not exist." in result.exception.args[0] + + +def test_create(client, seeder, app): + with app.app_context(): + from project.services.user import find_user_by_email + + user = find_user_by_email("test@test.de") + assert not user + + runner = app.test_cli_runner() + result = runner.invoke( + args=["user", "create", "test@test.de", "password", "--confirm"] + ) + assert "Created user test@test.de." in result.output + + with app.app_context(): + from project.services.user import find_user_by_email + + user = find_user_by_email("test@test.de") + assert user.confirmed_at is not None + + +def test_confirm(client, seeder, app): + runner = app.test_cli_runner() + result = runner.invoke(args=["user", "create", "test@test.de", "password"]) + + with app.app_context(): + from project.services.user import find_user_by_email + + user = find_user_by_email("test@test.de") + assert user.confirmed_at is None + + result = runner.invoke(args=["user", "confirm", "test@test.de"]) + assert "Confirmed user test@test.de." in result.output + + with app.app_context(): + from project.services.user import find_user_by_email + + user = find_user_by_email("test@test.de") + assert user.confirmed_at is not None