From 62338e4a8fc3401dad8b01adf165f34a63ebe907 Mon Sep 17 00:00:00 2001 From: Dan Brown <ssddanbrown@googlemail.com> Date: Sun, 29 Nov 2015 17:33:25 +0000 Subject: [PATCH] Added further tests, Fixed speed_update issues, improved search result query count --- app/Chapter.php | 3 +- app/Entity.php | 18 +++++-- app/Http/Controllers/BookController.php | 6 ++- app/Http/Controllers/ChapterController.php | 12 ++--- app/Repos/BookRepo.php | 2 +- database/factories/ModelFactory.php | 20 ++++---- .../2015_11_26_221857_add_entity_indexes.php | 40 ++++++++-------- readme.md | 12 +++++ resources/views/base.blade.php | 4 +- resources/views/books/sort-box.blade.php | 2 +- resources/views/books/sort.blade.php | 2 +- tests/EntityTest.php | 47 ++++++++++++++++++- 12 files changed, 115 insertions(+), 53 deletions(-) diff --git a/app/Chapter.php b/app/Chapter.php index c3001b69b..b6c8684a0 100644 --- a/app/Chapter.php +++ b/app/Chapter.php @@ -3,7 +3,6 @@ class Chapter extends Entity { - protected $fillable = ['name', 'description', 'priority', 'book_id']; public function book() @@ -18,7 +17,7 @@ class Chapter extends Entity public function getUrl() { - $bookSlug = isset($this->bookSlug) ? $this->bookSlug : $this->book->slug; + $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug; return '/books/' . $bookSlug. '/chapter/' . $this->slug; } diff --git a/app/Entity.php b/app/Entity.php index 1a8b02a4b..68c773592 100644 --- a/app/Entity.php +++ b/app/Entity.php @@ -69,19 +69,18 @@ abstract class Entity extends Model * @param $type * @return bool */ - public function isA($type) + public static function isA($type) { - return $this->getName() === strtolower($type); + return static::getName() === strtolower($type); } /** * Gets the class name. * @return string */ - public function getName() + public static function getName() { - $fullClassName = get_class($this); - return strtolower(array_slice(explode('\\', $fullClassName), -1, 1)[0]); + return strtolower(array_slice(explode('\\', static::class), -1, 1)[0]); } /** @@ -102,6 +101,15 @@ abstract class Entity extends Model foreach ($wheres as $whereTerm) { $search->where($whereTerm[0], $whereTerm[1], $whereTerm[2]); } + + if (!static::isA('book')) { + $search = $search->with('book'); + } + + if(static::isA('page')) { + $search = $search->with('chapter'); + } + return $search->get(); } diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index c4173730d..6b2d6928d 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -150,14 +150,16 @@ class BookController extends Controller { $this->checkPermission('book-update'); $book = $this->bookRepo->getBySlug($bookSlug); + $bookChildren = $this->bookRepo->getChildren($book); $books = $this->bookRepo->getAll(); - return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books]); + return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]); } public function getSortItem($bookSlug) { $book = $this->bookRepo->getBySlug($bookSlug); - return view('books/sort-box', ['book' => $book]); + $bookChildren = $this->bookRepo->getChildren($book); + return view('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren]); } /** diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php index 42ae11355..1fe1e8b3e 100644 --- a/app/Http/Controllers/ChapterController.php +++ b/app/Http/Controllers/ChapterController.php @@ -33,7 +33,6 @@ class ChapterController extends Controller /** * Show the form for creating a new chapter. - * * @param $bookSlug * @return Response */ @@ -46,7 +45,6 @@ class ChapterController extends Controller /** * Store a newly created chapter in storage. - * * @param $bookSlug * @param Request $request * @return Response @@ -62,8 +60,8 @@ class ChapterController extends Controller $chapter = $this->chapterRepo->newFromInput($request->all()); $chapter->slug = $this->chapterRepo->findSuitableSlug($chapter->name, $book->id); $chapter->priority = $this->bookRepo->getNewPriority($book); - $chapter->created_by = Auth::user()->id; - $chapter->updated_by = Auth::user()->id; + $chapter->created_by = auth()->user()->id; + $chapter->updated_by = auth()->user()->id; $book->chapters()->save($chapter); Activity::add($chapter, 'chapter_create', $book->id); return redirect($chapter->getUrl()); @@ -71,7 +69,6 @@ class ChapterController extends Controller /** * Display the specified chapter. - * * @param $bookSlug * @param $chapterSlug * @return Response @@ -87,7 +84,6 @@ class ChapterController extends Controller /** * Show the form for editing the specified chapter. - * * @param $bookSlug * @param $chapterSlug * @return Response @@ -102,7 +98,6 @@ class ChapterController extends Controller /** * Update the specified chapter in storage. - * * @param Request $request * @param $bookSlug * @param $chapterSlug @@ -115,7 +110,7 @@ class ChapterController extends Controller $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); $chapter->fill($request->all()); $chapter->slug = $this->chapterRepo->findSuitableSlug($chapter->name, $book->id, $chapter->id); - $chapter->updated_by = Auth::user()->id; + $chapter->updated_by = auth()->user()->id; $chapter->save(); Activity::add($chapter, 'chapter_update', $book->id); return redirect($chapter->getUrl()); @@ -137,7 +132,6 @@ class ChapterController extends Controller /** * Remove the specified chapter from storage. - * * @param $bookSlug * @param $chapterSlug * @return Response diff --git a/app/Repos/BookRepo.php b/app/Repos/BookRepo.php index 112971a27..9be77defe 100644 --- a/app/Repos/BookRepo.php +++ b/app/Repos/BookRepo.php @@ -141,7 +141,7 @@ class BookRepo */ public function getNewPriority($book) { - $lastElem = $book->children()->pop(); + $lastElem = $this->getChildren($book)->pop(); return $lastElem ? $lastElem->priority + 1 : 0; } diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index 31c7a3716..e0f155087 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -13,33 +13,35 @@ $factory->define(BookStack\User::class, function ($faker) { return [ - 'name' => $faker->name, - 'email' => $faker->email, - 'password' => str_random(10), + 'name' => $faker->name, + 'email' => $faker->email, + 'password' => str_random(10), 'remember_token' => str_random(10), ]; }); $factory->define(BookStack\Book::class, function ($faker) { return [ - 'name' => $faker->sentence, - 'slug' => str_random(10), + 'name' => $faker->sentence, + 'slug' => str_random(10), 'description' => $faker->paragraph ]; }); $factory->define(BookStack\Chapter::class, function ($faker) { return [ - 'name' => $faker->sentence, - 'slug' => str_random(10), + 'name' => $faker->sentence, + 'slug' => str_random(10), 'description' => $faker->paragraph ]; }); $factory->define(BookStack\Page::class, function ($faker) { + $html = '<p>' . implode('</p>', $faker->paragraphs(5)) . '</p>'; return [ 'name' => $faker->sentence, - 'slug' => str_random(10), - 'html' => '<p>' . implode('</p>', $faker->paragraphs(5)) . '</p>' + 'slug' => str_random(10), + 'html' => $html, + 'text' => strip_tags($html) ]; }); diff --git a/database/migrations/2015_11_26_221857_add_entity_indexes.php b/database/migrations/2015_11_26_221857_add_entity_indexes.php index 4e68dd68e..30f978cad 100644 --- a/database/migrations/2015_11_26_221857_add_entity_indexes.php +++ b/database/migrations/2015_11_26_221857_add_entity_indexes.php @@ -54,36 +54,36 @@ class AddEntityIndexes extends Migration public function down() { Schema::table('books', function (Blueprint $table) { - $table->dropIndex('slug'); - $table->dropIndex('created_by'); - $table->dropIndex('updated_by'); + $table->dropIndex('books_slug_index'); + $table->dropIndex('books_created_by_index'); + $table->dropIndex('books_updated_by_index'); }); Schema::table('pages', function (Blueprint $table) { - $table->dropIndex('slug'); - $table->dropIndex('book_id'); - $table->dropIndex('chapter_id'); - $table->dropIndex('priority'); - $table->dropIndex('created_by'); - $table->dropIndex('updated_by'); + $table->dropIndex('pages_slug_index'); + $table->dropIndex('pages_book_id_index'); + $table->dropIndex('pages_chapter_id_index'); + $table->dropIndex('pages_priority_index'); + $table->dropIndex('pages_created_by_index'); + $table->dropIndex('pages_updated_by_index'); }); Schema::table('page_revisions', function (Blueprint $table) { - $table->dropIndex('page_id'); + $table->dropIndex('page_revisions_page_id_index'); }); Schema::table('chapters', function (Blueprint $table) { - $table->dropIndex('slug'); - $table->dropIndex('book_id'); - $table->dropIndex('priority'); - $table->dropIndex('created_by'); - $table->dropIndex('updated_by'); + $table->dropIndex('chapters_slug_index'); + $table->dropIndex('chapters_book_id_index'); + $table->dropIndex('chapters_priority_index'); + $table->dropIndex('chapters_created_by_index'); + $table->dropIndex('chapters_updated_by_index'); }); Schema::table('activities', function (Blueprint $table) { - $table->dropIndex('book_id'); - $table->dropIndex('user_id'); - $table->dropIndex('entity_id'); + $table->dropIndex('activities_book_id_index'); + $table->dropIndex('activities_user_id_index'); + $table->dropIndex('activities_entity_id_index'); }); Schema::table('views', function (Blueprint $table) { - $table->dropIndex('user_id'); - $table->dropIndex('entity_id'); + $table->dropIndex('views_user_id_index'); + $table->dropIndex('views_viewable_id_index'); }); } } diff --git a/readme.md b/readme.md index 227c6210a..d2791f6e4 100644 --- a/readme.md +++ b/readme.md @@ -55,6 +55,18 @@ location / { } ``` +## Testing + +BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. To use you will need PHPUnit installed and accessible via command line. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the following database name, user name and password defined as `bookstack-test`. You will have to create that database and credentials before testing. + +The testing database will also need migrating and seeding beforehand. This can be done with the following commands: + +``` +php artisan migrate --database=mysql_testing +php artisan db:seed --class=DummyContentSeeder --database=mysql_testing +``` + +Once done you can run `phpunit` in the application root directory to run all tests. ## License diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php index 6bbe434b4..f9dc81fa3 100644 --- a/resources/views/base.blade.php +++ b/resources/views/base.blade.php @@ -47,8 +47,8 @@ </div> <div class="col-lg-4 col-sm-3 text-center"> <form action="/search/all" method="GET" class="search-box"> - <input type="text" name="term" tabindex="2" value="{{ isset($searchTerm) ? $searchTerm : '' }}"> - <button class="text-button"><i class="zmdi zmdi-search"></i></button> + <input id="header-search-box-input" type="text" name="term" tabindex="2" value="{{ isset($searchTerm) ? $searchTerm : '' }}"> + <button id="header-search-box-button" type="submit" class="text-button"><i class="zmdi zmdi-search"></i></button> </form> </div> <div class="col-lg-4 col-sm-5"> diff --git a/resources/views/books/sort-box.blade.php b/resources/views/books/sort-box.blade.php index 1af2d1a42..768a9f608 100644 --- a/resources/views/books/sort-box.blade.php +++ b/resources/views/books/sort-box.blade.php @@ -1,7 +1,7 @@ <div class="sort-box" data-type="book" data-id="{{ $book->id }}"> <h3 class="text-book"><i class="zmdi zmdi-book"></i>{{ $book->name }}</h3> <ul class="sortable-page-list sort-list"> - @foreach($book->children() as $bookChild) + @foreach($bookChildren as $bookChild) <li data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getName() }}" class="text-{{ $bookChild->getName() }}"> <i class="zmdi {{ $bookChild->isA('chapter') ? 'zmdi-collection-bookmark':'zmdi-file-text'}}"></i>{{ $bookChild->name }} @if($bookChild->isA('chapter')) diff --git a/resources/views/books/sort.blade.php b/resources/views/books/sort.blade.php index 5fe278128..efdc04cf0 100644 --- a/resources/views/books/sort.blade.php +++ b/resources/views/books/sort.blade.php @@ -7,7 +7,7 @@ <div class="row"> <div class="col-md-8" id="sort-boxes"> - @include('books/sort-box', ['book' => $book]) + @include('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren]) </div> diff --git a/tests/EntityTest.php b/tests/EntityTest.php index 493f99cac..7cc2d640f 100644 --- a/tests/EntityTest.php +++ b/tests/EntityTest.php @@ -51,6 +51,34 @@ class EntityTest extends TestCase return \BookStack\Book::find($book->id); } + public function testBookSortPageShows() + { + $books = \BookStack\Book::all(); + $bookToSort = $books[0]; + $this->asAdmin() + ->visit($bookToSort->getUrl()) + ->click('Sort') + ->seePageIs($bookToSort->getUrl() . '/sort') + ->seeStatusCode(200) + ->see($bookToSort->name) + // Ensure page shows other books + ->see($books[1]->name); + } + + public function testBookSortItemReturnsBookContent() + { + $books = \BookStack\Book::all(); + $bookToSort = $books[0]; + $firstPage = $bookToSort->pages[0]; + $firstChapter = $bookToSort->chapters[0]; + $this->asAdmin() + ->visit($bookToSort->getUrl() . '/sort-item') + // Ensure book details are returned + ->see($bookToSort->name) + ->see($firstPage->name) + ->see($firstChapter->name); + } + public function pageCreation($chapter) { $page = factory(\BookStack\Page::class)->make([ @@ -118,12 +146,29 @@ class EntityTest extends TestCase // Ensure duplicate names are given different slugs $this->asAdmin() ->visit('/books/create') - ->submitForm('Save Book', $book->toArray()) + ->type($book->name, '#name') + ->type($book->description, '#description') + ->press('Save Book') ->seePageIs('/books/my-first-book-2'); $book = \BookStack\Book::where('slug', '=', 'my-first-book')->first(); return $book; } + public function testPageSearch() + { + $book = \BookStack\Book::all()->first(); + $page = $book->pages->first(); + + $this->asAdmin() + ->visit('/') + ->type($page->name, 'term') + ->press('header-search-box-button') + ->see('Search Results') + ->see($page->name) + ->click($page->name) + ->seePageIs($page->getUrl()); + } + }