Passed
Push — master ( 969439...b2e0cb )
by Rafael
05:20
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 Behat\Symfony2Extension\Context\KernelAwareContext;
18
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
19
use Symfony\Component\HttpFoundation\File\File;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\HttpKernel\KernelInterface;
22
use Symfony\Component\PropertyAccess\PropertyAccessor;
23
use Symfony\Component\Stopwatch\Stopwatch;
24
use Ynlo\GraphQLBundle\Behat\Client\ClientAwareInterface;
25
use Ynlo\GraphQLBundle\Behat\Client\ClientAwareTrait;
26
use Ynlo\GraphQLBundle\Behat\Gherkin\YamlStringNode;
27
use Ynlo\GraphQLBundle\Definition\Registry\DefinitionRegistry;
28
29
/**
30
 * Context to work with GraphQL
31
 * send queries, mutations etc
32
 *
33
 * @property File $currentFeatureFile
34
 */
35
final class GraphQLContext implements Context, ClientAwareInterface, KernelAwareContext
36
{
37
    use ClientAwareTrait;
38
39
    /**
40
     * @var KernelInterface
41
     */
42
    private $kernel;
43
44
    /**
45
     * @var File
46
     */
47
    private static $currentFeatureFile;
48
49
    /**
50
     * @var bool
51
     */
52
    private static $cacheCleared = false;
53
54
    /**
55
     * @BeforeFeature
56
     */
57
    public static function prepareGraphQLContext(BeforeFeatureScope $scope)
58
    {
59
        self::$currentFeatureFile = new File($scope->getFeature()->getFile());
60
    }
61
62
    /**
63
     * @BeforeStep
64
     */
65
    public function beforeStep()
66
    {
67
        if (!self::$cacheCleared) {
68
            self::$cacheCleared = true;
69
            $this->kernel->getContainer()->get(DefinitionRegistry::class)->clearCache();
70
        }
71
    }
72
73
    /**
74
     * @AfterStep
75
     */
76
    public function after(AfterStepScope $scope)
77
    {
78
        if (!$scope->getTestResult()->isPassed()) {
79
            if ($this->client->getResponse() && $this->client->getResponse()->getStatusCode() >= 400) {
80
                $this->debugLastQuery();
81
            }
82
        }
83
    }
84
85
    /**
86
     * @param KernelInterface $kernel
87
     */
88
    public function setKernel(KernelInterface $kernel): void
89
    {
90
        $this->kernel = $kernel;
91
    }
92
93
    /**
94
     * Set GraphQL operation
95
     *
96
     * Example: Given the operation:
97
     *          """
98
     *          query($id: ID!){
99
     *              node(id: $id) {
100
     *                  id
101
     *                  ... on Post {
102
     *                      title
103
     *                      body
104
     *                  }
105
     *              }
106
     *          }
107
     *          """
108
     *
109
     * @Given /^the operation:$/
110
     */
111
    public function theOperation(PyStringNode $string)
112
    {
113
        $this->client->setGraphQL($string->getRaw());
114
    }
115
116
    /**
117
     * Find for a file to read the GraphQL query
118
     * the file must not contains more than one query
119
     *
120
     * Example: Given the operation in file 'some_query.graphql'
121
     *
122
     * @Given /^the operation in file "([^"]*)"$/
123
     */
124
    public function theOperationInFile($filename)
125
    {
126
        $queryFile = sprintf('%s%s%s', self::$currentFeatureFile->getPath(), DIRECTORY_SEPARATOR, $filename);
127
        if (file_exists($queryFile)) {
128
            $file = new File($queryFile);
129
            $this->client->setGraphQL(file_get_contents($file->getPathname()));
130
        } else {
131
            throw new FileNotFoundException(null, 0, null, $queryFile);
132
        }
133
    }
134
135
    /**
136
     * Find for specific query name in given file.
137
     * The file can contain multiple named queries.
138
     *
139
     * Example: Given the operation named "GetUser" in file 'queries.graphql'
140
     *
141
     * @Given /^the operation named "([^"]*)" in file "([^"]*)"$/
142
     */
143
    public function theOperationNamedInFile($queryName, $file)
144
    {
145
        $this->theOperationInFile($file);
146
        $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...
147
        if ($this->client->getGraphQL()) {
148
            // remove non necessary operations to avoid errors with unsettled variables
149
            $pattern = '/(query|mutation|subscription)\s+(?!'.$queryName.'\s*[\({])(.+\n)+}\n*/';
150
            $this->client->setGraphQL(preg_replace($pattern, null, $this->client->getGraphQL()));
151
        }
152
153
        if ($queryName) {
154
            if (strpos($this->client->getGraphQL(), $queryName) === false) {
155
                throw new \RuntimeException(sprintf('Does not exist any operation called "%s" in "%s"', $queryName, $file));
156
            }
157
        }
158
    }
159
160
    /**
161
     * Find for specific query name in file with the same name of current feature.
162
     * e.g. some_feature.feature => some_query.graphql
163
     *
164
     * The file can contain multiple named queries.
165
     *
166
     * Example: Given the operation named "GetUser"
167
     *
168
     * @Given /^the operation named "([^"]*)"$/
169
     */
170
    public function theOperationNamed($queryName)
171
    {
172
        $queryFilename = str_replace('.feature', '.graphql', self::$currentFeatureFile->getBasename());
173
        $this->theOperationNamedInFile($queryName, $queryFilename);
174
    }
175
176
    /**
177
     * @When send
178
     */
179
    public function send()
180
    {
181
        $watch = new Stopwatch();
182
        $watch->start('query');
183
        $this->client->sendQuery();
184
        $watch->stop('query');
185
    }
186
187
    /**
188
     * Set query variable with scalar value before run the given query
189
     *
190
     * Example: And variable "username" is "admin"
191
     * Example: And variable "limit" is 2
192
     * Example: And variable "visible" is true
193
     * Example: And variable "orderBy" is { [{field:'login', direction: 'DESC'}] }
194
     *
195
     * @Given /^variable "([^"]*)" is "?([^"]*)"?$/
196
     */
197
    public function setVariableEqualTo($path, $value)
198
    {
199
        $accessor = new PropertyAccessor();
200
        $variables = $this->client->getVariables();
201
        $accessor->setValue($variables, sprintf("[%s]", $path), $value);
202
        $this->client->setVariables($variables);
203
    }
204
205
    /**
206
     * Allow set multiple variables using YAML syntax
207
     *
208
     * Example:
209
     * And variables:
210
     *       """
211
     *       input:
212
     *          clientMutationId: "'{faker.randomNumber}'"
213
     *          status: PUBLISHED
214
     *          title: "{faker.sentence}"
215
     *          body: "{faker.paragraph}"
216
     *          tags: ['asd', 'asdsd']
217
     *          categories:
218
     *              - "#category1"
219
     *              - "#category2"
220
     *       """
221
     *
222
     * @Given /^variables:$/
223
     */
224
    public function variables(YamlStringNode $variables)
225
    {
226
        $this->client->setVariables($variables->toArray());
227
    }
228
229
    /**
230
     * Print helpful debug information for latest executed query
231
     *
232
     * @Then debug last query
233
     */
234
    public function debugLastQuery()
235
    {
236
        if ($this->client->getGraphQL()) {
237
238
            /** @var Response $response */
239
            $response = $this->client->getResponse();
240
241
            $content = $response->getContent();
242
            $json = @json_decode($content, true);
243
244
            $error = $response->getStatusCode() >= 400;
245
            if ($json && isset($json['errors'])) {
246
                $error = true;
247
            }
248
249
            $bg = $error ? 41 : 42;
250
            print_r("\n\n");
251
            print_r("\033[{$bg}m-------------------- RESPONSE ----------------------\033[0m\n\n");
252
            print_r(sprintf("STATUS: [%s] %s \n", $response->getStatusCode(), Response::$statusTexts[$response->getStatusCode()] ?? 'Unknown Status'));
253
254
            if ($json) {
255
                $output = json_encode($json, JSON_PRETTY_PRINT);
256
            } else {
257
                $output = $content;
258
            }
259
260
            print_r($output);
261
262
            print_r("\n\n");
263
            print_r("\033[46m------------------- VARIABLES-----------------------\033[0m\n\n");
264
            $variables = $this->client->getVariables() ?? null;
265
            print_r(json_encode($variables, JSON_PRETTY_PRINT));
266
267
            $query = $this->client->getGraphQL() ?? null;
268
            $type = 'QUERY';
269
            if (preg_match('/^\s*mutation/', $query)) {
270
                $type = 'MUTATION';
271
            }
272
273
            print_r("\n\n\033[43m----------------------- $type ---------------------\033[0m\n\n");
274
            print_r($query ?? null);
275
            print_r("\n\n");
276
            print_r("-----------------------------------------------------\n\n");
277
            ob_flush();
278
        } else {
279
            throw new \RuntimeException('Does not exist any executed query on current test, try use this method after "send" the query.');
280
        }
281
    }
282
283
284
}
285