Dan Brown

Added more tests to increase test coverage

...@@ -31,11 +31,7 @@ abstract class Entity extends Model ...@@ -31,11 +31,7 @@ abstract class Entity extends Model
31 31
32 if ($matches) return true; 32 if ($matches) return true;
33 33
34 - if ($entity->isA('chapter') && $this->isA('book')) { 34 + if (($entity->isA('chapter') || $entity->isA('page')) && $this->isA('book')) {
35 - return $entity->book_id === $this->id;
36 - }
37 -
38 - if ($entity->isA('page') && $this->isA('book')) {
39 return $entity->book_id === $this->id; 35 return $entity->book_id === $this->id;
40 } 36 }
41 37
...@@ -65,15 +61,6 @@ abstract class Entity extends Model ...@@ -65,15 +61,6 @@ abstract class Entity extends Model
65 } 61 }
66 62
67 /** 63 /**
68 - * Get just the views for the current user.
69 - * @return mixed
70 - */
71 - public function userViews()
72 - {
73 - return $this->views()->where('user_id', '=', auth()->user()->id);
74 - }
75 -
76 - /**
77 * Allows checking of the exact class, Used to check entity type. 64 * Allows checking of the exact class, Used to check entity type.
78 * Cleaner method for is_a. 65 * Cleaner method for is_a.
79 * @param $type 66 * @param $type
......
...@@ -62,9 +62,9 @@ class SearchController extends Controller ...@@ -62,9 +62,9 @@ class SearchController extends Controller
62 return redirect()->back(); 62 return redirect()->back();
63 } 63 }
64 $searchTerm = $request->get('term'); 64 $searchTerm = $request->get('term');
65 - $whereTerm = [['book_id', '=', $bookId]]; 65 + $searchWhereTerms = [['book_id', '=', $bookId]];
66 - $pages = $this->pageRepo->getBySearch($searchTerm, $whereTerm); 66 + $pages = $this->pageRepo->getBySearch($searchTerm, $searchWhereTerms);
67 - $chapters = $this->chapterRepo->getBySearch($searchTerm, $whereTerm); 67 + $chapters = $this->chapterRepo->getBySearch($searchTerm, $searchWhereTerms);
68 return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]); 68 return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
69 } 69 }
70 70
......
...@@ -116,9 +116,11 @@ class UserController extends Controller ...@@ -116,9 +116,11 @@ class UserController extends Controller
116 $this->validate($request, [ 116 $this->validate($request, [
117 'name' => 'required', 117 'name' => 'required',
118 'email' => 'required|email|unique:users,email,' . $id, 118 'email' => 'required|email|unique:users,email,' . $id,
119 - 'password' => 'min:5', 119 + 'password' => 'min:5|required_with:password_confirm',
120 - 'password-confirm' => 'same:password', 120 + 'password-confirm' => 'same:password|required_with:password',
121 'role' => 'exists:roles,id' 121 'role' => 'exists:roles,id'
122 + ], [
123 + 'password-confirm.required_with' => 'Password confirmation required'
122 ]); 124 ]);
123 125
124 $user = $this->user->findOrFail($id); 126 $user = $this->user->findOrFail($id);
...@@ -132,6 +134,7 @@ class UserController extends Controller ...@@ -132,6 +134,7 @@ class UserController extends Controller
132 $password = $request->get('password'); 134 $password = $request->get('password');
133 $user->password = bcrypt($password); 135 $user->password = bcrypt($password);
134 } 136 }
137 +
135 $user->save(); 138 $user->save();
136 return redirect('/users'); 139 return redirect('/users');
137 } 140 }
......
...@@ -43,6 +43,16 @@ class Role extends Model ...@@ -43,6 +43,16 @@ class Role extends Model
43 */ 43 */
44 public static function getDefault() 44 public static function getDefault()
45 { 45 {
46 - return static::where('name', '=', static::$default)->first(); 46 + return static::getRole(static::$default);
47 + }
48 +
49 + /**
50 + * Get the role object for the specified role.
51 + * @param $roleName
52 + * @return mixed
53 + */
54 + public static function getRole($roleName)
55 + {
56 + return static::where('name', '=', $roleName)->first();
47 } 57 }
48 } 58 }
......
...@@ -12,7 +12,7 @@ class DummyContentSeeder extends Seeder ...@@ -12,7 +12,7 @@ class DummyContentSeeder extends Seeder
12 public function run() 12 public function run()
13 { 13 {
14 $user = factory(BookStack\User::class, 1)->create(); 14 $user = factory(BookStack\User::class, 1)->create();
15 - $role = \BookStack\Role::where('name', '=', 'admin')->first(); 15 + $role = \BookStack\Role::getDefault();
16 $user->attachRole($role); 16 $user->attachRole($role);
17 17
18 18
......
...@@ -26,6 +26,6 @@ ...@@ -26,6 +26,6 @@
26 <env name="QUEUE_DRIVER" value="sync"/> 26 <env name="QUEUE_DRIVER" value="sync"/>
27 <env name="DB_CONNECTION" value="mysql_testing"/> 27 <env name="DB_CONNECTION" value="mysql_testing"/>
28 <env name="MAIL_PRETEND" value="true"/> 28 <env name="MAIL_PRETEND" value="true"/>
29 - <env name="DISABLE_EXTERNAL_SERVICES" value="true"/> 29 + <env name="DISABLE_EXTERNAL_SERVICES" value="false"/>
30 </php> 30 </php>
31 </phpunit> 31 </phpunit>
......
...@@ -127,7 +127,7 @@ module.exports = function (ngApp) { ...@@ -127,7 +127,7 @@ module.exports = function (ngApp) {
127 }]); 127 }]);
128 128
129 129
130 - ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', function ($scope, $http, $attrs) { 130 + ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', '$sce', function ($scope, $http, $attrs, $sce) {
131 $scope.searching = false; 131 $scope.searching = false;
132 $scope.searchTerm = ''; 132 $scope.searchTerm = '';
133 $scope.searchResults = ''; 133 $scope.searchResults = '';
...@@ -141,7 +141,7 @@ module.exports = function (ngApp) { ...@@ -141,7 +141,7 @@ module.exports = function (ngApp) {
141 var searchUrl = '/search/book/' + $attrs.bookId; 141 var searchUrl = '/search/book/' + $attrs.bookId;
142 searchUrl += '?term=' + encodeURIComponent(term); 142 searchUrl += '?term=' + encodeURIComponent(term);
143 $http.get(searchUrl).then((response) => { 143 $http.get(searchUrl).then((response) => {
144 - $scope.searchResults = response.data; 144 + $scope.searchResults = $sce.trustAsHtml(response.data);
145 }); 145 });
146 }; 146 };
147 147
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
9 <div class="col-md-6"></div> 9 <div class="col-md-6"></div>
10 <div class="col-md-6 faded"> 10 <div class="col-md-6 faded">
11 <div class="action-buttons"> 11 <div class="action-buttons">
12 - <a href="/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete user</a> 12 + <a href="/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete User</a>
13 </div> 13 </div>
14 </div> 14 </div>
15 </div> 15 </div>
......
...@@ -102,10 +102,10 @@ class AuthTest extends TestCase ...@@ -102,10 +102,10 @@ class AuthTest extends TestCase
102 ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]); 102 ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]);
103 } 103 }
104 104
105 - public function testUserControl() 105 + public function testUserCreation()
106 { 106 {
107 $user = factory(\BookStack\User::class)->make(); 107 $user = factory(\BookStack\User::class)->make();
108 - // Test creation 108 +
109 $this->asAdmin() 109 $this->asAdmin()
110 ->visit('/users') 110 ->visit('/users')
111 ->click('Add new user') 111 ->click('Add new user')
...@@ -118,9 +118,12 @@ class AuthTest extends TestCase ...@@ -118,9 +118,12 @@ class AuthTest extends TestCase
118 ->seeInDatabase('users', $user->toArray()) 118 ->seeInDatabase('users', $user->toArray())
119 ->seePageIs('/users') 119 ->seePageIs('/users')
120 ->see($user->name); 120 ->see($user->name);
121 - $user = $user->where('email', '=', $user->email)->first(); 121 + }
122 122
123 - // Test editing 123 + public function testUserUpdating()
124 + {
125 + $user = \BookStack\User::all()->last();
126 + $password = $user->password;
124 $this->asAdmin() 127 $this->asAdmin()
125 ->visit('/users') 128 ->visit('/users')
126 ->click($user->name) 129 ->click($user->name)
...@@ -129,20 +132,58 @@ class AuthTest extends TestCase ...@@ -129,20 +132,58 @@ class AuthTest extends TestCase
129 ->type('Barry Scott', '#name') 132 ->type('Barry Scott', '#name')
130 ->press('Save') 133 ->press('Save')
131 ->seePageIs('/users') 134 ->seePageIs('/users')
132 - ->seeInDatabase('users', ['id' => $user->id, 'name' => 'Barry Scott']) 135 + ->seeInDatabase('users', ['id' => $user->id, 'name' => 'Barry Scott', 'password' => $password])
133 ->notSeeInDatabase('users', ['name' => $user->name]); 136 ->notSeeInDatabase('users', ['name' => $user->name]);
134 - $user = $user->find($user->id); 137 + }
138 +
139 + public function testUserPasswordUpdate()
140 + {
141 + $user = \BookStack\User::all()->last();
142 + $userProfilePage = '/users/' . $user->id;
143 + $this->asAdmin()
144 + ->visit($userProfilePage)
145 + ->type('newpassword', '#password')
146 + ->press('Save')
147 + ->seePageIs($userProfilePage)
148 + ->see('Password confirmation required')
149 +
150 + ->type('newpassword', '#password')
151 + ->type('newpassword', '#password-confirm')
152 + ->press('Save')
153 + ->seePageIs('/users');
154 +
155 + $userPassword = \BookStack\User::find($user->id)->password;
156 + $this->assertTrue(Hash::check('newpassword', $userPassword));
157 + }
158 +
159 + public function testUserDeletion()
160 + {
161 + $userDetails = factory(\BookStack\User::class)->make();
162 + $user = $this->getNewUser($userDetails->toArray());
135 163
136 - // Test Deletion
137 $this->asAdmin() 164 $this->asAdmin()
138 ->visit('/users/' . $user->id) 165 ->visit('/users/' . $user->id)
139 - ->click('Delete user') 166 + ->click('Delete User')
140 ->see($user->name) 167 ->see($user->name)
141 ->press('Confirm') 168 ->press('Confirm')
142 ->seePageIs('/users') 169 ->seePageIs('/users')
143 ->notSeeInDatabase('users', ['name' => $user->name]); 170 ->notSeeInDatabase('users', ['name' => $user->name]);
144 } 171 }
145 172
173 + public function testUserCannotBeDeletedIfLastAdmin()
174 + {
175 + $adminRole = \BookStack\Role::getRole('admin');
176 + // Ensure we currently only have 1 admin user
177 + $this->assertEquals(1, $adminRole->users()->count());
178 + $user = $adminRole->users->first();
179 +
180 + $this->asAdmin()->visit('/users/' . $user->id)
181 + ->click('Delete User')
182 + ->press('Confirm')
183 + ->seePageIs('/users/' . $user->id)
184 + ->see('You cannot delete the only admin');
185 + }
186 +
146 public function testLogout() 187 public function testLogout()
147 { 188 {
148 $this->asAdmin() 189 $this->asAdmin()
......
...@@ -188,6 +188,29 @@ class EntityTest extends TestCase ...@@ -188,6 +188,29 @@ class EntityTest extends TestCase
188 ->seePageIs('/'); 188 ->seePageIs('/');
189 } 189 }
190 190
191 + public function testBookSearch()
192 + {
193 + $book = \BookStack\Book::all()->first();
194 + $page = $book->pages->last();
195 + $chapter = $book->chapters->last();
196 +
197 + $this->asAdmin()
198 + ->visit('/search/book/' . $book->id . '?term=' . urlencode($page->name))
199 + ->see($page->name)
200 +
201 + ->visit('/search/book/' . $book->id . '?term=' . urlencode($chapter->name))
202 + ->see($chapter->name);
203 + }
204 +
205 + public function testEmptyBookSearchRedirectsBack()
206 + {
207 + $book = \BookStack\Book::all()->first();
208 + $this->asAdmin()
209 + ->visit('/books')
210 + ->visit('/search/book/' . $book->id . '?term=')
211 + ->seePageIs('/books');
212 + }
213 +
191 214
192 public function testEntitiesViewableAfterCreatorDeletion() 215 public function testEntitiesViewableAfterCreatorDeletion()
193 { 216 {
......