Add and enable the drupal_cms_seo_tools recipe
```bash composer require drupal/drupal_cms_seo_tools drush recipe $(pwd)/recipes/drupal_cms_seo_tools ```
This commit is contained in:
parent
a4eacb5cf4
commit
bc2ab546d4
17 changed files with 2547 additions and 1 deletions
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\drupal_cms_seo_tools\Functional;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigInstallerInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\FunctionalTests\Core\Recipe\RecipeTestTrait;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* @group drupal_cms_seo_tools
|
||||
*/
|
||||
class ComponentValidationTest extends BrowserTestBase {
|
||||
|
||||
use RecipeTestTrait {
|
||||
applyRecipe as traitApplyRecipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
// Create a content type so we can test the changes made by the recipe.
|
||||
$this->drupalCreateContentType(['type' => 'test'])->id();
|
||||
}
|
||||
|
||||
private function applyRecipe(mixed ...$arguments): void {
|
||||
$dir = realpath(__DIR__ . '/../../..');
|
||||
$this->traitApplyRecipe($dir, ...$arguments);
|
||||
}
|
||||
|
||||
public function test(): void {
|
||||
// The recipe should apply cleanly.
|
||||
$this->applyRecipe();
|
||||
// Apply it again to prove that it is idempotent.
|
||||
$this->applyRecipe();
|
||||
|
||||
// There should be an SEO image field on our test content type, referencing
|
||||
// image media.
|
||||
$field_settings = FieldConfig::loadByName('node', 'test', 'field_seo_image')?->getSettings();
|
||||
$this->assertIsArray($field_settings);
|
||||
$this->assertSame('default:media', $field_settings['handler']);
|
||||
$this->assertContains('image', $field_settings['handler_settings']['target_bundles']);
|
||||
|
||||
// Check sitemap works as expected for anonymous users.
|
||||
$this->checkSitemap();
|
||||
|
||||
// Check sitemap works as expected for authenticated users too.
|
||||
$authenticated = $this->createUser();
|
||||
$this->drupalLogin($authenticated);
|
||||
$this->checkSitemap();
|
||||
}
|
||||
|
||||
public function testAutomaticSitemapSettings(): void {
|
||||
$this->applyRecipe();
|
||||
|
||||
// We should have Simple Sitemap settings for the extant content type.
|
||||
$settings = $this->container->get('config.storage')
|
||||
->listAll('simple_sitemap.bundle_settings');
|
||||
$this->assertSame(['simple_sitemap.bundle_settings.default.node.test'], $settings);
|
||||
|
||||
$get_settings = function (string $node_type): Config {
|
||||
return $this->config("simple_sitemap.bundle_settings.default.node.$node_type");
|
||||
};
|
||||
// If we create a new content type programmatically, Simple Sitemap settings
|
||||
// should be generated for it automatically.
|
||||
$node_type = $this->drupalCreateContentType()->id();
|
||||
$this->assertFalse($get_settings($node_type)->isNew());
|
||||
|
||||
// If we create a new content type in the UI, Simple Sitemap settings should
|
||||
// NOT be automatically generated.
|
||||
$account = $this->createUser([
|
||||
'administer content types',
|
||||
'administer sitemap settings',
|
||||
]);
|
||||
$this->drupalLogin($account);
|
||||
$this->drupalGet('/admin/structure/types/add');
|
||||
$node_type = $this->randomMachineName();
|
||||
$this->submitForm([
|
||||
'name' => $node_type,
|
||||
'type' => $node_type,
|
||||
'simple_sitemap[default][index]' => 0,
|
||||
], 'Save');
|
||||
$this->assertTrue($get_settings($node_type)->isNew());
|
||||
|
||||
// Extant settings should not be changed...
|
||||
$get_settings('test')->set('priority', '0.3')->save();
|
||||
$this->assertSame('0.3', $get_settings('test')->get('priority'));
|
||||
// ...even if we reapply the recipe...
|
||||
$this->applyRecipe();
|
||||
$this->assertSame('0.3', $get_settings('test')->get('priority'));
|
||||
// ...or sync config (here, we are simulating that the priority was changed
|
||||
// by a config sync).
|
||||
$this->container->get(ConfigInstallerInterface::class)->setSyncing(TRUE);
|
||||
$get_settings('test')->set('priority', '0.2')->save();
|
||||
$this->assertSame('0.2', $get_settings('test')->get('priority'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the sitemap is accessible and contains the expected links.
|
||||
*/
|
||||
private function checkSitemap(): void {
|
||||
// Create a main menu link to ensure it shows up in the site map.
|
||||
$node = $this->drupalCreateNode(['type' => 'test']);
|
||||
$menu_link = MenuLinkContent::create([
|
||||
'title' => $node->getTitle(),
|
||||
'link' => 'internal:' . $node->toUrl()->toString(),
|
||||
'menu_name' => 'main',
|
||||
]);
|
||||
$menu_link->save();
|
||||
|
||||
$this->drupalGet('/sitemap');
|
||||
|
||||
$assert_session = $this->assertSession();
|
||||
$assert_session->statusCodeEquals(200);
|
||||
$assert_session->linkByHrefNotExists('/rss.xml');
|
||||
|
||||
$site_map = $assert_session->elementExists('css', '.sitemap');
|
||||
$site_name = $this->config('system.site')->get('name');
|
||||
$this->assertTrue($site_map->hasLink("Front page of $site_name"), 'Front page link does not appear in the site map.');
|
||||
$this->assertTrue($site_map->hasLink($menu_link->label()), 'Main menu links do not appear in the site map.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\drupal_cms_seo_tools\Functional;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\FunctionalTests\Core\Recipe\RecipeTestTrait;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\media\Entity\Media;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* @group drupal_cms_seo_tools
|
||||
*/
|
||||
class ContentMetaTagsTest extends BrowserTestBase {
|
||||
|
||||
use RecipeTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
private function generateImage(string $extension): Media {
|
||||
$random = $this->getRandomGenerator();
|
||||
|
||||
$uri = uniqid('public://') . '.' . $extension;
|
||||
$uri = $random->image($uri, '100x100', '200x200');
|
||||
$this->assertFileExists($uri);
|
||||
$file = File::create(['uri' => $uri]);
|
||||
$file->save();
|
||||
|
||||
$media = Media::create([
|
||||
'name' => $random->word(16),
|
||||
'bundle' => 'image',
|
||||
'field_media_image' => [
|
||||
'target_id' => $file->id(),
|
||||
'alt' => $random->machineName(),
|
||||
],
|
||||
]);
|
||||
$media->save();
|
||||
|
||||
return $media;
|
||||
}
|
||||
|
||||
/**
|
||||
* @testWith ["drupal/drupal_cms_blog", "blog"]
|
||||
* ["drupal/drupal_cms_case_study", "case_study"]
|
||||
* ["drupal/drupal_cms_events", "event"]
|
||||
* ["drupal/drupal_cms_news", "news"]
|
||||
* ["drupal/drupal_cms_page", "page"]
|
||||
* ["drupal/drupal_cms_person", "person"]
|
||||
* ["drupal/drupal_cms_project", "project"]
|
||||
*/
|
||||
public function testMetaTagsForContentType(string $recipe, string $node_type): void {
|
||||
$dir = InstalledVersions::getInstallPath($recipe);
|
||||
$this->applyRecipe($dir);
|
||||
|
||||
$dir = realpath(__DIR__ . '/../../..');
|
||||
$this->applyRecipe($dir);
|
||||
|
||||
// If we create a node of this content type, all expected meta tags should
|
||||
// be there.
|
||||
$random = $this->getRandomGenerator();
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => $node_type,
|
||||
'field_featured_image' => $this->generateImage('png'),
|
||||
'field_description' => $random->sentences(4),
|
||||
'moderation_state' => 'published',
|
||||
]);
|
||||
$node_url = $node->toUrl();
|
||||
$this->drupalGet($node_url);
|
||||
$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();
|
||||
$assert_session->elementAttributeContains('css', 'link[rel="canonical"]', 'href', $absolute_node_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', 'article');
|
||||
$assert_session->elementAttributeContains('css', 'meta[property="og:url"]', 'content', $absolute_node_url);
|
||||
$assert_session->elementAttributeContains('css', 'meta[name="referrer"]', 'content', 'unsafe-url');
|
||||
$assert_session->elementAttributeContains('css', 'link[rel="shortlink"]', 'href', Url::fromRoute('<front>')->setAbsolute()->toString());
|
||||
$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 = $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;
|
||||
$node->setChangedTime($updated_changed_time);
|
||||
$save_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);
|
||||
$node->set('field_seo_image', $this->generateImage('jpg'));
|
||||
$save_node();
|
||||
$assert_image($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());
|
||||
$seo_title = $this->randomMachineName();
|
||||
$node->set('field_seo_title', $seo_title);
|
||||
$save_node();
|
||||
$assert_title($seo_title);
|
||||
|
||||
// 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);
|
||||
$node->set('field_seo_description', $random->sentences(4));
|
||||
$save_node();
|
||||
$assert_description($node->field_seo_description->value);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue