diff --git a/CHANGELOG.md b/CHANGELOG.md index 978e8f50..15c58400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Release Notes for Blitz -## 5.6.3 - Unreleased +## 5.7.0 - Unreleased + +### Added + +- Added compatibility for tracking of relation fields in Craft 5.3.0. +- Added compatibility for detecting eager-loading opportunities in the Blitz Hints utility in Craft 5.3.0. ### Fixed diff --git a/composer.json b/composer.json index bd694ef8..87847a49 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "putyourlightson/craft-blitz", "description": "Intelligent static page caching for creating lightning-fast sites.", - "version": "5.6.3", + "version": "5.7.0", "type": "craft-plugin", "homepage": "https://putyourlightson.com/plugins/blitz", "license": "proprietary", diff --git a/src/helpers/ElementQueryHelper.php b/src/helpers/ElementQueryHelper.php index 6ae771c9..d098aed9 100644 --- a/src/helpers/ElementQueryHelper.php +++ b/src/helpers/ElementQueryHelper.php @@ -275,6 +275,8 @@ public static function getNormalizedElementQueryIdParam(mixed $value): mixed /** * Returns whether the element query has numeric IDs that may be related element IDs. + * + * @see BaseRelationField::getRelationTargetIds() */ public static function hasRelatedElementIds(ElementQuery $elementQuery): bool { diff --git a/src/helpers/HintsHelper.php b/src/helpers/HintsHelper.php index afb21d75..0de8d19b 100644 --- a/src/helpers/HintsHelper.php +++ b/src/helpers/HintsHelper.php @@ -42,11 +42,14 @@ public static function getAll(): array $hint = new HintModel(); $hint->setAttributes($record->getAttributes(), false); - $field = Craft::$app->getFields()->getFieldById($hint->fieldId); - if ($field) { - $hint->field = $field; - $hints[] = $hint; + if ($hint->fieldId > 0) { + $field = Craft::$app->getFields()->getFieldById($hint->fieldId); + if ($field) { + $hint->field = $field; + } } + + $hints[] = $hint; } return $hints; diff --git a/src/services/HintsService.php b/src/services/HintsService.php index e95df49a..feeb7e7a 100644 --- a/src/services/HintsService.php +++ b/src/services/HintsService.php @@ -11,7 +11,6 @@ use Craft; use craft\base\Component; -use craft\base\FieldInterface; use craft\elements\db\ElementQuery; use putyourlightson\blitz\helpers\ElementQueryHelper; use putyourlightson\blitz\models\HintModel; @@ -58,10 +57,14 @@ public function checkElementQuery(ElementQuery $elementQuery): void return; } - // TODO: Figure out how to add field hints for related element IDs. + if ($elementQuery->eagerly || $elementQuery->wasEagerLoaded()) { + return; + } + // Required as of Craft 5.3.0. if (ElementQueryHelper::hasRelatedElementIds($elementQuery)) { - //$this->addFieldHint($field->id); + $this->addFieldHint(); + return; } @@ -70,10 +73,6 @@ public function checkElementQuery(ElementQuery $elementQuery): void return; } - if ($elementQuery->eagerly || $elementQuery->wasEagerLoaded()) { - return; - } - /** @see ElementQuery::wasEagerLoaded() */ $planHandle = $elementQuery->eagerLoadHandle; if (str_contains($planHandle, ':')) { @@ -116,17 +115,18 @@ public function save(): void } /** - * Adds a field hint. + * Adds a field hint. As of Craft 5.3.0, we may not be able to detect the field ID from the element query, if the relation field value is stored in the `content` column. In this case we set a field ID of zero, so we can still store it, maintaining unique keys. */ - private function addFieldHint(int $fieldId): void + private function addFieldHint(int $fieldId = 0): void { - $field = Craft::$app->getFields()->getFieldById($fieldId); + $fieldHandle = null; - if ($field === null) { - return; + if ($fieldId > 0) { + $field = Craft::$app->getFields()->getFieldById($fieldId); + $fieldHandle = $field->handle ?? null; } - $hint = $this->createHintWithTemplateLine($field); + $hint = $this->createHintWithTemplateLine($fieldId, $fieldHandle); if ($hint === null) { return; @@ -152,7 +152,7 @@ private function addFieldHint(int $fieldId): void /** * Returns a new hint with the template and line number of the rendered template. */ - protected function createHintWithTemplateLine(FieldInterface $field): ?HintModel + protected function createHintWithTemplateLine(int $fieldId, ?string $fieldHandle = null): ?HintModel { $hint = null; $traces = debug_backtrace(); @@ -167,7 +167,7 @@ protected function createHintWithTemplateLine(FieldInterface $field): ?HintModel if ($templatePath && $line) { if ($hint === null) { $hint = new HintModel([ - 'fieldId' => $field->id, + 'fieldId' => $fieldId, 'template' => $templatePath, 'line' => $line, 'stackTrace' => [$templatePath . ':' . $line], @@ -178,15 +178,17 @@ protected function createHintWithTemplateLine(FieldInterface $field): ?HintModel continue; } - // Read the contents of the template file, since the code cannot be retrieved from the source context with `devMode` disabled. - $templateCode = file($this->getTemplatePath($template)); - $code = $templateCode[$line - 1] ?? ''; - preg_match('/(\w+?)\.' . $field->handle . '/', $code, $matches); - $routeVariable = $matches[1] ?? null; - - // Don’t continue if the route variable is set. - if ($routeVariable && !empty($trace['args'][0]['variables'][$routeVariable])) { - return null; + if ($fieldHandle !== null) { + // Read the contents of the template file, since the code cannot be retrieved from the source context with `devMode` disabled. + $templateCode = file($this->getTemplatePath($template)); + $code = $templateCode[$line - 1] ?? ''; + preg_match('/(\w+?)\.' . $fieldHandle . '/', $code, $matches); + $routeVariable = $matches[1] ?? null; + + // Don’t continue if the route variable is set. + if ($routeVariable && !empty($trace['args'][0]['variables'][$routeVariable])) { + return null; + } } } } diff --git a/src/templates/_utilities/hints/components/hints.twig b/src/templates/_utilities/hints/components/hints.twig index 04fef1be..1db89c1a 100644 --- a/src/templates/_utilities/hints/components/hints.twig +++ b/src/templates/_utilities/hints/components/hints.twig @@ -29,14 +29,15 @@ {% for hint in hints %}
{{ hint.field.handle }}
field using lazy-eager-loading:
- {% set {{ hint.field.handle }} = entry.{{ hint.field.handle }}.eagerly().all() %}
+ Eager-load the {{ hint.field ? ('' ~ fieldHandle ~ '
')|raw : 'relation' }} field using lazy-eager-loading:
+ {% set result = entry.{{ fieldHandle }}.eagerly().all() %}
Or the with()
query param:
{{- "{% set entries = craft.entries" -}}
- {{- "\n .with('" ~ hint.field.handle ~ "')" -}}
+ {{- "\n .with('" ~ fieldHandle ~ "')" -}}
{{- "\n .all()" -}}
{{- "\n%}" -}}
diff --git a/tests/TESTS.md b/tests/TESTS.md
index 1330a997..4be19566 100644
--- a/tests/TESTS.md
+++ b/tests/TESTS.md
@@ -4,11 +4,252 @@ This document outlines the test specification for the Blitz plugin.
---
+## Architecture Tests
+
+### [Architecture](pest/Architecture/ArchitectureTest.php)
+
+_Tests the architecture of the plugin._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Source code does not contain any `var_dump` or `die` statements.
+
+## Drivers Tests
+
+### [FileStorage](pest/Drivers/FileStorageTest.php)
+
+_Tests functionality specific to the file storage driver._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Getting a site path works for a disabled or non-existent site.
+
## Feature Tests
+### [BlitzVariable](pest/Feature/BlitzVariableTest.php)
+
+_Tests the markup generated by the Blitz variable._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached include tag contains provided options.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached include tag does not contain unencoded slashes in params.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached include tag does not contain path param.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached include tag with AJAX request type results in inject script being registered.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Fetch URI tag does not contain unencoded slashes in params.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The CSRF input function returns a Blitz inject script.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The CSRF input function called in an AJAX request does not return a Blitz inject script.
+
+### [CacheRequest](pest/Feature/CacheRequestTest.php)
+
+_Tests whether requests are cacheable and under what circumstances._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request matching included URI pattern is cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request with generate token is cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request with `no-cache` param is not cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request with token is not cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request with a cached include prefix is a cached include.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request with a cached include action is a cached include.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request with a dynamic include prefix is a dynamic include.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Request with dynamic include action is a dynamic include.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Requested cacheable site URI includes allowed query strings when urls cached as unique pages.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Requested cacheable site URI does not include query strings when urls cached as same page.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Requested cacheable site URI includes page trigger.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Requested cacheable site URI works with regular expressions.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI with included URI pattern is cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI with excluded URI pattern is not cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI with `admin` in URI is cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI with `index.php` in URI is not cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI with max URI length is cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI with max URI length exceeded is not cacheable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) URI patterns with matching regular expressions are matched.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) URI patterns without matching regular expressions are not matched.
+
+### [CacheStorage](pest/Feature/CacheStorageTest.php)
+
+_Tests the storing of cached values using the cache storage drivers._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) 255 character site URI can be saved with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) 255 character site URI can be saved with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) 255 character site URI can be saved with data set `RedisStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Long site URI can be saved except for by file storage driver with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Long site URI can be saved except for by file storage driver with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Long site URI can be saved except for by file storage driver with data set `RedisStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI is decoded before being saved with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI is decoded before being saved with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URI is decoded before being saved with data set `RedisStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Compressed cached value can be fetched compressed and uncompressed with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Compressed cached value can be fetched compressed and uncompressed with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Compressed cached value can be fetched compressed and uncompressed with data set `RedisStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value of site URI is deleted with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value of site URI is deleted with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value of site URI is deleted with data set `RedisStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Compressed cached value of site URI is deleted with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Compressed cached value of site URI is deleted with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Compressed cached value of site URI is deleted with data set `RedisStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value of decoded site URI is deleted with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value of decoded site URI is deleted with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value of decoded site URI is deleted with data set `RedisStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) All cached values are deleted with data set `FileStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) All cached values are deleted with data set `YiiCacheStorage`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) All cached values are deleted with data set `RedisStorage`.
+
+### [ExpireCache](pest/Feature/ExpireCacheTest.php)
+
+_Tests marking cached values as expired when `REFRESH_MODE_EXPIRE` is selected._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Refreshing the entire cache marks the cache as expired.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Refreshing a site marks the cache as expired.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Refreshing a site URI marks the cache as expired.
+
### [GenerateCache](pest/Feature/GenerateCacheTest.php)
_Tests the saving of cached values, element cache records and element query records._
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value is saved with output comments.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value is saved without output comments.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value is saved with output comments when file extension is ".html".
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cached value is saved without output comments when file extension is not `.html`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cache record with max URI length is saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cache record with max URI length exceeded throws exception.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved without custom fields.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved with custom fields.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved with custom fields with renamed handles.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved with eager-loaded custom fields.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved with nested eager-loaded custom fields.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved with eager-loaded matrix fields.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved with eager-loaded custom fields in variable.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved for preloaded single.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache record is saved with eager-loaded custom fields for preloaded single.
![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache records are saved with all statuses for relation field queries.
![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache records are saved with all statuses for eager-loaded relation field queries.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache records are saved irrespective of the criteria for eager-loaded relation field queries.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache records are saved for archived and deleted elements with eager-loaded relation field queries.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query records without specific identifiers are saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query records with specific identifiers are not saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with select is saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with join is saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with relation field is not saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with related to param is saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with query param is saved without the param.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with expression is not saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record for entry in single sections is not saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with option field data is converted to value.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record with multi options field data is converted to array of values.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record keeps limit and offset params.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record keeps order by if a limit param is present.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record keeps order by if an offset param is present.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record does not keep order by if no limit or offset param is present.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query record respects excluded tracked element query params.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query cache records are saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query source records with specific source identifiers are saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query source records without specific source identifiers are not saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Entry query source records are saved when only a structure ID is set.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query attribute records are saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query attribute records are saved with order by.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query attribute records are saved with order by parts array.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query attribute records are saved with before.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query field records are saved with order by.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query field records are saved with order by array.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query field records with renamed handles are saved with order by.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query field records with section are saved with order by only for fields in layouts.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cache tags are saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Include record is saved.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) SSI include cache record is saved.
+
+### [Hints](pest/Feature/HintsTest.php)
+
+_Tests hints functionality._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Hint is recorded for a related element query that is lazy-loaded.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Hint is not recorded for a related element query that is lazy eager-loaded.
+
+### [RefreshCache](pest/Feature/RefreshCacheTest.php)
+
+_Tests the tracking of changes to elements and the resulting element cache IDs and element query type records._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is not tracked when it is unchanged.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when `refreshCacheWhenElementSavedUnchanged` is `true` and it is unchanged.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is not tracked when disabled and its attribute is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when disabled and `refreshCacheWhenElementSavedNotLive` is `true` and its attribute is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when its status is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when its status for another site is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when it expires.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when it is deleted.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when its attribute is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when its field is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when its attribute and field are changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element is tracked when its status and attribute and field are changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Asset is tracked when its file is replaced.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Asset is tracked when its filename is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Asset is tracked when its focal point is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element expiry date record is saved when an entry has a future post date.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element expiry date record is saved when an entry has a future expiry date.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache IDs are returned when an entry is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache IDs are returned when an entry is changed by attributes.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element cache IDs are not returned when an entry is changed by custom fields.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query cache IDs are returned when a disabled entry is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records are returned when an entry is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records without a cache ID are not returned when an entry is changed.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records are returned when an entry is changed by attributes used in the query.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records are not returned when an entry is changed by attributes not used in the query.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records are returned when an entry is changed by custom fields used in the query.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records are not returned when an entry is changed by custom fields not used in the query.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records are returned when an entry is changed with the date updated used in the query.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Element query type records are deleted when executing them results in an exception.
+
+### [RefreshMode](pest/Feature/RefreshModeTest.php)
+
+_Tests what should happen when, based on the refresh modes._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be cleared on refresh” with data set `clear only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be cleared on refresh” with data set `clear and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be cleared on refresh with data set `expire only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be cleared on refresh with data set `expire and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be cleared on refresh when forcing a clear with data set `clear only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be cleared on refresh when forcing a clear with data set `clear and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be generated on refresh with data set `clear and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be generated on refresh with data set `expire and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be generated on refresh with data set `clear only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be generated on refresh with data set `expire only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be generated on refresh when forcing a generate with data set `clear only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be generated on refresh when forcing a generate with data set `expire only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be expired on refresh with data set `expire only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be expired on refresh with data set `expire and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be expired on refresh with data set `clear only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be expired on refresh with data set `clear and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be expired on refresh when forcing a generate with data set `expire only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be expired on refresh when forcing a generate with data set `expire and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be purged after refresh with data set `expire only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should be purged after refresh with data set `expire and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be purged after refresh with data set `clear only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be purged after refresh with data set `clear and generate`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be purged after refresh when forcing a clear with data set `expire only`.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) The cache should not be purged after refresh when forcing a clear with data set `expire and generate`.
+
+### [SiteUri](pest/Feature/SiteUriTest.php)
+
+_Tests the site URI helper methods._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URIs are returned from assets with transforms.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) HTML mime type is returned when site URI is HTML.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) JSON mime type is returned when site URI is JSON.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URIs with page triggers are paginated.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Site URIs without page triggers are not paginated.
+
+## Integration Tests
+
+### [Commerce](pest/Integration/CommerceTest.php)
+
+_Tests that Commerce variants are refreshed on order completion so that their stock is updated._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Variant with inventory is refreshed on order completion.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Variant without inventory is not refreshed on order completion.
+
+### [FeedMe](pest/Integration/FeedMeTest.php)
+
+_Tests that Feed Me imports refresh the cache with batch mode enabled._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Cache is refreshed with batch mode enabled.
+
+### [Seomatic](pest/Integration/SeomaticTest.php)
+
+_Tests that cached pages are refreshed when SEOmatic meta containers are invalidated._
+
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Invalidate container caches event without a URL or source triggers a refresh all.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Invalidate container caches event with a specific source triggers a refresh.
+![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) Invalidate container caches event for a specific element does not trigger a refresh.