This Composer package provides a seamless integration for testing SEO aspects of your Laravel applications. Compatible with both Pest and PHPUnit, it offers a collection of tools and assertions specifically designed to evaluate on-page SEO elements like meta tags, title tags, canonical URLs, and structured data. By automating SEO testing, this plugin ensures that your application consistently adheres to best SEO practices, helping you catch potential SEO issues early in the development cycle.
You can install the package via composer:
composer require raiolanetworks/plugin-seo-test --dev
// Create TestSEO instance using the response:
$seo = new TestSEO($htmlResponse);
// Perform assertions:
$seo->assertTitleEndsWith(' - My Website');
// Assert the data yourself:
$this->assertEquals(
'My title - My Website',
$seo->data->title()
);
Look at the following examples using PHPUnit, Laravel, and Pest.
public function testLandingPageSEO()
{
// Arrange
// ...
// Act
$response = $client->get('/')->send();
// Assert
$this->assertEquals(200, $response->getStatusCode());
$html = json_decode($response->getBody(true), true);
$seo = new TestSEO($html);
// Assert
$seo
->assertTitleEndsWith(' - My Website')
->assertCanonicalIs('https://www.mywebsite.com/');
}
public function test_landing_page_SEO()
{
// Arrange
// ...
// Act
$response = $this->get('/');
// Assert
$response->assertStatus(200);
$seo = new TestSEO($response->getContent());
$seo
->assertTitleEndsWith(' - My Website')
->assertCanonicalIs('https://www.mywebsite.com/');
}
test('landing page SEO tags', function () {
// Arrange
// ...
// Act
$response = get('/')->assertStatus(200);
$seo = new TestSEO($response->getContent());
// Assert
expect($seo->data)
->title()->toEndWith(' - My Website')
->description()->toBe('This is my description')
->canonical()->not()->toBeNull()
->robots()->index()->toBeTrue()
->robots()->nofollow()->toBeTrue();
});
You can access the SEO Data yourself by accessing the public property TestSEO->data
.
Here are the available methods:
Method | Returns | Description |
---|---|---|
title() |
?string |
<title>{this}</title> |
description() |
?string |
<meta name="description" content="{this}"> |
image() |
?Url ๐ |
<meta name="image" content="{this}"> |
robots() |
Robots ๐ |
<meta name="robots" content="{this}"> |
canonical() |
?Url ๐ |
<link rel="canonical" href="{this}"> |
prev() |
?Url ๐ |
<link rel="prev" href="{this}"> |
next() |
?Url ๐ |
<link rel="next" href="{this}"> |
openGraph() |
TagCollection ๐ |
<meta property="og:{key}" content="{value}"> |
twitter() |
TagCollection ๐ |
<meta name="twitter:{key}" content="{value}"> |
alternateHrefLang() |
AlternateHrefLangCollection ๐ |
<link name="alternate" hreflang="{hreflang}" href={href}> |
images() |
array<array{src: string, alt: string, title: string}> |
All images in the page. <img src="..."> |
h1s() |
array<string> |
All H1 in the page. <h1>{this}</h1> |
h2s() |
array<string> |
All H2 in the page. <h2>{this}</h2> |
charset() |
?string |
<meta charset="utf-8"> |
The SEOData class is Macroable, so feel free to extend it yourself.
Method | Notes |
---|---|
assertCanonicalIs(string $expected) |
|
assertCanonicalIsEmpty() |
|
assertRobotsIsEmpty() |
|
assertRobotsIsNoIndexNoFollow() |
Checks that the robots are noindex, nofollo or none |
assertPaginationIsEmpty() |
prev and next are both missing. |
assertAlternateHrefLangIsEmpty() |
|
assertTitleIs(string $expected) |
|
assertTitleContains(string $expected) |
|
assertTitleEndsWith(string $expected) |
|
assertDescriptionIs(string $expected) |
|
assertThereIsOnlyOneH1() |
Make sure there is only one H1 in the entire website. |
assertAllImagesHaveAltText() |
Make sure all images have an alt="..." |
Suggest your own! | These assertions can help devs to follow the best SEO practices. Make a PR if you think some are missing! |
When it comes to SEO, a snapshot test is a great way to ensure nothing has been changed by accident.
Here is an example:
$seo = new TestSEO($response->getContent(), snapshotSerializer: null);
$json = json_encode($seo);
By default, the SEO tags are serialized using the SimpleSerializer
.
Make your own serializer by implementing the SnapshotSerializer
interface:
$seo = new TestSEO($response->getContent(), new MyCustomSerializer());
$json = json_encode($seo);
use function Spatie\Snapshots\{assertMatchesSnapshot, assertMatchesJsonSnapshot};
use Raiolanetworks\PluginSEOTest\TestSEO;
test('landing page SEO', function () {
$response = $this->get('/');
$response->assertStatus(200);
$seo = new TestSEO($response->getContent());
assertMatchesJsonSnapshot(json_encode($seo));
});
Note: this example requires spatie/pest-plugin-snapshots
.
Please see CONTRIBUTING for details.
The MIT License (MIT). Please see License File for more information.