mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Added testing coverage for tag index
Also: - Extracted out index table row to its own view. - Added empty state. - Ensured query params are set on pagination links.
This commit is contained in:
parent
f8f9e74992
commit
899349c4b4
@ -26,7 +26,11 @@ class TagController extends Controller
|
||||
$nameFilter = $request->get('name', '');
|
||||
$tags = $this->tagRepo
|
||||
->queryWithTotals($search, $nameFilter)
|
||||
->paginate(20);
|
||||
->paginate(50)
|
||||
->appends(array_filter([
|
||||
'search' => $search,
|
||||
'name' => $nameFilter
|
||||
]));
|
||||
|
||||
return view('tags.index', [
|
||||
'tags' => $tags,
|
||||
|
@ -267,6 +267,7 @@ return [
|
||||
'tags_all_values' => 'All values',
|
||||
'tags_view_tags' => 'View Tags',
|
||||
'tags_view_existing_tags' => 'View existing tags',
|
||||
'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.',
|
||||
'attachments' => 'Attachments',
|
||||
'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.',
|
||||
'attachments_explain_instant_save' => 'Changes here are saved instantly.',
|
||||
|
@ -11,7 +11,7 @@
|
||||
<div>
|
||||
<div class="block inline mr-xs">
|
||||
<form method="get" action="{{ url("/tags") }}">
|
||||
@include('form.request-query-inputs', ['params' => ['page', 'name']])
|
||||
@include('form.request-query-inputs', ['params' => ['name']])
|
||||
<input type="text"
|
||||
name="search"
|
||||
placeholder="{{ trans('common.search') }}"
|
||||
@ -32,52 +32,23 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(count($tags) > 0)
|
||||
<table class="table expand-to-padding mt-m">
|
||||
@foreach($tags as $tag)
|
||||
@include('tags.parts.table-row', ['tag' => $tag, 'nameFilter' => $nameFilter])
|
||||
@endforeach
|
||||
</table>
|
||||
|
||||
<table class="table expand-to-padding mt-m">
|
||||
@foreach($tags as $tag)
|
||||
<tr>
|
||||
<td>
|
||||
<span class="text-bigger mr-xl">@include('entities.tag', ['tag' => $tag])</span>
|
||||
</td>
|
||||
<td width="60" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() }}"
|
||||
title="{{ trans('entities.tags_usages') }}"
|
||||
class="pill text-muted">@icon('leaderboard'){{ $tag->usages }}</a>
|
||||
</td>
|
||||
<td width="60" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:page}' }}"
|
||||
title="{{ trans('entities.tags_assigned_pages') }}"
|
||||
class="pill text-page">@icon('page'){{ $tag->page_count }}</a>
|
||||
</td>
|
||||
<td width="60" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:chapter}' }}"
|
||||
title="{{ trans('entities.tags_assigned_chapters') }}"
|
||||
class="pill text-chapter">@icon('chapter'){{ $tag->chapter_count }}</a>
|
||||
</td>
|
||||
<td width="60" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:book}' }}"
|
||||
title="{{ trans('entities.tags_assigned_books') }}"
|
||||
class="pill text-book">@icon('book'){{ $tag->book_count }}</a>
|
||||
</td>
|
||||
<td width="60" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:bookshelf}' }}"
|
||||
title="{{ trans('entities.tags_assigned_shelves') }}"
|
||||
class="pill text-bookshelf">@icon('bookshelf'){{ $tag->shelf_count }}</a>
|
||||
</td>
|
||||
<td class="text-right text-muted">
|
||||
@if($tag->values ?? false)
|
||||
<a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_x_unique_values', ['count' => $tag->values]) }}</a>
|
||||
@elseif(empty($nameFilter))
|
||||
<a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_all_values') }}</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
|
||||
<div>
|
||||
{{ $tags->links() }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $tags->links() }}
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted italic my-xl">
|
||||
{{ trans('common.no_items') }}.
|
||||
<br>
|
||||
{{ trans('entities.tags_list_empty_hint') }}
|
||||
</p>
|
||||
@endif
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
37
resources/views/tags/parts/table-row.blade.php
Normal file
37
resources/views/tags/parts/table-row.blade.php
Normal file
@ -0,0 +1,37 @@
|
||||
<tr>
|
||||
<td>
|
||||
<span class="text-bigger mr-xl">@include('entities.tag', ['tag' => $tag])</span>
|
||||
</td>
|
||||
<td width="70" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() }}"
|
||||
title="{{ trans('entities.tags_usages') }}"
|
||||
class="pill text-muted">@icon('leaderboard'){{ $tag->usages }}</a>
|
||||
</td>
|
||||
<td width="70" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:page}' }}"
|
||||
title="{{ trans('entities.tags_assigned_pages') }}"
|
||||
class="pill text-page">@icon('page'){{ $tag->page_count }}</a>
|
||||
</td>
|
||||
<td width="70" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:chapter}' }}"
|
||||
title="{{ trans('entities.tags_assigned_chapters') }}"
|
||||
class="pill text-chapter">@icon('chapter'){{ $tag->chapter_count }}</a>
|
||||
</td>
|
||||
<td width="70" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:book}' }}"
|
||||
title="{{ trans('entities.tags_assigned_books') }}"
|
||||
class="pill text-book">@icon('book'){{ $tag->book_count }}</a>
|
||||
</td>
|
||||
<td width="70" class="px-xs">
|
||||
<a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:bookshelf}' }}"
|
||||
title="{{ trans('entities.tags_assigned_shelves') }}"
|
||||
class="pill text-bookshelf">@icon('bookshelf'){{ $tag->shelf_count }}</a>
|
||||
</td>
|
||||
<td class="text-right text-muted">
|
||||
@if($tag->values ?? false)
|
||||
<a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_x_unique_values', ['count' => $tag->values]) }}</a>
|
||||
@elseif(empty($nameFilter))
|
||||
<a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_all_values') }}</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
@ -3,6 +3,7 @@
|
||||
namespace Tests\Entity;
|
||||
|
||||
use BookStack\Actions\Tag;
|
||||
use BookStack\Entities\Models\Book;
|
||||
use BookStack\Entities\Models\Entity;
|
||||
use BookStack\Entities\Models\Page;
|
||||
use Tests\TestCase;
|
||||
@ -98,4 +99,95 @@ class TagTest extends TestCase
|
||||
$resp->assertElementContains('[href="' . $page->getUrl() . '"]', 'color');
|
||||
$resp->assertElementContains('[href="' . $page->getUrl() . '"]', 'red');
|
||||
}
|
||||
|
||||
public function test_tags_index_shows_tag_name_as_expected_with_right_counts()
|
||||
{
|
||||
/** @var Page $page */
|
||||
$page = Page::query()->first();
|
||||
$page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
|
||||
$page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
|
||||
|
||||
$resp = $this->asEditor()->get('/tags');
|
||||
$resp->assertSee('Category');
|
||||
$resp->assertElementCount('.tag-item', 1);
|
||||
$resp->assertDontSee('GreatTestContent');
|
||||
$resp->assertDontSee('OtherTestContent');
|
||||
$resp->assertElementContains('a[title="Total tag usages"]', '2');
|
||||
$resp->assertElementContains('a[title="Assigned to Pages"]', '2');
|
||||
$resp->assertElementContains('a[title="Assigned to Books"]', '0');
|
||||
$resp->assertElementContains('a[title="Assigned to Chapters"]', '0');
|
||||
$resp->assertElementContains('a[title="Assigned to Shelves"]', '0');
|
||||
$resp->assertElementContains('a[href$="/tags?name=Category"]', '2 unique values');
|
||||
|
||||
/** @var Book $book */
|
||||
$book = Book::query()->first();
|
||||
$book->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
|
||||
$resp = $this->asEditor()->get('/tags');
|
||||
$resp->assertElementContains('a[title="Total tag usages"]', '3');
|
||||
$resp->assertElementContains('a[title="Assigned to Books"]', '1');
|
||||
$resp->assertElementContains('a[href$="/tags?name=Category"]', '2 unique values');
|
||||
}
|
||||
|
||||
public function test_tag_index_can_be_searched()
|
||||
{
|
||||
/** @var Page $page */
|
||||
$page = Page::query()->first();
|
||||
$page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
|
||||
|
||||
$resp = $this->asEditor()->get('/tags?search=cat');
|
||||
$resp->assertElementContains('.tag-item .tag-name', 'Category');
|
||||
|
||||
$resp = $this->asEditor()->get('/tags?search=content');
|
||||
$resp->assertElementContains('.tag-item .tag-name', 'Category');
|
||||
$resp->assertElementContains('.tag-item .tag-value', 'GreatTestContent');
|
||||
|
||||
$resp = $this->asEditor()->get('/tags?search=other');
|
||||
$resp->assertElementNotExists('.tag-item .tag-name');
|
||||
}
|
||||
|
||||
public function test_tag_index_can_be_scoped_to_specific_tag_name()
|
||||
{
|
||||
/** @var Page $page */
|
||||
$page = Page::query()->first();
|
||||
$page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
|
||||
$page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
|
||||
$page->tags()->create(['name' => 'OtherTagName', 'value' => 'OtherValue']);
|
||||
|
||||
$resp = $this->asEditor()->get('/tags?name=Category');
|
||||
$resp->assertSee('Category');
|
||||
$resp->assertSee('GreatTestContent');
|
||||
$resp->assertSee('OtherTestContent');
|
||||
$resp->assertDontSee('OtherTagName');
|
||||
$resp->assertElementCount('table .tag-item', 2);
|
||||
$resp->assertSee('Active Filter:');
|
||||
$resp->assertElementContains('form[action$="/tags"]', 'Clear Filter');
|
||||
}
|
||||
|
||||
public function test_tags_index_adheres_to_page_permissions()
|
||||
{
|
||||
/** @var Page $page */
|
||||
$page = Page::query()->first();
|
||||
$page->tags()->create(['name' => 'SuperCategory', 'value' => 'GreatTestContent']);
|
||||
|
||||
$resp = $this->asEditor()->get('/tags');
|
||||
$resp->assertSee('SuperCategory');
|
||||
$resp = $this->get('/tags?name=SuperCategory');
|
||||
$resp->assertSee('GreatTestContent');
|
||||
|
||||
$page->restricted = true;
|
||||
$this->regenEntityPermissions($page);
|
||||
|
||||
$resp = $this->asEditor()->get('/tags');
|
||||
$resp->assertDontSee('SuperCategory');
|
||||
$resp = $this->get('/tags?name=SuperCategory');
|
||||
$resp->assertDontSee('GreatTestContent');
|
||||
}
|
||||
|
||||
public function test_tag_index_shows_message_on_no_results()
|
||||
{
|
||||
/** @var Page $page */
|
||||
$resp = $this->asEditor()->get('/tags?search=testingval');
|
||||
$resp->assertSee('No items available');
|
||||
$resp->assertSee('Tags can be assigned via the page editor sidebar');
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,26 @@ class TestResponse extends BaseTestResponse
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the response contains the given count of elements
|
||||
* that match the given css selector.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function assertElementCount(string $selector, int $count)
|
||||
{
|
||||
$elements = $this->crawler()->filter($selector);
|
||||
PHPUnit::assertTrue(
|
||||
$elements->count() === $count,
|
||||
'Unable to ' . $count . ' element(s) matching the selector: ' . PHP_EOL . PHP_EOL .
|
||||
"[{$selector}]" . PHP_EOL . PHP_EOL .
|
||||
'found ' . $elements->count() . ' within' . PHP_EOL . PHP_EOL .
|
||||
"[{$this->getContent()}]."
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the response does not contain the specified element.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user