]> BookStack Code Mirror - bookstack/blob - tests/Entity/TagTest.php
ab06276013f041a4c99a686f6f379a4e0e42969a
[bookstack] / tests / Entity / TagTest.php
1 <?php
2
3 namespace Tests\Entity;
4
5 use BookStack\Actions\Tag;
6 use BookStack\Entities\Models\Entity;
7 use BookStack\Entities\Models\Page;
8 use Tests\TestCase;
9
10 class TagTest extends TestCase
11 {
12     protected $defaultTagCount = 20;
13
14     /**
15      * Get an instance of a page that has many tags.
16      */
17     protected function getEntityWithTags($class, ?array $tags = null): Entity
18     {
19         $entity = $class::first();
20
21         if (is_null($tags)) {
22             $tags = Tag::factory()->count($this->defaultTagCount)->make();
23         }
24
25         $entity->tags()->saveMany($tags);
26
27         return $entity;
28     }
29
30     public function test_tag_name_suggestions()
31     {
32         // Create some tags with similar names to test with
33         $attrs = collect();
34         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'country']));
35         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'color']));
36         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'city']));
37         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'county']));
38         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'planet']));
39         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'plans']));
40         $page = $this->getEntityWithTags(Page::class, $attrs->all());
41
42         $this->asAdmin()->get('/ajax/tags/suggest/names?search=dog')->assertSimilarJson([]);
43         $this->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson(['color', 'country', 'county']);
44         $this->get('/ajax/tags/suggest/names?search=cou')->assertSimilarJson(['country', 'county']);
45         $this->get('/ajax/tags/suggest/names?search=pla')->assertSimilarJson(['planet', 'plans']);
46     }
47
48     public function test_tag_value_suggestions()
49     {
50         // Create some tags with similar values to test with
51         $attrs = collect();
52         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'country', 'value' => 'cats']));
53         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'color', 'value' => 'cattery']));
54         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'city', 'value' => 'castle']));
55         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'county', 'value' => 'dog']));
56         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'planet', 'value' => 'catapult']));
57         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'plans', 'value' => 'dodgy']));
58         $page = $this->getEntityWithTags(Page::class, $attrs->all());
59
60         $this->asAdmin()->get('/ajax/tags/suggest/values?search=ora')->assertSimilarJson([]);
61         $this->get('/ajax/tags/suggest/values?search=cat')->assertSimilarJson(['cats', 'cattery', 'catapult']);
62         $this->get('/ajax/tags/suggest/values?search=do')->assertSimilarJson(['dog', 'dodgy']);
63         $this->get('/ajax/tags/suggest/values?search=cas')->assertSimilarJson(['castle']);
64     }
65
66     public function test_entity_permissions_effect_tag_suggestions()
67     {
68         // Create some tags with similar names to test with and save to a page
69         $attrs = collect();
70         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'country']));
71         $attrs = $attrs->merge(Tag::factory()->count(5)->make(['name' => 'color']));
72         $page = $this->getEntityWithTags(Page::class, $attrs->all());
73
74         $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson(['color', 'country']);
75         $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson(['color', 'country']);
76
77         // Set restricted permission the page
78         $page->restricted = true;
79         $page->save();
80         $page->rebuildPermissions();
81
82         $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson(['color', 'country']);
83         $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson([]);
84     }
85
86     public function test_tags_shown_on_search_listing()
87     {
88         $tags = [
89             Tag::factory()->make(['name' => 'category', 'value' => 'buckets']),
90             Tag::factory()->make(['name' => 'color', 'value' => 'red']),
91         ];
92
93         $page = $this->getEntityWithTags(Page::class, $tags);
94         $resp = $this->asEditor()->get('/search?term=[category]');
95         $resp->assertSee($page->name);
96         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'category');
97         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'buckets');
98         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'color');
99         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'red');
100     }
101
102     public function test_tags_index_shows_tag_name_as_expected_with_right_counts()
103     {
104         $page = $this->entities->page();
105         $page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
106         $page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
107
108         $resp = $this->asEditor()->get('/tags');
109         $resp->assertSee('Category');
110         $html = $this->withHtml($resp);
111         $html->assertElementCount('.tag-item', 1);
112         $resp->assertDontSee('GreatTestContent');
113         $resp->assertDontSee('OtherTestContent');
114         $html->assertElementContains('a[title="Total tag usages"]', '2');
115         $html->assertElementContains('a[title="Assigned to Pages"]', '2');
116         $html->assertElementContains('a[title="Assigned to Books"]', '0');
117         $html->assertElementContains('a[title="Assigned to Chapters"]', '0');
118         $html->assertElementContains('a[title="Assigned to Shelves"]', '0');
119         $html->assertElementContains('a[href$="/tags?name=Category"]', '2 unique values');
120
121         $book = $this->entities->book();
122         $book->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
123         $resp = $this->asEditor()->get('/tags');
124         $this->withHtml($resp)->assertElementContains('a[title="Total tag usages"]', '3');
125         $this->withHtml($resp)->assertElementContains('a[title="Assigned to Books"]', '1');
126         $this->withHtml($resp)->assertElementContains('a[href$="/tags?name=Category"]', '2 unique values');
127     }
128
129     public function test_tag_index_can_be_searched()
130     {
131         $page = $this->entities->page();
132         $page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
133
134         $resp = $this->asEditor()->get('/tags?search=cat');
135         $this->withHtml($resp)->assertElementContains('.tag-item .tag-name', 'Category');
136
137         $resp = $this->asEditor()->get('/tags?search=content');
138         $this->withHtml($resp)->assertElementContains('.tag-item .tag-name', 'Category');
139         $this->withHtml($resp)->assertElementContains('.tag-item .tag-value', 'GreatTestContent');
140
141         $resp = $this->asEditor()->get('/tags?search=other');
142         $this->withHtml($resp)->assertElementNotExists('.tag-item .tag-name');
143     }
144
145     public function test_tag_index_search_will_show_mulitple_values_of_a_single_tag_name()
146     {
147         $page = $this->entities->page();
148         $page->tags()->create(['name' => 'Animal', 'value' => 'Catfish']);
149         $page->tags()->create(['name' => 'Animal', 'value' => 'Catdog']);
150
151         $resp = $this->asEditor()->get('/tags?search=cat');
152         $this->withHtml($resp)->assertElementContains('.tag-item .tag-value', 'Catfish');
153         $this->withHtml($resp)->assertElementContains('.tag-item .tag-value', 'Catdog');
154     }
155
156     public function test_tag_index_can_be_scoped_to_specific_tag_name()
157     {
158         $page = $this->entities->page();
159         $page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
160         $page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
161         $page->tags()->create(['name' => 'OtherTagName', 'value' => 'OtherValue']);
162
163         $resp = $this->asEditor()->get('/tags?name=Category');
164         $resp->assertSee('Category');
165         $resp->assertSee('GreatTestContent');
166         $resp->assertSee('OtherTestContent');
167         $resp->assertDontSee('OtherTagName');
168         $resp->assertSee('Active Filter:');
169         $this->withHtml($resp)->assertElementCount('table .tag-item', 2);
170         $this->withHtml($resp)->assertElementContains('form[action$="/tags"]', 'Clear Filter');
171     }
172
173     public function test_tags_index_adheres_to_page_permissions()
174     {
175         $page = $this->entities->page();
176         $page->tags()->create(['name' => 'SuperCategory', 'value' => 'GreatTestContent']);
177
178         $resp = $this->asEditor()->get('/tags');
179         $resp->assertSee('SuperCategory');
180         $resp = $this->get('/tags?name=SuperCategory');
181         $resp->assertSee('GreatTestContent');
182
183         $page->restricted = true;
184         $this->entities->regenPermissions($page);
185
186         $resp = $this->asEditor()->get('/tags');
187         $resp->assertDontSee('SuperCategory');
188         $resp = $this->get('/tags?name=SuperCategory');
189         $resp->assertDontSee('GreatTestContent');
190     }
191
192     public function test_tag_index_shows_message_on_no_results()
193     {
194         $resp = $this->asEditor()->get('/tags?search=testingval');
195         $resp->assertSee('No items available');
196         $resp->assertSee('Tags can be assigned via the page editor sidebar');
197     }
198
199     public function test_tag_classes_visible_on_entities()
200     {
201         $this->asEditor();
202
203         foreach ($this->entities->all() as $entity) {
204             $entity->tags()->create(['name' => 'My Super Tag Name', 'value' => 'An-awesome-value']);
205             $html = $this->withHtml($this->get($entity->getUrl()));
206             $html->assertElementExists('body.tag-name-mysupertagname.tag-value-anawesomevalue.tag-pair-mysupertagname-anawesomevalue');
207         }
208     }
209
210     public function test_tag_classes_are_escaped()
211     {
212         $page = $this->entities->page();
213         $page->tags()->create(['name' => '<>']);
214         $resp = $this->asEditor()->get($page->getUrl());
215         $resp->assertDontSee('tag-name-<>', false);
216         $resp->assertSee('tag-name-&lt;&gt;', false);
217     }
218 }