]> BookStack Code Mirror - bookstack/blob - tests/Entity/TagTest.php
Played around with a new app structure
[bookstack] / tests / Entity / TagTest.php
1 <?php
2
3 namespace Tests\Entity;
4
5 use BookStack\Activity\Models\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         $this->permissions->setEntityPermissions($page, [], []);
79
80         $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson(['color', 'country']);
81         $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson([]);
82     }
83
84     public function test_tags_shown_on_search_listing()
85     {
86         $tags = [
87             Tag::factory()->make(['name' => 'category', 'value' => 'buckets']),
88             Tag::factory()->make(['name' => 'color', 'value' => 'red']),
89         ];
90
91         $page = $this->getEntityWithTags(Page::class, $tags);
92         $resp = $this->asEditor()->get('/search?term=[category]');
93         $resp->assertSee($page->name);
94         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'category');
95         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'buckets');
96         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'color');
97         $this->withHtml($resp)->assertElementContains('[href="' . $page->getUrl() . '"]', 'red');
98     }
99
100     public function test_tags_index_shows_tag_name_as_expected_with_right_counts()
101     {
102         $page = $this->entities->page();
103         $page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
104         $page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
105
106         $resp = $this->asEditor()->get('/tags');
107         $resp->assertSee('Category');
108         $html = $this->withHtml($resp);
109         $html->assertElementCount('.tag-item', 1);
110         $resp->assertDontSee('GreatTestContent');
111         $resp->assertDontSee('OtherTestContent');
112         $html->assertElementContains('a[title="Total tag usages"]', '2');
113         $html->assertElementContains('a[title="Assigned to Pages"]', '2');
114         $html->assertElementContains('a[title="Assigned to Books"]', '0');
115         $html->assertElementContains('a[title="Assigned to Chapters"]', '0');
116         $html->assertElementContains('a[title="Assigned to Shelves"]', '0');
117         $html->assertElementContains('a[href$="/tags?name=Category"]', '2 unique values');
118
119         $book = $this->entities->book();
120         $book->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
121         $resp = $this->asEditor()->get('/tags');
122         $this->withHtml($resp)->assertElementContains('a[title="Total tag usages"]', '3');
123         $this->withHtml($resp)->assertElementContains('a[title="Assigned to Books"]', '1');
124         $this->withHtml($resp)->assertElementContains('a[href$="/tags?name=Category"]', '2 unique values');
125     }
126
127     public function test_tag_index_can_be_searched()
128     {
129         $page = $this->entities->page();
130         $page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
131
132         $resp = $this->asEditor()->get('/tags?search=cat');
133         $this->withHtml($resp)->assertElementContains('.tag-item .tag-name', 'Category');
134
135         $resp = $this->asEditor()->get('/tags?search=content');
136         $this->withHtml($resp)->assertElementContains('.tag-item .tag-name', 'Category');
137         $this->withHtml($resp)->assertElementContains('.tag-item .tag-value', 'GreatTestContent');
138
139         $resp = $this->asEditor()->get('/tags?search=other');
140         $this->withHtml($resp)->assertElementNotExists('.tag-item .tag-name');
141     }
142
143     public function test_tag_index_search_will_show_mulitple_values_of_a_single_tag_name()
144     {
145         $page = $this->entities->page();
146         $page->tags()->create(['name' => 'Animal', 'value' => 'Catfish']);
147         $page->tags()->create(['name' => 'Animal', 'value' => 'Catdog']);
148
149         $resp = $this->asEditor()->get('/tags?search=cat');
150         $this->withHtml($resp)->assertElementContains('.tag-item .tag-value', 'Catfish');
151         $this->withHtml($resp)->assertElementContains('.tag-item .tag-value', 'Catdog');
152     }
153
154     public function test_tag_index_can_be_scoped_to_specific_tag_name()
155     {
156         $page = $this->entities->page();
157         $page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
158         $page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
159         $page->tags()->create(['name' => 'OtherTagName', 'value' => 'OtherValue']);
160
161         $resp = $this->asEditor()->get('/tags?name=Category');
162         $resp->assertSee('Category');
163         $resp->assertSee('GreatTestContent');
164         $resp->assertSee('OtherTestContent');
165         $resp->assertDontSee('OtherTagName');
166         $resp->assertSee('Active Filter:');
167         $this->withHtml($resp)->assertElementCount('.item-list .tag-item', 2);
168         $this->withHtml($resp)->assertElementContains('form[action$="/tags"]', 'Clear Filter');
169     }
170
171     public function test_tags_index_adheres_to_page_permissions()
172     {
173         $page = $this->entities->page();
174         $page->tags()->create(['name' => 'SuperCategory', 'value' => 'GreatTestContent']);
175
176         $resp = $this->asEditor()->get('/tags');
177         $resp->assertSee('SuperCategory');
178         $resp = $this->get('/tags?name=SuperCategory');
179         $resp->assertSee('GreatTestContent');
180
181         $this->permissions->setEntityPermissions($page, [], []);
182
183         $resp = $this->asEditor()->get('/tags');
184         $resp->assertDontSee('SuperCategory');
185         $resp = $this->get('/tags?name=SuperCategory');
186         $resp->assertDontSee('GreatTestContent');
187     }
188
189     public function test_tag_index_shows_message_on_no_results()
190     {
191         $resp = $this->asEditor()->get('/tags?search=testingval');
192         $resp->assertSee('No items available');
193         $resp->assertSee('Tags can be assigned via the page editor sidebar');
194     }
195
196     public function test_tag_classes_visible_on_entities()
197     {
198         $this->asEditor();
199
200         foreach ($this->entities->all() as $entity) {
201             $entity->tags()->create(['name' => 'My Super Tag Name', 'value' => 'An-awesome-value']);
202             $html = $this->withHtml($this->get($entity->getUrl()));
203             $html->assertElementExists('body.tag-name-mysupertagname.tag-value-anawesomevalue.tag-pair-mysupertagname-anawesomevalue');
204         }
205     }
206
207     public function test_tag_classes_are_escaped()
208     {
209         $page = $this->entities->page();
210         $page->tags()->create(['name' => '<>']);
211         $resp = $this->asEditor()->get($page->getUrl());
212         $resp->assertDontSee('tag-name-<>', false);
213         $resp->assertSee('tag-name-&lt;&gt;', false);
214     }
215 }