Passed
Push — feature/tests ( a58f99...9b7685 )
by Marc
02:09
created

SitemapContext::theSitemapShouldBeAValidSitemap()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 1
dl 0
loc 19
rs 9.8666
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A SitemapContext::theMultilanguageSitemapShouldNotPassGoogleValidation() 0 5 1
1
<?php
2
3
namespace MOrtola\BehatSEOContexts\Context;
4
5
use PHPUnit\Framework\Assert;
6
use Symfony\Component\Routing\Exception\RouteNotFoundException;
7
8
class SitemapContext extends BaseContext
9
{
10
    const SITEMAP_SCHEMA_FILE = __DIR__ . '/../Resources/schemas/sitemap.xsd';
11
    const SITEMAP_XHTML_SCHEMA_FILE = __DIR__ . '/../Resources/schemas/sitemap_xhtml.xsd';
12
    const SITEMAP_INDEX_SCHEMA_FILE = __DIR__ . '/../Resources/schemas/sitemap_index.xsd';
13
14
    /**
15
     * @var \DOMDocument
16
     */
17
    private $sitemapXml;
18
19
    /**
20
     * @throws \Exception
21
     *
22
     * @Given the sitemap :sitemapUrl
23
     */
24
    public function theSitemap(string $sitemapUrl)
25
    {
26
        $this->sitemapXml = $this->getSitemapXml($sitemapUrl);
27
    }
28
29
    /**
30
     * @throws \Exception
31
     */
32
    private function getSitemapXml(string $sitemapUrl): \DOMDocument
33
    {
34
        $sitemapUrl = $this->toAbsoluteUrl($sitemapUrl);
35
36
        $xml = new \DOMDocument();
37
        @$xmlLoaded = $xml->load($sitemapUrl);
38
39
        Assert::assertNotFalse(
40
            $xmlLoaded,
41
            sprintf(
42
                'Error loading %s Sitemap using DOMDocument',
43
                $sitemapUrl
44
            )
45
        );
46
47
        return $xml;
48
    }
49
50
    /**
51
     * @throws \Exception
52
     *
53
     * @Then the index sitemap should have a child with URL :childSitemapUrl
54
     */
55
    public function theIndexSitemapShouldHaveAChildWithUrl(string $childSitemapUrl)
56
    {
57
        $this->assertSitemapHasBeenRead();
58
59
        $xpathExpression = sprintf(
60
            '//sm:sitemapindex/sm:sitemap/sm:loc[contains(text(),"%s")]',
61
            $childSitemapUrl
62
        );
63
64
        Assert::assertGreaterThanOrEqual(
65
            1,
66
            $this->getXpathInspector()->query($xpathExpression)->length,
67
            sprintf(
68
                'Sitemap index %s has not child sitemap %s',
69
                $this->sitemapXml->documentURI,
70
                $childSitemapUrl
71
            )
72
        );
73
    }
74
75
    /**
76
     * @throws \Exception
77
     */
78
    private function assertSitemapHasBeenRead()
79
    {
80
        if (null == $this->sitemapXml) {
81
            throw new \Exception(
82
                'You should execute "Given the sitemap :sitemapUrl" step before executing this step.'
83
            );
84
        }
85
    }
86
87
    private function getXpathInspector(): \DOMXPath
88
    {
89
        $xpath = new \DOMXPath($this->sitemapXml);
90
        $xpath->registerNamespace('sm', 'http://www.sitemaps.org/schemas/sitemap/0.9');
91
        $xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
92
93
        return $xpath;
94
    }
95
96
    /**
97
     * @throws \Exception
98
     *
99
     * @Then /^the sitemap should have ([0-9]+) children$/
100
     */
101
    public function theSitemapShouldHaveChildren(int $expectedChildrenCount)
102
    {
103
        $this->assertSitemapHasBeenRead();
104
105
        $sitemapChildrenCount = $this->getXpathInspector()
106
            ->query('/*[self::sm:sitemapindex or self::sm:urlset]/*[self::sm:sitemap or self::sm:url]/sm:loc')
107
            ->length;
108
109
        Assert::assertEquals(
110
            $expectedChildrenCount,
111
            $sitemapChildrenCount,
112
            sprintf(
113
                'Sitemap %s has %d children, expected value was: %d',
114
                $this->sitemapXml->documentURI,
115
                $sitemapChildrenCount,
116
                $expectedChildrenCount
117
            )
118
        );
119
    }
120
121
    /**
122
     * @throws \Exception
123
     *
124
     * @Then the multilanguage sitemap should pass Google validation
125
     */
126
    public function theMultilanguageSitemapShouldPassGoogleValidation()
127
    {
128
        $this->assertSitemapHasBeenRead();
129
130
        $this->assertValidSitemap(self::SITEMAP_XHTML_SCHEMA_FILE);
131
132
        $urlsNodes = $this->getXpathInspector()->query('//sm:urlset/sm:url');
133
134
        /** @var \DOMElement $urlNode */
135
        foreach ($urlsNodes as $urlNode) {
136
            $urlLoc = $urlNode->getElementsByTagName('loc')->item(0)->nodeValue;
137
138
            /** @var \DOMElement $alternateLink */
139
            foreach ($urlNode->getElementsByTagName('link') as $alternateLink) {
140
                $alternateLinkHref = $alternateLink->getAttribute('href');
141
142
                if ($alternateLinkHref !== $urlLoc) {
143
                    $alternateLinkNodes = $this->getXpathInspector()->query(
144
                        sprintf('//sm:urlset/sm:url/sm:loc[text()="%s"]', $alternateLinkHref)
145
                    );
146
147
                    Assert::assertGreaterThanOrEqual(
148
                        1,
149
                        $alternateLinkNodes->length,
150
                        sprintf(
151
                            'Url %s has not reciprocous URL for alternative link %s in Sitemap %s',
152
                            $urlLoc,
153
                            $alternateLinkHref,
154
                            $this->sitemapXml->documentURI
155
                        )
156
                    );
157
                }
158
            }
159
        }
160
    }
161
162
    /**
163
     * @throws \Exception
164
     */
165
    private function assertValidSitemap(string $sitemapSchemaFile)
166
    {
167
        Assert::assertFileExists(
168
            $sitemapSchemaFile,
169
            sprintf('Sitemap schema file %s does not exist', $sitemapSchemaFile)
170
        );
171
172
        Assert::assertTrue(
173
            @$this->sitemapXml->schemaValidate($sitemapSchemaFile),
174
            sprintf(
175
                'Sitemap %s does not pass validation using %s schema',
176
                $this->sitemapXml->documentURI,
177
                $sitemapSchemaFile
178
            )
179
        );
180
    }
181
182
    /**
183
     * @throws \Exception
184
     *
185
     * @Then the sitemap URLs should be alive
186
     */
187
    public function theSitemapUrlsShouldBeAlive()
188
    {
189
        $this->assertSitemapHasBeenRead();
190
191
        $locNodes = $this->getXpathInspector()->query('//sm:urlset/sm:url/sm:loc');
192
193
        /** @var \DOMElement $locNode */
194
        foreach ($locNodes as $locNode) {
195
            try {
196
                $this->visit($locNode->nodeValue);
197
            } catch (RouteNotFoundException $e) {
198
                throw new \Exception(
199
                    sprintf(
200
                        'Sitemap Url %s is not valid in Sitemap: %s. Exception: %s',
201
                        $locNode->nodeValue,
202
                        $this->sitemapXml->documentURI,
203
                        $e->getMessage()
204
                    )
205
                );
206
            }
207
208
            Assert::assertEquals(
209
                200,
210
                $this->getStatusCode(),
211
                sprintf(
212
                    'Sitemap Url %s is not valid in Sitemap: %s. Response status code: %s',
213
                    $locNode->nodeValue,
214
                    $this->sitemapXml->documentURI,
215
                    $this->getStatusCode()
216
                )
217
            );
218
        }
219
    }
220
221
    /**
222
     * @Then /^the (index |multilanguage |)sitemap should not be valid$/
223
     */
224
    public function theSitemapShouldNotBeValid(string $sitemapType = '')
225
    {
226
        $this->assertInverse(
227
            function () use ($sitemapType) {
228
                $this->theSitemapShouldBeValid($sitemapType);
229
            },
230
            sprintf('The sitemap is a valid %s sitemap.', $sitemapType)
231
        );
232
    }
233
234
    /**
235
     * @throws \Exception
236
     *
237
     * @Then /^the (index |multilanguage |)sitemap should be valid$/
238
     */
239
    public function theSitemapShouldBeValid(string $sitemapType = '')
240
    {
241
        $sitemapType = trim($sitemapType);
242
        $this->assertSitemapHasBeenRead();
243
244
        switch ($sitemapType) {
245
            case 'index':
246
                $sitemapSchemaFile = self::SITEMAP_INDEX_SCHEMA_FILE;
247
248
                break;
249
            case 'multilanguage':
250
                $sitemapSchemaFile = self::SITEMAP_XHTML_SCHEMA_FILE;
251
252
                break;
253
            default:
254
                $sitemapSchemaFile = self::SITEMAP_SCHEMA_FILE;
255
        }
256
257
        $this->assertValidSitemap($sitemapSchemaFile);
258
    }
259
260
    /**
261
     * @Then the multilanguage sitemap should not pass Google validation
262
     */
263
    public function theMultilanguageSitemapShouldNotPassGoogleValidation()
264
    {
265
        $this->assertInverse(
266
            [$this, 'theMultilanguageSitemapShouldPassGoogleValidation'],
267
            sprintf('The multilanguage sitemap passes Google validation.')
268
        );
269
    }
270
}
271