diff --git a/src/Pest.php b/src/Pest.php
index d9d56ff..59d70ae 100644
--- a/src/Pest.php
+++ b/src/Pest.php
@@ -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;
@@ -39,6 +40,7 @@ function bootstrap($app)
function (DefineBehaviorsEvent $event) {
$event->behaviors[] = ExpectableBehavior::class;
$event->behaviors[] = TestableElementBehavior::class;
+ $event->behaviors[] = SnapshotableBehavior::class;
}
);
diff --git a/src/behaviors/SnapshotableBehavior.php b/src/behaviors/SnapshotableBehavior.php
new file mode 100644
index 0000000..d2da10f
--- /dev/null
+++ b/src/behaviors/SnapshotableBehavior.php
@@ -0,0 +1,39 @@
+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();
+ }
+}
diff --git a/src/factories/Section.php b/src/factories/Section.php
index bd0d0f9..f2b2d1f 100644
--- a/src/factories/Section.php
+++ b/src/factories/Section.php
@@ -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 {
@@ -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;
@@ -90,8 +100,9 @@ function definition(int $index = 0) {
]);
return [$site->id => $settings];
- })->toArray(),
- ];
+ })->toArray();
+
+ return $definition;
}
/**
diff --git a/src/test/SnapshotAssertions.php b/src/test/SnapshotAssertions.php
index a988446..eb98e73 100644
--- a/src/test/SnapshotAssertions.php
+++ b/src/test/SnapshotAssertions.php
@@ -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()
diff --git a/src/test/TestCase.php b/src/test/TestCase.php
index bbf768d..a798133 100644
--- a/src/test/TestCase.php
+++ b/src/test/TestCase.php
@@ -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 {
@@ -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]);
+ }
+
}
diff --git a/tests/.pest/snapshots/SnapshotTest/it_asserts_dom_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_asserts_dom_snapshots.snap
new file mode 100644
index 0000000..d024baf
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_asserts_dom_snapshots.snap
@@ -0,0 +1,4 @@
+[
+ "
\n - one<\/li>\n
- two<\/li>\n
- three<\/li>\n <\/ul>",
+ "
\n - one<\/li>\n
- two<\/li>\n
- three<\/li>\n <\/ul>"
+]
\ No newline at end of file
diff --git a/tests/.pest/snapshots/SnapshotTest/it_asserts_html_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_asserts_html_snapshots.snap
new file mode 100644
index 0000000..59ed560
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_asserts_html_snapshots.snap
@@ -0,0 +1,16 @@
+
heading text
+inner html
+
+
diff --git a/tests/.pest/snapshots/SnapshotTest/it_asserts_view_dom_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_asserts_view_dom_snapshots.snap
new file mode 100644
index 0000000..5bd3584
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_asserts_view_dom_snapshots.snap
@@ -0,0 +1,3 @@
+[
+ "heading text<\/h1>"
+]
\ No newline at end of file
diff --git a/tests/.pest/snapshots/SnapshotTest/it_asserts_view_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_asserts_view_snapshots.snap
new file mode 100644
index 0000000..59ed560
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_asserts_view_snapshots.snap
@@ -0,0 +1,16 @@
+heading text
+
inner html
+
+
diff --git a/tests/.pest/snapshots/SnapshotTest/it_expects_dom_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_expects_dom_snapshots.snap
new file mode 100644
index 0000000..d024baf
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_expects_dom_snapshots.snap
@@ -0,0 +1,4 @@
+[
+ "\n - one<\/li>\n
- two<\/li>\n
- three<\/li>\n <\/ul>",
+ "
\n - one<\/li>\n
- two<\/li>\n
- three<\/li>\n <\/ul>"
+]
\ No newline at end of file
diff --git a/tests/.pest/snapshots/SnapshotTest/it_expects_html_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_expects_html_snapshots.snap
new file mode 100644
index 0000000..59ed560
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_expects_html_snapshots.snap
@@ -0,0 +1,16 @@
+
heading text
+inner html
+
+
diff --git a/tests/.pest/snapshots/SnapshotTest/it_matches_entry_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_matches_entry_snapshots.snap
new file mode 100644
index 0000000..9e26dc3
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_matches_entry_snapshots.snap
@@ -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
+}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/SnapshotTest/it_matches_nested_entry_snapshots.snap b/tests/.pest/snapshots/SnapshotTest/it_matches_nested_entry_snapshots.snap
new file mode 100644
index 0000000..080d227
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_matches_nested_entry_snapshots.snap
@@ -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
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/SnapshotTest/it_renders_views_with_variables.snap b/tests/.pest/snapshots/SnapshotTest/it_renders_views_with_variables.snap
new file mode 100644
index 0000000..5716ca5
--- /dev/null
+++ b/tests/.pest/snapshots/SnapshotTest/it_renders_views_with_variables.snap
@@ -0,0 +1 @@
+bar
diff --git a/tests/Pest.php b/tests/Pest.php
index 6f4811a..f0f11fa 100644
--- a/tests/Pest.php
+++ b/tests/Pest.php
@@ -27,8 +27,8 @@
|
*/
-expect()->extend('toBeOne', function () {
- return $this->toBe(1);
+expect()->extend('toMatchElementSnapshot', function () {
+ $this->toSnapshot()->toMatchSnapshot();
});
/*
@@ -41,8 +41,3 @@
| global functions to help you to reduce the number of lines of code in your test files.
|
*/
-
-function something()
-{
- // ..
-}
diff --git a/tests/README.md b/tests/README.md
deleted file mode 100644
index f5f5f47..0000000
--- a/tests/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Tests
-
-Tests can't be stored in a `yii-extension` because they are not loaded by the
-bootstrapping process.
-
-Instead, they are stored in `markhuot/craft-pest` which requires this as a
-dependency.
diff --git a/tests/SnapshotTest.php b/tests/SnapshotTest.php
index 1c9fb6c..43339f3 100644
--- a/tests/SnapshotTest.php
+++ b/tests/SnapshotTest.php
@@ -1,5 +1,8 @@
get('/selectors')
->assertOk()
@@ -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();
+});
diff --git a/tests/templates/variable.twig b/tests/templates/variable.twig
new file mode 100644
index 0000000..db8a8a4
--- /dev/null
+++ b/tests/templates/variable.twig
@@ -0,0 +1 @@
+{{ foo }}