Passed
Push — master ( b2e0cb...ba1565 )
by Rafael
04:39
created

GraphQLContext::beforeStep()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 0
crap 6
1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Behat\Context;
12
13
use Behat\Behat\Context\Context;
14
use Behat\Behat\Hook\Scope\AfterStepScope;
15
use Behat\Behat\Hook\Scope\BeforeFeatureScope;
16
use Behat\Gherkin\Node\PyStringNode;
17
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
18
use Symfony\Component\HttpFoundation\File\File;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\PropertyAccess\PropertyAccessor;
21
use Symfony\Component\Stopwatch\Stopwatch;
22
use Ynlo\GraphQLBundle\Behat\Client\ClientAwareInterface;
23
use Ynlo\GraphQLBundle\Behat\Client\ClientAwareTrait;
24
use Ynlo\GraphQLBundle\Behat\Gherkin\YamlStringNode;
25
26
/**
27
 * Context to work with GraphQL
28
 * send queries, mutations etc
29
 *
30
 * @property File $currentFeatureFile
31
 */
32
final class GraphQLContext implements Context, ClientAwareInterface
33
{
34
    use ClientAwareTrait;
35
36
    /**
37
     * @var File
38
     */
39
    private static $currentFeatureFile;
40
41
    /**
42
     * @BeforeFeature
43
     */
44
    public static function prepareGraphQLContext(BeforeFeatureScope $scope)
45
    {
46
        self::$currentFeatureFile = new File($scope->getFeature()->getFile());
47
    }
48
49
    /**
50
     * @AfterStep
51
     */
52
    public function after(AfterStepScope $scope)
53
    {
54
        if (!$scope->getTestResult()->isPassed()) {
55
            if ($this->client->getResponse() && $this->client->getResponse()->getStatusCode() >= 400) {
56
                $this->debugLastQuery();
57
            }
58
        }
59
    }
60
61
    /**
62
     * Set GraphQL operation
63
     *
64
     * Example: Given the operation:
65
     *          """
66
     *          query($id: ID!){
67
     *              node(id: $id) {
68
     *                  id
69
     *                  ... on Post {
70
     *                      title
71
     *                      body
72
     *                  }
73
     *              }
74
     *          }
75
     *          """
76
     *
77
     * @Given /^the operation:$/
78
     */
79
    public function theOperation(PyStringNode $string)
80
    {
81
        $this->client->setGraphQL($string->getRaw());
82
    }
83
84
    /**
85
     * Find for a file to read the GraphQL query
86
     * the file must not contains more than one query
87
     *
88
     * Example: Given the operation in file 'some_query.graphql'
89
     *
90
     * @Given /^the operation in file "([^"]*)"$/
91
     */
92
    public function theOperationInFile($filename)
93
    {
94
        $queryFile = sprintf('%s%s%s', self::$currentFeatureFile->getPath(), DIRECTORY_SEPARATOR, $filename);
95
        if (file_exists($queryFile)) {
96
            $file = new File($queryFile);
97
            $this->client->setGraphQL(file_get_contents($file->getPathname()));
98
        } else {
99
            throw new FileNotFoundException(null, 0, null, $queryFile);
100
        }
101
    }
102
103
    /**
104
     * Find for specific query name in given file.
105
     * The file can contain multiple named queries.
106
     *
107
     * Example: Given the operation named "GetUser" in file 'queries.graphql'
108
     *
109
     * @Given /^the operation named "([^"]*)" in file "([^"]*)"$/
110
     */
111
    public function theOperationNamedInFile($queryName, $file)
112
    {
113
        $this->theOperationInFile($file);
114
        $this->operationName = $queryName;
0 ignored issues
show
Bug Best Practice introduced by
The property operationName does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
115
        if ($this->client->getGraphQL()) {
116
            // remove non necessary operations to avoid errors with unsettled variables
117
            $pattern = '/(query|mutation|subscription)\s+(?!'.$queryName.'\s*[\({])(.+\n)+}\n*/';
118
            $this->client->setGraphQL(preg_replace($pattern, null, $this->client->getGraphQL()));
119
        }
120
121
        if ($queryName) {
122
            if (strpos($this->client->getGraphQL(), $queryName) === false) {
123
                throw new \RuntimeException(sprintf('Does not exist any operation called "%s" in "%s"', $queryName, $file));
124
            }
125
        }
126
    }
127
128
    /**
129
     * Find for specific query name in file with the same name of current feature.
130
     * e.g. some_feature.feature => some_query.graphql
131
     *
132
     * The file can contain multiple named queries.
133
     *
134
     * Example: Given the operation named "GetUser"
135
     *
136
     * @Given /^the operation named "([^"]*)"$/
137
     */
138
    public function theOperationNamed($queryName)
139
    {
140
        $queryFilename = str_replace('.feature', '.graphql', self::$currentFeatureFile->getBasename());
141
        $this->theOperationNamedInFile($queryName, $queryFilename);
142
    }
143
144
    /**
145
     * @When send
146
     */
147
    public function send()
148
    {
149
        $watch = new Stopwatch();
150
        $watch->start('query');
151
        $this->client->sendQuery();
152
        $watch->stop('query');
153
    }
154
155
    /**
156
     * Set query variable with scalar value before run the given query
157
     *
158
     * Example: And variable "username" is "admin"
159
     * Example: And variable "limit" is 2
160
     * Example: And variable "visible" is true
161
     * Example: And variable "orderBy" is { [{field:'login', direction: 'DESC'}] }
162
     *
163
     * @Given /^variable "([^"]*)" is "?([^"]*)"?$/
164
     */
165
    public function setVariableEqualTo($path, $value)
166
    {
167
        $accessor = new PropertyAccessor();
168
        $variables = $this->client->getVariables();
169
        $accessor->setValue($variables, sprintf("[%s]", $path), $value);
170
        $this->client->setVariables($variables);
171
    }
172
173
    /**
174
     * Allow set multiple variables using YAML syntax
175
     *
176
     * Example:
177
     * And variables:
178
     *       """
179
     *       input:
180
     *          clientMutationId: "'{faker.randomNumber}'"
181
     *          status: PUBLISHED
182
     *          title: "{faker.sentence}"
183
     *          body: "{faker.paragraph}"
184
     *          tags: ['asd', 'asdsd']
185
     *          categories:
186
     *              - "#category1"
187
     *              - "#category2"
188
     *       """
189
     *
190
     * @Given /^variables:$/
191
     */
192
    public function variables(YamlStringNode $variables)
193
    {
194
        $this->client->setVariables($variables->toArray());
195
    }
196
197
    /**
198
     * Print helpful debug information for latest executed query
199
     *
200
     * @Then debug last query
201
     */
202
    public function debugLastQuery()
203
    {
204
        if ($this->client->getGraphQL()) {
205
206
            /** @var Response $response */
207
            $response = $this->client->getResponse();
208
209
            $content = $response->getContent();
210
            $json = @json_decode($content, true);
211
212
            $error = $response->getStatusCode() >= 400;
213
            if ($json && isset($json['errors'])) {
214
                $error = true;
215
            }
216
217
            $bg = $error ? 41 : 42;
218
            print_r("\n\n");
219
            print_r("\033[{$bg}m-------------------- RESPONSE ----------------------\033[0m\n\n");
220
            print_r(sprintf("STATUS: [%s] %s \n", $response->getStatusCode(), Response::$statusTexts[$response->getStatusCode()] ?? 'Unknown Status'));
221
222
            if ($json) {
223
                $output = json_encode($json, JSON_PRETTY_PRINT);
224
            } else {
225
                $output = $content;
226
            }
227
228
            print_r($output);
229
230
            print_r("\n\n");
231
            print_r("\033[46m------------------- VARIABLES-----------------------\033[0m\n\n");
232
            $variables = $this->client->getVariables() ?? null;
233
            print_r(json_encode($variables, JSON_PRETTY_PRINT));
234
235
            $query = $this->client->getGraphQL() ?? null;
236
            $type = 'QUERY';
237
            if (preg_match('/^\s*mutation/', $query)) {
238
                $type = 'MUTATION';
239
            }
240
241
            print_r("\n\n\033[43m----------------------- $type ---------------------\033[0m\n\n");
242
            print_r($query ?? null);
243
            print_r("\n\n");
244
            print_r("-----------------------------------------------------\n\n");
245
            ob_flush();
246
        } else {
247
            throw new \RuntimeException('Does not exist any executed query on current test, try use this method after "send" the query.');
248
        }
249
    }
250
251
252
}
253