Issues (95)

Branch: main

tests/features/bootstrap/MinkContext.php (2 issues)

Severity
1
<?php
2
3
/**
4
 * Copyright 2020 SURFnet B.V.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\StepupGateway\Behat;
20
21
use Behat\Mink\Exception\ExpectationException;
22
use Behat\MinkExtension\Context\MinkContext as BaseMinkContext;
23
use DMore\ChromeDriver\ChromeDriver;
24
use DOMDocument;
25
use DOMXPath;
26
use RobRichards\XMLSecLibs\XMLSecurityDSig;
27
use RuntimeException;
28
use SAML2\XML\mdui\Common;
29
use SAML2\XML\shibmd\Scope;
30
31
/**
32
 * Mink-enabled context.
33
 */
34
class MinkContext extends BaseMinkContext
35
{
36
    /**
37
     * @var array a list of window names identified by the name the tester refers to them in the step definitions.
38
     * @example ['My tab' => 'WindowNameGivenByBrowser', 'My other tab' => 'WindowNameGivenByBrowser']
39
     */
40
    private $windows = [];
41
42
    /**
43
     * @Then /^the response should contain \'([^\']*)\'$/
44
     */
45
    public function theResponseShouldContain($string): void
46
    {
47
        $this->assertSession()->responseContains($string);
48
    }
49
50
    /**
51
     * @Then /^the response should match xpath \'([^\']*)\'$/
52
     */
53
    public function theResponseShouldMatchXpath($xpath): void
54
    {
55
        $document = new DOMDocument();
56
        if ($this->getSession()->getDriver() instanceof ChromeDriver) {
57
            // Chrome uses a user friendly viewer, get the xml from the dressed document and assert on that xml.
58
            $this->getSession()->wait(1000, "document.getElementById('webkit-xml-viewer-source-xml') !== null");
59
            $xml = $this->getSession()->evaluateScript("document.getElementById('webkit-xml-viewer-source-xml').innerHTML");
60
        } else {
61
            $xml = $this->getSession()->getPage()->getContent();
62
        }
63
        $document->loadXML($xml);
64
65
        $xpathObj = new DOMXPath($document);
66
        $xpathObj->registerNamespace('ds', XMLSecurityDSig::XMLDSIGNS);
67
        $xpathObj->registerNamespace('mdui', Common::NS);
68
        $xpathObj->registerNamespace('mdash', Common::NS);
69
        $xpathObj->registerNamespace('shibmd', Scope::NS);
70
        $nodeList = $xpathObj->query($xpath);
71
72
        if (!$nodeList || $nodeList->length === 0) {
0 ignored issues
show
$nodeList is of type DOMNodeList, thus it always evaluated to true.
Loading history...
73
            $message = sprintf('The xpath "%s" did not result in at least one match.', $xpath);
74
            throw new ExpectationException($message, $this->getSession());
75
        }
76
    }
77
78
    /**
79
     * @Then /^the ADFS response should match xpath \'([^\']*)\'$/
80
     */
81
    public function theAdfsResponseShouldMatchXpath($xpath): void
82
    {
83
        $document = new DOMDocument();
84
        $xml = $this->getSession()->getPage()->findById('saml-response-xml')->getText();
85
        $document->loadXML($xml);
86
87
        $xpathObj = new DOMXPath($document);
88
        $xpathObj->registerNamespace('ds', XMLSecurityDSig::XMLDSIGNS);
89
        $xpathObj->registerNamespace('mdui', Common::NS);
90
        $xpathObj->registerNamespace('mdash', Common::NS);
91
        $xpathObj->registerNamespace('shibmd', Scope::NS);
92
        $nodeList = $xpathObj->query($xpath);
93
94
        if (!$nodeList || $nodeList->length === 0) {
0 ignored issues
show
$nodeList is of type DOMNodeList, thus it always evaluated to true.
Loading history...
95
            $message = sprintf('The xpath "%s" did not result in at least one match.', $xpath);
96
            throw new ExpectationException($message, $this->getSession());
97
        }
98
    }
99
100
    /**
101
     * @Then /^the ADFS response should carry the ADFS POST parameters$/
102
     */
103
    public function theAdfsResponseShouldHaveAdfsPostParams(): void
104
    {
105
        $context = $this->getSession()->getPage()->findById('Context')->getText();
106
        $authMethod = $this->getSession()->getPage()->findById('AuthMethod')->getText();
107
        if ($context !== '<EncryptedData></EncryptedData>') {
108
            throw new ExpectationException('The Adfs Context POST parameter was not found or contained an invalid value', $this->getSession());
109
        }
110
        if ($authMethod !== 'ADFS.SCSA') {
111
            throw new ExpectationException('The Adfs AuthMethod POST parameter was not found or contained an invalid value', $this->getSession());
112
        }
113
    }
114
115
    /**
116
     * @Then /^the response should not match xpath \'([^\']*)\'$/
117
     */
118
    public function theResponseShouldNotMatchXpath($xpath): void
119
    {
120
        $document = new DOMDocument();
121
        $document->loadXML($this->getSession()->getPage()->getContent());
122
123
        $xpathObj = new DOMXPath($document);
124
        $xpathObj->registerNamespace('ds', XMLSecurityDSig::XMLDSIGNS);
125
        $xpathObj->registerNamespace('mdui', Common::NS);
126
        $nodeList = $xpathObj->query($xpath);
127
128
        if ($nodeList && $nodeList->length > 0) {
129
            $message = sprintf(
130
                'The xpath "%s" resulted in "%d" matches, where it should result in no matches"',
131
                $xpath,
132
                $nodeList->length
133
            );
134
            throw new ExpectationException($message, $this->getSession());
135
        }
136
    }
137
138
    /**
139
     * @Given /^I should see URL "([^"]*)"$/
140
     */
141
    public function iShouldSeeUrl($url): void
142
    {
143
        $this->assertSession()->responseContains($url);
144
    }
145
146
    /**
147
     * @Given /^I should not see URL "([^"]*)"$/
148
     */
149
    public function iShouldNotSeeUrl($url): void
150
    {
151
        $this->assertSession()->responseNotContains($url);
152
    }
153
154
    /**
155
     * @Given /^I open (\d+) browser tabs identified by "([^"]*)"$/
156
     */
157
    public function iOpenTwoBrowserTabsIdentifiedBy($numberOfTabs, $tabNames): void
158
    {
159
        // On successive scenarios, reset the session to get rid of browser (session) state from previous scenarios
160
        if ($this->getMink()->getSession()->isStarted()) {
161
            $this->getMink()->getSession()->restart();
162
        }
163
        // Make sure the browser is ready (without this other browser interactions fail)
164
        $this->getSession()->visit($this->locatePath('#'));
165
166
        $tabs = explode(',', $tabNames);
167
        if (count($tabs) != $numberOfTabs) {
168
            throw new RuntimeException(
169
                'Please identify all tabs you are opening in order to refer to them at a later stage'
170
            );
171
        }
172
173
        foreach ($tabs as $tab) {
174
            $this->getMink()
175
                ->getSession()
176
                ->executeScript("window.open('/','_blank');");
177
178
            $windowsNames = $this->getSession()->getWindowNames();
179
180
            if (!$windowsNames) {
181
                throw new RuntimeException('The windows where not opened correctly.');
182
            }
183
            // Grab the window name (which is the last one added to the window list)
184
            $windowName = array_pop($windowsNames);
185
            // Keep track of the opened windows in order allow switching between them
186
            $this->windows[trim($tab)] = $windowName;
187
        }
188
189
    }
190
191
    /**
192
     * @Given /^I switch to "([^"]*)"$/
193
     */
194
    public function iSwitchToWindow($windowName): void
195
    {
196
        // (re) set the default session to the chrome session.
197
        $this->switchToWindow($windowName);
198
    }
199
200
    public function switchToWindow($windowName): void
201
    {
202
        if (!isset($this->windows[$windowName])) {
203
            throw new RuntimeException(sprintf('Unknown window/tab name "%s"', $windowName));
204
        }
205
        $this->getSession()->switchToWindow($this->windows[$windowName]);
206
    }
207
}
208