Completed
Pull Request — master (#206)
by Albin
14:41
created

ApiContext::getHeaders()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 16
rs 9.2
c 1
b 0
f 0
cc 4
eloc 9
nc 4
nop 0
1
<?php
2
3
namespace Knp\FriendlyContexts\Context;
4
5
use Behat\Gherkin\Node\TableNode;
6
use Behat\Gherkin\Node\PyStringNode;
7
use Guzzle\Common\ToArrayInterface;
8
use Guzzle\Http\Exception\BadResponseException;
9
use Knp\FriendlyContexts\Http\Security\HttpExtension;
10
11
class ApiContext extends RawPageContext
12
{
13
    protected $response;
14
15
    public function getResponse()
16
    {
17
        return $this->response;
18
    }
19
20
    protected function getHeaders()
21
    {
22
        if (null !== $this->response) {
23
            throw new \InvalidArgumentException('First, you need to send a request.');
24
        }
25
26
        $headers = $this->response->getHeaders();
27
28
        if ($headers instanceof ToArrayInterface) {
29
            return $headers->toArray();
30
        } elseif (is_array($headers)) {
31
            return $headers;
32
        }
33
34
        throw new \RuntimeException('Unable to retrieve response headers.');
35
    }
36
37
    /**
38
     * @Given /^I prepare a (?<method>[A-Za-z]+) request on "(?<page>[^"].*)?"$/
39
     * @Given /^I prepare a (?<method>[A-Za-z]+) request on the (.*) (?<hasPage>page|resource)$/
40
     * @Given /^I prepare a (?<method>[A-Za-z]+) request on the (.*) (?<hasPage>page|resource) with:?$/
41
     */
42
    public function iPrepareRequest($method, $page, $hasPage = false, TableNode $table = null)
43
    {
44
        $hasPage = (bool)$hasPage;
45
        $method  = strtoupper($method);
46
47
        $this->getRequestBuilder()->setMethod($method);
48
49
        if ($hasPage) {
50
            $path = $this->getPagePath($page, $table);
51
        } else {
52
            $path = $page;
53
        }
54
55
        $this->getRequestBuilder()->setUri($path);
56
    }
57
58
    /**
59
     * @Given /^I specified the following request http (?<scheme>[a-z]+) credentials:?$/
60
     */
61
    public function iSpecifiedTheFollowingHttpAuthentication(TableNode $credentialsTable, $scheme)
62
    {
63
        $this
64
            ->getRequestBuilder()
65
            ->setCredentials($credentialsTable->getRowsHash())
66
            ->addSecurityExtension(new HttpExtension($scheme))
67
        ;
68
    }
69
70
    /**
71
     * @Given /^I specified the following request oauth credentials:?$/
72
     */
73
    public function iSpecifiedTheFollowingOauthCredentials(TableNode $credentialsTable)
74
    {
75
        $this
76
            ->getRequestBuilder()
77
            ->setCredentials($credentialsTable->getRowsHash())
78
            ->addSecurityExtension(new OauthExtension)
79
        ;
80
    }
81
82
    /**
83
     * @Given /^I specified the following request headers:?$/
84
     */
85
    public function iSpecifiedHeaders(TableNode $table)
86
    {
87
        $this->getRequestBuilder()->setHeaders($table->getRowsHash());
88
    }
89
90
    /**
91
     * @Given /^I specified the following request queries:?$/
92
     */
93
    public function iSpecifiedQueries(TableNode $table)
94
    {
95
        $this->getRequestBuilder()->setQueries($table->getRowsHash());
96
    }
97
98
    /**
99
     * @Given /^I specified the following request body:?$/
100
     */
101
    public function iSpecifiedTheBody($data)
102
    {
103
        if (is_object($data) and $data instanceof TableNode) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
104
            $data = $data->getRowsHash();
105
        }
106
107
        if (is_object($data) and $data instanceof PyStringNode) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
108
            $data = (string)$data;
109
        }
110
111
        $this->getRequestBuilder()->setBody($data);
112
    }
113
114
    /**
115
     * @Given /^I specified the following request data:?$/
116
     */
117
    public function iSpecifiedData(TableNode $dataTable)
118
    {
119
        $requestBuilder = $this->getRequestBuilder();
120
121
        if ('POST' === $requestBuilder->getMethod()) {
122
            $requestBuilder->setPostBody($dataTable->getRowsHash());
123
        } else {
124
            $requestBuilder->setBody($dataTable->getRowsHash());
125
        }
126
    }
127
128
    /**
129
     * @Given /^I specified the following request files:?$/
130
     */
131
    public function iSpecifiedFiles(TableNode $fileTable)
132
    {
133
        $requestBuilder = $this->getRequestBuilder();
134
135
        if ('POST' !== $requestBuilder->getMethod()) {
136
            throw new \RuntimeException('You can\'t send files with a non POST method');
137
        }
138
139
        foreach ($fileTable->getRowsHash() as $name => $path) {
140
            $requestBuilder->addFile($name, $path);
141
        }
142
    }
143
144
    /**
145
     * @Given /^I specified the following request cookies:?$/
146
     */
147
    public function iSpecifiedCookies(TableNode $cookiesTable)
148
    {
149
        $this->getRequestBuilder()->setCookies($cookiesTable->getRowsHash());
150
    }
151
152
    /**
153
     * @Given /^I specified the following request options:?$/
154
     */
155
    public function iSpecifiedOptions(TableNode $optionsTable)
156
    {
157
        $this->getRequestBuilder()->setOptions($optionsTable->getRowsHash());
158
    }
159
160
    /**
161
     * @When /^I send the request$/
162
     */
163
    public function iSendTheRequest()
164
    {
165
        try {
166
            $this->response = $this->getRequestBuilder()->build()->send();
167
        } catch (BadResponseException $e) {
168
            $this->response = $e->getResponse();
169
        }
170
    }
171
172
    /**
173
     * @Then /^I should receive a (?<httpCode>[0-9]+) response$/
174
     * @Then /^I should receive a (?<httpCode>[0-9]+) (?<shortType>[a-zA-Z]+) response$/
175
     */
176
    public function iShouldReceiveResponse($httpCode, $shortType = null)
177
    {
178
        $httpCode = (int)$httpCode;
179
180
        if (null === $this->response) {
181
            throw new \RuntimeException('You must send a request before testing a response.');
182
        }
183
184
        $this->getAsserter()->assertEquals(
185
            $httpCode,
186
            $this->response->getStatusCode(),
187
            sprintf(
188
                'Expecting response code to be "%d" but "%d" given',
189
                $httpCode,
190
                $this->response->getStatusCode()
191
            )
192
        );
193
194
        if (null !== $shortType) {
195
            $contentTypes = $this->getHttpContentTypeGuesser()->guess($shortType);
196
197
            foreach ($contentTypes as $contentType) {
198
                try {
199
                    $formatedContentType = explode(';', $this->response->getContentType());
200
                    $formatedContentType = $formatedContentType[0];
201
202
                    $this
203
                        ->getAsserter()
204
                        ->assertEquals($contentType, $formatedContentType)
205
                     ;
206
                    return;
207
                } catch (\Exception $e) {
208
                    continue;
209
                }
210
            }
211
212
            throw new \Exception(sprintf(
213
                'The response Content-Type ("%s") is not a(n) "%s" response type',
214
                $formatedContentType,
215
                $shortType
216
            ));
217
        }
218
    }
219
220
    /**
221
     * @Then /^the response should contains? the following headers:?$/
222
     */
223
    public function theResponseShouldContainsHeaders(TableNode $headerTable)
224
    {
225
        if (null === $this->response) {
226
            throw new \RuntimeException('You must send a request before testing a response.');
227
        }
228
229
        $expectedHeaders = $headerTable->getRowsHash();
230
        $this->getAsserter()->assertArrayContains(
231
            $expectedHeaders,
232
            $this->getHeaders()
233
        );
234
    }
235
236
    /**
237
     * @Then /^the response should contains? the following json:?$/
238
     */
239
    public function theResponsShouldContainsJson($jsonData)
240
    {
241
        if (!is_object($jsonData)) {
242
            throw new \InvalidArgumentException('Invalid json data');
243
        }
244
245
        $json = false;
246
247
        if ($jsonData instanceof PyStringNode) {
248
            $json = json_decode($jsonData->getRaw(), true);
249
        } elseif ($jsonData instanceof TableNode) {
250
            $json = $jsonData->getRowsHash();
251
        } elseif ($jsonData instanceof \stdClass || true === is_array($jsonData)) {
252
            $json = $jsonData;
253
        }
254
255
        if (false === $json) {
256
            throw new \InvalidArgumentException(sprintf(
257
                'Invalid json data class ("%s")',
258
                get_class($jsonData)
259
            ));
260
        }
261
262
        $expected = json_encode($json);
263
        $real     = json_encode($this->response->json());
264
265
        $this->getAsserter()->assertEquals(
266
            $expected,
267
            $real,
268
            sprintf("The given json\r\n\r\n%s\r\nis not equal to the expected\r\n\r\n%s",
269
                $real,
270
                $expected
271
            )
272
        );
273
    }
274
275
    /**
276
     * @Then /^the response should contains?:?$/
277
     */
278
    public function theResponseShouldContains(PyStringNode $bodyNode)
279
    {
280
        $this->getAsserter()->assertEquals($bodyNode->getRaw(), $this->response->getBody(true));
281
    }
282
}
283