Password recovery rebuild, correctly translated Mails and some minor fixes #658
This commit is contained in:
commit
c0e97bfe75
|
@ -13,6 +13,12 @@ $route->get('/login', 'AuthController@login');
|
|||
$route->post('/login', 'AuthController@postLogin');
|
||||
$route->get('/logout', 'AuthController@logout');
|
||||
|
||||
// Password recovery
|
||||
$route->get('/password/reset', 'PasswordResetController@reset');
|
||||
$route->post('/password/reset', 'PasswordResetController@postReset');
|
||||
$route->get('/password/reset/{token:.+}', 'PasswordResetController@resetPassword');
|
||||
$route->post('/password/reset/{token:.+}', 'PasswordResetController@postResetPassword');
|
||||
|
||||
// Stats
|
||||
$route->get('/metrics', 'Metrics\\Controller@metrics');
|
||||
$route->get('/stats', 'Metrics\\Controller@stats');
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Engelsystem\Database\DB;
|
||||
use Engelsystem\Models\User\PasswordReset;
|
||||
use Engelsystem\Models\User\State;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\ShiftCalendarRenderer;
|
||||
|
@ -311,120 +310,6 @@ function users_list_controller()
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step of password recovery: set a new password using the token link from email
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function user_password_recovery_set_new_controller()
|
||||
{
|
||||
$request = request();
|
||||
$passwordReset = PasswordReset::whereToken($request->input('token'))->first();
|
||||
if (!$passwordReset) {
|
||||
error(__('Token is not correct.'));
|
||||
redirect(page_link_to('login'));
|
||||
}
|
||||
|
||||
if ($request->hasPostData('submit')) {
|
||||
$valid = true;
|
||||
|
||||
if (
|
||||
$request->has('password')
|
||||
&& strlen($request->postData('password')) >= config('min_password_length')
|
||||
) {
|
||||
if ($request->postData('password') != $request->postData('password2')) {
|
||||
$valid = false;
|
||||
error(__('Your passwords don\'t match.'));
|
||||
}
|
||||
} else {
|
||||
$valid = false;
|
||||
error(__('Your password is to short (please use at least 6 characters).'));
|
||||
}
|
||||
|
||||
if ($valid) {
|
||||
auth()->setPassword($passwordReset->user, $request->postData('password'));
|
||||
success(__('Password saved.'));
|
||||
$passwordReset->delete();
|
||||
redirect(page_link_to('login'));
|
||||
}
|
||||
}
|
||||
|
||||
return User_password_set_view();
|
||||
}
|
||||
|
||||
/**
|
||||
* First step of password recovery: display a form that asks for your email and send email with recovery link
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function user_password_recovery_start_controller()
|
||||
{
|
||||
$request = request();
|
||||
if ($request->hasPostData('submit')) {
|
||||
$valid = true;
|
||||
|
||||
$user_source = null;
|
||||
if ($request->has('email') && strlen(strip_request_item('email')) > 0) {
|
||||
$email = strip_request_item('email');
|
||||
if (check_email($email)) {
|
||||
/** @var User $user_source */
|
||||
$user_source = User::whereEmail($email)->first();
|
||||
if (!$user_source) {
|
||||
$valid = false;
|
||||
error(__('E-mail address is not correct.'));
|
||||
}
|
||||
} else {
|
||||
$valid = false;
|
||||
error(__('E-mail address is not correct.'));
|
||||
}
|
||||
} else {
|
||||
$valid = false;
|
||||
error(__('Please enter your e-mail.'));
|
||||
}
|
||||
|
||||
if ($valid) {
|
||||
$token = User_generate_password_recovery_token($user_source);
|
||||
engelsystem_email_to_user(
|
||||
$user_source,
|
||||
__('Password recovery'),
|
||||
sprintf(
|
||||
__('Please visit %s to recover your password.'),
|
||||
page_link_to('user_password_recovery', ['token' => $token])
|
||||
)
|
||||
);
|
||||
success(__('We sent an email containing your password recovery link.'));
|
||||
redirect(page_link_to('login'));
|
||||
}
|
||||
}
|
||||
|
||||
return User_password_recovery_view();
|
||||
}
|
||||
|
||||
/**
|
||||
* User password recovery in 2 steps.
|
||||
* (By email)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function user_password_recovery_controller()
|
||||
{
|
||||
if (request()->has('token')) {
|
||||
return user_password_recovery_set_new_controller();
|
||||
}
|
||||
|
||||
return user_password_recovery_start_controller();
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu title for password recovery.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function user_password_recovery_title()
|
||||
{
|
||||
return __('Password recovery');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a user from param user_id.
|
||||
*
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use Carbon\Carbon;
|
||||
use Engelsystem\Database\DB;
|
||||
use Engelsystem\Models\User\PasswordReset;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\ValidationResult;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
|
@ -227,24 +226,6 @@ function User_reset_api_key($user, $log = true)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new password recovery token for given user.
|
||||
*
|
||||
* @param User $user
|
||||
* @return string
|
||||
*/
|
||||
function User_generate_password_recovery_token($user)
|
||||
{
|
||||
$reset = PasswordReset::findOrNew($user->id);
|
||||
$reset->user_id = $user->id;
|
||||
$reset->token = md5($user->name . time() . rand());
|
||||
$reset->save();
|
||||
|
||||
engelsystem_log('Password recovery for ' . User_Nick_render($user, true) . ' started.');
|
||||
|
||||
return $reset->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return float
|
||||
|
|
|
@ -242,9 +242,9 @@ function guest_register()
|
|||
redirect(page_link_to('register'));
|
||||
}
|
||||
|
||||
// If a welcome message is present, display registration success page.
|
||||
// If a welcome message is present, display it on the next page
|
||||
if ($message = $config->get('welcome_msg')) {
|
||||
return User_registration_success_view($message);
|
||||
info((new Parsedown())->text($message));
|
||||
}
|
||||
|
||||
redirect(page_link_to('/'));
|
||||
|
|
|
@ -107,46 +107,6 @@ function User_settings_view(
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the welcome message to the user and shows a login form.
|
||||
*
|
||||
* @param string $event_welcome_message
|
||||
* @return string
|
||||
*/
|
||||
function User_registration_success_view($event_welcome_message)
|
||||
{
|
||||
$parsedown = new Parsedown();
|
||||
$event_welcome_message = $parsedown->text($event_welcome_message);
|
||||
|
||||
return page_with_title(__('Registration successful'), [
|
||||
msg(),
|
||||
div('row', [
|
||||
div('col-md-4', [
|
||||
$event_welcome_message
|
||||
]),
|
||||
div('col-md-4', [
|
||||
'<h2>' . __('Login') . '</h2>',
|
||||
form([
|
||||
form_text('login', __('Nick'), ''),
|
||||
form_password('password', __('Password')),
|
||||
form_submit('submit', __('Login')),
|
||||
buttons([
|
||||
button(page_link_to('user_password_recovery'), __('I forgot my password'))
|
||||
]),
|
||||
info(__('Please note: You have to activate cookies!'), true)
|
||||
], page_link_to('login'))
|
||||
]),
|
||||
div('col-md-4', [
|
||||
'<h2>' . __('What can I do?') . '</h2>',
|
||||
'<p>' . __('Please read about the jobs you can do to help us.') . '</p>',
|
||||
buttons([
|
||||
button(page_link_to('angeltypes', ['action' => 'about']), __('Teams/Job description') . ' »')
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gui for deleting user with password field.
|
||||
*
|
||||
|
@ -255,13 +215,13 @@ function Users_view(
|
|||
];
|
||||
|
||||
$user_table_headers = [
|
||||
'name' => Users_table_header_link('name', __('Nick'), $order_by)
|
||||
'name' => Users_table_header_link('name', __('Nick'), $order_by)
|
||||
];
|
||||
if(config('enable_user_name')) {
|
||||
if (config('enable_user_name')) {
|
||||
$user_table_headers['first_name'] = Users_table_header_link('first_name', __('Prename'), $order_by);
|
||||
$user_table_headers['last_name'] = Users_table_header_link('last_name', __('Name'), $order_by);
|
||||
}
|
||||
if(config('enable_dect')) {
|
||||
if (config('enable_dect')) {
|
||||
$user_table_headers['dect'] = Users_table_header_link('dect', __('DECT'), $order_by);
|
||||
}
|
||||
$user_table_headers['arrived'] = Users_table_header_link('arrived', __('Arrived'), $order_by);
|
||||
|
@ -271,8 +231,16 @@ function Users_view(
|
|||
$user_table_headers['force_active'] = Users_table_header_link('force_active', __('Forced'), $order_by);
|
||||
$user_table_headers['got_shirt'] = Users_table_header_link('got_shirt', __('T-Shirt'), $order_by);
|
||||
$user_table_headers['shirt_size'] = Users_table_header_link('shirt_size', __('Size'), $order_by);
|
||||
$user_table_headers['arrival_date'] = Users_table_header_link('planned_arrival_date', __('Planned arrival'), $order_by);
|
||||
$user_table_headers['departure_date'] = Users_table_header_link('planned_departure_date', __('Planned departure'), $order_by);
|
||||
$user_table_headers['arrival_date'] = Users_table_header_link(
|
||||
'planned_arrival_date',
|
||||
__('Planned arrival'),
|
||||
$order_by
|
||||
);
|
||||
$user_table_headers['departure_date'] = Users_table_header_link(
|
||||
'planned_departure_date',
|
||||
__('Planned departure'),
|
||||
$order_by
|
||||
);
|
||||
$user_table_headers['last_login_at'] = Users_table_header_link('last_login_at', __('Last login'), $order_by);
|
||||
$user_table_headers['actions'] = '';
|
||||
|
||||
|
@ -791,41 +759,6 @@ function User_view_state_admin($freeloader, $user_source)
|
|||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* View for password recovery step 1: E-Mail
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function User_password_recovery_view()
|
||||
{
|
||||
return page_with_title(user_password_recovery_title(), [
|
||||
msg(),
|
||||
__('We will send you an e-mail with a password recovery link. Please use the email address you used for registration.'),
|
||||
form([
|
||||
form_text('email', __('E-Mail'), ''),
|
||||
form_submit('submit', __('Recover'))
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* View for password recovery step 2: New password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function User_password_set_view()
|
||||
{
|
||||
return page_with_title(user_password_recovery_title(), [
|
||||
msg(),
|
||||
__('Please enter a new password.'),
|
||||
form([
|
||||
form_password('password', __('Password')),
|
||||
form_password('password2', __('Confirm password')),
|
||||
form_submit('submit', __('Save'))
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array[] $user_angeltypes
|
||||
* @return string
|
||||
|
|
|
@ -619,9 +619,9 @@ msgid "Please visit %s to recover your password."
|
|||
msgstr "Bitte besuche %s, um Dein Passwort zurückzusetzen"
|
||||
|
||||
#: includes/controller/users_controller.php:394
|
||||
msgid "We sent an email containing your password recovery link."
|
||||
msgid "We sent you an email containing your password recovery link."
|
||||
msgstr ""
|
||||
"Wir haben eine eMail mit einem Link zum Passwort-zurücksetzen geschickt."
|
||||
"Wir haben dir eine eMail mit einem Link zum Passwort-zurücksetzen geschickt."
|
||||
|
||||
#: includes/helper/email_helper.php:41
|
||||
#, php-format
|
||||
|
@ -2769,3 +2769,21 @@ msgstr "Bitte gib ein Passwort an."
|
|||
|
||||
msgid "validation.login.required"
|
||||
msgstr "Bitte gib einen Loginnamen an."
|
||||
|
||||
msgid "form.submit"
|
||||
msgstr "Absenden"
|
||||
|
||||
msgid "validation.email.required"
|
||||
msgstr "Bitte gib eine E-Mail-Adresse an."
|
||||
|
||||
msgid "validation.email.email"
|
||||
msgstr "Die E-Mail-Adresse ist nicht gültig."
|
||||
|
||||
msgid "validation.password.min"
|
||||
msgstr "Dein angegebenes Passwort ist zu kurz."
|
||||
|
||||
msgid "validation.password.confirmed"
|
||||
msgstr "Deine Passwörter stimmen nicht überein."
|
||||
|
||||
msgid "validation.password_confirmation.required"
|
||||
msgstr "Du musst dein Passwort bestätigen."
|
||||
|
|
|
@ -30,3 +30,21 @@ msgstr "The password is required."
|
|||
|
||||
msgid "validation.login.required"
|
||||
msgstr "The login name is required."
|
||||
|
||||
msgid "form.submit"
|
||||
msgstr "Submit"
|
||||
|
||||
msgid "validation.email.required"
|
||||
msgstr "The email address is required."
|
||||
|
||||
msgid "validation.email.email"
|
||||
msgstr "This email address is not valid."
|
||||
|
||||
msgid "validation.password.min"
|
||||
msgstr "Your password is too short."
|
||||
|
||||
msgid "validation.password.confirmed"
|
||||
msgstr "Your passwords are not equal."
|
||||
|
||||
msgid "validation.password_confirmation.required"
|
||||
msgstr "You have to confirm your password."
|
||||
|
|
|
@ -551,7 +551,7 @@ msgid "Please visit %s to recover your password."
|
|||
msgstr "Por favor visite %s para recuperar sua senha"
|
||||
|
||||
#: includes/controller/users_controller.php:271
|
||||
msgid "We sent an email containing your password recovery link."
|
||||
msgid "We sent you an email containing your password recovery link."
|
||||
msgstr "Nós enviamos um email com o link para recuperação da sua senha."
|
||||
|
||||
#: includes/helper/email_helper.php:12
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{ __('Hi %s,', [username]) }}
|
||||
{% block title %}{{ __('Hi %s,', [username]) }}{% endblock %}
|
||||
|
||||
{{ __('here is a message for you from the %s:', [config('app_name')]) }}
|
||||
{{ message|raw }}
|
||||
{% block introduction %}{{ __('here is a message for you from the %s:', [config('app_name')]) }}{% endblock %}
|
||||
{% block message %}{{ message|raw }}{% endblock %}
|
||||
|
||||
{{ __('This email is autogenerated and has not been signed. You got this email because you are registered in the %s.', [config('app_name')]) }}
|
||||
{% block footer %}{{ __('This email is autogenerated and has not been signed. You got this email because you are registered in the %s.', [config('app_name')]) }}{% endblock %}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{% extends "emails/mail.twig" %}
|
||||
|
||||
{% block message %}{{ __('Please visit %s to recover your password.', [url('/password/reset/') ~ reset.token]) }}{% endblock %}
|
|
@ -0,0 +1,18 @@
|
|||
{% macro input(name, label, type, required) %}
|
||||
<div class="form-group">
|
||||
{% if label %}
|
||||
<label for="{{ name }}">{{ label }}</label>
|
||||
{% endif %}
|
||||
<input type="{{ type|default('text') }}" class="form-control" id="{{ name }}" name="{{ name }}"
|
||||
{%- if required|default(false) %} required="required"{% endif -%}
|
||||
>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro hidden(name, value) %}
|
||||
<input type="hidden" id="{{ name }}" name="{{ name }}" value="{{ value }}">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro submit(label) %}
|
||||
<button type="submit" class="btn btn-default">{{ label|default(__('form.submit')) }}</button>
|
||||
{% endmacro %}
|
|
@ -32,6 +32,7 @@
|
|||
<div class="col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
|
||||
<div class="panel panel-primary first">
|
||||
<div class="panel-body">
|
||||
{{ msg() }}
|
||||
{% for message in errors|default([]) %}
|
||||
{{ m.alert(__(message), 'danger') }}
|
||||
{% endfor %}
|
||||
|
@ -61,7 +62,7 @@
|
|||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<a href="{{ url('user-password-recovery') }}" class="">
|
||||
<a href="{{ url('/password/reset') }}" class="">
|
||||
{{ __('I forgot my password') }}
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "pages/password/reset.twig" %}
|
||||
{% import 'macros/base.twig' as m %}
|
||||
{% import 'macros/form.twig' as f %}
|
||||
|
||||
{% block row_content %}
|
||||
<div class="col-md-8">
|
||||
<form action="" enctype="multipart/form-data" method="post">
|
||||
{{ csrf() }}
|
||||
|
||||
{{ f.input('password', __('Password'), 'password', true) }}
|
||||
{{ f.input('password_confirmation', __('Confirm password'), 'password', true) }}
|
||||
|
||||
<div class="form-group">
|
||||
{{ f.submit(__('Save')) }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,12 @@
|
|||
{% extends "pages/password/reset.twig" %}
|
||||
{% import 'macros/base.twig' as m %}
|
||||
|
||||
{% block row_content %}
|
||||
<div class="col-md-12">
|
||||
{% if type == 'email' %}
|
||||
{{ m.alert(__('We sent you an email containing your password recovery link.'), 'info') }}
|
||||
{% elseif type == 'reset' %}
|
||||
{{ m.alert(__('Password saved.'), 'success') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,32 @@
|
|||
{% extends 'layouts/app.twig' %}
|
||||
{% import 'macros/base.twig' as m %}
|
||||
{% import 'macros/form.twig' as f %}
|
||||
|
||||
{% block title %}{{ __('Password recovery') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{{ __('Password recovery') }}</h1>
|
||||
|
||||
{% for message in errors|default([]) %}
|
||||
{{ m.alert(__(message), 'danger') }}
|
||||
{% endfor %}
|
||||
|
||||
<div class="row">
|
||||
{% block row_content %}
|
||||
<div class="col-md-8">
|
||||
<form action="" enctype="multipart/form-data" method="post">
|
||||
{{ csrf() }}
|
||||
|
||||
{{ __('We will send you an e-mail with a password recovery link. Please use the email address you used for registration.') }}
|
||||
{{ f.input('email', __('E-Mail'), 'email', true) }}
|
||||
|
||||
<div class="form-group">
|
||||
{{ f.submit(__('Recover')) }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -88,7 +88,7 @@ class AuthController extends BaseController
|
|||
$user = $this->auth->authenticate($data['login'], $data['password']);
|
||||
|
||||
if (!$user instanceof User) {
|
||||
$this->session->set('errors', $this->session->get('errors', []) + ['auth.not-found']);
|
||||
$this->session->set('errors', array_merge($this->session->get('errors', []), ['auth.not-found']));
|
||||
|
||||
return $this->showLogin();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Controllers;
|
||||
|
||||
use Engelsystem\Http\Exceptions\HttpNotFound;
|
||||
use Engelsystem\Http\Request;
|
||||
use Engelsystem\Http\Response;
|
||||
use Engelsystem\Mail\EngelsystemMailer;
|
||||
use Engelsystem\Models\User\PasswordReset;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
|
||||
class PasswordResetController extends BaseController
|
||||
{
|
||||
/** @var LoggerInterface */
|
||||
protected $log;
|
||||
|
||||
/** @var EngelsystemMailer */
|
||||
protected $mail;
|
||||
|
||||
/** @var Response */
|
||||
protected $response;
|
||||
|
||||
/** @var SessionInterface */
|
||||
protected $session;
|
||||
|
||||
/** @var array */
|
||||
protected $permissions = [
|
||||
'reset' => 'login',
|
||||
'postReset' => 'login',
|
||||
'resetPassword' => 'login',
|
||||
'postResetPassword' => 'login',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
* @param SessionInterface $session
|
||||
* @param EngelsystemMailer $mail
|
||||
* @param LoggerInterface $log
|
||||
*/
|
||||
public function __construct(
|
||||
Response $response,
|
||||
SessionInterface $session,
|
||||
EngelsystemMailer $mail,
|
||||
LoggerInterface $log
|
||||
) {
|
||||
$this->log = $log;
|
||||
$this->mail = $mail;
|
||||
$this->response = $response;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response
|
||||
*/
|
||||
public function reset(): Response
|
||||
{
|
||||
return $this->showView('pages/password/reset');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function postReset(Request $request): Response
|
||||
{
|
||||
$data = $this->validate($request, [
|
||||
'email' => 'required|email',
|
||||
]);
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::whereEmail($data['email'])->first();
|
||||
if ($user) {
|
||||
$reset = PasswordReset::findOrNew($user->id);
|
||||
$reset->user_id = $user->id;
|
||||
$reset->token = md5(random_bytes(64));
|
||||
$reset->save();
|
||||
|
||||
$this->log->info(
|
||||
sprintf('Password recovery for %s (%u)', $user->name, $user->id),
|
||||
['user' => $user->toJson()]
|
||||
);
|
||||
|
||||
$this->mail->sendViewTranslated(
|
||||
$user,
|
||||
'Password recovery',
|
||||
'emails/password-reset',
|
||||
['username' => $user->name, 'reset' => $reset]
|
||||
);
|
||||
}
|
||||
|
||||
return $this->showView('pages/password/reset-success', ['type' => 'email']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function resetPassword(Request $request): Response
|
||||
{
|
||||
$this->requireToken($request);
|
||||
|
||||
return $this->showView('pages/password/reset-form');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function postResetPassword(Request $request): Response
|
||||
{
|
||||
$reset = $this->requireToken($request);
|
||||
|
||||
$data = $this->validate($request, [
|
||||
'password' => 'required|min:' . config('min_password_length'),
|
||||
'password_confirmation' => 'required',
|
||||
]);
|
||||
|
||||
if ($data['password'] !== $data['password_confirmation']) {
|
||||
$this->session->set('errors',
|
||||
array_merge($this->session->get('errors', []), ['validation.password.confirmed']));
|
||||
|
||||
return $this->showView('pages/password/reset-form');
|
||||
}
|
||||
|
||||
auth()->setPassword($reset->user, $data['password']);
|
||||
$reset->delete();
|
||||
|
||||
return $this->showView('pages/password/reset-success', ['type' => 'reset']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $view
|
||||
* @param array $data
|
||||
* @return Response
|
||||
*/
|
||||
protected function showView($view = 'pages/password/reset', $data = []): Response
|
||||
{
|
||||
$errors = Collection::make(Arr::flatten($this->session->get('errors', [])));
|
||||
$this->session->remove('errors');
|
||||
|
||||
return $this->response->withView(
|
||||
$view,
|
||||
array_merge_recursive(['errors' => $errors], $data)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return PasswordReset
|
||||
*/
|
||||
protected function requireToken(Request $request): PasswordReset
|
||||
{
|
||||
$token = $request->getAttribute('token');
|
||||
/** @var PasswordReset|null $reset */
|
||||
$reset = PasswordReset::whereToken($token)->first();
|
||||
|
||||
if (!$reset) {
|
||||
throw new HttpNotFound();
|
||||
}
|
||||
|
||||
return $reset;
|
||||
}
|
||||
}
|
|
@ -41,8 +41,10 @@ class TranslationServiceProvider extends ServiceProvider
|
|||
'localeChangeCallback' => [$this, 'setLocale'],
|
||||
]
|
||||
);
|
||||
$this->app->instance(Translator::class, $translator);
|
||||
$this->app->instance('translator', $translator);
|
||||
$this->app->singleton(Translator::class, function () use ($translator) {
|
||||
return $translator;
|
||||
});
|
||||
$this->app->alias(Translator::class, 'translator');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Http\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class HttpNotFound extends HttpException
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $headers
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(
|
||||
string $message = '',
|
||||
array $headers = [],
|
||||
int $code = 0,
|
||||
Throwable $previous = null
|
||||
) {
|
||||
parent::__construct(404, $message, $headers, $code, $previous);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace Engelsystem\Http;
|
||||
|
||||
use Engelsystem\Renderer\Renderer;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
||||
|
||||
|
@ -11,21 +12,21 @@ class Response extends SymfonyResponse implements ResponseInterface
|
|||
use MessageTrait;
|
||||
|
||||
/** @var Renderer */
|
||||
protected $view;
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @param Renderer $view
|
||||
* @param Renderer $renderer
|
||||
*/
|
||||
public function __construct(
|
||||
$content = '',
|
||||
int $status = 200,
|
||||
array $headers = [],
|
||||
Renderer $view = null
|
||||
Renderer $renderer = null
|
||||
) {
|
||||
$this->view = $view;
|
||||
$this->renderer = $renderer;
|
||||
parent::__construct($content, $status, $headers);
|
||||
}
|
||||
|
||||
|
@ -47,7 +48,7 @@ class Response extends SymfonyResponse implements ResponseInterface
|
|||
* provided status code; if none is provided, implementations MAY
|
||||
* use the defaults as suggested in the HTTP specification.
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException For invalid status code arguments.
|
||||
* @throws InvalidArgumentException For invalid status code arguments.
|
||||
*/
|
||||
public function withStatus($code, $reasonPhrase = '')
|
||||
{
|
||||
|
@ -107,12 +108,12 @@ class Response extends SymfonyResponse implements ResponseInterface
|
|||
*/
|
||||
public function withView($view, $data = [], $status = 200, $headers = [])
|
||||
{
|
||||
if (!$this->view instanceof Renderer) {
|
||||
throw new \InvalidArgumentException('Renderer not defined');
|
||||
if (!$this->renderer instanceof Renderer) {
|
||||
throw new InvalidArgumentException('Renderer not defined');
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->setContent($this->view->render($view, $data));
|
||||
$new->setContent($this->renderer->render($view, $data));
|
||||
$new->setStatusCode($status, ($status == $this->getStatusCode() ? $this->statusText : null));
|
||||
|
||||
foreach ($headers as $key => $values) {
|
||||
|
@ -144,4 +145,14 @@ class Response extends SymfonyResponse implements ResponseInterface
|
|||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the renderer to use
|
||||
*
|
||||
* @param Renderer $renderer
|
||||
*/
|
||||
public function setRenderer(Renderer $renderer)
|
||||
{
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Http\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Rules\Between as RespectBetween;
|
||||
|
||||
class Between extends RespectBetween
|
||||
{
|
||||
use StringInputLength;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Http\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Rules\Max as RespectMax;
|
||||
|
||||
class Max extends RespectMax
|
||||
{
|
||||
use StringInputLength;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Http\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Rules\Min as RespectMin;
|
||||
|
||||
class Min extends RespectMin
|
||||
{
|
||||
use StringInputLength;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Http\Validation\Rules;
|
||||
|
||||
use DateTime;
|
||||
use Illuminate\Support\Str;
|
||||
use Throwable;
|
||||
|
||||
trait StringInputLength
|
||||
{
|
||||
/**
|
||||
* Use the input length of a string
|
||||
*
|
||||
* @param mixed $input
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($input): bool
|
||||
{
|
||||
if (
|
||||
is_string($input)
|
||||
&& !is_numeric($input)
|
||||
&& !$this->isDateTime($input)
|
||||
) {
|
||||
$input = Str::length($input);
|
||||
}
|
||||
|
||||
return parent::validate($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $input
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDateTime($input): bool
|
||||
{
|
||||
try {
|
||||
new DateTime($input);
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Engelsystem\Mail;
|
||||
|
||||
use Engelsystem\Helpers\Translation\Translator;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\Renderer\Renderer;
|
||||
use Swift_Mailer as SwiftMailer;
|
||||
|
||||
|
@ -10,30 +12,75 @@ class EngelsystemMailer extends Mailer
|
|||
/** @var Renderer|null */
|
||||
protected $view;
|
||||
|
||||
/** @var Translator|null */
|
||||
protected $translation;
|
||||
|
||||
/** @var string */
|
||||
protected $subjectPrefix = null;
|
||||
|
||||
/**
|
||||
* @param SwiftMailer $mailer
|
||||
* @param Renderer $view
|
||||
* @param Translator $translation
|
||||
*/
|
||||
public function __construct(SwiftMailer $mailer, Renderer $view = null)
|
||||
public function __construct(SwiftMailer $mailer, Renderer $view = null, Translator $translation = null)
|
||||
{
|
||||
parent::__construct($mailer);
|
||||
|
||||
$this->translation = $translation;
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|string[]|User $to
|
||||
* @param string $subject
|
||||
* @param string $template
|
||||
* @param array $data
|
||||
* @param string|null $locale
|
||||
* @return int
|
||||
*/
|
||||
public function sendViewTranslated(
|
||||
$to,
|
||||
string $subject,
|
||||
string $template,
|
||||
array $data = [],
|
||||
?string $locale = null
|
||||
): int {
|
||||
if ($to instanceof User) {
|
||||
$locale = $locale ?: $to->settings->language;
|
||||
$to = $to->contact->email ? $to->contact->email : $to->email;
|
||||
}
|
||||
|
||||
$activeLocale = null;
|
||||
if (
|
||||
$locale
|
||||
&& $this->translation
|
||||
&& isset($this->translation->getLocales()[$locale])
|
||||
) {
|
||||
$activeLocale = $this->translation->getLocale();
|
||||
$this->translation->setLocale($locale);
|
||||
}
|
||||
|
||||
$subject = $this->translation ? $this->translation->translate($subject) : $subject;
|
||||
$sentMails = $this->sendView($to, $subject, $template, $data);
|
||||
|
||||
if ($activeLocale) {
|
||||
$this->translation->setLocale($activeLocale);
|
||||
}
|
||||
|
||||
return $sentMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a template
|
||||
*
|
||||
* @param string $to
|
||||
* @param string $subject
|
||||
* @param string $template
|
||||
* @param array $data
|
||||
* @param string|string[] $to
|
||||
* @param string $subject
|
||||
* @param string $template
|
||||
* @param array $data
|
||||
* @return int
|
||||
*/
|
||||
public function sendView($to, $subject, $template, $data = []): int
|
||||
public function sendView($to, string $subject, string $template, array $data = []): int
|
||||
{
|
||||
$body = $this->view->render($template, $data);
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
|||
'shifts_json_export',
|
||||
'users',
|
||||
'user_driver_licenses',
|
||||
'user_password_recovery',
|
||||
'user_worklog',
|
||||
];
|
||||
|
||||
|
@ -112,11 +111,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
|||
case 'shifts_json_export':
|
||||
require_once realpath(__DIR__ . '/../../includes/controller/shifts_controller.php');
|
||||
shifts_json_export_controller();
|
||||
case 'user_password_recovery':
|
||||
require_once realpath(__DIR__ . '/../../includes/controller/users_controller.php');
|
||||
$title = user_password_recovery_title();
|
||||
$content = user_password_recovery_controller();
|
||||
return [$title, $content];
|
||||
case 'public_dashboard':
|
||||
return public_dashboard_controller();
|
||||
case 'angeltypes':
|
||||
|
|
|
@ -32,6 +32,7 @@ class Legacy extends TwigExtension
|
|||
new TwigFunction('menuUserHints', 'header_render_hints', $isSafeHtml),
|
||||
new TwigFunction('menuUserSubmenu', 'make_user_submenu', $isSafeHtml),
|
||||
new TwigFunction('page', [$this, 'getPage']),
|
||||
new TwigFunction('msg', 'msg', $isSafeHtml),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,8 @@ use Engelsystem\Http\Validation\Validator;
|
|||
use Engelsystem\Models\User\Settings;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use Illuminate\Support\Collection;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
|
@ -66,6 +65,7 @@ class AuthControllerTest extends TestCase
|
|||
$session = new Session(new MockArraySessionStorage());
|
||||
/** @var Validator|MockObject $validator */
|
||||
$validator = new Validator();
|
||||
$session->set('errors', [['bar' => 'some.bar.error']]);
|
||||
|
||||
$user = new User([
|
||||
'name' => 'foo',
|
||||
|
@ -89,7 +89,7 @@ class AuthControllerTest extends TestCase
|
|||
|
||||
$response->expects($this->once())
|
||||
->method('withView')
|
||||
->with('pages/login', ['errors' => Collection::make(['auth.not-found'])])
|
||||
->with('pages/login', ['errors' => collect(['some.bar.error', 'auth.not-found'])])
|
||||
->willReturn($response);
|
||||
$response->expects($this->once())
|
||||
->method('redirectTo')
|
||||
|
|
|
@ -155,8 +155,8 @@ class StatsTest extends TestCase
|
|||
$this->initDatabase();
|
||||
$this->addUsers();
|
||||
|
||||
(new PasswordReset(['use_id' => 1, 'token' => 'loremIpsum123']))->save();
|
||||
(new PasswordReset(['use_id' => 3, 'token' => '5omeR4nd0mTok3N']))->save();
|
||||
(new PasswordReset(['user_id' => 1, 'token' => 'loremIpsum123']))->save();
|
||||
(new PasswordReset(['user_id' => 3, 'token' => '5omeR4nd0mTok3N']))->save();
|
||||
|
||||
$stats = new Stats($this->database);
|
||||
$this->assertEquals(2, $stats->passwordResets());
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Controllers;
|
||||
|
||||
use Engelsystem\Config\Config;
|
||||
use Engelsystem\Controllers\PasswordResetController;
|
||||
use Engelsystem\Helpers\Authenticator;
|
||||
use Engelsystem\Http\Exceptions\HttpNotFound;
|
||||
use Engelsystem\Http\Exceptions\ValidationException;
|
||||
use Engelsystem\Http\Request;
|
||||
use Engelsystem\Http\Response;
|
||||
use Engelsystem\Http\Validation\Validator;
|
||||
use Engelsystem\Mail\EngelsystemMailer;
|
||||
use Engelsystem\Models\User\PasswordReset;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\Renderer\Renderer;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\Test\TestLogger;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
|
||||
class PasswordResetControllerTest extends TestCase
|
||||
{
|
||||
use HasDatabase;
|
||||
|
||||
/** @var array */
|
||||
protected $args = [];
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::reset
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::__construct
|
||||
*/
|
||||
public function testReset(): void
|
||||
{
|
||||
$controller = $this->getController('pages/password/reset');
|
||||
$response = $controller->reset();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
||||
*/
|
||||
public function testPostReset(): void
|
||||
{
|
||||
$this->initDatabase();
|
||||
$request = new Request([], ['email' => 'foo@bar.batz']);
|
||||
$user = $this->createUser();
|
||||
|
||||
$controller = $this->getController(
|
||||
'pages/password/reset-success',
|
||||
['type' => 'email', 'errors' => collect()]
|
||||
);
|
||||
/** @var TestLogger $log */
|
||||
$log = $this->args['log'];
|
||||
/** @var EngelsystemMailer|MockObject $mailer */
|
||||
$mailer = $this->args['mailer'];
|
||||
$this->setExpects($mailer, 'sendViewTranslated');
|
||||
|
||||
$controller->postReset($request);
|
||||
|
||||
$this->assertNotEmpty(PasswordReset::find($user->id)->first());
|
||||
$this->assertTrue($log->hasInfoThatContains($user->name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
||||
*/
|
||||
public function testPostResetInvalidRequest(): void
|
||||
{
|
||||
$request = new Request();
|
||||
|
||||
$controller = $this->getController();
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$controller->postReset($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
||||
*/
|
||||
public function testPostResetNoUser(): void
|
||||
{
|
||||
$this->initDatabase();
|
||||
$request = new Request([], ['email' => 'foo@bar.batz']);
|
||||
|
||||
$controller = $this->getController(
|
||||
'pages/password/reset-success',
|
||||
['type' => 'email', 'errors' => collect()]
|
||||
);
|
||||
|
||||
$controller->postReset($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::resetPassword
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::requireToken
|
||||
*/
|
||||
public function testResetPassword(): void
|
||||
{
|
||||
$this->initDatabase();
|
||||
|
||||
$user = $this->createUser();
|
||||
$token = $this->createToken($user);
|
||||
$request = new Request([], [], ['token' => $token->token]);
|
||||
|
||||
$controller = $this->getController('pages/password/reset-form');
|
||||
|
||||
$controller->resetPassword($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::resetPassword
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::requireToken
|
||||
*/
|
||||
public function testResetPasswordNoToken(): void
|
||||
{
|
||||
$this->initDatabase();
|
||||
$controller = $this->getController();
|
||||
|
||||
$this->expectException(HttpNotFound::class);
|
||||
$controller->resetPassword(new Request());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::postResetPassword
|
||||
*/
|
||||
public function testPostResetPassword(): void
|
||||
{
|
||||
$this->initDatabase();
|
||||
|
||||
$this->app->instance('config', new Config(['min_password_length' => 3]));
|
||||
$user = $this->createUser();
|
||||
$token = $this->createToken($user);
|
||||
$password = 'SomeRandomPasswordForAmazingSecurity';
|
||||
$request = new Request(
|
||||
[],
|
||||
['password' => $password, 'password_confirmation' => $password],
|
||||
['token' => $token->token]
|
||||
);
|
||||
|
||||
$controller = $this->getController(
|
||||
'pages/password/reset-success',
|
||||
['type' => 'reset', 'errors' => collect()]
|
||||
);
|
||||
|
||||
$auth = new Authenticator($request, $this->args['session'], $user);
|
||||
$this->app->instance('authenticator', $auth);
|
||||
|
||||
$response = $controller->postResetPassword($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
$this->assertEmpty(PasswordReset::find($user->id));
|
||||
$this->assertNotNull(auth()->authenticate($user->name, $password));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::postResetPassword
|
||||
* @covers \Engelsystem\Controllers\PasswordResetController::showView
|
||||
*/
|
||||
public function testPostResetPasswordNotMatching(): void
|
||||
{
|
||||
$this->initDatabase();
|
||||
|
||||
$this->app->instance('config', new Config(['min_password_length' => 3]));
|
||||
$user = $this->createUser();
|
||||
$token = $this->createToken($user);
|
||||
$password = 'SomeRandomPasswordForAmazingSecurity';
|
||||
$request = new Request(
|
||||
[],
|
||||
['password' => $password, 'password_confirmation' => $password . 'OrNot'],
|
||||
['token' => $token->token]
|
||||
);
|
||||
|
||||
$controller = $this->getController(
|
||||
'pages/password/reset-form',
|
||||
['errors' => collect(['some.other.error', 'validation.password.confirmed'])]
|
||||
);
|
||||
/** @var Session $session */
|
||||
$session = $this->args['session'];
|
||||
$session->set('errors', ['foo' => ['bar' => 'some.other.error']]);
|
||||
|
||||
$controller->postResetPassword($request);
|
||||
$this->assertEmpty($session->get('errors'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getControllerArgs(): array
|
||||
{
|
||||
$response = new Response();
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
/** @var EngelsystemMailer|MockObject $mailer */
|
||||
$mailer = $this->createMock(EngelsystemMailer::class);
|
||||
$log = new TestLogger();
|
||||
$renderer = $this->createMock(Renderer::class);
|
||||
$response->setRenderer($renderer);
|
||||
|
||||
return $this->args = [
|
||||
'response' => $response,
|
||||
'session' => $session,
|
||||
'mailer' => $mailer,
|
||||
'log' => $log,
|
||||
'renderer' => $renderer
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $view
|
||||
* @param array $data
|
||||
* @return PasswordResetController
|
||||
*/
|
||||
protected function getController(?string $view = null, ?array $data = null): PasswordResetController
|
||||
{
|
||||
/** @var Response $response */
|
||||
/** @var Session $session */
|
||||
/** @var EngelsystemMailer|MockObject $mailer */
|
||||
/** @var TestLogger $log */
|
||||
/** @var Renderer|MockObject $renderer */
|
||||
list($response, $session, $mailer, $log, $renderer) = array_values($this->getControllerArgs());
|
||||
$controller = new PasswordResetController($response, $session, $mailer, $log);
|
||||
$controller->setValidator(new Validator());
|
||||
|
||||
if ($view) {
|
||||
$args = [$view];
|
||||
if ($data) {
|
||||
$args[] = $data;
|
||||
}
|
||||
|
||||
$this->setExpects($renderer, 'render', $args, 'Foo');
|
||||
}
|
||||
|
||||
return $controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
protected function createUser(): User
|
||||
{
|
||||
$user = new User([
|
||||
'name' => 'foo',
|
||||
'password' => '',
|
||||
'email' => 'foo@bar.batz',
|
||||
'api_key' => '',
|
||||
]);
|
||||
$user->save();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return PasswordReset
|
||||
*/
|
||||
protected function createToken(User $user): PasswordReset
|
||||
{
|
||||
$reset = new PasswordReset(['user_id' => $user->id, 'token' => 'SomeTestToken123']);
|
||||
$reset->save();
|
||||
|
||||
return $reset;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Engelsystem\Test\Unit;
|
||||
|
||||
use Engelsystem\Application;
|
||||
use Engelsystem\Database\Database;
|
||||
use Engelsystem\Database\Migration\Migrate;
|
||||
use Engelsystem\Database\Migration\MigrationServiceProvider;
|
||||
|
@ -27,12 +26,11 @@ trait HasDatabase
|
|||
$connection->getPdo()->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->database = new Database($connection);
|
||||
|
||||
$app = new Application();
|
||||
$app->instance(Database::class, $this->database);
|
||||
$app->register(MigrationServiceProvider::class);
|
||||
$this->app->instance(Database::class, $this->database);
|
||||
$this->app->register(MigrationServiceProvider::class);
|
||||
|
||||
/** @var Migrate $migration */
|
||||
$migration = $app->get('db.migration');
|
||||
$migration = $this->app->get('db.migration');
|
||||
$migration->initMigration();
|
||||
|
||||
$this->database
|
||||
|
|
|
@ -21,7 +21,7 @@ class TranslationServiceProviderTest extends ServiceProviderTest
|
|||
$locales = ['fo_OO' => 'Foo', 'fo_OO.BAR' => 'Foo (Bar)', 'te_ST.WTF-9' => 'WTF\'s Testing?'];
|
||||
$config = new Config(['locales' => $locales, 'default_locale' => $defaultLocale]);
|
||||
|
||||
$app = $this->getApp(['make', 'instance', 'get']);
|
||||
$app = $this->getApp(['make', 'singleton', 'alias', 'get']);
|
||||
/** @var Session|MockObject $session */
|
||||
$session = $this->createMock(Session::class);
|
||||
/** @var Translator|MockObject $translator */
|
||||
|
@ -60,12 +60,16 @@ class TranslationServiceProviderTest extends ServiceProviderTest
|
|||
)
|
||||
->willReturn($translator);
|
||||
|
||||
$app->expects($this->exactly(2))
|
||||
->method('instance')
|
||||
->withConsecutive(
|
||||
[Translator::class, $translator],
|
||||
['translator', $translator]
|
||||
);
|
||||
$app->expects($this->once())
|
||||
->method('singleton')
|
||||
->willReturnCallback(function (string $abstract, callable $callback) use ($translator) {
|
||||
$this->assertEquals(Translator::class, $abstract);
|
||||
$this->assertEquals($translator, $callback());
|
||||
});
|
||||
|
||||
$app->expects($this->once())
|
||||
->method('alias')
|
||||
->with(Translator::class, 'translator');
|
||||
|
||||
$serviceProvider->register();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Http\Exceptions;
|
||||
|
||||
use Engelsystem\Http\Exceptions\HttpNotFound;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class HttpNotFoundTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @covers \Engelsystem\Http\Exceptions\HttpNotFound::__construct
|
||||
*/
|
||||
public function testConstruct()
|
||||
{
|
||||
$exception = new HttpNotFound();
|
||||
$this->assertEquals(404, $exception->getStatusCode());
|
||||
$this->assertEquals('', $exception->getMessage());
|
||||
|
||||
$exception = new HttpNotFound('Nothing to see here!');
|
||||
$this->assertEquals('Nothing to see here!', $exception->getMessage());
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ class ResponseTest extends TestCase
|
|||
|
||||
/**
|
||||
* @covers \Engelsystem\Http\Response::withView
|
||||
* @covers \Engelsystem\Http\Response::setRenderer
|
||||
*/
|
||||
public function testWithView()
|
||||
{
|
||||
|
@ -73,6 +74,17 @@ class ResponseTest extends TestCase
|
|||
$this->assertEquals('Foo ipsum!', $newResponse->getContent());
|
||||
$this->assertEquals(505, $newResponse->getStatusCode());
|
||||
$this->assertArraySubset(['test' => ['er']], $newResponse->getHeaders());
|
||||
|
||||
/** @var REnderer|MockObject $renderer */
|
||||
$anotherRenderer = $this->createMock(Renderer::class);
|
||||
$anotherRenderer->expects($this->once())
|
||||
->method('render')
|
||||
->with('bar')
|
||||
->willReturn('Stuff');
|
||||
|
||||
$response->setRenderer($anotherRenderer);
|
||||
$response = $response->withView('bar');
|
||||
$this->assertEquals('Stuff', $response->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Engelsystem\Test\Unit\Http\SessionHandlers;
|
|||
|
||||
use Engelsystem\Http\SessionHandlers\DatabaseHandler;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
|
||||
class DatabaseHandlerTest extends TestCase
|
||||
{
|
||||
|
@ -90,6 +90,7 @@ class DatabaseHandlerTest extends TestCase
|
|||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->initDatabase();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Http\Validation\Rules;
|
||||
|
||||
use Engelsystem\Http\Validation\Rules\Between;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
|
||||
class BetweenTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @covers \Engelsystem\Http\Validation\Rules\Between
|
||||
*/
|
||||
public function testValidate()
|
||||
{
|
||||
$rule = new Between(3, 10);
|
||||
$this->assertFalse($rule->validate(1));
|
||||
$this->assertFalse($rule->validate('11'));
|
||||
$this->assertTrue($rule->validate(5));
|
||||
$this->assertFalse($rule->validate('AS'));
|
||||
$this->assertFalse($rule->validate('TestContentThatCounts'));
|
||||
$this->assertTrue($rule->validate('TESTING'));
|
||||
|
||||
$rule = new Between('2042-01-01', '2042-10-10');
|
||||
$this->assertFalse($rule->validate('2000-01-01'));
|
||||
$this->assertFalse($rule->validate('3000-01-01'));
|
||||
$this->assertTrue($rule->validate('2042-05-11'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Http\Validation\Rules;
|
||||
|
||||
use Engelsystem\Http\Validation\Rules\Max;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
|
||||
class MaxTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @covers \Engelsystem\Http\Validation\Rules\Max
|
||||
*/
|
||||
public function testValidate()
|
||||
{
|
||||
$rule = new Max(3);
|
||||
$this->assertFalse($rule->validate(10));
|
||||
$this->assertFalse($rule->validate('22'));
|
||||
$this->assertTrue($rule->validate(3));
|
||||
$this->assertFalse($rule->validate('TEST'));
|
||||
$this->assertTrue($rule->validate('AS'));
|
||||
|
||||
$rule = new Max('2042-01-01');
|
||||
$this->assertFalse($rule->validate('2100-01-01'));
|
||||
$this->assertTrue($rule->validate('2000-01-01'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Http\Validation\Rules;
|
||||
|
||||
use Engelsystem\Http\Validation\Rules\Min;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
|
||||
class MinTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @covers \Engelsystem\Http\Validation\Rules\Min
|
||||
*/
|
||||
public function testValidate()
|
||||
{
|
||||
$rule = new Min(3);
|
||||
$this->assertFalse($rule->validate(1));
|
||||
$this->assertFalse($rule->validate('2'));
|
||||
$this->assertTrue($rule->validate(3));
|
||||
$this->assertFalse($rule->validate('AS'));
|
||||
$this->assertTrue($rule->validate('TEST'));
|
||||
|
||||
$rule = new Min('2042-01-01');
|
||||
$this->assertFalse($rule->validate('2000-01-01'));
|
||||
$this->assertTrue($rule->validate('2345-01-01'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Http\Validation\Rules;
|
||||
|
||||
use Engelsystem\Test\Unit\Http\Validation\Rules\Stub\UsesStringInputLength;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
|
||||
class StringInputLengthTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @covers \Engelsystem\Http\Validation\Rules\StringInputLength::validate
|
||||
* @covers \Engelsystem\Http\Validation\Rules\StringInputLength::isDateTime
|
||||
* @dataProvider validateProvider
|
||||
* @param mixed $input
|
||||
* @param mixed $expectedInput
|
||||
*/
|
||||
public function testValidate($input, $expectedInput)
|
||||
{
|
||||
$rule = new UsesStringInputLength();
|
||||
$rule->validate($input);
|
||||
|
||||
$this->assertEquals($expectedInput, $rule->lastInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function validateProvider()
|
||||
{
|
||||
return [
|
||||
['TEST', 4],
|
||||
['?', 1],
|
||||
['2042-01-01 00:00', '2042-01-01 00:00'],
|
||||
['3', '3'],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Http\Validation\Rules\Stub;
|
||||
|
||||
class ParentClassImplementation
|
||||
{
|
||||
/** @var bool */
|
||||
public $validateResult = true;
|
||||
|
||||
/** @var mixed */
|
||||
public $lastInput;
|
||||
|
||||
/**
|
||||
* @param mixed $input
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($input): bool
|
||||
{
|
||||
$this->lastInput = $input;
|
||||
|
||||
return $this->validateResult;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Http\Validation\Rules\Stub;
|
||||
|
||||
use Engelsystem\Http\Validation\Rules\StringInputLength;
|
||||
|
||||
class UsesStringInputLength extends ParentClassImplementation
|
||||
{
|
||||
use StringInputLength;
|
||||
}
|
|
@ -50,9 +50,10 @@ class ValidatorTest extends TestCase
|
|||
));
|
||||
|
||||
$this->assertFalse($val->validate(
|
||||
['lorem' => 2],
|
||||
['lorem' => 'required|min:3|max:10']
|
||||
['lorem' => 'OMG'],
|
||||
['lorem' => 'required|min:4|max:10']
|
||||
));
|
||||
$this->assertEquals(['lorem' => ['validation.lorem.min']], $val->getErrors());
|
||||
$this->assertFalse($val->validate(
|
||||
['lorem' => 42],
|
||||
['lorem' => 'required|min:3|max:10']
|
||||
|
|
|
@ -2,15 +2,22 @@
|
|||
|
||||
namespace Engelsystem\Test\Unit\Mail;
|
||||
|
||||
use Engelsystem\Helpers\Translation\Translator;
|
||||
use Engelsystem\Mail\EngelsystemMailer;
|
||||
use Engelsystem\Models\User\Contact;
|
||||
use Engelsystem\Models\User\Settings;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\Renderer\Renderer;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Swift_Mailer as SwiftMailer;
|
||||
use Swift_Message as SwiftMessage;
|
||||
|
||||
class EngelsystemMailerTest extends TestCase
|
||||
{
|
||||
use HasDatabase;
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Mail\EngelsystemMailer::__construct
|
||||
* @covers \Engelsystem\Mail\EngelsystemMailer::sendView
|
||||
|
@ -24,21 +31,69 @@ class EngelsystemMailerTest extends TestCase
|
|||
/** @var EngelsystemMailer|MockObject $mailer */
|
||||
$mailer = $this->getMockBuilder(EngelsystemMailer::class)
|
||||
->setConstructorArgs(['mailer' => $swiftMailer, 'view' => $view])
|
||||
->setMethods(['send'])
|
||||
->onlyMethods(['send'])
|
||||
->getMock();
|
||||
$mailer->expects($this->once())
|
||||
->method('send')
|
||||
->with('foo@bar.baz', 'Lorem dolor', 'Rendered Stuff!')
|
||||
->willReturn(1);
|
||||
$view->expects($this->once())
|
||||
->method('render')
|
||||
->with('test/template.tpl', ['dev' => true])
|
||||
->willReturn('Rendered Stuff!');
|
||||
$this->setExpects($mailer, 'send', ['foo@bar.baz', 'Lorem dolor', 'Rendered Stuff!'], 1);
|
||||
$this->setExpects($view, 'render', ['test/template.tpl', ['dev' => true]], 'Rendered Stuff!');
|
||||
|
||||
$return = $mailer->sendView('foo@bar.baz', 'Lorem dolor', 'test/template.tpl', ['dev' => true]);
|
||||
$this->equalTo(1, $return);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Mail\EngelsystemMailer::sendViewTranslated
|
||||
*/
|
||||
public function testSendViewTranslated()
|
||||
{
|
||||
$this->initDatabase();
|
||||
|
||||
$settings = new Settings([
|
||||
'language' => 'de_DE',
|
||||
'theme' => '',
|
||||
]);
|
||||
$contact = new Contact(['email' => null]);
|
||||
$user = new User([
|
||||
'id' => 42,
|
||||
'name' => 'username',
|
||||
'email' => 'foo@bar.baz',
|
||||
'password' => '',
|
||||
'api_key' => '',
|
||||
]);
|
||||
$user->save();
|
||||
$settings->user()->associate($user)->save();
|
||||
$contact->user()->associate($user)->save();
|
||||
|
||||
/** @var Renderer|MockObject $view */
|
||||
$view = $this->createMock(Renderer::class);
|
||||
/** @var SwiftMailer|MockObject $swiftMailer */
|
||||
$swiftMailer = $this->createMock(SwiftMailer::class);
|
||||
/** @var Translator|MockObject $translator */
|
||||
$translator = $this->createMock(Translator::class);
|
||||
|
||||
/** @var EngelsystemMailer|MockObject $mailer */
|
||||
$mailer = $this->getMockBuilder(EngelsystemMailer::class)
|
||||
->setConstructorArgs(['mailer' => $swiftMailer, 'view' => $view, 'translation' => $translator])
|
||||
->onlyMethods(['sendView'])
|
||||
->getMock();
|
||||
|
||||
$this->setExpects($mailer, 'sendView', ['foo@bar.baz', 'Lorem dolor', 'test/template.tpl', ['dev' => true]], 1);
|
||||
$this->setExpects($translator, 'getLocales', null, ['de_DE' => 'de_DE', 'en_US' => 'en_US']);
|
||||
$this->setExpects($translator, 'getLocale', null, 'en_US');
|
||||
$this->setExpects($translator, 'translate', ['translatable.text'], 'Lorem dolor');
|
||||
$translator->expects($this->exactly(2))
|
||||
->method('setLocale')
|
||||
->withConsecutive(['de_DE'], ['en_US']);
|
||||
|
||||
$return = $mailer->sendViewTranslated(
|
||||
$user,
|
||||
'translatable.text',
|
||||
'test/template.tpl',
|
||||
['dev' => true],
|
||||
'de_DE'
|
||||
);
|
||||
$this->equalTo(1, $return);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Mail\EngelsystemMailer::getSubjectPrefix
|
||||
* @covers \Engelsystem\Mail\EngelsystemMailer::send
|
||||
|
@ -50,32 +105,12 @@ class EngelsystemMailerTest extends TestCase
|
|||
$message = $this->createMock(SwiftMessage::class);
|
||||
/** @var SwiftMailer|MockObject $swiftMailer */
|
||||
$swiftMailer = $this->createMock(SwiftMailer::class);
|
||||
$swiftMailer->expects($this->once())
|
||||
->method('createMessage')
|
||||
->willReturn($message);
|
||||
$swiftMailer->expects($this->once())
|
||||
->method('send')
|
||||
->willReturn(1);
|
||||
|
||||
$message->expects($this->once())
|
||||
->method('setTo')
|
||||
->with(['to@xam.pel'])
|
||||
->willReturn($message);
|
||||
|
||||
$message->expects($this->once())
|
||||
->method('setFrom')
|
||||
->with('foo@bar.baz', 'Lorem Ipsum')
|
||||
->willReturn($message);
|
||||
|
||||
$message->expects($this->once())
|
||||
->method('setSubject')
|
||||
->with('[Mail test] Foo Bar')
|
||||
->willReturn($message);
|
||||
|
||||
$message->expects($this->once())
|
||||
->method('setBody')
|
||||
->with('Lorem Ipsum!')
|
||||
->willReturn($message);
|
||||
$this->setExpects($swiftMailer, 'createMessage', null, $message);
|
||||
$this->setExpects($swiftMailer, 'send', null, 1);
|
||||
$this->setExpects($message, 'setTo', [['to@xam.pel']], $message);
|
||||
$this->setExpects($message, 'setFrom', ['foo@bar.baz', 'Lorem Ipsum'], $message);
|
||||
$this->setExpects($message, 'setSubject', ['[Mail test] Foo Bar'], $message);
|
||||
$this->setExpects($message, 'setBody', ['Lorem Ipsum!'], $message);
|
||||
|
||||
$mailer = new EngelsystemMailer($swiftMailer);
|
||||
$mailer->setFromAddress('foo@bar.baz');
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Engelsystem\Test\Unit\Models;
|
|||
use Carbon\Carbon;
|
||||
use Engelsystem\Models\EventConfig;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
|
||||
class EventConfigTest extends TestCase
|
||||
{
|
||||
|
@ -102,7 +102,8 @@ class EventConfigTest extends TestCase
|
|||
*/
|
||||
protected function getEventConfig()
|
||||
{
|
||||
return new class extends EventConfig {
|
||||
return new class extends EventConfig
|
||||
{
|
||||
/**
|
||||
* @param string $value
|
||||
* @param string $type
|
||||
|
@ -122,6 +123,7 @@ class EventConfigTest extends TestCase
|
|||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->initDatabase();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Engelsystem\Test\Unit\Models;
|
|||
|
||||
use Engelsystem\Models\LogEntry;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
class LogEntryTest extends TestCase
|
||||
|
@ -38,6 +38,7 @@ class LogEntryTest extends TestCase
|
|||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->initDatabase();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ namespace Engelsystem\Test\Unit\Models;
|
|||
use Engelsystem\Models\User\HasUserModel;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use Engelsystem\Test\Unit\Models\User\Stub\HasUserModelImplementation;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class HasUserModelTest extends TestCase
|
||||
{
|
||||
|
@ -28,6 +28,7 @@ class HasUserModelTest extends TestCase
|
|||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->initDatabase();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use Engelsystem\Models\User\Settings;
|
|||
use Engelsystem\Models\User\State;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
|
||||
class UserTest extends TestCase
|
||||
{
|
||||
|
@ -95,6 +95,7 @@ class UserTest extends TestCase
|
|||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->initDatabase();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ class LegacyTest extends ExtensionTest
|
|||
$this->assertExtensionExists('menuUserHints', 'header_render_hints', $functions, $isSafeHtml);
|
||||
$this->assertExtensionExists('menuUserSubmenu', 'make_user_submenu', $functions, $isSafeHtml);
|
||||
$this->assertExtensionExists('page', [$extension, 'getPage'], $functions);
|
||||
$this->assertExtensionExists('msg', 'msg', $functions, $isSafeHtml);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,18 +2,22 @@
|
|||
|
||||
namespace Engelsystem\Test\Unit;
|
||||
|
||||
use PHPUnit\Framework\MockObject\Matcher\InvokedRecorder;
|
||||
use Engelsystem\Application;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
|
||||
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
|
||||
|
||||
abstract class TestCase extends PHPUnitTestCase
|
||||
{
|
||||
/** @var Application */
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @param MockObject $object
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @param mixed $return
|
||||
* @param InvokedRecorder $times
|
||||
* @param InvocationOrder $times
|
||||
*/
|
||||
protected function setExpects($object, $method, $arguments = null, $return = null, $times = null)
|
||||
{
|
||||
|
@ -34,4 +38,12 @@ abstract class TestCase extends PHPUnitTestCase
|
|||
$invocation->willReturn($return);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before each test run
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->app = new Application(__DIR__ . '/../../');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue