diff --git a/recipes/drupal_cms_seo_tools/config/metatag.metatag_defaults.front.yml b/recipes/drupal_cms_seo_tools/config/metatag.metatag_defaults.front.yml index 3bbddf2f0812ce5926ebf997f09e6939c653a503..66bd5e18d6a369e9da50e9838f4bee7fd6bc00eb 100644 --- a/recipes/drupal_cms_seo_tools/config/metatag.metatag_defaults.front.yml +++ b/recipes/drupal_cms_seo_tools/config/metatag.metatag_defaults.front.yml @@ -5,13 +5,13 @@ label: 'Front page' tags: canonical_url: '[site:url]' description: '[node:field_seo_description|node:field_description]' - image_src: '[node:field_seo_image:entity:field_media_image:social_media_facebook|node:field_image:entity:field_media_image:social_media_facebook]' + image_src: '[node:field_seo_image:entity:field_media_image:social_media_facebook|node:field_featured_image:entity:field_media_image:social_media_facebook]' og_description: '[node:field_seo_description|node:field_description]' - og_image: '[node:field_seo_image:entity:field_media_image:social_media_facebook|node:field_image:entity:field_media_image:social_media_facebook]' - og_image_alt: '[node:field_seo_image:entity:field_media_image:alt|node:field_image:entity:field_media_image:alt]' - og_image_height: '[node:field_seo_image:entity:field_media_image:social_media_facebook:height|node:field_image:entity:field_media_image:social_media_facebook:height]' - og_image_type: '[node:field_seo_image:entity:field_media_image:social_media_facebook:mimetype|node:field_image:entity:field_media_image:social_media_facebook:mimetype]' - og_image_width: '[node:field_seo_image:entity:field_media_image:social_media_facebook:width|node:field_image:entity:field_media_image:social_media_facebook:width]' + og_image: '[node:field_seo_image:entity:field_media_image:social_media_facebook|node:field_featured_image:entity:field_media_image:social_media_facebook]' + og_image_alt: '[node:field_seo_image:entity:field_media_image:alt|node:field_featured_image:entity:field_media_image:alt]' + og_image_height: '[node:field_seo_image:entity:field_media_image:social_media_facebook:height|node:field_featured_image:entity:field_media_image:social_media_facebook:height]' + og_image_type: '[node:field_seo_image:entity:field_media_image:social_media_facebook:mimetype|node:field_featured_image:entity:field_media_image:social_media_facebook:mimetype]' + og_image_width: '[node:field_seo_image:entity:field_media_image:social_media_facebook:width|node:field_featured_image:entity:field_media_image:social_media_facebook:width]' og_site_name: '[site:name]' og_title: '[node:field_seo_title|node:title]' og_type: website @@ -22,7 +22,7 @@ tags: shortlink: '[site:url]' title: '[node:field_seo_title|node:title] | [site:name]' twitter_cards_description: '[node:field_seo_description|node:field_description]' - twitter_cards_image: '[node:field_seo_image:entity:field_media_image:social_media_x:url|node:field_image:entity:field_media_image:social_media_x:url]' - twitter_cards_image_alt: '[node:field_seo_image:entity:field_media_image:alt|node:field_image:entity:field_media_image:alt]' + twitter_cards_image: '[node:field_seo_image:entity:field_media_image:social_media_x:url|node:field_featured_image:entity:field_media_image:social_media_x:url]' + twitter_cards_image_alt: '[node:field_seo_image:entity:field_media_image:alt|node:field_featured_image:entity:field_media_image:alt]' twitter_cards_title: '[node:field_seo_title|node:title]' twitter_cards_type: summary_large_image diff --git a/recipes/drupal_cms_seo_tools/tests/src/Functional/ContentMetaTagsTest.php b/recipes/drupal_cms_seo_tools/tests/src/Functional/ContentMetaTagsTest.php index 961b20e447c002d14cf84db8fc07da0e18a14340..5fc99cd417ffa33f1dc786d68c1c4a9c591daec3 100644 --- a/recipes/drupal_cms_seo_tools/tests/src/Functional/ContentMetaTagsTest.php +++ b/recipes/drupal_cms_seo_tools/tests/src/Functional/ContentMetaTagsTest.php @@ -10,6 +10,7 @@ use Drupal\file\Entity\File; use Drupal\FunctionalTests\Core\Recipe\RecipeTestTrait; use Drupal\image\Entity\ImageStyle; use Drupal\media\Entity\Media; +use Drupal\node\NodeInterface; use Drupal\Tests\BrowserTestBase; /** @@ -76,12 +77,6 @@ class ContentMetaTagsTest extends BrowserTestBase { $assert_session = $this->assertSession(); $assert_session->statusCodeEquals(200); - $save_node = function () use ($node): void { - $node->save(); - $this->container->get('cache.page')->deleteAll(); - $this->getSession()->reload(); - }; - // Assert the meta tags which are static, or don't have any configured // overrides. $absolute_node_url = $node_url->setAbsolute()->toString(); @@ -100,61 +95,155 @@ class ContentMetaTagsTest extends BrowserTestBase { // Re-saving the node should update the og:updated_time meta tag. $updated_changed_time = $original_changed_time + 30; $node->setChangedTime($updated_changed_time); - $save_node(); + $this->saveNode($node); $assert_session->elementAttributeContains('css', 'meta[property="og:updated_time"]', 'content', date('c', $updated_changed_time)); // Assert the meta tags for field_featured_image, and that field_seo_image // takes precedence over it. - $assert_image = function (Media $image) use ($assert_session): void { - /** @var \Drupal\file\FileInterface $file */ - $file = $image->field_media_image->entity; - $name = $file->getFilename(); - - $facebook_dimensions = []; - ImageStyle::load('social_media_facebook') - ?->transformDimensions($facebook_dimensions, $file->getFileUri()); - $this->assertArrayHasKey('width', $facebook_dimensions); - $this->assertArrayHasKey('height', $facebook_dimensions); - - $assert_session->elementAttributeContains('css', 'link[rel="image_src"]', 'href', $name); - $assert_session->elementAttributeContains('css', 'meta[property="og:image"]', 'content', $name); - $alt_text = $image->field_media_image->alt; - $assert_session->elementAttributeContains('css', 'meta[property="og:image:alt"]', 'content', $alt_text); - $assert_session->elementAttributeContains('css', 'meta[property="og:image:width"]', 'content', (string) $facebook_dimensions['width']); - $assert_session->elementAttributeContains('css', 'meta[property="og:image:height"]', 'content', (string) $facebook_dimensions['height']); - $assert_session->elementAttributeContains('css', 'meta[property="og:image:type"]', 'content', 'image/webp'); - $assert_session->elementAttributeContains('css', 'meta[name="twitter:image"]', 'content', $name); - $assert_session->elementAttributeContains('css', 'meta[name="twitter:image:alt"]', 'content', $alt_text); - }; - $assert_image($node->field_featured_image->entity); + $this->assertImage($node->field_featured_image->entity); $node->set('field_seo_image', $this->generateImage('jpg')); - $save_node(); - $assert_image($node->field_seo_image->entity); + $this->saveNode($node); + $this->assertImage($node->field_seo_image->entity); // Assert the meta tags for the node title and that field_seo_title takes // precedence over it. - $assert_title = function (string $title) use ($assert_session, $site_name): void { - $assert_session->elementAttributeContains('css', 'meta[property="og:title"]', 'content', $title); - $assert_session->elementAttributeContains('css', 'meta[name="twitter:title"]', 'content', $title); - $assert_session->titleEquals("$title | $site_name"); - }; - $assert_title($node->getTitle()); + $this->assertTitle($node->getTitle(), $site_name); $seo_title = $this->randomMachineName(); $node->set('field_seo_title', $seo_title); - $save_node(); - $assert_title($seo_title); + $this->saveNode($node); + $this->assertTitle($seo_title, $site_name); // Assert the meta tags for field_description and that field_seo_description // takes precedence over it. - $assert_description = function (string $description) use ($assert_session): void { - $assert_session->elementAttributeContains('css', 'meta[name="description"]', 'content', $description); - $assert_session->elementAttributeContains('css', 'meta[property="og:description"]', 'content', $description); - $assert_session->elementAttributeContains('css', 'meta[name="twitter:description"]', 'content', $description); - }; - $assert_description($node->field_description->value); + $this->assertDescription($node->field_description->value); $node->set('field_seo_description', $random->sentences(4)); - $save_node(); - $assert_description($node->field_seo_description->value); + $this->saveNode($node); + $this->assertDescription($node->field_seo_description->value); + } + + /** + * Test the meta tags for front page. + */ + public function testFrontPageMetaTags(): void { + // Apply drupal_cms_page recipe to add the page content type. + $this->applyRecipe(InstalledVersions::getInstallPath('drupal/drupal_cms_page')); + + // Apply this recipe. + $dir = realpath(__DIR__ . '/../../..'); + $this->applyRecipe($dir); + + // Create a node and assign it as the front page. + $random = $this->getRandomGenerator(); + $front_page_node = $this->drupalCreateNode([ + 'type' => 'page', + 'field_featured_image' => $this->generateImage('png'), + 'field_description' => $random->sentences(4), + 'moderation_state' => 'published', + ]); + $this->config('system.site')->set('page.front', $front_page_node->toUrl()->toString())->save(); + + // Access the front page. + $this->drupalGet('<front>'); + $assert_session = $this->assertSession(); + $assert_session->statusCodeEquals(200); + + // Assert the meta tags which are static, or don't have any configured + // overrides. + $front_page_url = Url::fromRoute('<front>')->setAbsolute()->toString(); + $assert_session->elementAttributeContains('css', 'link[rel="canonical"]', 'href', $front_page_url); + $site_name = $this->config('system.site')->get('name'); + $assert_session->elementAttributeContains('css', 'meta[property="og:site_name"]', 'content', $site_name); + $assert_session->elementAttributeContains('css', 'meta[property="og:type"]', 'content', 'website'); + $assert_session->elementAttributeContains('css', 'meta[property="og:url"]', 'content', $front_page_url); + $assert_session->elementAttributeContains('css', 'meta[name="referrer"]', 'content', 'unsafe-url'); + $assert_session->elementAttributeContains('css', 'link[rel="shortlink"]', 'href', $front_page_url); + $assert_session->elementAttributeContains('css', 'meta[name="rights"]', 'content', sprintf('Copyright ©%s All rights reserved.', date('Y'))); + $assert_session->elementAttributeContains('css', 'meta[name="twitter:card"]', 'content', 'summary_large_image'); + $original_changed_time = $front_page_node->getChangedTime(); + $assert_session->elementAttributeContains('css', 'meta[property="og:updated_time"]', 'content', date('c', $original_changed_time)); + + // Re-saving the node should update the og:updated_time meta tag. + $updated_changed_time = $original_changed_time + 30; + $front_page_node->setChangedTime($updated_changed_time); + $this->saveNode($front_page_node); + $assert_session->elementAttributeContains('css', 'meta[property="og:updated_time"]', 'content', date('c', $updated_changed_time)); + + // Assert the meta tags for field_featured_image, and that field_seo_image + // takes precedence over it. + $this->assertImage($front_page_node->field_featured_image->entity); + $front_page_node->set('field_seo_image', $this->generateImage('jpg')); + $this->saveNode($front_page_node); + $this->assertImage($front_page_node->field_seo_image->entity); + + // Assert the meta tags for the node title and that field_seo_title takes + // precedence over it. + $this->assertTitle($front_page_node->getTitle(), $site_name); + $seo_title = $this->randomMachineName(); + $front_page_node->set('field_seo_title', $seo_title); + $this->saveNode($front_page_node); + $this->assertTitle($seo_title, $site_name); + + // Assert the meta tags for field_description and that field_seo_description + // takes precedence over it. + $this->assertDescription($front_page_node->field_description->value); + $front_page_node->set('field_seo_description', $random->sentences(4)); + $this->saveNode($front_page_node); + $this->assertDescription($front_page_node->field_seo_description->value); + } + + /** + * Saves a node. + */ + protected function saveNode(NodeInterface $node) { + $node->save(); + $this->container->get('cache.page')->deleteAll(); + $this->getSession()->reload(); + } + + /** + * Asserts the image meta tags. + */ + protected function assertImage(Media $image): void { + $assert_session = $this->assertSession(); + /** @var \Drupal\file\FileInterface $file */ + $file = $image->field_media_image->entity; + $name = $file->getFilename(); + + $facebook_dimensions = []; + ImageStyle::load('social_media_facebook') + ?->transformDimensions($facebook_dimensions, $file->getFileUri()); + $this->assertArrayHasKey('width', $facebook_dimensions); + $this->assertArrayHasKey('height', $facebook_dimensions); + + $assert_session->elementAttributeContains('css', 'link[rel="image_src"]', 'href', $name); + $assert_session->elementAttributeContains('css', 'meta[property="og:image"]', 'content', $name); + $alt_text = $image->field_media_image->alt; + $assert_session->elementAttributeContains('css', 'meta[property="og:image:alt"]', 'content', $alt_text); + $assert_session->elementAttributeContains('css', 'meta[property="og:image:width"]', 'content', (string) $facebook_dimensions['width']); + $assert_session->elementAttributeContains('css', 'meta[property="og:image:height"]', 'content', (string) $facebook_dimensions['height']); + $assert_session->elementAttributeContains('css', 'meta[property="og:image:type"]', 'content', 'image/webp'); + $assert_session->elementAttributeContains('css', 'meta[name="twitter:image"]', 'content', $name); + $assert_session->elementAttributeContains('css', 'meta[name="twitter:image:alt"]', 'content', $alt_text); + } + + /** + * Asserts the title meta tags. + */ + protected function assertTitle(string $title, string $site_name): void { + $assert_session = $this->assertSession(); + $assert_session->elementAttributeContains('css', 'meta[property="og:title"]', 'content', $title); + $assert_session->elementAttributeContains('css', 'meta[name="twitter:title"]', 'content', $title); + $assert_session->titleEquals("$title | $site_name"); + } + + /** + * Asserts the description meta tags. + */ + protected function assertDescription(string $description): void { + $assert_session = $this->assertSession(); + $assert_session->elementAttributeContains('css', 'meta[name="description"]', 'content', $description); + $assert_session->elementAttributeContains('css', 'meta[property="og:description"]', 'content', $description); + $assert_session->elementAttributeContains('css', 'meta[name="twitter:description"]', 'content', $description); } }