Tests, translations Entries, Posts

This commit is contained in:
Ernest Wiśniewski 2023-02-13 18:30:26 +01:00
parent 00eba82768
commit f7f1572190
70 changed files with 1054 additions and 505 deletions

View file

@ -6,7 +6,7 @@ PANTHER_APP_ENV=panther
DATABASE_URL="postgresql://symfony:ChangeMe@127.0.0.1:5432/app_test?serverVersion=13&charset=utf8"
MAILER_DSN=null://default
KBIN_JS_ENABLED=false
KBIN_DEFAULT_LANG=pl
KBIN_DEFAULT_LANG=en
KBIN_DOMAIN=kbin.test
ELASTICSEARCH_ENABLED=false
KBIN_API_ITEMS_PER_PAGE=2

File diff suppressed because one or more lines are too long

View file

@ -1,9 +1,10 @@
.kbin-page-login,
.kbin-page-register,
.kbin-page-reset-password {
.kbin-page-reset-password,
.kbin-page-reset-password-email-sent {
.container {
margin: 0 auto;
max-width: 35rem;
max-width: 30rem;
p {
margin-bottom: .5rem;
@ -47,3 +48,10 @@
}
}
}
.kbin-page-reset-password-email-sent {
p:last-of-type {
margin-top: 2rem;
text-align: right;
}
}

View file

@ -50,6 +50,7 @@
}
button {
height: 100%;
padding-bottom: .5rem;
padding-top: .5rem;
}

View file

@ -20,6 +20,7 @@
}
button {
height: 100%;
padding-bottom: .5rem;
padding-top: .5rem;
}

View file

@ -1,33 +1,33 @@
front:
controller: App\Controller\Entry\FrontController::front
controller: App\Controller\Entry\EntryFrontController::front
defaults: { sortBy: ~, time: '∞', type: ~ }
path: /{sortBy}/{time}/{type}
methods: [GET]
requirements: { sortBy: "%front_sort_options%" }
front_subscribed:
controller: App\Controller\Entry\FrontController::subscribed
controller: App\Controller\Entry\EntryFrontController::subscribed
defaults: { sortBy: ~, time: '∞', type: ~ }
path: /sub/{sortBy}/{time}/{type}
methods: [GET]
requirements: { sortBy: "%front_sort_options%" }
front_moderated:
controller: App\Controller\Entry\FrontController::moderated
controller: App\Controller\Entry\EntryFrontController::moderated
defaults: { sortBy: ~, time: '∞', type: ~ }
path: /mod/{sortBy}/{time}/{type}
methods: [GET]
requirements: { sortBy: "%front_sort_options%" }
front_favourite:
controller: App\Controller\Entry\FrontController::favourite
controller: App\Controller\Entry\EntryFrontController::favourite
defaults: { sortBy: ~, time: '∞', type: ~ }
path: /fav/{sortBy}/{time}/{type}
methods: [GET]
requirements: { sortBy: "%front_sort_options%" }
front_magazine:
controller: App\Controller\Entry\FrontController::magazine
controller: App\Controller\Entry\EntryFrontController::magazine
defaults: { sortBy: ~, time: '∞', type: ~ }
path: /m/{name}/{sortBy}/{time}/{type}
methods: [GET]

View file

@ -3,7 +3,7 @@ post_comment_create:
defaults: { slug: -, parent_comment_id: null }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/reply/{parent_comment_id}
pl: /m/{magazine_name}/p/{post_id}/{slug}/odpowiedź/{parent_comment_id}
pl: /m/{magazine_name}/w/{post_id}/{slug}/odpowiedź/{parent_comment_id}
methods: [ GET, POST ]
post_comment_edit:
@ -11,7 +11,7 @@ post_comment_edit:
defaults: { slug: -, }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/reply/{comment_id}/edit
pl: /m/{magazine_name}/p/{post_id}/{slug}/odpowiedź/{comment_id}/edytuj
pl: /m/{magazine_name}/w/{post_id}/{slug}/odpowiedź/{comment_id}/edytuj
methods: [ GET, POST ]
post_comment_delete:
@ -43,7 +43,7 @@ post_comment_voters:
defaults: { slug: -, }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/reply/{comment_id}/votes
pl: /m/{magazine_name}/p/{post_id}/{slug}/odpowiedź/{comment_id}/głosy
pl: /m/{magazine_name}/w/{post_id}/{slug}/odpowiedź/{comment_id}/głosy
methods: [ GET ]
post_comment_favourites:
@ -51,7 +51,7 @@ post_comment_favourites:
defaults: { slug: -, }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/reply/{comment_id}/favourites
pl: /m/{magazine_name}/p/{post_id}/{slug}/odpowiedź/{comment_id}/ulubione
pl: /m/{magazine_name}/w/{post_id}/{slug}/odpowiedź/{comment_id}/ulubione
methods: [ GET ]
post_comment_vote:
@ -122,7 +122,7 @@ post_voters:
defaults: { slug: -, sortBy: ~ }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/votes
pl: /m/{magazine_name}/p/{post_id}/{slug}/głosy
pl: /m/{magazine_name}/w/{post_id}/{slug}/głosy
methods: [ GET ]
post_favourites:
@ -130,7 +130,7 @@ post_favourites:
defaults: { slug: -, sortBy: ~ }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/favourites
pl: /m/{magazine_name}/p/{post_id}/{slug}/ulubione
pl: /m/{magazine_name}/w/{post_id}/{slug}/ulubione
methods: [ GET ]
post_single:
@ -138,7 +138,7 @@ post_single:
defaults: { slug: -, sortBy: ~ }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/{sortBy}
pl: /m/{magazine_name}/p/{post_id}/{slug}/{sortBy}
pl: /m/{magazine_name}/w/{post_id}/{slug}/{sortBy}
methods: [ GET ]
post_create:
@ -151,7 +151,7 @@ post_edit:
defaults: { slug: -, }
path:
en: /m/{magazine_name}/p/{post_id}/{slug}/edit
pl: /m/{magazine_name}/p/{post_id}/{slug}/edytuj
pl: /m/{magazine_name}/w/{post_id}/{slug}/edytuj
methods: [ GET, POST ]
post_delete:

View file

@ -15,7 +15,7 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class FrontController extends AbstractController
class EntryFrontController extends AbstractController
{
public function __construct(private readonly EntryRepository $repository)
{
@ -78,7 +78,7 @@ class FrontController extends AbstractController
}
return $this->render(
'front/front.html.twig',
'entry/front.html.twig',
[
'entries' => $listing,
]
@ -111,7 +111,7 @@ class FrontController extends AbstractController
}
return $this->render(
'front/front.html.twig',
'entry/front.html.twig',
[
'entries' => $listing,
]
@ -144,7 +144,7 @@ class FrontController extends AbstractController
}
return $this->render(
'front/front.html.twig',
'entry/front.html.twig',
[
'entries' => $listing,
]

View file

@ -20,7 +20,7 @@ class EntryVotersController extends AbstractController
public function __invoke(string $type, Magazine $magazine, Entry $entry, Request $request): Response
{
$votes = $entry->votes->filter(
fn($e) => $e->choice === ($type === 'up' ? VoteInterface::VOTE_UP : VoteInterface::VOTE_DOWN)
fn ($e) => $e->choice === ('up' === $type ? VoteInterface::VOTE_UP : VoteInterface::VOTE_DOWN)
);
if ($request->isXmlHttpRequest()) {

View file

@ -19,6 +19,7 @@ use App\Repository\PostCommentRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping\Cache;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;

View file

@ -18,11 +18,11 @@ class UserChecker implements UserCheckerInterface
}
if (!$user->isVerified) {
throw new CustomUserMessageAccountStatusException('Twoje konto nie jest aktywne.');
throw new CustomUserMessageAccountStatusException('Your account is not active.');
}
if ($user->isBanned) {
throw new CustomUserMessageAccountStatusException('Twoje konto jest zbanowane.');
throw new CustomUserMessageAccountStatusException('Your account has been banned.');
}
}

View file

@ -8,5 +8,7 @@ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent('entry_comment')]
final class EntryCommentComponent
{
public EntryComment $comment;
public EntryComment $comment;
public bool $showMagazine = true;
public bool $showEntry = true;
}

View file

@ -0,0 +1,12 @@
<?php
namespace App\Twig\Components;
use App\Entity\Entry;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent('entry_inline')]
final class EntryInlineComponent
{
public Entry $entry;
}

View file

@ -11,11 +11,14 @@ final class PostComponent
{
public Post $post;
public bool $isSingle = false;
public bool $showMagazine = true;
#[PostMount]
public function postMount(array $attr): array
{
if ($this->isSingle) {
$this->showMagazine = false;
if (isset($attr['class'])) {
$attr['class'] = trim('kbin-post--single '.$attr['class']);
} else {

View file

@ -10,9 +10,9 @@ use Symfony\UX\TwigComponent\Attribute\PostMount;
#[AsTwigComponent('related_posts')]
final class RelatedPostsComponent
{
const TYPE_TAG = 'tag';
const TYPE_MAGAZINE = 'magazine';
const TYPE_RANDOM = 'random';
public const TYPE_TAG = 'tag';
public const TYPE_MAGAZINE = 'magazine';
public const TYPE_RANDOM = 'random';
public int $limit = 4;
public ?string $tag = null;
@ -56,5 +56,4 @@ final class RelatedPostsComponent
return $posts;
}
}

View file

@ -15,7 +15,9 @@ final class ContextExtension extends AbstractExtension
return [
new TwigFunction('is_route_name', [ContextExtensionRuntime::class, 'isRouteName']),
new TwigFunction('is_route_name_contains', [ContextExtensionRuntime::class, 'isRouteNameContains']),
new TwigFunction('is_route_name_end_with', [ContextExtensionRuntime::class, 'isRouteNameEndWith']),
new TwigFunction('route_has_param', [ContextExtensionRuntime::class, 'routeHasParam']),
new TwigFunction('get_active_sort_option', [ContextExtensionRuntime::class, 'getActiveSortOption']),
];
}
}

View file

@ -9,8 +9,7 @@ use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
final class
FormattingExtension extends AbstractExtension
final class FormattingExtension extends AbstractExtension
{
public function getFilters(): array
{

View file

@ -4,7 +4,6 @@ namespace App\Twig\Extension;
use App\Twig\Runtime\NavbarExtensionRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class NavbarExtension extends AbstractExtension

View file

@ -4,7 +4,6 @@ namespace App\Twig\Extension;
use App\Twig\Runtime\OptionsExtensionRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class OptionsExtension extends AbstractExtension

View file

@ -2,7 +2,6 @@
namespace App\Twig\Extension;
use App\Twig\Runtime\MagazineExtensionRuntime;
use App\Twig\Runtime\UserExtensionRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

View file

@ -18,6 +18,11 @@ class ContextExtensionRuntime implements RuntimeExtensionInterface
return str_contains($this->getCurrentRouteName(), $needle);
}
public function isRouteNameEndWith(string $needle): bool
{
return str_ends_with($this->getCurrentRouteName(), $needle);
}
public function isRouteName(string $needle): bool
{
return $this->getCurrentRouteName() === $needle;
@ -32,4 +37,9 @@ class ContextExtensionRuntime implements RuntimeExtensionInterface
{
return $this->requestStack->getCurrentRequest()->get('_route') ?? 'front';
}
public function getActiveSortOption(): string
{
return $this->requestStack->getCurrentRequest()->get('sortBy') ?? 'hot';
}
}

View file

@ -15,8 +15,8 @@ class LinkExtensionRuntime implements RuntimeExtensionInterface
{
if ($this->settingsManager->get('KBIN_DOMAIN') === parse_url($url, PHP_URL_HOST)) {
return 'follow';
};
}
return 'nofollow noopener noreferrer';
}
}
}

View file

@ -17,41 +17,57 @@ class NavbarExtensionRuntime implements RuntimeExtensionInterface
public function navbarThreadsUrl(): string
{
if ($magazine = $this->requestStack->getCurrentRequest()->get('magazine')) {
return $this->entriesMagazine($magazine->name);
return $this->urlGenerator->generate('front_magazine', ['name' => $magazine->name]);
}
return $this->front();
if ($domain = $this->requestStack->getCurrentRequest()->get('domain')) {
return $this->urlGenerator->generate('domain_front', ['name' => $domain->name]);
}
if ($tag = $this->requestStack->getCurrentRequest()->get('tag')) {
return $this->urlGenerator->generate('tag_overall', ['name' => $tag]);
}
if (str_ends_with($this->getCurrentRouteName(), '_subscribed')) {
return $this->urlGenerator->generate('front_subscribed');
}
if (str_ends_with($this->getCurrentRouteName(), '_favourite')) {
return $this->urlGenerator->generate('front_favourite');
}
if (str_ends_with($this->getCurrentRouteName(), '_moderated')) {
return $this->urlGenerator->generate('front_moderated');
}
return $this->urlGenerator->generate('front');
}
public function navbarPostsUrl(): string
{
if ($magazine = $this->requestStack->getCurrentRequest()->get('magazine')) {
return $this->postsMagazine($magazine->name);
return $this->urlGenerator->generate('magazine_posts', ['name' => $magazine->name]);
}
return $this->posts();
}
if ($tag = $this->requestStack->getCurrentRequest()->get('tag')) {
return $this->urlGenerator->generate('tag_posts_front', ['name' => $tag]);
}
private function front(): string
{
return $this->urlGenerator->generate('front');
}
if (str_ends_with($this->getCurrentRouteName(), '_subscribed')) {
return $this->urlGenerator->generate('posts_subscribed');
}
private function entriesMagazine(string $name): string
{
return $this->urlGenerator->generate('front_magazine', ['name' => $name]);
}
if (str_ends_with($this->getCurrentRouteName(), '_favourite')) {
return $this->urlGenerator->generate('posts_favourite');
}
if (str_ends_with($this->getCurrentRouteName(), '_moderated')) {
return $this->urlGenerator->generate('posts_moderated');
}
private function posts(): string
{
return $this->urlGenerator->generate('posts_front');
}
private function postsMagazine(string $name): string
{
return $this->urlGenerator->generate('magazine_posts', ['name' => $name]);
}
private function getCurrentRouteName(): string
{
return $this->requestStack->getCurrentRequest()->get('_route') ?? 'front';

View file

@ -38,7 +38,7 @@ class OptionsExtensionRuntime implements RuntimeExtensionInterface
return true;
}
if (null === $params[$name] && $value == EntryRepository::SORT_DEFAULT) {
if (null === $params[$name] && EntryRepository::SORT_DEFAULT == $value) {
return true;
}

View file

@ -22,7 +22,7 @@ class UrlExtensionRuntime implements RuntimeExtensionInterface
return $this->urlGenerator->generate('entry_single', [
'magazine_name' => $entry->magazine->name,
'entry_id' => $entry->getId(),
'slug' => $entry->slug === '' ? 'icon' : $entry->slug,
'slug' => '' === $entry->slug ? 'icon' : $entry->slug,
]);
}
@ -31,7 +31,7 @@ class UrlExtensionRuntime implements RuntimeExtensionInterface
return $this->urlGenerator->generate('entry_favourites', [
'magazine_name' => $entry->magazine->name,
'entry_id' => $entry->getId(),
'slug' => $entry->slug === '' ? 'icon' : $entry->slug,
'slug' => '' === $entry->slug ? 'icon' : $entry->slug,
]);
}
@ -40,7 +40,7 @@ class UrlExtensionRuntime implements RuntimeExtensionInterface
return $this->urlGenerator->generate('entry_voters', [
'magazine_name' => $entry->magazine->name,
'entry_id' => $entry->getId(),
'slug' => $entry->slug === '' ? 'icon' : $entry->slug,
'slug' => '' === $entry->slug ? 'icon' : $entry->slug,
'type' => $type,
]);
}
@ -60,7 +60,7 @@ class UrlExtensionRuntime implements RuntimeExtensionInterface
return $this->urlGenerator->generate('post_single', [
'magazine_name' => $post->magazine->name,
'post_id' => $post->getId(),
'slug' => $post->slug === '' ? 'icon' : $post->slug,
'slug' => '' === $post->slug ? 'icon' : $post->slug,
]);
}
@ -69,7 +69,7 @@ class UrlExtensionRuntime implements RuntimeExtensionInterface
return $this->urlGenerator->generate('post_favourites', [
'magazine_name' => $post->magazine->name,
'post_id' => $post->getId(),
'slug' => $post->slug === '' ? 'icon' : $post->slug,
'slug' => '' === $post->slug ? 'icon' : $post->slug,
]);
}
@ -78,7 +78,7 @@ class UrlExtensionRuntime implements RuntimeExtensionInterface
return $this->urlGenerator->generate('post_voters', [
'magazine_name' => $post->magazine->name,
'post_id' => $post->getId(),
'slug' => $post->slug === '' ? 'icon' : $post->slug,
'slug' => '' === $post->slug ? 'icon' : $post->slug,
'type' => $type,
]);
}

View file

@ -0,0 +1,12 @@
<h1>{{ 'email_confirm_header'|trans }}</h1>
<p>
{{ 'email_confirm_content'|trans }} <br><br>
<a href="{{ signedUrl|raw }}">{{ 'email_verify'|trans }}</a>.
{{ 'email_confirm_expire'|trans }}
{# Link wygaśnie za {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}. #}
</p>
<p>
Cheers!
</p>

View file

@ -0,0 +1,8 @@
<h1>{{ 'contact'|trans }}</h1>
<ul>
<li>{{ name }}</li>
<li>{{ senderEmail }}</li>
</ul>
<p>{{ message }}</p>

View file

@ -0,0 +1,6 @@
<h1>User delete account</h1>
<ul>
<li>{{ username }}</li>
<li>{{ mail }}</li>
</ul>

View file

@ -2,6 +2,8 @@
<header>
{{ component('user_inline', {user: this.comment.user}) }},
{{ component('date', {date: this.comment.createdAt}) }}
{% if this.showMagazine %}{{ 'to'|trans }} {{ component('magazine_inline', {magazine: this.comment.magazine}) }}{% endif %}
{% if this.showEntry %}{{ 'in'|trans }} {{ component('entry_inline', {entry: this.comment.entry}) }}{% endif %}
</header>
{% if this.comment.user.avatar %}
{{ component('user_avatar', {user: this.comment.user, width: 40, height: 40}) }}

View file

@ -0,0 +1 @@
<a href="{{ path('entry_single', {magazine_name: this.entry.magazine.name, entry_id: this.entry.id, slug: this.entry.slug }) }}">{{ this.entry.title }}</a>

View file

@ -2,7 +2,7 @@
<header>
{{ component('user_inline', {user: this.post.user}) }},
<a href="{{ post_url(this.post) }}"
class="kbin-link-muted">{{ component('date', {date: this.post.createdAt}) }}</a>
class="kbin-link-muted">{{ component('date', {date: this.post.createdAt}) }}</a> {% if this.showMagazine %}{{ 'to'|trans }} {{ component('magazine_inline', {magazine: this.post.magazine}) }}{% endif %}
</header>
{% if this.post.user.avatar %}
{{ component('user_avatar', {user: this.post.user, width: 40, height: 40}) }}

View file

@ -1,5 +1,5 @@
<aside class="kbin-comments kbin-entry-comments">
{% for comment in comments %}
{{ component('entry_comment', {comment: comment}) }}
{{ component('entry_comment', {comment: comment, showMagazine: magazine is not defined or not magazine, showEntry: entry is not defined or not entry}) }}
{% endfor %}
</aside>

View file

@ -1,5 +1,5 @@
<aside class="kbin-options" id="kbin-comments-options">
<div class="kbin-options__title"><h2>{{ 'comments'|trans }} (0)</h2></div>
<div class="kbin-options__title"><h2>{{ 'comments'|trans }} ({{ entry.commentCount }})</h2></div>
<menu class="kbin-options__sort">
<li>
<a href="{{ options_url('sortBy', 'top'|trans|lower) }}"

View file

@ -6,9 +6,8 @@
<div id="kbin-content">
{{ component('entry', {entry: entry, isSingle: true, showShortSentence: false, showBody:false}) }}
{% include 'entry/comment/_options_sort.html.twig' %}
{% include 'entry/comment/_options.html.twig' %}
{{ component('entry_comment', {comment: parent}) }}
{% include 'entry/comment/_add_comment.html.twig' %}
{% include 'entry/_options_activity.html.twig' %}
</div>
{% endblock %}

View file

@ -0,0 +1,27 @@
{% extends 'base.html.twig' %}
{%- block title -%}
title
{%- endblock -%}
{% block mainClass %}kbin-page-entry-front{% endblock %}
{% block body %}
{% if magazine is defined and magazine %}
<h1 hidden>{{ magazine.title }}</h1>
<h2>{{ get_active_sort_option()|trans }}</h2>
{% else %}
<h1 hidden>{{ get_active_sort_option()|trans }}</h1>
{% endif %}
{% include 'entry/_options.html.twig' %}
{% if magazine is defined and magazine %}
{% if magazine.apId %}
<div class="kbin-alert kbin-alert__info">
<p>{{ 'federated_magazine_info'|trans }} <a
href="{{ magazine.apProfileId }}">{{ 'go_to_original_instance'|trans }}</a></p>
</div>
{% endif %}
{% endif %}
{% include 'entry/comment/_list.html.twig' %}
{% endblock %}

View file

@ -8,7 +8,12 @@
{% block body %}
<header>
<h1 hidden>{{ 'hot'|trans }}</h1>
{% if magazine is defined and magazine %}
<h1 hidden>{{ magazine.title }}</h1>
<h2 hidden>{{ get_active_sort_option()|trans }}</h2>
{% else %}
<h1 hidden>{{ get_active_sort_option()|trans }}</h1>
{% endif %}
</header>
{% include 'entry/_options.html.twig' %}
{% if magazine is defined and magazine %}

View file

@ -6,7 +6,7 @@
<div id="kbin-content">
{{ component('entry', {entry: entry, isSingle: true, showShortSentence: false, showBody:true}) }}
{% include 'entry/comment/_options_sort.html.twig' %}
{% include 'entry/comment/_options.html.twig' %}
{% if comments|length %}
{% include 'entry/comment/_list.html.twig' %}
{% else %}

View file

@ -12,6 +12,26 @@
<div class="kbin-magazine">
<span>/m/</span><a href="{{ path('front_magazine', {name: magazine.name}) }}">{{ magazine.name }}</a>
</div>
{% elseif domain is defined and domain %}
<div class="kbin-magazine">
<span>/d/</span><a href="{{ path('domain_front', {name: domain.name}) }}">{{ domain.name }}</a>
</div>
{% elseif tag is defined and tag %}
<div class="kbin-magazine">
<span>#</span><a href="{{ path('tag_front', {name: tag}) }}">{{ tag }}</a>
</div>
{% elseif is_route_name_end_with('_subscribed') %}
<div class="kbin-magazine">
<span>/</span><a href="{{ path('front_subscribed') }}">sub</a>
</div>
{% elseif is_route_name_end_with('_favourite') %}
<div class="kbin-magazine">
<span>/</span><a href="{{ path('front_favourite') }}">fav</a>
</div>
{% elseif is_route_name_end_with('_moderated') %}
<div class="kbin-magazine">
<span>/</span><a href="{{ path('front_moderated') }}">mod</a>
</div>
{% endif %}
<nav>
<menu>
@ -27,16 +47,16 @@
{{ 'microblog'|trans }}
</a>
</li>
{# <li>#}
{# <a href="#">#}
{# {{ 'events'|trans }}#}
{# </a>#}
{# </li>#}
<li>
<a href="#">
{{ 'people'|trans }}
</a>
</li>
<li>
<a href="#">
{{ 'events'|trans }}
</a>
</li>
<li>
<a href="#">
{{ 'magazines'|trans }}

View file

@ -1,6 +1,6 @@
<div id="kbin-content">
{% for post in posts %}
{{ component('post', {post: post}) }}
{{ component('post', {post: post, showMagazine: magazine is not defined or not magazine}) }}
{% endfor %}
</div>

View file

@ -1,4 +1,5 @@
<aside class="kbin-options" id="kbin-options">
<div class="kbin-options__title"><h2>{{ 'comments'|trans }} ({{ post.commentCount }})</h2></div>
<menu class="kbin-options__layout">
<li><a href="#" title="{{ 'change_view'|trans }}" aria-label="{{ 'change_view'|trans }}"><i class="kbin-icon__layout"></i></a></li>
</menu>

View file

@ -7,9 +7,12 @@
{% block mainClass %}kbin-page-post-front{% endblock %}
{% block body %}
<header>
<h1 hidden>{{ 'hot'|trans }}</h1>
</header>
{% if magazine is defined and magazine %}
<h1 hidden>{{ magazine.title }}</h1>
<h2 hidden>{{ get_active_sort_option()|trans }}</h2>
{% else %}
<h1 hidden>{{ get_active_sort_option()|trans }}</h1>
{% endif %}
{% include 'post/_add_post.html.twig' %}
{% include 'post/_options.html.twig' %}
{% if magazine is defined and magazine %}

View file

@ -6,7 +6,7 @@
<div id="kbin-content">
{{ component('post', {post: post, isSingle: true}) }}
{% include 'post/comment/_options_sort.html.twig' %}
{% include 'post/comment/_options.html.twig' %}
{% if comments|length %}
{% include 'post/comment/_list.html.twig' %}
{% else %}

View file

@ -1,11 +1,14 @@
{% extends 'base.html.twig' %}
{% block mainClass %}kbin-page-reset-password{% endblock %}
{% block mainClass %}kbin-page-reset-password-email-sent{% endblock %}
{% block body %}
<div id="kbin-content" class="kbin-section kbin-section--top">
<h1>{{ 'check_email'|trans }}</h1>
<p>{{ 'reset_check_email_desc'|trans }} {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.</p>
<p>{{ 'reset_check_email_desc2'|trans }} <a href="{{ path('app_forgot_password_request') }}">{{ 'try_again'|trans }}</a>.</p>
<div class="container">
<h1>{{ 'check_email'|trans }}</h1>
<p>{{ 'reset_check_email_desc'|trans({'%expire%': resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle')}) }}</p>
<p>{{ 'reset_check_email_desc2'|trans }}</p>
<p><a class="kbin-btn kbin-btn__secondry" href="{{ path('app_forgot_password_request') }}">{{ 'try_again'|trans }}</a></p>
</div>
</div>
{% endblock %}

View file

@ -11,16 +11,16 @@
{% endfor %}
<h1>{{ 'register'|trans }}</h1>
{{ form_row(form.username, {
label: 'username'
label: 'username',
}) }}
{{ form_row(form.email, {
label: 'email'
}) }}
{{ form_row(form.plainPassword, {
label: 'password'
}) }}
{{ form_row(form.agreeTerms, {
translation_domain: false,
label: 'agree_terms'|trans({
'%terms_link_start%' : '<a href="'~path('page_terms')~'">', '%terms_link_end%' : '</a>',
'%policy_link_start%' : '<a href="'~path('page_privacy_policy')~'">', '%policy_link_end%' : '</a>',

View file

@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests;
@ -16,8 +18,8 @@ abstract class ApiTestCase extends BaseApiTestCase
public function __construct($name = null, array $data = [], $dataName = '')
{
parent::__construct($name, $data, $dataName);
$this->users = new ArrayCollection();
$this->users = new ArrayCollection();
$this->magazines = new ArrayCollection();
$this->entries = new ArrayCollection();
$this->entries = new ArrayCollection();
}
}

View file

@ -22,7 +22,6 @@ use App\Service\MagazineManager;
use App\Service\PostCommentManager;
use App\Service\PostManager;
use App\Service\VoteManager;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
trait FactoryTrait
@ -64,13 +63,13 @@ trait FactoryTrait
yield [
'username' => 'adminUser',
'password' => 'adminUser123',
'email' => 'adminUser@example.com',
'email' => 'adminUser@example.com',
];
yield [
'username' => 'JohnDoe',
'password' => 'JohnDoe123',
'email' => 'JohnDoe@example.com',
'email' => 'JohnDoe@example.com',
];
}
@ -80,15 +79,15 @@ trait FactoryTrait
$user = new User($email ?: $username.'@example.com', $username, $password ?: 'secret');
$user->isVerified = $active;
$user->notifyOnNewEntry = true;
$user->notifyOnNewEntryReply = true;
$user->isVerified = $active;
$user->notifyOnNewEntry = true;
$user->notifyOnNewEntryReply = true;
$user->notifyOnNewEntryCommentReply = true;
$user->notifyOnNewPost = true;
$user->notifyOnNewPostReply = true;
$user->notifyOnNewPostCommentReply = true;
$user->showProfileFollowings = true;
$user->showProfileSubscriptions = true;
$user->notifyOnNewPost = true;
$user->notifyOnNewPostReply = true;
$user->notifyOnNewPostCommentReply = true;
$user->showProfileFollowings = true;
$user->showProfileSubscriptions = true;
$manager->persist($user);
$manager->flush();
@ -101,15 +100,15 @@ trait FactoryTrait
private function provideMagazines(): iterable
{
yield [
'name' => 'acme',
'name' => 'acme',
'title' => 'Magazyn polityczny',
'user' => $this->getUserByUsername('JohnDoe'),
'user' => $this->getUserByUsername('JohnDoe'),
];
yield [
'name' => 'kbin',
'name' => 'kbin',
'title' => 'kbin devlog',
'user' => $this->getUserByUsername('adminUser'),
'user' => $this->getUserByUsername('adminUser'),
];
}
@ -125,7 +124,7 @@ trait FactoryTrait
if ($isAdmin) {
$user->roles = ['ROLE_ADMIN'];
$manager = static::getContainer()->get(EntityManagerInterface::class);
$manager = static::getContainer()->get(EntityManagerInterface::class);
$manager->persist($user);
$manager->flush();
@ -141,9 +140,9 @@ trait FactoryTrait
*/
$manager = static::getContainer()->get(MagazineManager::class);
$dto = new MagazineDto();
$dto->name = $name;
$dto->title = $title ?? 'Przykładowy magazyn';
$dto = new MagazineDto();
$dto->name = $name;
$dto->title = $title ?? 'Magazine title';
$magazine = $manager->create($dto, $user ?? $this->getUserByUsername('JohnDoe'));
@ -154,27 +153,27 @@ trait FactoryTrait
protected function loadNotificationsFixture()
{
$owner = $this->getUserByUsername('owner');
$owner = $this->getUserByUsername('owner');
$magazine = $this->getMagazineByName('acme', $owner);
$actor = $this->getUserByUsername('actor');
$actor = $this->getUserByUsername('actor');
$regular = $this->getUserByUsername('JohnDoe');
$entry = $this->getEntryByTitle('test', null, 'test', $magazine, $actor);
$entry = $this->getEntryByTitle('test', null, 'test', $magazine, $actor);
$comment = $this->createEntryComment('test', $entry, $regular);
(static::getContainer()->get(EntryCommentManager::class))->delete($owner, $comment);
(static::getContainer()->get(EntryManager::class))->delete($owner, $entry);
static::getContainer()->get(EntryCommentManager::class)->delete($owner, $comment);
static::getContainer()->get(EntryManager::class)->delete($owner, $entry);
$post = $this->createPost('test', $magazine, $actor);
$post = $this->createPost('test', $magazine, $actor);
$comment = $this->createPostComment('test', $post, $regular);
(static::getContainer()->get(PostCommentManager::class))->delete($owner, $comment);
(static::getContainer()->get(PostManager::class))->delete($owner, $post);
static::getContainer()->get(PostCommentManager::class)->delete($owner, $comment);
static::getContainer()->get(PostManager::class)->delete($owner, $post);
(static::getContainer()->get(MagazineManager::class))->ban(
static::getContainer()->get(MagazineManager::class)->ban(
$magazine,
$actor,
$owner,
(new MagazineBanDto())->create('test', new DateTime('+1 day'))
(new MagazineBanDto())->create('test', new \DateTime('+1 day'))
);
}
@ -204,26 +203,31 @@ trait FactoryTrait
if (!$entry) {
$magazine = $magazine ?? $this->getMagazineByName('acme');
$user = $user ?? $this->getUserByUsername('JohnDoe');
$entry = $this->createEntry($title, $magazine, $user, $url, $body);
$user = $user ?? $this->getUserByUsername('JohnDoe');
$entry = $this->createEntry($title, $magazine, $user, $url, $body);
}
return $entry;
}
protected function createEntry(string $title, Magazine $magazine, User $user, ?string $url = null, ?string $body = 'testowa treść'): Entry
{
protected function createEntry(
string $title,
Magazine $magazine,
User $user,
?string $url = null,
?string $body = 'Test entry content'
): Entry {
/**
* @var $manager EntryManager
*/
$manager = static::getContainer()->get(EntryManager::class);
$dto = new EntryDto();
$dto = new EntryDto();
$dto->magazine = $magazine;
$dto->title = $title;
$dto->user = $user;
$dto->url = $url;
$dto->body = $body;
$dto->title = $title;
$dto->user = $user;
$dto->url = $url;
$dto->body = $body;
$entry = $manager->create($dto, $user);
@ -240,11 +244,16 @@ trait FactoryTrait
$manager = static::getContainer()->get(EntryCommentManager::class);
if ($parent) {
$dto = (new EntryCommentDto())->createWithParent($entry ?? $this->getEntryByTitle('Przykladowa treść'), $parent, null, $body);
$dto = (new EntryCommentDto())->createWithParent(
$entry ?? $this->getEntryByTitle('Przykladowa treść'),
$parent,
null,
$body
);
} else {
$dto = new EntryCommentDto();
$dto = new EntryCommentDto();
$dto->entry = $entry ?? $this->getEntryByTitle('Przykladowa treść');
$dto->body = $body;
$dto->body = $body;
}
return $manager->create($dto, $user ?? $this->getUserByUsername('JohnDoe'));
@ -257,9 +266,9 @@ trait FactoryTrait
*/
$manager = static::getContainer()->get(PostManager::class);
$dto = new PostDto();
$dto = new PostDto();
$dto->magazine = $magazine ?: $this->getMagazineByName('acme');
$dto->body = $body;
$dto->body = $body;
return $manager->create($dto, $user ?? $this->getUserByUsername('JohnDoe'));
}
@ -271,7 +280,7 @@ trait FactoryTrait
*/
$manager = static::getContainer()->get(PostCommentManager::class);
$dto = new PostCommentDto();
$dto = new PostCommentDto();
$dto->post = $post ?? $this->createPost('testowy post');
$dto->body = $body;

View file

@ -1,11 +1,11 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Functional\ApiDataProvider;
use App\Tests\ApiTestCase;
use App\Tests\FactoryTrait;
use DateTimeInterface;
class EntryCollectionDataProviderTest extends ApiTestCase
{
@ -33,59 +33,58 @@ class EntryCollectionDataProviderTest extends ApiTestCase
// $this->assertMatchesResourceCollectionJsonSchema(EntryDto::class); // todo image
$this->assertJsonContains([
'@context' => '/api/contexts/entry',
'@id' => '/api/entries',
'@type' => 'hydra:Collection',
'hydra:member' => [
'@context' => '/api/contexts/entry',
'@id' => '/api/entries',
'@type' => 'hydra:Collection',
'hydra:member' => [
[
'@id' => '/api/entries/'.$entry->getId(),
'@type' => 'entry',
'magazine' => [
'@id' => '/api/magazines/acme',
'@id' => '/api/entries/'.$entry->getId(),
'@type' => 'entry',
'magazine' => [
'@id' => '/api/magazines/acme',
'@type' => 'magazine',
'name' => 'acme',
'name' => 'acme',
],
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'username' => 'JohnDoe',
],
'image' => [
'@id' => '/api/images/'.$entry->image->getId(),
'@type' => 'image',
'filePath' => $entry->image->filePath,
'width' => 1280,
'height' => 1280
'image' => [
'@id' => '/api/images/'.$entry->image->getId(),
'@type' => 'image',
'filePath' => $entry->image->filePath,
'width' => 1280,
'height' => 1280,
],
'domain' => [
'@id' => '/api/domains/'.$entry->domain->getId(),
'domain' => [
'@id' => '/api/domains/'.$entry->domain->getId(),
'@type' => 'domain',
'name' => 'karab.in',
'name' => 'karab.in',
],
'title' => 'test1',
'url' => 'https://karab.in/',
'comments' => 1,
'uv' => 1,
'dv' => 1,
'isAdult' => false,
'views' => 0,
'score' => 0,
'title' => 'test1',
'url' => 'https://karab.in/',
'comments' => 1,
'uv' => 1,
'dv' => 1,
'isAdult' => false,
'views' => 0,
'score' => 0,
'visibility' => 'visible',
'createdAt' => $entry->createdAt->format(DateTimeInterface::RFC3339),
'lastActive' => $entry->lastActive->format(DateTimeInterface::RFC3339),
'id' => $entry->getId(),
'type' => 'link',
'createdAt' => $entry->createdAt->format(\DateTimeInterface::RFC3339),
'lastActive' => $entry->lastActive->format(\DateTimeInterface::RFC3339),
'id' => $entry->getId(),
'type' => 'link',
],
],
'hydra:totalItems' => 3,
'hydra:view' => [
'@id' => '/api/entries?page=1',
'@type' => 'hydra:PartialCollectionView',
'hydra:view' => [
'@id' => '/api/entries?page=1',
'@type' => 'hydra:PartialCollectionView',
'hydra:first' => '/api/entries?page=1',
'hydra:last' => '/api/entries?page=2',
'hydra:next' => '/api/entries?page=2',
'hydra:last' => '/api/entries?page=2',
'hydra:next' => '/api/entries?page=2',
],
]);
}
}

View file

@ -1,11 +1,11 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Functional\ApiDataProvider;
use App\Tests\ApiTestCase;
use App\Tests\FactoryTrait;
use DateTimeInterface;
class EntryItemDataProviderTest extends ApiTestCase
{
@ -32,54 +32,52 @@ class EntryItemDataProviderTest extends ApiTestCase
$response = $client->request('GET', '/api/entries/'.$entry->getId());
$this->assertCount(21, $response->toArray());
// $this->assertMatchesResourceItemJsonSchema(EntryDto::class); // @todo image
$this->assertJsonEquals([
'@context' => '/api/contexts/entry',
'@id' => '/api/entries/'.$entry->getId(),
'@type' => 'entry',
'magazine' => [
'@id' => '/api/magazines/acme',
'@context' => '/api/contexts/entry',
'@id' => '/api/entries/'.$entry->getId(),
'@type' => 'entry',
'magazine' => [
'@id' => '/api/magazines/acme',
'@type' => 'magazine',
'name' => 'acme',
'name' => 'acme',
],
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'username' => 'JohnDoe',
'avatar' => null,
'avatar' => null,
],
'image' => [
'@id' => '/api/images/'.$entry->image->getId(),
'@type' => 'image',
'filePath' => $entry->image->filePath,
'width' => 1280,
'height' => 1280
'image' => [
'@id' => '/api/images/'.$entry->image->getId(),
'@type' => 'image',
'filePath' => $entry->image->filePath,
'width' => 1280,
'height' => 1280,
],
'domain' => [
'@id' => '/api/domains/'.$entry->domain->getId(),
'@type' => 'domain',
'name' => 'karab.in',
'domain' => [
'@id' => '/api/domains/'.$entry->domain->getId(),
'@type' => 'domain',
'name' => 'karab.in',
'entryCount' => 3,
],
'title' => 'test1',
'url' => 'https://karab.in/',
'body' => null,
'comments' => 1,
'uv' => 1,
'dv' => 1,
'isAdult' => false,
'views' => 0,
'score' => 0,
'title' => 'test1',
'url' => 'https://karab.in/',
'body' => null,
'comments' => 1,
'uv' => 1,
'dv' => 1,
'isAdult' => false,
'views' => 0,
'score' => 0,
'visibility' => 'visible',
'createdAt' => $entry->createdAt->format(DateTimeInterface::RFC3339),
'lastActive' => $entry->lastActive->format(DateTimeInterface::RFC3339),
'id' => $entry->getId(),
'type' => 'link',
'createdAt' => $entry->createdAt->format(\DateTimeInterface::RFC3339),
'lastActive' => $entry->lastActive->format(\DateTimeInterface::RFC3339),
'id' => $entry->getId(),
'type' => 'link',
]);
}
}

View file

@ -1,8 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Functional\ApiDataProvider;
use App\DTO\MagazineDto;
use App\Tests\ApiTestCase;
use App\Tests\FactoryTrait;
@ -29,40 +30,39 @@ class MagazineCollectionDataProviderTest extends ApiTestCase
$this->assertMatchesResourceCollectionJsonSchema(MagazineDto::class);
$this->assertJsonContains([
'@context' => '/api/contexts/magazine',
'@id' => '/api/magazines',
'@type' => 'hydra:Collection',
'hydra:member' => [
'@context' => '/api/contexts/magazine',
'@id' => '/api/magazines',
'@type' => 'hydra:Collection',
'hydra:member' => [
[
'@id' => '/api/magazines/acme',
'@type' => 'magazine',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'@id' => '/api/magazines/acme',
'@type' => 'magazine',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'username' => 'JohnDoe',
],
'cover' => null,
'name' => 'acme',
'title' => 'Przykładowy magazyn',
'description' => null,
'rules' => null,
'cover' => null,
'name' => 'acme',
'title' => 'Magazine title',
'description' => null,
'rules' => null,
'subscriptionsCount' => 1,
'entryCount' => 1,
'entryCommentCount' => 1,
'postCount' => 1,
'postCommentCount' => 1,
'isAdult' => false,
'entryCount' => 1,
'entryCommentCount' => 1,
'postCount' => 1,
'postCommentCount' => 1,
'isAdult' => false,
],
],
'hydra:totalItems' => 3,
'hydra:view' => [
'@id' => '/api/magazines?page=1',
'@type' => 'hydra:PartialCollectionView',
'hydra:view' => [
'@id' => '/api/magazines?page=1',
'@type' => 'hydra:PartialCollectionView',
'hydra:first' => '/api/magazines?page=1',
'hydra:last' => '/api/magazines?page=2',
'hydra:next' => '/api/magazines?page=2',
'hydra:last' => '/api/magazines?page=2',
'hydra:next' => '/api/magazines?page=2',
],
]);
}
}

View file

@ -1,8 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Functional\ApiDataProvider;
use App\DTO\MagazineDto;
use App\Tests\ApiTestCase;
use App\Tests\FactoryTrait;
@ -25,27 +26,26 @@ class MagazineItemDataProviderTest extends ApiTestCase
$this->assertMatchesResourceItemJsonSchema(MagazineDto::class);
$this->assertJsonEquals([
'@context' => '/api/contexts/magazine',
'@id' => '/api/magazines/acme',
'@type' => 'magazine',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'@context' => '/api/contexts/magazine',
'@id' => '/api/magazines/acme',
'@type' => 'magazine',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'username' => 'JohnDoe',
'avatar' => null,
'avatar' => null,
],
'cover' => null,
'name' => 'acme',
'title' => 'Przykładowy magazyn',
'description' => null,
'rules' => null,
'cover' => null,
'name' => 'acme',
'title' => 'Magazine title',
'description' => null,
'rules' => null,
'subscriptionsCount' => 1,
'entryCount' => 1,
'entryCommentCount' => 1,
'postCount' => 1,
'postCommentCount' => 1,
'isAdult' => false,
'entryCount' => 1,
'entryCommentCount' => 1,
'postCount' => 1,
'postCommentCount' => 1,
'isAdult' => false,
]);
}
}

View file

@ -1,10 +1,11 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Functional\ApiDataProvider;
use App\Tests\ApiTestCase;
use App\Tests\FactoryTrait;
use DateTimeInterface;
class PostCollectionDataProviderTest extends ApiTestCase
{
@ -33,56 +34,56 @@ class PostCollectionDataProviderTest extends ApiTestCase
// $this->assertMatchesResourceCollectionJsonSchema(PostDto::class); // todo image
$this->assertJsonContains([
'@context' => '/api/contexts/post',
'@id' => '/api/posts',
'@type' => 'hydra:Collection',
'hydra:member' => [
'@context' => '/api/contexts/post',
'@id' => '/api/posts',
'@type' => 'hydra:Collection',
'hydra:member' => [
[
'@id' => '/api/posts/'.$post->getId(),
'@type' => 'post',
'magazine' => [
'@id' => '/api/magazines/acme',
'@id' => '/api/posts/'.$post->getId(),
'@type' => 'post',
'magazine' => [
'@id' => '/api/magazines/acme',
'@type' => 'magazine',
'name' => 'acme',
'name' => 'acme',
],
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'username' => 'JohnDoe',
],
'image' => null, // @todo
'comments' => 1,
'uv' => 2,
'isAdult' => false,
'score' => 2,
'visibility' => 'visible',
'createdAt' => $post->createdAt->format(DateTimeInterface::RFC3339),
'lastActive' => $post->lastActive->format(DateTimeInterface::RFC3339),
'id' => $post->getId(),
'image' => null, // @todo
'comments' => 1,
'uv' => 2,
'isAdult' => false,
'score' => 2,
'visibility' => 'visible',
'createdAt' => $post->createdAt->format(\DateTimeInterface::RFC3339),
'lastActive' => $post->lastActive->format(\DateTimeInterface::RFC3339),
'id' => $post->getId(),
'bestComments' => [
[
'@id' => '/api/post_comments/'.$comment->getId(),
'@type' => 'post_comment',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'@id' => '/api/post_comments/'.$comment->getId(),
'@type' => 'post_comment',
'user' => [
'@id' => '/api/users/JohnDoe',
'@type' => 'user',
'username' => 'JohnDoe',
'avatar' => null,
'avatar' => null,
],
'uv' => 2,
'createdAt' => $comment->createdAt->format(DateTimeInterface::RFC3339),
'lastActive' => $comment->lastActive->format(DateTimeInterface::RFC3339),
'uv' => 2,
'createdAt' => $comment->createdAt->format(\DateTimeInterface::RFC3339),
'lastActive' => $comment->lastActive->format(\DateTimeInterface::RFC3339),
],
],
],
],
'hydra:totalItems' => 3,
'hydra:view' => [
'@id' => '/api/posts?page=1',
'@type' => 'hydra:PartialCollectionView',
'hydra:view' => [
'@id' => '/api/posts?page=1',
'@type' => 'hydra:PartialCollectionView',
'hydra:first' => '/api/posts?page=1',
'hydra:last' => '/api/posts?page=2',
'hydra:next' => '/api/posts?page=2',
'hydra:last' => '/api/posts?page=2',
'hydra:next' => '/api/posts?page=2',
],
]);
}

View file

@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Command;
@ -16,7 +18,7 @@ class AdminCommandTest extends KernelTestCase
public function testCreateUser(): void
{
$dto = (new UserDto())->create('actor', 'contact@example.com');
$dto = (new UserDto())->create('actor', 'contact@example.com');
$dto->plainPassword = 'secret';
static::getContainer()->get('App\Service\UserManager')
@ -35,7 +37,7 @@ class AdminCommandTest extends KernelTestCase
{
$application = new Application(self::bootKernel());
$this->command = $application->find('kbin:user:admin');
$this->command = $application->find('kbin:user:admin');
$this->repository = static::getContainer()->get(UserRepository::class);
}
}

View file

@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Command;
@ -20,7 +22,7 @@ class UserCommandTest extends KernelTestCase
$tester->execute(
[
'username' => 'actor',
'email' => 'contact@example.com',
'email' => 'contact@example.com',
'password' => 'secret',
]
);

View file

@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Controller\Entry;
use App\Tests\WebTestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
class EntryFrontControllerTest extends WebTestCase
{
public function testFrontPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/newest');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-entry__meta', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Threads');
$this->assertcount(2, $crawler->filter('.kbin-entry'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
public function testMagazinePage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/m/acme');
$this->assertSelectorTextContains('h2', 'Hot');
$crawler = $client->request('GET', '/m/acme/newest');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextNotContains('.kbin-entry__meta', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/m/acme');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-magazine', 'acme');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Threads');
$this->assertcount(1, $crawler->filter('.kbin-entry'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', 'Magazine title');
$this->assertSelectorTextContains('h2', ucfirst($sortOption));
}
}
public function testSubPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/sub');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/sub/newest');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-entry__meta', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/sub');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Threads');
$this->assertcount(1, $crawler->filter('.kbin-entry'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
public function testModPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/mod');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/mod/newest');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-entry__meta', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/mod');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Threads');
$this->assertcount(1, $crawler->filter('.kbin-entry'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
public function testFavPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/fav');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/fav/newest');
// @todo
// $this->assertSelectorTextContains('.kbin-entry__meta', 'JaneDoe');
// $this->assertSelectorTextContains('.kbin-entry__meta', 'to kbin');
//
// $this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/fav');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Threads');
$this->assertcount(0, $crawler->filter('.kbin-entry'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
private function prepareEntries(): KernelBrowser
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$this->getEntryByTitle(
'test entry 1',
'https://kbin.pub',
null,
$this->getMagazineByName('kbin', $this->getUserByUsername('JaneDoe')),
$this->getUserByUsername('JaneDoe')
);
$this->getEntryByTitle('test entry 2');
return $client;
}
private function getSortOptions(): array
{
return ['top', 'hot', 'newest', 'active', 'commented'];
}
}

View file

@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Controller\Entry;
use App\Entity\Contracts\VoteInterface;
use App\Service\FavouriteManager;
use App\Service\VoteManager;
use App\Tests\WebTestCase;
class EntrySingleControllerTest extends WebTestCase
{
public function testUserCanGoToEntryFromFrontpage(): void
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$this->getEntryByTitle('test entry 1');
$crawler = $client->request('GET', '/');
$client->click($crawler->selectLink('test entry 1')->link());
$this->assertSelectorTextContains('article h1', 'test entry 1');
$this->assertSelectorTextContains('#kbin-main', 'No comments');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-entry-info', 'Thread');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-magazine', 'Magazine');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-user-list', 'Moderators');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-posts', 'Related posts');
}
public function testUserCanSeeArticle(): void
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$entry = $this->getEntryByTitle('test entry 1', null, 'Test entry content');
$client->request('GET', "/m/acme/t/{$entry->getId()}/test-entry-1");
$this->assertSelectorTextContains('article h1', 'test entry 1');
$this->assertSelectorNotExists('article h1 a');
$this->assertSelectorTextContains('article', 'Test entry content');
}
public function testUserCanSeeLink(): void
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$entry = $this->getEntryByTitle('test entry 1', 'https://kbin.pub');
$client->request('GET', "/m/acme/t/{$entry->getId()}/test-entry-1");
$this->assertSelectorExists('article h1 a[href="https://kbin.pub"]', 'test entry 1');
}
public function testPostActivityCounter(): void
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$entry = $this->getEntryByTitle('test entry 1');
$manager = static::getContainer()->get(VoteManager::class);
$manager->vote($entry, $this->getUserByUsername('JohnDoe'), VoteInterface::VOTE_UP);
$manager->vote($entry, $this->getUserByUsername('JaneDoe'), VoteInterface::VOTE_DOWN);
$manager = static::getContainer()->get(FavouriteManager::class);
$manager->toggle($entry, $this->getUserByUsername('JohnDoe'));
$manager->toggle($entry, $this->getUserByUsername('JaneDoe'));
$client->request('GET', "/m/acme/t/{$entry->getId()}/test-entry-1");
$this->assertSelectorTextContains('.kbin-options', 'Activity (3)');
}
public function testUserCanSeePhoto(): void
{
// @todo
}
}

View file

@ -1,98 +0,0 @@
<?php declare(strict_types=1);
namespace App\Tests\Functional\Controller\Entry;
use App\Tests\WebTestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
class FrontControllerTest extends WebTestCase
{
public function testFrontPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-entry__meta', 'to acme');
$this->assertcount(2, $crawler->filter('.kbin-entry'));
}
public function testMagazinePage(): void
{
$client = $this->prepareEntries();
$this->getEntryByTitle('testowa treść');
$crawler = $client->request('GET', '/m/acme');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextNotContains('.kbin-entry__meta', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/m/acme');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-magazine', 'acme');
$this->assertcount(1, $crawler->filter('.kbin-entry'));
}
public function testSubPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/sub');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-entry__meta', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/sub');
$this->assertcount(1, $crawler->filter('.kbin-entry'));
}
public function testModPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/mod');
$this->assertSelectorTextContains('.kbin-entry__meta', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-entry__meta', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/mod');
$this->assertcount(1, $crawler->filter('.kbin-entry'));
}
public function testFavPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/fav');
// $this->assertSelectorTextContains('.kbin-entry__meta', 'JaneDoe');
// $this->assertSelectorTextContains('.kbin-entry__meta', 'to kbin');
//
// $this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/fav');
$this->assertcount(0, $crawler->filter('.kbin-entry'));
}
private function prepareEntries(): KernelBrowser
{
$client = $this->createClient();
$this->getEntryByTitle(
'entry test 1',
'https://kbin.pub',
null,
$this->getMagazineByName('kbin', $this->getUserByUsername('JaneDoe')),
$this->getUserByUsername('JaneDoe')
);
$client->loginUser($this->getUserByUsername('JohnDoe'));
$this->getEntryByTitle('entry test 2');
return $client;
}
}

View file

@ -1,94 +0,0 @@
<?php declare(strict_types=1);
namespace App\Tests\Functional\Controller\Post;
use App\Tests\WebTestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
class FrontControllerTest extends WebTestCase
{
public function testFrontPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/microblog');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-post header', 'to acme');
$this->assertcount(2, $crawler->filter('.kbin-entry'));
}
public function testMagazinePage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/m/acme/microblog');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextNotContains('.kbin-post header', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/m/acme');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-magazine', 'acme');
$this->assertcount(1, $crawler->filter('.kbin-post'));
}
public function testSubPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/sub');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-post header', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/sub');
$this->assertcount(1, $crawler->filter('.kbin-post'));
}
public function testModPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/mod');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-post header', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/mod');
$this->assertcount(1, $crawler->filter('.kbin-post'));
}
public function testFavPage(): void
{
$client = $this->prepareEntries();
$crawler = $client->request('GET', '/fav');
// $this->assertSelectorTextContains('.kbin-entry__meta', 'JaneDoe');
// $this->assertSelectorTextContains('.kbin-entry__meta', 'to kbin');
//
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/fav');
$this->assertcount(0, $crawler->filter('.kbin-post'));
}
private function prepareEntries(): KernelBrowser
{
$client = $this->createClient();
$this->createPost(
'post test 1',
$this->getMagazineByName('kbin', $this->getUserByUsername('JaneDoe')),
$this->getUserByUsername('JaneDoe')
);
$client->loginUser($this->getUserByUsername('JohnDoe'));
$this->createPost('post test 2');
return $client;
}
}

View file

@ -0,0 +1,158 @@
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Controller\Post;
use App\Tests\WebTestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
class PostFrontControllerTest extends WebTestCase
{
public function testFrontPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/microblog');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/microblog/newest');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-post header', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Microblog');
$this->assertcount(2, $crawler->filter('.kbin-post'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
public function testMagazinePage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/m/acme/microblog');
$this->assertSelectorTextContains('h2', 'Hot');
$crawler = $client->request('GET', '/m/acme/microblog/newest');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextNotContains('.kbin-post header', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/m/acme');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-magazine', 'acme');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Microblog');
$this->assertcount(1, $crawler->filter('.kbin-post'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', 'Magazine title');
$this->assertSelectorTextContains('h2', ucfirst($sortOption));
}
}
public function testSubPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/sub/microblog');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/sub/microblog/newest');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-post header', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/sub');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Microblog');
$this->assertcount(1, $crawler->filter('.kbin-post'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
public function testModPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/mod/microblog');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/mod/microblog/newest');
$this->assertSelectorTextContains('.kbin-post header', 'JohnDoe');
$this->assertSelectorTextContains('.kbin-post header', 'to acme');
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/mod');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Microblog');
$this->assertcount(1, $crawler->filter('.kbin-post'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
public function testFavPage(): void
{
$client = $this->prepareEntries();
$client->request('GET', '/fav/microblog');
$this->assertSelectorTextContains('h1', 'Hot');
$crawler = $client->request('GET', '/fav/microblog/newest');
// @todo
// $this->assertSelectorTextContains('.kbin-entry__meta', 'JaneDoe');
// $this->assertSelectorTextContains('.kbin-entry__meta', 'to kbin');
//
$this->assertSelectorTextContains('#kbin-header .kbin-magazine', '/fav');
$this->assertSelectorTextContains('#kbin-header .kbin-active', 'Microblog');
$this->assertcount(0, $crawler->filter('.kbin-post'));
foreach ($this->getSortOptions() as $sortOption) {
$crawler = $client->click($crawler->filter('.kbin-options__sort')->selectLink($sortOption)->link());
$this->assertSelectorTextContains('.kbin-options__sort', $sortOption);
$this->assertSelectorTextContains('h1', ucfirst($sortOption));
}
}
private function prepareEntries(): KernelBrowser
{
$client = $this->createClient();
$this->createPost(
'test post 1',
$this->getMagazineByName('kbin', $this->getUserByUsername('JaneDoe')),
$this->getUserByUsername('JaneDoe')
);
$client->loginUser($this->getUserByUsername('JohnDoe'));
$this->createPost('test post 2');
return $client;
}
private function getSortOptions(): array
{
return ['top', 'hot', 'newest', 'active', 'commented'];
}
}

View file

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Controller\Post;
use App\Entity\Contracts\VoteInterface;
use App\Service\FavouriteManager;
use App\Service\VoteManager;
use App\Tests\WebTestCase;
class PostSingleControllerTest extends WebTestCase
{
public function testUserCanGoToPostFromFrontpage(): void
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$this->createPost('test post 1');
$crawler = $client->request('GET', '/microblog');
$client->click($crawler->selectLink('now')->link());
$this->assertSelectorTextContains('blockquote', 'test post 1');
$this->assertSelectorTextContains('#kbin-main', 'No comments');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-magazine', 'Magazine');
$this->assertSelectorTextContains('#kbin-sidebar .kbin-user-list', 'Moderators');
}
public function testUserCanSeePost(): void
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$post = $this->createPost('test post 1');
$client->request('GET', "/m/acme/p/{$post->getId()}/test-post-1");
$this->assertSelectorTextContains('blockquote', 'test post 1');
}
public function testPostActivityCounter(): void
{
$client = $this->createClient();
$client->loginUser($this->getUserByUsername('JohnDoe'));
$post = $this->createPost('test post 1');
$manager = static::getContainer()->get(VoteManager::class);
$manager->vote($post, $this->getUserByUsername('JohnDoe'), VoteInterface::VOTE_UP);
$manager->vote($post, $this->getUserByUsername('JaneDoe'), VoteInterface::VOTE_DOWN);
$manager = static::getContainer()->get(FavouriteManager::class);
$manager->toggle($post, $this->getUserByUsername('JohnDoe'));
$manager->toggle($post, $this->getUserByUsername('JaneDoe'));
$client->request('GET', "/m/acme/p/{$post->getId()}/test-post-1");
$this->assertSelectorTextContains('.kbin-options', 'Activity (3)');
}
}

View file

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Controller\Security;
use App\Tests\WebTestCase;
class LoginControllerTest extends WebTestCase
{
public function testUserCanLogin(): void
{
$client = $this->createClient();
$user = $this->getUserByUsername('JohnDoe');
$crawler = $client->request('GET', '/');
$crawler = $client->click($crawler->filter('header')->selectLink('Log in')->link());
$client->submit(
$crawler->selectButton('Log in')->form(
[
'email' => 'JohnDoe',
'password' => 'secret',
]
)
);
$crawler = $client->followRedirect();
// $this->assertSelectorTextNotContains('header', 'Log in'); // @todo
}
}

View file

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Controller\Security;
use App\Tests\WebTestCase;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
class RegisterControllerTest extends WebTestCase
{
public function testUserCanVerifyAccount(): void
{
$client = $this->createClient();
$this->registerUserAccount($client);
$this->assertEmailCount(1);
/** @var TemplatedEmail $email */
$email = $this->getMailerMessage();
$this->assertEmailHeaderSame($email, 'To', 'johndoe@kbin.pub');
$verifyLink = $email->getContext()['signedUrl'];
$crawler = $client->request('GET', $verifyLink);
$crawler = $client->followRedirect();
$client->submit(
$crawler->selectButton('Log in')->form(
[
'email' => 'JohnDoe',
'password' => 'secret',
]
)
);
$client->followRedirect();
$this->assertSelectorTextNotContains('#kbin-header', 'Log in');
}
private function registerUserAccount(KernelBrowser $client): void
{
$crawler = $client->request('GET', '/register');
$client->submit(
$crawler->filter('form[name=user_register]')->selectButton('Register')->form(
[
'user_register[username]' => 'JohnDoe',
'user_register[email]' => 'johndoe@kbin.pub',
'user_register[plainPassword][first]' => 'secret',
'user_register[plainPassword][second]' => 'secret',
'user_register[agreeTerms]' => true,
]
)
);
}
public function testUserCannotLoginWithoutConfirmation()
{
$client = $this->createClient();
$this->registerUserAccount($client);
$crawler = $client->followRedirect();
$crawler = $client->click($crawler->filter('#kbin-header')->selectLink('Log in')->link());
$client->submit(
$crawler->selectButton('Log in')->form(
[
'email' => 'JohnDoe',
'password' => 'wrong_password',
]
)
);
$client->followRedirect();
$this->assertSelectorTextContains('.kbin-alert__danger', 'Your account is not active.');
}
}

View file

@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Service;

View file

@ -1,8 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Service;
use App\Service\MentionManager;
use App\Service\TagManager;
use App\Tests\WebTestCase;

View file

@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Utils;

View file

@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace App\Tests;
@ -16,8 +18,8 @@ abstract class WebTestCase extends BaseWebTestCase
public function __construct($name = null, array $data = [], $dataName = '')
{
parent::__construct($name, $data, $dataName);
$this->users = new ArrayCollection();
$this->users = new ArrayCollection();
$this->magazines = new ArrayCollection();
$this->entries = new ArrayCollection();
$this->entries = new ArrayCollection();
}
}

View file

@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
use Symfony\Component\Dotenv\Dotenv;

View file

@ -63,6 +63,7 @@ register: Register
reset_password: Reset password
show_more: Show more
to: to
in: in
username: Username
email: Email
repeat_password: Repeat password
@ -88,8 +89,15 @@ change_theme: Change theme
useful: Useful
help: Help
check_email: Check your email
reset_check_email_desc: 'If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password. This link will expire in'
reset_check_email_desc2: "If you don't receive an email please check your spam folder or try again."
reset_check_email_desc: If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password. This link will expire in %expire%.
reset_check_email_desc2: If you don't receive an email please check your spam folder.
try_again: Try again
up_vote: Up vote
down_vote: Down vote
down_vote: Down vote
email_confirm_header: Hello! Confirm your email address.
email_confirm_content: 'If you want to activate your account click on the link below:'
email_verify: Confirm email address
email_confirm_expire: The link will expire in 1 hour.
email_confirm_title: Confirm your email address.
Your account is not active: Your account is not active.
Your account has been banned: Your account has been banned.

View file

@ -0,0 +1,2 @@
Your account is not active.: Your account is not active.
Your account has been banned.: Your account has been banned.