Skip to content

Commit

Permalink
element snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
markhuot committed Nov 22, 2023
1 parent 41b2d50 commit e1fbb5a
Show file tree
Hide file tree
Showing 18 changed files with 267 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use markhuot\craftpest\actions\RenderCompiledClasses;
use markhuot\craftpest\behaviors\ExpectableBehavior;
use markhuot\craftpest\behaviors\FieldTypeHintBehavior;
use markhuot\craftpest\behaviors\SnapshotableBehavior;
use markhuot\craftpest\behaviors\TestableElementBehavior;
use markhuot\craftpest\behaviors\TestableElementQueryBehavior;
use markhuot\craftpest\console\PestController;
Expand All @@ -39,6 +40,7 @@ function bootstrap($app)
function (DefineBehaviorsEvent $event) {
$event->behaviors[] = ExpectableBehavior::class;
$event->behaviors[] = TestableElementBehavior::class;
$event->behaviors[] = SnapshotableBehavior::class;
}
);

Expand Down
39 changes: 39 additions & 0 deletions src/behaviors/SnapshotableBehavior.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace markhuot\craftpest\behaviors;

use craft\elements\db\ElementQuery;
use craft\elements\db\EntryQuery;
use craft\elements\db\MatrixBlockQuery;
use craft\elements\ElementCollection;
use craft\elements\Entry;
use craft\fields\Entries;
use craft\fields\Matrix;
use yii\base\Behavior;

class SnapshotableBehavior extends Behavior
{
public function toSnapshot()
{
return collect($this->owner->toArray())
->except([
'id', 'postDate', 'sectionId', 'uid', 'siteSettingsId',
'fieldLayoutId', 'contentId', 'dateCreated', 'dateUpdated',
'canonicalId', 'typeId',
])

// filter out any non-eager loaded queries because we can't snapshot on them, their
// values change too often between runs
->filter(fn ($value, $handle) => ! ($this->owner->{$handle} instanceof ElementQuery))

// Remap any element collections (eager loaded relations) to their nested snapshots
->map(function ($value, $handle) {
if ($this->owner->{$handle} instanceof ElementCollection) {
return $this->owner->{$handle}->map->toSnapshot();
}

return $value;
})
->all();
}
}
23 changes: 17 additions & 6 deletions src/factories/Section.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* @method self name(string $name)
* @method self handle(string $name)
* @method self type(string $type)
* @method \craft\models\Section|Collection create()
* @method \craft\models\Section|Collection<\craft\models\Section> create(array $definition = [])
*/
class Section extends Factory {

Expand Down Expand Up @@ -72,13 +72,23 @@ function newElement()
*/
function definition(int $index = 0) {
$name = $this->faker->words(2, true);
$handle = StringHelper::toCamelCase($name);

return [
'name' => $name,
'handle' => $handle,
'type' => 'channel',
'siteSettings' => collect(\Craft::$app->sites->getAllSites())->mapWithkeys(function ($site) use ($name, $handle) {
];
}

public function inferences(array $definition = [])
{
if (! empty($definition['name']) && empty($definition['handle'])) {
$definition['handle'] = StringHelper::toCamelCase($definition['name']);
}

$name = $definition['name'];
$handle = $definition['handle'];
$definition['siteSettings'] = collect(\Craft::$app->sites->getAllSites())
->mapWithkeys(function ($site) use ($name, $handle) {
$settings = new Section_SiteSettings();
$settings->siteId = $site->id;
$settings->hasUrls = $this->hasUrls;
Expand All @@ -90,8 +100,9 @@ function definition(int $index = 0) {
]);

return [$site->id => $settings];
})->toArray(),
];
})->toArray();

return $definition;
}

/**
Expand Down
14 changes: 14 additions & 0 deletions src/test/SnapshotAssertions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

namespace markhuot\craftpest\test;

/**
* # Snapshots
*
* A variety of snapshot assertions are available to help you test your HTML and DOM output in craft-pest. In
* many places you can simply expect an object `->toMatchSnapshot()` and Pest will handle the rest for you.
*
* For example, responses, DOM Lists, and views are all snapshotable.
*
* ```php
* it('matches responses')->get('/')->assertMatchesSnapshot();
* it('matches dom lists')->get('/')->querySelector('h1')->assertMatchesSnapshot();
* it('matches views')->renderTemplate('_news/entry', $variables)->assertMatchesSnapshot();
* ```
*/
trait SnapshotAssertions
{
public function assertMatchesSnapshot()
Expand Down
8 changes: 8 additions & 0 deletions src/test/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use markhuot\craftpest\actions\CallSeeders;
use markhuot\craftpest\actions\RenderCompiledClasses;
use markhuot\craftpest\console\TestableResponse;
use markhuot\craftpest\web\ViewResponse;
use Symfony\Component\Process\Process;

class TestCase extends \PHPUnit\Framework\TestCase {
Expand Down Expand Up @@ -235,4 +236,11 @@ public function console(array|string $command)
return new TestableResponse($exitCode, $stdout, $stderr);
}

public function renderTemplate(...$args)
{
$content = \Craft::$app->getView()->renderTemplate(...$args);

return new \markhuot\craftpest\web\TestableResponse(['content' => $content]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
"<ul>\n <li>one<\/li>\n <li>two<\/li>\n <li>three<\/li>\n <\/ul>",
"<ul>\n <li>one<\/li>\n <li>two<\/li>\n <li>three<\/li>\n <\/ul>"
]
16 changes: 16 additions & 0 deletions tests/.pest/snapshots/SnapshotTest/it_asserts_html_snapshots.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<h1>heading text</h1>
<p class="paragraph-element">inner html</p>
<div id="first">
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
<div id="second">
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
"<h1>heading text<\/h1>"
]
16 changes: 16 additions & 0 deletions tests/.pest/snapshots/SnapshotTest/it_asserts_view_snapshots.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<h1>heading text</h1>
<p class="paragraph-element">inner html</p>
<div id="first">
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
<div id="second">
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
"<ul>\n <li>one<\/li>\n <li>two<\/li>\n <li>three<\/li>\n <\/ul>",
"<ul>\n <li>one<\/li>\n <li>two<\/li>\n <li>three<\/li>\n <\/ul>"
]
16 changes: 16 additions & 0 deletions tests/.pest/snapshots/SnapshotTest/it_expects_html_snapshots.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<h1>heading text</h1>
<p class="paragraph-element">inner html</p>
<div id="first">
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
<div id="second">
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
27 changes: 27 additions & 0 deletions tests/.pest/snapshots/SnapshotTest/it_matches_entry_snapshots.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"expiryDate": null,
"deletedWithEntryType": false,
"_authorId": null,
"tempId": null,
"draftId": null,
"revisionId": null,
"isProvisionalDraft": false,
"enabled": true,
"archived": false,
"siteId": 2,
"title": "foo bar",
"slug": "foo-bar",
"uri": "posts\/foo-bar",
"dateLastMerged": null,
"dateDeleted": null,
"trashed": false,
"isNewForSite": true,
"isDraft": false,
"isRevision": false,
"isUnpublishedDraft": false,
"ref": "posts\/foo-bar",
"status": "live",
"structureId": null,
"url": "http:\/\/localhost:8080\/posts\/foo-bar",
"authorId": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"expiryDate": null,
"deletedWithEntryType": false,
"_authorId": null,
"tempId": null,
"draftId": null,
"revisionId": null,
"isProvisionalDraft": false,
"enabled": true,
"archived": false,
"siteId": 2,
"title": "foo bar",
"slug": "foo-bar",
"uri": "posts\/foo-bar",
"dateLastMerged": null,
"dateDeleted": null,
"trashed": false,
"isNewForSite": false,
"isDraft": false,
"isRevision": false,
"isUnpublishedDraft": false,
"ref": "posts\/foo-bar",
"status": "live",
"structureId": null,
"url": "http:\/\/localhost:8080\/posts\/foo-bar",
"authorId": null,
"entriesField": [
{
"expiryDate": null,
"deletedWithEntryType": false,
"_authorId": null,
"tempId": null,
"draftId": null,
"revisionId": null,
"isProvisionalDraft": false,
"enabled": true,
"archived": false,
"siteId": 2,
"title": "child",
"slug": "child",
"uri": "posts\/child",
"dateLastMerged": null,
"dateDeleted": null,
"trashed": false,
"isNewForSite": false,
"isDraft": false,
"isRevision": false,
"isUnpublishedDraft": false,
"ref": "posts\/child",
"status": "live",
"structureId": null,
"url": "http:\/\/localhost:8080\/posts\/child",
"authorId": null
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bar
9 changes: 2 additions & 7 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
|
*/

expect()->extend('toBeOne', function () {
return $this->toBe(1);
expect()->extend('toMatchElementSnapshot', function () {
$this->toSnapshot()->toMatchSnapshot();
});

/*
Expand All @@ -41,8 +41,3 @@
| global functions to help you to reduce the number of lines of code in your test files.
|
*/

function something()
{
// ..
}
7 changes: 0 additions & 7 deletions tests/README.md

This file was deleted.

41 changes: 41 additions & 0 deletions tests/SnapshotTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?php

use markhuot\craftpest\factories\Entry;
use markhuot\craftpest\factories\Section;

it('asserts html snapshots')
->get('/selectors')
->assertOk()
Expand All @@ -23,3 +26,41 @@

expect($dom)->assertMatchesSnapshot();
});

it('asserts view snapshots')
->renderTemplate('selectors')
->assertMatchesSnapshot();

it('asserts view dom snapshots')
->renderTemplate('selectors')
->querySelector('h1')
->assertMatchesSnapshot();

it('renders views with variables')
->renderTemplate('variable', ['foo' => 'bar'])
->assertMatchesSnapshot();

it('matches entry snapshots', function () {
$entry = Entry::factory()
->section('posts')
->title('foo bar')
->create();

expect($entry)->toMatchElementSnapshot();
});

it('matches nested entry snapshots', function () {
$child = Entry::factory()
->section('posts')
->title('child');

$parent = Entry::factory()
->section('posts')
->title('foo bar')
->entriesField([$child])
->create();

$entry = \craft\elements\Entry::find()->id($parent->id)->with(['entriesField'])->one();

expect($entry)->toMatchElementSnapshot();
});
1 change: 1 addition & 0 deletions tests/templates/variable.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ foo }}

0 comments on commit e1fbb5a

Please sign in to comment.