juampi92 / test-seo
Easy way to test your SEO
Installs: 324 771
Dependents: 0
Suggesters: 0
Security: 0
Stars: 26
Watchers: 1
Forks: 5
Open Issues: 0
pkg:composer/juampi92/test-seo
Requires
- php: ^8.0.2
- illuminate/macroable: ^7.0|^8.0|^9.0|^10.0|^11.0|^12.0
- phpunit/phpunit: ^8.3|^9.0|^10.0|^11.0
- spatie/url: ^1.3.4|^2.0
- symfony/dom-crawler: ^5.4|^6.1
Requires (Dev)
- laravel/pint: ^1.0
- phpstan/phpstan: ^1.8
README
An easy-to-use package for testing SEO. The package allows you to extract SEO tags from a given HTML and verify that the SEO structure is correct.
Installation
You can install the package via composer:
composer require juampi92/test-seo --dev
Usage
// 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.
PHPUnit
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/'); }
Laravel
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/'); }
Pest
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(); });
SEO Data
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.
Assertions
| Method | Notes | 
|---|---|
| assertCanonicalIs(string $expected) | |
| assertCanonicalIsEmpty() | |
| assertRobotsIsEmpty() | |
| assertRobotsIsNoIndexNoFollow() | Checks that the robots are noindex, nofolloornone | 
| assertPaginationIsEmpty() | prevandnextare 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! | 
Snapshots
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);
Pest Example
use function Spatie\Snapshots\{assertMatchesSnapshot, assertMatchesJsonSnapshot}; use Juampi92\TestSEO\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.
Contributing
Please see CONTRIBUTING for details.
Credits
License
The MIT License (MIT). Please see License File for more information.