diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index 91cd4bd51..edad13636 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -70,7 +70,7 @@ class BookController extends Controller $book->updated_by = Auth::user()->id; $book->save(); Activity::add($book, 'book_create', $book->id); - return redirect('/books'); + return redirect($book->getUrl()); } /** diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php index 98e7a6678..dd58608cb 100644 --- a/app/Http/Controllers/ChapterController.php +++ b/app/Http/Controllers/ChapterController.php @@ -65,7 +65,7 @@ class ChapterController extends Controller $chapter->updated_by = Auth::user()->id; $book->chapters()->save($chapter); Activity::add($chapter, 'chapter_create', $book->id); - return redirect($book->getUrl()); + return redirect($chapter->getUrl()); } /** diff --git a/app/Repos/BookRepo.php b/app/Repos/BookRepo.php index 5ddf0b1ef..7f7517e92 100644 --- a/app/Repos/BookRepo.php +++ b/app/Repos/BookRepo.php @@ -35,6 +35,11 @@ class BookRepo return $this->book->where('slug', '=', $slug)->first(); } + /** + * Get a new book instance from request input. + * @param $input + * @return Book + */ public function newFromInput($input) { return $this->book->fill($input); diff --git a/config/database.php b/config/database.php index f6cf86b4c..9650de117 100644 --- a/config/database.php +++ b/config/database.php @@ -64,6 +64,18 @@ return [ 'strict' => false, ], + 'mysql_testing' => [ + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'bookstack-test', + 'username' => 'bookstack-test', + 'password' => 'bookstack-test', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + 'strict' => false, + ], + 'pgsql' => [ 'driver' => 'pgsql', 'host' => env('DB_HOST', 'localhost'), diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index bf4526f80..e68c2f293 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -13,9 +13,30 @@ $factory->define(Oxbow\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(Oxbow\Book::class, function ($faker) { + return [ + 'name' => $faker->sentence, + 'description' => $faker->paragraph + ]; +}); + +$factory->define(Oxbow\Chapter::class, function ($faker) { + return [ + 'name' => $faker->sentence, + 'description' => $faker->paragraph + ]; +}); + +$factory->define(Oxbow\Page::class, function ($faker) { + return [ + 'name' => $faker->sentence, + 'html' => '<p>' . implode('</p>', $faker->paragraphs(5)) . '</p>' + ]; +}); \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index 276262dbc..59afb8613 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -24,5 +24,6 @@ <env name="CACHE_DRIVER" value="array"/> <env name="SESSION_DRIVER" value="array"/> <env name="QUEUE_DRIVER" value="sync"/> + <env name="DB_CONNECTION" value="mysql_testing"/> </php> </phpunit> diff --git a/resources/assets/sass/_buttons.scss b/resources/assets/sass/_buttons.scss index df4712e32..373726c00 100644 --- a/resources/assets/sass/_buttons.scss +++ b/resources/assets/sass/_buttons.scss @@ -51,6 +51,17 @@ $button-border-radius: 2px; } } +.text-button { + @extend .link; + background-color: transparent; + padding: 0; + margin: 0; + border: none; + &:focus, &:active { + outline: 0; + } +} + .button-group { @include clearfix; .button, button[type="button"] { diff --git a/resources/assets/sass/_text.scss b/resources/assets/sass/_text.scss index 7015400a9..3dc48efc8 100644 --- a/resources/assets/sass/_text.scss +++ b/resources/assets/sass/_text.scss @@ -43,11 +43,13 @@ h1, h2, h3, h4 { /* * Link styling */ -a { +a, .link { color: $primary; cursor: pointer; text-decoration: none; transition: color ease-in-out 80ms; + font-family: $text; + line-height: 1.6; &:hover { text-decoration: underline; color: darken($primary, 20%); diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss index 2c3a70bcf..1c9f92d4f 100644 --- a/resources/assets/sass/styles.scss +++ b/resources/assets/sass/styles.scss @@ -309,8 +309,10 @@ h1, h2, h3, h4, h5, h6 { } .faded { - a { + a, button, span { color: #666; + } + .text-button { opacity: 0.5; transition: all ease-in-out 120ms; &:hover { @@ -324,12 +326,9 @@ h1, h2, h3, h4, h5, h6 { color: #000; font-size: 0.9em; background-color: rgba(21, 101, 192, 0.15); - a, span { - color: #000; - } } -.breadcrumbs a, .action-buttons a { +.breadcrumbs .text-button, .action-buttons .text-button { display: inline-block; padding: $-s; &:last-child { @@ -340,7 +339,7 @@ h1, h2, h3, h4, h5, h6 { text-align: right; &.text-left { text-align: left; - a { + .text-button { padding-right: $-m; padding-left: 0; } diff --git a/resources/views/books/index.blade.php b/resources/views/books/index.blade.php index 4a06a19c4..a6def8845 100644 --- a/resources/views/books/index.blade.php +++ b/resources/views/books/index.blade.php @@ -9,7 +9,7 @@ <div class="col-md-6 faded"> <div class="action-buttons"> @if($currentUser->can('book-create')) - <a href="/books/create" class="text-pos"><i class="zmdi zmdi-plus"></i>Add new book</a> + <a href="/books/create" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>Add new book</a> @endif </div> </div> diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php index 9d2841a22..faf352f68 100644 --- a/resources/views/books/show.blade.php +++ b/resources/views/books/show.blade.php @@ -8,17 +8,17 @@ <div class="col-md-12"> <div class="action-buttons faded"> @if($currentUser->can('page-create')) - <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos"><i class="zmdi zmdi-plus"></i> New Page</a> + <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Page</a> @endif @if($currentUser->can('chapter-create')) - <a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos"><i class="zmdi zmdi-plus"></i> New Chapter</a> + <a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Chapter</a> @endif @if($currentUser->can('book-update')) - <a href="{{$book->getEditUrl()}}" class="text-primary"><i class="zmdi zmdi-edit"></i>Edit</a> - <a href="{{ $book->getUrl() }}/sort" class="text-primary"><i class="zmdi zmdi-sort"></i>Sort</a> + <a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> + <a href="{{ $book->getUrl() }}/sort" class="text-primary text-button"><i class="zmdi zmdi-sort"></i>Sort</a> @endif @if($currentUser->can('book-delete')) - <a href="{{ $book->getUrl() }}/delete" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a> + <a href="{{ $book->getUrl() }}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> @endif </div> </div> diff --git a/resources/views/chapters/form.blade.php b/resources/views/chapters/form.blade.php index 7603fb443..70df4737a 100644 --- a/resources/views/chapters/form.blade.php +++ b/resources/views/chapters/form.blade.php @@ -13,5 +13,5 @@ <div class="form-group"> <a href="{{ back()->getTargetUrl() }}" class="button muted">Cancel</a> - <button type="submit" class="button pos">Save</button> + <button type="submit" class="button pos">Save Chapter</button> </div> diff --git a/resources/views/chapters/show.blade.php b/resources/views/chapters/show.blade.php index bd61500aa..fb734f163 100644 --- a/resources/views/chapters/show.blade.php +++ b/resources/views/chapters/show.blade.php @@ -7,19 +7,19 @@ <div class="row"> <div class="col-md-4 faded"> <div class="breadcrumbs"> - <a href="{{$book->getUrl()}}" class="text-book"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a> + <a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a> </div> </div> <div class="col-md-8 faded"> <div class="action-buttons"> @if($currentUser->can('chapter-create')) - <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-pos"><i class="zmdi zmdi-plus"></i>New Page</a> + <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>New Page</a> @endif @if($currentUser->can('chapter-update')) - <a href="{{$chapter->getUrl() . '/edit'}}" class="text-primary"><i class="zmdi zmdi-edit"></i>Edit</a> + <a href="{{$chapter->getUrl() . '/edit'}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> @endif @if($currentUser->can('chapter-delete')) - <a href="{{$chapter->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a> + <a href="{{$chapter->getUrl() . '/delete'}}" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> @endif </div> </div> diff --git a/resources/views/pages/form.blade.php b/resources/views/pages/form.blade.php index 5a78ba0b4..bd4351e0a 100644 --- a/resources/views/pages/form.blade.php +++ b/resources/views/pages/form.blade.php @@ -9,13 +9,13 @@ <div class="row"> <div class="col-md-4 faded"> <div class="action-buttons text-left"> - <a onclick="$('body>header').slideToggle();" class="text-primary"><i class="zmdi zmdi-swap-vertical"></i>Toggle Header</a> + <a onclick="$('body>header').slideToggle();" class="text-button text-primary"><i class="zmdi zmdi-swap-vertical"></i>Toggle Header</a> </div> </div> <div class="col-md-8 faded"> <div class="action-buttons"> - <a href="{{ back()->getTargetUrl() }}" class="text-primary"><i class="zmdi zmdi-close"></i>Cancel</a> - <a onclick="$(this).submitForm();" type="submit" class="text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</a> + <a href="{{ back()->getTargetUrl() }}" class="text-button text-primary"><i class="zmdi zmdi-close"></i>Cancel</a> + <button type="submit" class="text-button text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</button> </div> </div> </div> diff --git a/resources/views/pages/revisions.blade.php b/resources/views/pages/revisions.blade.php index ac34906f1..ab589ebaa 100644 --- a/resources/views/pages/revisions.blade.php +++ b/resources/views/pages/revisions.blade.php @@ -7,7 +7,7 @@ <div class="row"> <div class="col-md-6 faded"> <div class="breadcrumbs"> - <a href="{{$page->getUrl()}}" class="text-primary"><i class="zmdi zmdi-arrow-left"></i>Back to page</a> + <a href="{{$page->getUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-arrow-left"></i>Back to page</a> </div> </div> <div class="col-md-6 faded"> diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php index 51f615669..d7a9f6915 100644 --- a/resources/views/pages/show.blade.php +++ b/resources/views/pages/show.blade.php @@ -7,10 +7,10 @@ <div class="row"> <div class="col-md-6 faded"> <div class="breadcrumbs"> - <a href="{{$book->getUrl()}}" class="text-book"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a> + <a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a> @if($page->hasChapter()) <span class="sep">»</span> - <a href="{{ $page->chapter->getUrl() }}" class="text-chapter"> + <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button"> <i class="zmdi zmdi-collection-bookmark"></i> {{$page->chapter->name}} </a> @@ -20,11 +20,11 @@ <div class="col-md-6 faded"> <div class="action-buttons"> @if($currentUser->can('page-update')) - <a href="{{$page->getUrl() . '/revisions'}}" class="text-primary"><i class="zmdi zmdi-replay"></i>Revisions</a> - <a href="{{$page->getUrl() . '/edit'}}" class="text-primary" ><i class="zmdi zmdi-edit"></i>Edit</a> + <a href="{{$page->getUrl() . '/revisions'}}" class="text-primary text-button"><i class="zmdi zmdi-replay"></i>Revisions</a> + <a href="{{$page->getUrl() . '/edit'}}" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>Edit</a> @endif @if($currentUser->can('page-delete')) - <a href="{{$page->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a> + <a href="{{$page->getUrl() . '/delete'}}" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete</a> @endif </div> </div> diff --git a/resources/views/pages/sidebar-tree-list.blade.php b/resources/views/pages/sidebar-tree-list.blade.php index ed439e62c..9338cbab8 100644 --- a/resources/views/pages/sidebar-tree-list.blade.php +++ b/resources/views/pages/sidebar-tree-list.blade.php @@ -6,7 +6,7 @@ @foreach($book->children() as $bookChild) <li class="list-item-{{ $bookChild->getName() }}"> <a href="{{$bookChild->getUrl()}}" class="{{ $bookChild->getName() }} {{ $current->matches($bookChild)? 'selected' : '' }}"> - @if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark chapter-toggle"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }} + @if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }} </a> @if($bookChild->isA('chapter') && count($bookChild->pages) > 0) diff --git a/resources/views/settings/navbar.blade.php b/resources/views/settings/navbar.blade.php index 2824089e4..f25836493 100644 --- a/resources/views/settings/navbar.blade.php +++ b/resources/views/settings/navbar.blade.php @@ -3,8 +3,8 @@ <div class="container"> <div class="row"> <div class="col-md-12 setting-nav"> - <a href="/settings" @if($selected == 'settings') class="selected" @endif><i class="zmdi zmdi-settings"></i>Settings</a> - <a href="/users" @if($selected == 'users') class="selected" @endif><i class="zmdi zmdi-accounts"></i>Users</a> + <a href="/settings" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>Settings</a> + <a href="/users" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>Users</a> </div> </div> </div> diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index 79ad4e0fc..2e3d8618f 100644 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -9,7 +9,7 @@ <div class="col-md-6"></div> <div class="col-md-6 faded"> <div class="action-buttons"> - <a href="/users/{{$user->id}}/delete" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete User</a> + <a href="/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete User</a> </div> </div> </div> diff --git a/tests/AuthTest.php b/tests/AuthTest.php new file mode 100644 index 000000000..ad9265fc1 --- /dev/null +++ b/tests/AuthTest.php @@ -0,0 +1,32 @@ +<?php + +class AuthTest extends TestCase +{ + + public function testAuthWorking() + { + $this->visit('/') + ->seePageIs('/login'); + } + + public function testLogin() + { + $this->visit('/') + ->seePageIs('/login') + ->type('admin@admin.com', '#email') + ->type('password', '#password') + ->press('Sign In') + ->seePageIs('/') + ->see('BookStack'); + } + + public function testLogout() + { + $this->asAdmin() + ->visit('/') + ->seePageIs('/') + ->visit('/logout') + ->visit('/') + ->seePageIs('/login'); + } +} diff --git a/tests/EntityTest.php b/tests/EntityTest.php new file mode 100644 index 000000000..1cfc21773 --- /dev/null +++ b/tests/EntityTest.php @@ -0,0 +1,121 @@ +<?php + +class EntityTest extends TestCase +{ + + public function testEntityCreation() + { + + // Test Creation + $book = $this->bookCreation(); + $chapter = $this->chapterCreation($book); + $page = $this->pageCreation($chapter); + + // Test Updating + $book = $this->bookUpdate($book); + + // Test Deletion + $this->bookDelete($book); + } + + public function bookDelete(\Oxbow\Book $book) + { + $this->asAdmin() + ->visit($book->getUrl()) + // Check link works correctly + ->click('Delete') + ->seePageIs($book->getUrl() . '/delete') + // Ensure the book name is show to user + ->see($book->name) + ->press('Confirm') + ->seePageIs('/books') + ->notSeeInDatabase('books', ['id' => $book->id]); + } + + public function bookUpdate(\Oxbow\Book $book) + { + $newName = $book->name . ' Updated'; + $this->asAdmin() + // Go to edit screen + ->visit($book->getUrl() . '/edit') + ->see($book->name) + // Submit new name + ->type($newName, '#name') + ->press('Save Book') + // Check page url and text + ->seePageIs($book->getUrl() . '-updated') + ->see($newName); + + return \Oxbow\Book::find($book->id); + } + + public function pageCreation($chapter) + { + $page = factory(\Oxbow\Page::class)->make([ + 'name' => 'My First Page' + ]); + + $this->asAdmin() + // Navigate to page create form + ->visit($chapter->getUrl()) + ->click('New Page') + ->seePageIs($chapter->getUrl() . '/create-page') + // Fill out form + ->type($page->name, '#name') + ->type($page->html, '#html') + ->press('Save Page') + // Check redirect and page + ->seePageIs($chapter->book->getUrl() . '/page/my-first-page') + ->see($page->name); + + $page = \Oxbow\Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first(); + return $page; + } + + public function chapterCreation(\Oxbow\Book $book) + { + $chapter = factory(\Oxbow\Chapter::class)->make([ + 'name' => 'My First Chapter' + ]); + + $this->asAdmin() + // Navigate to chapter create page + ->visit($book->getUrl()) + ->click('New Chapter') + ->seePageIs($book->getUrl() . '/chapter/create') + // Fill out form + ->type($chapter->name, '#name') + ->type($chapter->description, '#description') + ->press('Save Chapter') + // Check redirect and landing page + ->seePageIs($book->getUrl() . '/chapter/my-first-chapter') + ->see($chapter->name)->see($chapter->description); + + $chapter = \Oxbow\Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first(); + return $chapter; + } + + public function bookCreation() + { + $book = factory(\Oxbow\Book::class)->make([ + 'name' => 'My First Book' + ]); + $this->asAdmin() + ->visit('/books') + // Choose to create a book + ->click('Add new book') + ->seePageIs('/books/create') + // Fill out form & save + ->type($book->name, '#name') + ->type($book->description, '#description') + ->press('Save Book') + // Check it redirects correctly + ->seePageIs('/books/my-first-book') + ->see($book->name)->see($book->description); + + $book = \Oxbow\Book::where('slug', '=', 'my-first-book')->first(); + return $book; + } + + +} diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php deleted file mode 100644 index 7e81d37aa..000000000 --- a/tests/ExampleTest.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -use Illuminate\Foundation\Testing\WithoutMiddleware; -use Illuminate\Foundation\Testing\DatabaseMigrations; -use Illuminate\Foundation\Testing\DatabaseTransactions; - -class ExampleTest extends TestCase -{ - /** - * A basic functional test example. - * - * @return void - */ - public function testBasicExample() - { - $this->visit('/') - ->see('Laravel 5'); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 8578b17e4..d48bb3ef2 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,13 +1,19 @@ <?php +use Illuminate\Foundation\Testing\DatabaseTransactions; + class TestCase extends Illuminate\Foundation\Testing\TestCase { + + use DatabaseTransactions; + /** * The base URL to use while testing the application. * * @var string */ protected $baseUrl = 'http://localhost'; + private $admin; /** * Creates the application. @@ -22,4 +28,12 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase return $app; } + + public function asAdmin() + { + if($this->admin === null) { + $this->admin = \Oxbow\User::find(1); + } + return $this->actingAs($this->admin); + } }