<?php

namespace Tests\Feature;

use Illuminate\Support\Str;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\URL;

use App\Events\PageSaved;
use App\Events\BlogSaved;
use App\Events\CourseSaved;
use App\Events\ContentElementCreated;
use App\Events\ContentElementSaved;
use App\Events\ContentElementRemoved;

use App\Models\User;
use App\Models\TextBlock;
use App\Models\Tag;
use App\Models\Blog;
use App\Models\Page;
use App\Models\ContentElement;
use App\Models\Version;
use App\Models\Role;
use App\Models\Contentable;
use App\Models\PageSlug;

use App\Mail\PublishingRequest;
use App\Mail\PublishingCompleted;

trait PagesTestTrait
{
    abstract protected function getModel();
    abstract protected function getClassname();

    public function test_a_page_can_be_loaded_via_ajax()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $text_block = $content_element->content;
        $page = $content_element->{Str::plural($this->getClassname())}->first();

        $this->withoutExceptionHandling();

        $this->json('GET', route('pages.load', ['page' => $page->full_slug]))
            ->assertSuccessful()
            ->assertJsonFragment([
                'body' => $text_block->body,
                'type' => $page->type,
            ]);
        $this->assertTrue(editing());
    }

    public function test_a_page_can_be_set_to_unlisted_and_not_unlisted()
    {
        $page = $this->getModel();
        $page->publish();

        $version = $page->version;

        $version->unlisted = false;
        $version->save();
        $version->refresh();
        $page->refresh();
        cache()->tags([cache_name($page)])->flush();

        $this->assertFalse($version->unlisted);
        $this->assertFalse($page->version->unlisted);

        $this->json('POST', route(Str::plural($this->getClassname()).'.unlist', ['id' => $page->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->withoutExceptionHandling();
        $this->json('POST', route(Str::plural($this->getClassname()).'.unlist', ['id' => $page->id]))
            ->assertStatus(403);

        $this->signInAdmin();
        $this->enableEditing();

        $this->json('POST', route(Str::plural($this->getClassname()).'.unlist', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Hidden',
             ]);

        $page->refresh();
        cache()->tags([cache_name($page)])->flush();

        $draft_version = $page->version;
        $this->assertNotEquals($version->id, $draft_version->id);

        $this->assertTrue($page->version->unlisted);

        $this->json('POST', route(Str::plural($this->getClassname()).'.reveal', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Revealed',
             ]);

        $page->refresh();

        $this->assertFalse($page->version->unlisted);
    }

    public function test_when_a_page_is_saved_an_event_is_broadcast()
    {
        Event::fake();

        $page = $this->getModel();
        $input['version'] = Version::factory()->{$this->getClassname()}()->raw();

        $this->signInAdmin();
        $this->enableEditing();

        //$this->withoutExceptionHandling();
        $this->postJson(route(Str::plural($this->getClassname()).'.update', ['id' => $page->id]), $input)
            //->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
                'full_slug' => $page->refresh()->full_slug,
            ]);

        $page->refresh();

        if ($this->getClassname() === 'page') {
            Event::assertDispatched(function (PageSaved $event) use ($page) {
                return $event->{$this->getClassname()}->id === $page->id;
            });
        } elseif ($this->getClassname() === 'blog') {
            Event::assertDispatched(function (BlogSaved $event) use ($page) {
                return $event->{$this->getClassname()}->id === $page->id;
            });
        } elseif ($this->getClassname() === 'course') {
            Event::assertDispatched(function (CourseSaved $event) use ($page) {
                return $event->{$this->getClassname()}->id === $page->id;
            });
        }
    }

    public function test_loading_a_page_includes_its_tags()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $text_block = $content_element->content;
        $page = $content_element->{Str::plural($this->getClassname())}->first();

        $tag_name = $this->faker->firstName;
        $page->addTag($tag_name);
        $page->refresh();

        $this->assertEquals(1, $page->tags->count());

        $this->withoutExceptionHandling();

        $this->json('GET', route('pages.load', ['page' => $page->full_slug]))
            ->assertSuccessful()
            ->assertJsonFragment([
                'body' => $text_block->body,
                'type' => $page->type,
                'name' => $tag_name,
            ]);
        $this->assertTrue(editing());
    }

    public function test_a_pages_tags_are_saved_on_update()
    {
        $page = $this->getModel();
        $input['version'] = Version::factory()->{$this->getClassname()}()->raw();

        $tag1 = Tag::factory()->create();
        $tag2 = Tag::factory()->create();
        $tag3 = Tag::factory()->create();

        $input['tags'] = [
            $tag1,
            $tag2,
        ];

        $this->signInAdmin();
        $this->enableEditing();

        $this->withoutExceptionHandling();

        $this->postJson(route(Str::plural($this->getClassname()).'.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $page->refresh();

        $this->assertTrue($page->tags->contains('id', $tag1->id));
        $this->assertTrue($page->tags->contains('id', $tag2->id));

        $input['tags'] = [
            $tag1,
            $tag3,
        ];

        $this->postJson(route(Str::plural($this->getClassname()).'.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $page->refresh();

        $this->assertTrue($page->tags->contains('id', $tag1->id));
        $this->assertFalse($page->tags->contains('id', $tag2->id));
        $this->assertTrue($page->tags->contains('id', $tag3->id));

        $input = array();
        $input['version'] = Version::factory()->{$this->getClassname()}()->raw();

        $this->postJson(route(Str::plural($this->getClassname()).'.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $page->refresh();

        $this->assertEquals(0, $page->tags->count());
    }

    public function test_a_page_can_be_rendered()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $text_block = $content_element->content;
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $page->publish();

        $this->withoutExceptionHandling();
        $this->json('GET', route('pages.load', ['page' => $page->full_slug, 'render' => 'true']))
            ->assertSuccessful();
    }

    public function test_a_content_elements_draft_can_be_removed_from_a_page()
    {
        $page = $this->getModel();
        $page->publish();

        $published_content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create();

        $published_content_element->{Str::plural($this->getClassname())}()->detach();
        $published_content_element->{Str::plural($this->getClassname())}()->attach($page, ['sort_order' => $this->faker->randomNumber(1), 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->published_version_id]);

        $this->assertInstanceOf(get_class($page), $published_content_element->{Str::plural($this->getClassname())}()->first());
        $page = $published_content_element->{Str::plural($this->getClassname())}()->first();

        $this->assertNotNull($page->publishedVersion);
        $this->assertInstanceOf(Version::class, $page->publishedVersion);

        $content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create([
            'uuid' => $published_content_element->uuid,
        ]);

        $content_element->{Str::plural($this->getClassname())}()->detach();
        $content_element->{Str::plural($this->getClassname())}()->attach($page, ['sort_order' => $this->faker->randomNumber(1), 'unlisted' => false, 'randomize' => false, 'no_margin' => false, 'guest' => false, 'version_id' => $page->draft_version_id]);

        $page->refresh();

        $this->assertEquals(2, $page->contentElements()->count());

        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]))
            ->assertStatus(422)
            ->assertJsonValidationErrors(['pivot.contentable_id', 'pivot.contentable_type']);

        $input = [];
        $input['pivot'] = [
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
        ];

        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), $input)
            ->assertStatus(403);

        $this->assertEquals(1, ContentElement::where('id', $content_element->id)->get()->count());
        $this->signInAdmin();
        $this->enableEditing();

        $content_element_id = $content_element->id;

        $this->assertNotNull($content_element->getPreviousVersion($page));
        $this->assertEquals($published_content_element->id, $content_element->getPreviousVersion($page)->id);

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Version Removed',
                'id' => $published_content_element->id,
                'uuid' => $content_element->uuid,
            ]);

        $this->assertEquals(0, ContentElement::where('id', $content_element->id)->get()->count());
        $this->assertEquals(0, Contentable::where('content_element_id', $content_element_id)->get()->count());
    }

    public function test_a_content_element_can_be_restored()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $content_element->delete();
        $this->assertEquals(0, ContentElement::where('id', $content_element->id)->get()->count());

        $this->json('POST', route('content-elements.restore', ['id' => $content_element->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.restore', ['id' => $content_element->id]))
            ->assertStatus(403);

        $this->signInAdmin();

        $this->json('POST', route('content-elements.restore', ['id' => $content_element->id]))
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Restored',
            ]);

        $this->assertEquals(1, ContentElement::where('id', $content_element->id)->get()->count());
    }

    /* this is replaced by the test below
    public function a_content_element_can_be_completely_removed()
    {
        $page = $this->getModel();
        $page->publish();
        $published_content_element = $this->createContentElement(TextBlock::factory(), $page, $page->publishedVersion);

        $content_element = ContentElement::factory()->for(TextBlock::factory(), 'content')->create([
            'uuid' => $published_content_element->uuid,
        ]);

        $content_element->pages()->detach();
        $content_element->pages()->attach($page, ['sort_order' => $this->faker->randomNumber(1), 'unlisted' => false, 'version_id' => $page->draft_version_id]);

        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), ['remove_all' => true])
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), ['remove_all' => true])
             ->assertStatus(422)
             ->assertJsonValidationErrors(['pivot.contentable_id', 'pivot.contentable_type']);

        $input = ['remove_all' => 'true'];
        $input['pivot'] = [
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
        ];

        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), $input)
            ->assertStatus(403);

        $this->assertEquals(1, ContentElement::where('id', $content_element->id)->get()->count());
        $this->signInAdmin();

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Removed From Page',
            ]);

        $page->refresh();

        $this->assertEquals(0, ContentElement::where('id', $content_element->id)->get()->count());
        $this->assertEquals(0, ContentElement::where('id', $published_content_element->id)->get()->count());

        $this->assertFalse($page->all_content_elements->contains('id', $content_element->id));
    }
     */

    public function test_a_content_element_is_only_removed_from_the_current_version_of_a_page_when_completely_removing_it_not_just_a_draft()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();

        $header = $content_element->content->header;
        $this->assertNotNull($header);

        $page->publish();

        $input = $content_element->toArray();
        $text_block_input = TextBlock::factory()->raw();
        $input['content']['header'] = Arr::get($text_block_input, 'header');
        $input['content']['body'] = Arr::get($text_block_input, 'body');
        $input['content']['style'] = Arr::get($text_block_input, 'style');
        $input['pivot'] = $this->getContentableArray($page);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => null,
            'guest' => false,
        ];
         */

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $page->refresh();

        $this->assertEquals(2, $page->contentElements()->count());
        $this->assertEquals(2, $page->versions()->count());

        $new_content_element = $page->contentElements()->get()->last();
        $this->assertNotEquals($content_element->id, $new_content_element->id);

        $contentable = $new_content_element->contentables()->first();

        $this->assertInstanceOf(Contentable::class, $contentable);

        // remove the draft we just created
        $input['pivot'] = [
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
        ];

        $this->assertEquals(1, ContentElement::where('id', $content_element->id)->get()->count());
        $contentable->refresh();
        $this->assertNull($contentable->version->published_at);

        $this->json('POST', route('content-elements.remove', ['id' => $new_content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Version Removed',
            ]);

        $page->refresh();
        $this->assertEquals(0, ContentElement::where('id', $new_content_element->id)->get()->count());

        $this->json('GET', $page->full_slug)
             ->assertSuccessful()
             ->assertJsonMissing([
                'header' => Arr::get($text_block_input, 'header'),
             ])
             ->assertJsonFragment([
                'header' => $header,
             ]);

        $this->get($page->full_slug)
             ->assertSuccessful()
             ->assertDontSee(Arr::get($text_block_input, 'header'));

        $text_block_input2 = TextBlock::factory()->raw();
        $input['content']['header'] = Arr::get($text_block_input2, 'header');
        $input['content']['body'] = Arr::get($text_block_input2, 'body');
        $input['content']['style'] = Arr::get($text_block_input2, 'style');
        $input['pivot'] = $this->getContentableArray($page);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => null,
            'guest' => false,
        ];
         */

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $page->refresh();

        $this->assertEquals(2, $page->contentElements()->count());
        $this->assertEquals(2, $page->versions()->count());

        $newest_content_element = $page->contentElements()->get()->last();
        $this->assertNotEquals($content_element->id, $newest_content_element->id);

        $newest_contentable = $newest_content_element->contentables()->first();

        // remove the content element completely from the page
        $input = ['remove_all' => 'true'];
        $input['pivot'] = [
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
        ];

        $this->json('POST', route('content-elements.remove', ['id' => $newest_content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Removed From Page',
            ]);

        $page->refresh();
        $newest_contentable->refresh();
        $this->assertNotNull($newest_contentable->deleted_at);

        $this->json('GET', $page->full_slug)
             ->assertSuccessful()
             ->assertJsonMissing([
                'header' => Arr::get($text_block_input2, 'header'),
                'header' => $header,
             ]);

        $this->get($page->full_slug)
             ->assertSuccessful()
             ->assertDontSee(Arr::get($text_block_input2, 'header'))
             ->assertDontSee($header);
    }

    public function test_completely_removing_a_published_content_element_needs_a_new_draft_with_deleted_at()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $page->publish();
        $page->refresh();

        $this->assertEquals(1, $page->versions()->count());
        $published_version = $page->publishedVersion;

        $this->assertEquals(1, $page->contentElements()->count());

        $contentable = $content_element->contentables()->first();

        $this->assertInstanceOf(Contentable::class, $contentable);

        $input = ['remove_all' => 'true'];
        $input['pivot'] = [
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
        ];

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Removed From Page',
            ]);

        $page->refresh();
        $contentable->refresh();
        $content_element->refresh();

        $this->assertEquals(2, $page->versions()->count());
        $this->assertEquals(2, $page->contentElements()->count());

        $draft_content_element = $page->contentElements()->get()->last();
        $this->assertNotEquals($content_element->id, $draft_content_element->id);

        $draft_contentable = $draft_content_element->contentables()->first();
        $this->assertNotNull($draft_contentable->deleted_at);

        $this->json('POST', route(Str::plural($this->getClassname()).'.publish', ['id' => $page->id]))
             ->assertSuccessful();

        $this->json('GET', $page->full_slug)
             ->assertSuccessful()
            ->assertJsonMissing([
                'header' => $content_element->content->header,
                'header' => $draft_content_element->content->header,
            ]);
    }

    public function test_a_user_with_page_editing_can_update_content_elements()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();

        $this->assertInstanceOf(get_class($this->getModel()), $page);

        $user = User::factory()->create();
        $page->createPermission('update', $user);
        $user->refresh();

        $this->assertTrue($user->can('update', $page));

        $content_element['pivot'] = $this->getContentableArray($page);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => $this->faker->randomNumber(1),
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */
        $input = $content_element->toArray();
        $input['content'] = TextBlock::factory()->raw();

        $this->signIn($user);
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ])
             ->assertSuccessful();
    }

    public function test_a_user_with_page_editing_can_create_content_elements()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $this->assertInstanceOf(get_class($this->getModel()), $page);

        $user = User::factory()->create();
        $page->createPermission('update', $user);
        $user->refresh();

        $this->assertTrue($user->can('update', $page));

        $content_element['pivot'] = $this->getContentableArray($page);
        $content_element['pivot']['contentable_id'] = null;
        $content_element['pivot']['contentable_type'] = null;
        /*
            'sort_order' => $this->faker->randomNumber(1),
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */
        $input = $content_element->toArray();
        $input['content'] = TextBlock::factory()->raw();

        $this->signIn($user);

        $this->json('POST', route('content-elements.store'), $input)
            ->assertStatus(422)
            ->assertJsonValidationErrors([
                'pivot.contentable_id',
                'pivot.contentable_type',
            ]);

        $input['pivot']['contentable_id'] = $page->id;
        $input['pivot']['contentable_type'] = get_class($page);

        $this->json('POST', route('content-elements.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);
    }

    public function test_saving_a_new_content_element_when_there_is_already_a_draft_version_should_not_create_a_new_content_element()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $text_block = $content_element->content;
        $content_element_id = $content_element->id;
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $this->assertInstanceOf(get_class($this->getModel()), $page);
        $this->assertEquals(1, $content_element->{Str::plural($this->getClassname())}()->count());

        $this->withoutExceptionHandling();
        $this->json('POST', route(Str::plural($this->getClassname()).'.publish', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Published',
             ]);

        $page->refresh();
        $content_element->refresh();

        $this->assertEquals(1, $content_element->contentables()->count());

        $this->assertEquals($content_element->getPageVersion($page)->id, $page->publishedVersion->id);

        $content_element = ContentElement::find($content_element_id);

        $this->assertNotNull($page->published_at);
        $this->assertNotNull($content_element->getPageVersion($page)->published_at);

        $content_element['pivot'] = $this->getContentableArray($page, [
            'sort_order' => $this->faker->randomNumber(1),
        ]);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => $this->faker->randomNumber(1),
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */
        $input = $content_element->toArray();
        $input['content'] = TextBlock::factory()->raw();
        //$input['version_id'] = $page->getDraftVersion()->id;

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
                'id' => $content_element_id + 1,
             ]);

        $draft_content_element = ContentElement::all()->last();

        $this->assertNull($draft_content_element->published_at);
        $this->assertNotEquals($content_element_id, $draft_content_element->id);
        $this->assertEquals($content_element_id + 1, $draft_content_element->id);

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
                'id' => $draft_content_element->id,
             ]);

        $this->assertEquals($draft_content_element->id, ContentElement::all()->last()->id);
    }

    public function test_an_event_is_broadcast_when_a_content_element_is_saved()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $this->assertInstanceOf(get_class($this->getModel()), $page);

        Event::fake();

        $this->signInAdmin();

        $content_element['pivot'] = $this->getContentableArray($page, [
            'sort_order' => $this->faker->randomNumber(1),
        ]);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => $this->faker->randomNumber(1),
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */
        $input = $content_element->toArray();
        $input['content'] = TextBlock::factory()->raw();

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $content_element = ContentElement::all()->last();

        Event::assertDispatched(function (ContentElementSaved $event) use ($content_element) {
            return $event->content_element->id === $content_element->id;
        });
    }

    public function test_an_event_is_broadcast_when_a_content_element_is_created()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $page = $content_element->pages->first();
        $this->assertInstanceOf(Page::class, $page);

        Event::fake();

        $this->signInAdmin();

        $content_element['pivot'] = $this->getContentableArray($page, [
            'sort_order' => $this->faker->randomNumber(1),
        ]);
        $input = $content_element->toArray();
        $input['content'] = TextBlock::factory()->raw();

        $this->json('POST', route('content-elements.store'), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $content_element = ContentElement::all()->last();

        Event::assertDispatched(function (ContentElementCreated $event) use ($content_element, $page) {
            return $event->content_element->id === $content_element->id && $event->page->id === $page->id;
        });
    }

    public function test_a_content_element_can_be_loaded()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $this->assertInstanceOf(get_class($this->getModel()), $page);
        $sort_order = $page->pivot->sort_order;
        $this->assertTrue($sort_order > 0);

        $this->json('POST', route('content-elements.load', ['id' => $content_element->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route('content-elements.load', ['id' => $content_element->id]), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'pivot.contentable_id',
                'pivot.contentable_type',
             ]);

        $input = [];
        $input['pivot']['contentable_id'] = $page->id;
        $input['pivot']['contentable_type'] = get_class($page);

        $this->json('POST', route('content-elements.load', ['id' => $content_element->id]), $input)
            ->assertStatus(403);

        $this->signInAdmin();

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.load', ['id' => $content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'id' => $content_element->id,
                'sort_order' => $sort_order,
            ]);
    }

    public function test_an_event_is_broadcast_when_a_content_element_is_deleted()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $this->assertInstanceOf(get_class($this->getModel()), $page);

        Event::fake();

        $this->signInAdmin();

        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors([
                'pivot.contentable_id',
                'pivot.contentable_type',
             ]);

        $input = [];
        $input['pivot']['contentable_id'] = $page->id;
        $input['pivot']['contentable_type'] = get_class($page);

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.remove', ['id' => $content_element->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Text Block Version Removed',
                //'uuid' => $content_element->uuid,
            ]);

        Event::assertDispatched(function (ContentElementRemoved $event) use ($content_element, $page) {
            return $event->content_element->id === $content_element->id && $event->page->id === $page->id;
        });
    }

    public function test_a_content_element_can_be_instanced_onto_another_page()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $this->assertInstanceOf(get_class($this->getModel()), $page);

        $new_page = $this->getModel();
        $this->assertEquals(0, $new_page->contentElements()->count());

        $this->signInAdmin();

        $content_element['pivot'] = $this->getContentableArray($new_page);
        /*
            'contentable_id' => $new_page->id,
            'contentable_type' => get_class($new_page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */

        $input = $content_element->toArray();

        $this->assertEquals($new_page->id, Arr::get($input, 'pivot.contentable_id'));

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $new_page->refresh();
        $page->refresh();

        $this->assertEquals(1, $page->contentElements()->count());
        $this->assertEquals(1, $new_page->contentElements()->count());
        $this->assertTrue($page->contentElements()->get()->contains('id', $content_element->id));
        $this->assertTrue($new_page->contentElements()->get()->contains('id', $content_element->id));

        $input['content'] = TextBlock::factory()->raw();

        // Update the content

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $page->refresh();
        $new_page->refresh();

        $this->assertEquals(1, $page->contentElements()->count());
        $this->assertEquals(1, $new_page->contentElements()->count());
        $this->assertTrue($page->contentElements()->get()->contains('id', $content_element->id));
        $this->assertTrue($new_page->contentElements()->get()->contains('id', $content_element->id));

        // Make sure the changes show up on both pages

        $this->assertEquals(Arr::get($input, 'content.body'), $page->contentElements()->first()->content->body);
        $this->assertEquals(Arr::get($input, 'content.body'), $new_page->contentElements()->first()->content->body);
    }

    public function test_creating_a_new_version_of_a_content_element_copies_its_permissions()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $this->assertInstanceOf(get_class($this->getModel()), $page);

        $user = User::factory()->create();
        $content_element->createPermission('view', $user);

        $page->publish();

        $this->assertNotNull($content_element->content->header);
        $this->get($page->full_slug)
            ->assertDontSee($content_element->content->header);

        $this->signIn($user);

        $this->get($page->full_slug)
            ->assertSee($content_element->content->header);

        $this->signInAdmin();

        $input = $content_element->toArray();

        $input['pivot'] = $this->getContentableArray($page);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */

        $input['content']['header'] = 'foobar';

        $this->withoutExceptionHandling();
        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $content_element->refresh();
        $user->refresh();

        $this->assertNotEquals('foobar', $content_element->header);

        $new_content_element = ContentElement::all()->last();

        $this->assertEquals('foobar', $new_content_element->content->header);

        $this->assertTrue($user->canPerformAction('view', $new_content_element));

        $page->publish();

        $this->signIn($user);

        $this->get($page->full_slug)
            ->assertSee($new_content_element->content->header);
    }

    public function test_guests_cant_view_preview_content_elements()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $header = $content_element->content->header;
        $this->assertNotNull($header);
        $page->publish();
        $page->refresh();

        $this->assertTrue($page->versions()->count() > 0);
        $this->assertNotNull($page->publishedVersion);
        $this->assertNotNull($page->version);
        $this->assertNotNull($page->getSlug());

        $this->withoutExceptionHandling();
        $this->get($page->full_slug)
             ->assertSuccessful()
            ->assertSee($header);

        $this->signInAdmin();
        $this->enableEditing();

        $input = $content_element->toArray();
        $text_block_input = TextBlock::factory()->raw();
        $input['content']['header'] = Arr::get($text_block_input, 'header');
        $input['content']['body'] = Arr::get($text_block_input, 'body');
        $input['content']['style'] = Arr::get($text_block_input, 'style');
        $input['pivot'] = $this->getContentableArray($page);
        /*
            'contentable_id' => $page->id,
            'contentable_type' => get_class($page),
            'sort_order' => 1,
            'unlisted' => false,
            'expandable' => false,
            'guest' => false,
        ];
         */

        $header2 = Arr::get($text_block_input, 'header');

        $this->json('POST', route('content-elements.update', ['id' => $content_element->id]), $input)
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => 'Text Block Saved',
             ]);

        $page->refresh();

        $this->assertEquals(2, $page->contentElements()->count());
        $new_content_element = $page->contentElements()->get()->last();

        $this->json('GET', $page->full_slug)
             ->assertJsonFragment([
                'header' => $header2,
             ]);

        $this->disableEditing();
        auth()->logout();

        $this->get($page->full_slug)
            ->assertDontSee($header2);

        $this->enableEditing();
        $this->get($page->full_slug, ['preview' => 'true'])
            ->assertDontSee($header2);

        $user = User::factory()->create();
        $page->createPermission('update', $user);
        $user->refresh();
        $page->refresh();

        $this->assertTrue($user->can('update', $page));
    }

    public function test_pages_have_a_content_attribute()
    {
        $content_element = $this->createContentElement(TextBlock::factory(), $this->getModel());
        $page = $content_element->{Str::plural($this->getClassname())}->first();
        $page->publish();
        $page->refresh();

        $this->assertNotNull($page->content);
        $this->assertEquals($page->published_content_elements->first()->content->body, $page->content->first()->content->body);

        $this->withoutExceptionHandling();
        $this->get($page->full_slug)
             ->assertSuccessful()
            ->assertSee($content_element->content->header);

        $content_element2 = $this->createContentElement(TextBlock::factory(), $page);

        $page->refresh();

        $this->get($page->full_slug)
             ->assertSuccessful()
            ->assertDontSee($content_element2->content->header);

        $this->signInAdmin();
        $this->enableEditing();

        $page->refresh();

        $this->assertEquals($page->all_content_elements, $page->content);

        $this->json('GET', $page->full_slug)
             ->assertSuccessful()
            ->assertJsonFragment([
                'header' => $content_element2->content->header,
            ]);
    }

    public function test_a_page_editor_can_request_that_a_page_should_be_published()
    {
        $page = $this->getModel();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);
        $this->assertTrue($page->versions()->count() > 0);
        $page_role = Role::factory()->create();
        $page->createPermission('update', $page_role);

        $editor = User::factory()->create();
        $editor_role = Role::where('name', Str::plural($this->getClassname()).'-editor')->first();
        $this->assertInstanceOf(Role::class, $editor_role);
        $editor->addRole($editor_role);
        $editor->addRole($page_role);

        $page->refresh();
        $editor->refresh();

        $publisher = User::factory()->create();
        $page->createPermission('publish', $publisher);
        $blog_publisher = Role::where('name', 'blogs-publisher')->first();
        $announcement_publisher = Role::where('name', 'announcements-publisher')->first();
        $publisher->addRole($blog_publisher);
        $publisher->addRole($announcement_publisher);

        $this->assertTrue($editor->can('update', $page));

        $content_element = $this->createContentElement(TextBlock::factory(), $page);

        $this->json('POST', route(Str::plural($this->getClassname()).'.publishing-request', ['id' => $page->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->json('POST', route(Str::plural($this->getClassname()).'.publishing-request', ['id' => $page->id]))
            ->assertStatus(403);

        $this->signIn($editor);
        $this->enableEditing();

        $page->refresh();

        $this->assertTrue($page->versions()->count() > 0);
        $this->assertNotNull($page->version);

        Mail::fake();

        $this->withoutExceptionHandling();
        $this->json('POST', route(Str::plural($this->getClassname()).'.publishing-request', ['id' => $page->id]))
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Publishing Requested',
            ]);

        Mail::assertQueued(PublishingRequest::class, function ($mail) use ($publisher) {
            return $mail->hasTo($publisher->email);
        });

        $page->refresh();
        cache()->tags([cache_name($page)])->flush();
        $this->assertNotNull($page->version->publishing_requested_at);
        $this->assertNotNull($page->version->publishing_requested_user_id);

        $this->assertNotNull($page->version->publishingRequestedUser);
        $this->assertInstanceOf(User::class, $page->version->publishingRequestedUser);
        $this->assertEquals($editor->id, $page->version->publishingRequestedUser->id);
    }

    public function test_when_a_requested_page_is_published_an_email_is_sent_to_the_requester()
    {
        $page = $this->getModel();
        $version = $page->versions()->first();
        $this->assertInstanceOf(Version::class, $version);

        $publisher = User::factory()->create();
        $page->createPermission('publish', $publisher);
        $blog_publisher = Role::where('name', 'blogs-publisher')->first();
        $publisher->addRole($blog_publisher);
        $announcement_publisher = Role::where('name', 'announcements-publisher')->first();
        $publisher->addRole($announcement_publisher);

        $user = User::factory()->create();

        $user->addRole('pages-editor');
        $page->createPermission('update', $user);

        $page->refresh();
        $user->refresh();

        $this->assertTrue($user->can('update', $page));
        $this->assertFalse($user->can('publish', $page));

        $this->signIn($user);
        $this->enableEditing();

        Mail::fake();

        $this->json('POST', route(Str::plural($this->getClassname()).'.publishing-request', ['id' => $page->id]))
             ->assertSuccessful()
            ->assertJsonFragment([
                'success' => 'Publishing Requested',
            ]);

        Mail::assertQueued(PublishingRequest::class, function ($mail) use ($publisher) {
            return $mail->hasTo($publisher->email);
        });

        $page->refresh();
        cache()->tags([cache_name($page)])->flush();
        $this->assertNotNull($page->version->publishing_requested_at);
        $this->assertNotNull($page->version->publishing_requested_user_id);

        $this->assertNotNull($page->version->publishingRequestedUser);
        $this->assertInstanceOf(User::class, $page->version->publishingRequestedUser);
        $this->assertEquals($user->id, $page->version->publishingRequestedUser->id);


        $this->signInAdmin();
        $this->enableEditing();

        Mail::fake();

        $this->json('POST', route(Str::plural($this->getClassname()).'.publish', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Published',
             ]);

        $page->refresh();

        Mail::assertQueued(PublishingCompleted::class, function ($mail) use ($user) {
            return $mail->hasTo($user->email);
        });
    }

    public function test_adding_editing_equals_true_to_a_url_will_set_editing_mode()
    {
        $page = $this->getModel();
        $page->publish();

        $user = User::factory()->create();
        $user->addRole('pages-editor');
        $user->addRole('blogs-editor');
        $page->createPermission('update', $user);

        $page->refresh();
        $user->refresh();

        $this->assertTrue($user->can('update', $page));

        $this->get($page->full_slug.'?editing=true')
            ->assertRedirect('/login');

        $this->signIn($user);
        $this->assertTrue(auth()->user()->can('update', $page));

        $this->assertFalse(editing());

        $this->assertNotNull($page->full_slug);

        $this->get($page->full_slug.'?editing=true')
             ->assertSuccessful();
        $this->assertTrue(editing());
    }

    public function test_a_draft_page_that_is_previewed_not_in_editing_mode_can_be_displayed()
    {
        $this->signInAdmin();
        $this->enableEditing();

        $page = Page::factory()->create();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);

        $full_slug = $page->full_slug;
        $this->assertNotNull($full_slug);
        $this->assertNull($page->published_version_id);

        $url = $full_slug.'?preview=true';

        $this->disableEditing();
        auth()->logout();

        $this->get($url)
            ->assertStatus(404);

        $this->assertNull($page->preview_url);
        $this->signInAdmin();
        $this->enableEditing();

        $this->assertNotNull($page->preview_url);

        //$this->withoutExceptionHandling();
        $this->get($page->preview_url)
            ->assertSuccessful()
            ->assertSee($content_element->content->header);
    }

    public function test_all_urls_are_lowercase()
    {
        $page = $this->getModel();
        $page->publish();

        $this->assertNotNull($page->full_slug);
        $url = Str::upper($page->full_slug);

        $this->get($url)
            ->assertSuccessful();
    }

    public function test_a_pages_attributes_can_be_loaded()
    {
        $page = $this->getModel();
        $content_element = $this->createContentElement(TextBlock::factory(), $page);

        $this->json('POST', route(Str::plural($this->getClassname()).'.attributes', ['id' => $page->id]), ['attributes' => ['full_slug', 'preview_content_elements']])
            ->assertStatus(401);

        $user = User::factory()->create();

        $this->signIn($user);

        $this->json('POST', route(Str::plural($this->getClassname()).'.attributes', ['id' => $page->id]), ['attributes' => ['full_slug', 'preview_content_elements']])
            ->assertStatus(403);

        $page->createPermission('update', $user);
        $this->enableEditing();

        $page->refresh();
        $user->refresh();
        $this->assertTrue($user->can('update', $page));

        $this->json('POST', route(Str::plural($this->getClassname()).'.attributes', ['id' => $page->id]), [])
             ->assertStatus(422)
             ->assertJsonValidationErrors(['attributes']);

        $page->refresh();
        $this->assertNotNull($page->full_slug);
        $this->assertNotNull($content_element->content->header);

        $this->json('POST', route(Str::plural($this->getClassname()).'.attributes', ['id' => $page->id]), ['attributes' => ['full_slug', 'preview_content_elements']])
             ->assertSuccessful()
             ->assertJsonFragment([
                'full_slug' => $page->full_slug,
                'header' => $content_element->content->header,
             ]);
    }

    public function test_all_pageables_have_a_name_attribute()
    {
        // if we dont have a published version and we are not in editing mode
        // we still need to provied a name for indexes

        $model = $this->getModel();
        $this->assertNull($model->version);

        $this->assertNotNull($model->name);
        $this->assertEquals($model->versions()->first()->name, $model->name);
    }

    public function test_a_page_can_have_a_signed_url()
    {
        $this->signInAdmin();
        $this->enableEditing();
        $page = $this->getModel();
        $version = $page->versions()->first();

        $this->assertNotNull($page->version);

        $input['version'] = Version::factory()->{$this->getClassname()}()->raw([
            'signed_url' => true,
        ]);

        $this->postJson(route(Str::plural($this->getClassname()).'.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $version->refresh();
        $page->refresh();

        $this->assertEquals(1, $version->signed_url);
        $this->assertNotNull($page->signed_url);

        // toggle
        $input['version'] = Version::factory()->{$this->getClassname()}()->raw([
            'signed_url' => false,
        ]);

        $this->postJson(route(Str::plural($this->getClassname()).'.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $version->refresh();
        $page->refresh();

        $this->assertEquals(0, $version->signed_url);
        $this->assertNull($page->signed_url);

        // turn it back on
        $input['version'] = Version::factory()->{$this->getClassname()}()->raw([
            'signed_url' => true,
        ]);

        $this->postJson(route(Str::plural($this->getClassname()).'.update', ['id' => $page->id]), $input)
            ->assertSuccessful()
            ->assertJsonFragment([
                'success' => Arr::get($input, 'version.name').' Saved',
            ]);

        $version->refresh();

        $this->assertEquals(1, $version->signed_url);

        $page->publish();

        $this->disableEditing();
        auth()->logout();

        $this->get($page->full_slug)
             ->assertRedirect('/');

        $this->assertNotNull($page->signed_url);

        $this->get($page->signed_url)
            ->assertSuccessful();

        $this->assertFalse(collect($page->append('signed_url')->toArray())->contains('signed_url'));

        $this->postJson(route(Str::plural($this->getClassname()).'.signed-url', ['id' => $page->id]))
            ->assertStatus(401);

        $user = User::factory()->create();
        $this->signIn($user);

        $this->postJson(route(Str::plural($this->getClassname()).'.signed-url', ['id' => $page->id]))
            ->assertStatus(403);

        $user->createPermission('update', $page);

        $user->refresh();

        $this->postJson(route(Str::plural($this->getClassname()).'.signed-url', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'signed_url' => $page->signed_url,
             ]);
    }

    public function test_a_preview_link_can_be_created_for_a_page()
    {
        $page = $this->getModel();

        $this->postJson(route(Str::plural($this->getClassname()).'.preview-url', ['id' => $page->id]))
            ->assertStatus(401);

        $this->signIn(User::factory()->create());

        $this->postJson(route(Str::plural($this->getClassname()).'.preview-url', ['id' => $page->id]))
            ->assertStatus(403);

        $this->signInAdmin();
        $this->enableEditing();

        $this->assertNotNull($page->preview_url);
        $full_slug = $page->full_slug;
        $this->assertNotNull($full_slug);
        $preview_url = $page->preview_url;

        $this->assertFalse(collect($page->append('preview_url')->toArray())->contains('preview_url'));

        $this->postJson(route(Str::plural($this->getClassname()).'.preview-url', ['id' => $page->id]))
             ->assertSuccessful()
             ->assertJsonFragment([
                'preview_url' => $preview_url,
             ]);

        $this->disableEditing();
        auth()->logout();

        $this->get(route('pages.load', ['page' => $full_slug, 'preview' => 'true']))
            ->assertStatus(404);

        $this->get($preview_url)
            ->assertSuccessful();
    }

    public function test_a_page_preview_can_be_viewed_by_a_logged_in_user_using_the_preview_url()
    {
        $content_element = $this->createContentElement(TextBlock::factory());
        $text_block = $content_element->content;
        $page = $content_element->pages->first();

        $this->signInAdmin();
        $this->enableEditing();
        $preview_url = $page->preview_url;

        $this->get($preview_url)
             ->assertSuccessful()
            ->assertSee($text_block->body);

        $this->disableEditing();
        auth()->logout();

        $this->signIn(User::factory()->create());

        $this->get($preview_url)
             ->assertSuccessful()
            ->assertSee($text_block->body);
    }

    public function test_a_page_can_be_published_when_revealing_or_hiding_from_the_index()
    {
        $page = $this->getModel();
        $page->publish();

        $version = $page->version;
        $version->unlisted = false;
        $version->save();
        $page->refresh();
        $version->refresh();

        $this->assertFalse($page->version->unlisted);
        $this->assertNotNull($page->version->published_at);

        $user = User::factory()->create();
        $user->createPermission('update', $page);
        $this->signIn($user);
        $this->enableEditing();

        $this->json('POST', route(Str::plural($this->getClassname()).'.unlist', ['id' => $page->id]), ['publish' => true])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Hidden',
             ]);

        $page->refresh();
        cache()->tags([cache_name($page)])->flush();

        $draft_version = $page->version;
        $this->assertNotEquals($version->id, $draft_version->id);
        $this->assertNull($draft_version->published_at);
        $this->assertTrue($page->version->unlisted);

        $this->json('POST', route(Str::plural($this->getClassname()).'.reveal', ['id' => $page->id]), ['publish' => true])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Revealed',
             ]);

        $page->refresh();
        $draft_version->refresh();

        $this->assertFalse($page->version->unlisted);
        $this->assertNull($draft_version->published_at);

        $user->createPermission('publish', $page);
        $user->addRole('blogs-publisher');
        $user->addRole('announcements-publisher');
        $user->refresh();
        $page->refresh();
        $page->publish();

        $this->assertTrue($user->can('publish', $page));

        $this->json('POST', route(Str::plural($this->getClassname()).'.unlist', ['id' => $page->id]), ['publish' => true])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Hidden',
             ]);

        $page->refresh();
        $this->assertNotEquals($page->version->id, $draft_version->id);
        $new_version = $page->version;
        $this->assertNotNull($page->version->published_at);

        $this->json('POST', route(Str::plural($this->getClassname()).'.reveal', ['id' => $page->id]), ['publish' => true])
             ->assertSuccessful()
             ->assertJsonFragment([
                'success' => Str::title($this->getClassname()).' Revealed',
             ]);

        $page->refresh();
        $this->assertNotEquals($page->version->id, $new_version->id);
        $newest_version = $page->version;
        $this->assertNotNull($page->version->published_at);
    }

    public function test_a_page_can_randomize_some_of_its_content_elements()
    {
        $page = $this->getModel();
        $version = $page->getDraftVersion();
        $version->randomize = 3;
        $version->save();
        $page->refresh();
        $version->refresh();

        $content_element1 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element2 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element3 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element4 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element5 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element6 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element7 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element8 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element9 = $this->createContentElement(TextBlock::factory(), $page);
        $content_element10 = $this->createContentElement(TextBlock::factory(), $page);

        $randoms = collect([
            $content_element2,
            $content_element3,
            $content_element4,
            $content_element6,
            $content_element7,
            $content_element8,
            $content_element10,
        ]);

        foreach ($randoms as $random) {
            $pivot_data = $random->contentables()->first()->toArray();
            $page->contentElements()->updateExistingPivot($random->id, [...$pivot_data, 'randomize' => true]);
            $random->refresh();
            $this->assertTrue($random->contentables()->first()->randomize);
        }

        $page->publish();

        $content = $page->content;

        $this->assertEquals(6, $content->count());
        $this->assertEquals($content_element1->id, $content->first()->id);
        $this->assertTrue($content->contains('id', $content_element5->id));
        $this->assertTrue($content->contains('id', $content_element9->id));
    }
}
