GraphQLContext   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 8
Bugs 0 Features 1
Metric Value
wmc 23
eloc 61
c 8
b 0
f 1
dl 0
loc 216
ccs 0
cts 96
cp 0
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A variables() 0 3 1
A theOperationInFile() 0 8 2
A send() 0 3 1
B debugLastQuery() 0 46 7
A theOperation() 0 3 1
A after() 0 5 4
A setVariableEqualTo() 0 6 1
A theOperationNamedInFile() 0 16 4
A theOperationNamed() 0 4 1
A prepareGraphQLContext() 0 3 1
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 Ynlo\GraphQLBundle\Behat\Client\ClientAwareInterface;
22
use Ynlo\GraphQLBundle\Behat\Client\ClientAwareTrait;
23
use Ynlo\GraphQLBundle\Behat\Gherkin\YamlStringNode;
24
25
/**
26
 * Context to work with GraphQL
27
 * send queries, mutations etc
28
 *
29
 * @property File $currentFeatureFile
30
 */
31
final class GraphQLContext implements Context, ClientAwareInterface
32
{
33
    use ClientAwareTrait;
34
35
    /**
36
     * @var File
37
     */
38
    private static $currentFeatureFile;
39
40
    /**
41
     * @BeforeFeature
42
     */
43
    public static function prepareGraphQLContext(BeforeFeatureScope $scope)
44
    {
45
        self::$currentFeatureFile = new File($scope->getFeature()->getFile());
0 ignored issues
show
Bug introduced by
It seems like $scope->getFeature()->getFile() can also be of type null; however, parameter $path of Symfony\Component\HttpFo...ile\File::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

45
        self::$currentFeatureFile = new File(/** @scrutinizer ignore-type */ $scope->getFeature()->getFile());
Loading history...
46
    }
47
48
    /**
49
     * @AfterStep
50
     */
51
    public function after(AfterStepScope $scope)
52
    {
53
        if (!$scope->getTestResult()->isPassed()) {
54
            if ($this->client->getResponse() && $this->client->getResponse()->getStatusCode() >= 400) {
55
                $this->debugLastQuery();
56
            }
57
        }
58
    }
59
60
    /**
61
     * Set GraphQL operation
62
     *
63
     * Example: Given the operation:
64
     *          """
65
     *          query($id: ID!){
66
     *              node(id: $id) {
67
     *                  id
68
     *                  ... on Post {
69
     *                      title
70
     *                      body
71
     *                  }
72
     *              }
73
     *          }
74
     *          """
75
     *
76
     * @Given /^the operation:$/
77
     */
78
    public function theOperation(PyStringNode $string)
79
    {
80
        $this->client->setGraphQL($string->getRaw());
81
    }
82
83
    /**
84
     * Find for a file to read the GraphQL query
85
     * the file must not contains more than one query
86
     *
87
     * Example: Given the operation in file 'some_query.graphql'
88
     *
89
     * @Given /^the operation in file "([^"]*)"$/
90
     */
91
    public function theOperationInFile($filename)
92
    {
93
        $queryFile = sprintf('%s%s%s', self::$currentFeatureFile->getPath(), DIRECTORY_SEPARATOR, $filename);
94
        if (file_exists($queryFile)) {
95
            $file = new File($queryFile);
96
            $this->client->setGraphQL(file_get_contents($file->getPathname()));
97
        } else {
98
            throw new FileNotFoundException(null, 0, null, $queryFile);
99
        }
100
    }
101
102
    /**
103
     * Find for specific query name in given file.
104
     * The file can contain multiple named queries.
105
     *
106
     * Example: Given the operation named "GetUser" in file 'queries.graphql'
107
     *
108
     * @Given /^the operation named "([^"]*)" in file "([^"]*)"$/
109
     */
110
    public function theOperationNamedInFile($queryName, $file)
111
    {
112
        // TODO: add support for fragments
113
        // if a fragment is not used in some operation in the same file a error is thrown
114
115
        $this->theOperationInFile($file);
116
        $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...
117
        if ($this->client->getGraphQL()) {
118
            // remove non necessary operations to avoid errors with unsettled variables
119
            $pattern = '/(query|mutation|subscription)\s+(?!'.$queryName.'\s*[\({])(.+\n)+}\n*/';
120
            $this->client->setGraphQL(preg_replace($pattern, null, $this->client->getGraphQL()));
121
        }
122
123
        if ($queryName) {
124
            if (strpos($this->client->getGraphQL(), $queryName) === false) {
125
                throw new \RuntimeException(sprintf('Does not exist any operation called "%s" in "%s"', $queryName, $file));
126
            }
127
        }
128
    }
129
130
    /**
131
     * Find for specific query name in file with the same name of current feature.
132
     * e.g. some_feature.feature => some_query.graphql
133
     *
134
     * The file can contain multiple named queries.
135
     *
136
     * Example: Given the operation named "GetUser"
137
     *
138
     * @Given /^the operation named "([^"]*)"$/
139
     */
140
    public function theOperationNamed($queryName)
141
    {
142
        $queryFilename = str_replace('.feature', '.graphql', self::$currentFeatureFile->getBasename());
143
        $this->theOperationNamedInFile($queryName, $queryFilename);
144
    }
145
146
    /**
147
     * @When send
148
     */
149
    public function send()
150
    {
151
        $this->client->sendQuery();
152
    }
153
154
    /**
155
     * Set query variable with scalar value before run the given query
156
     *
157
     * Example: And variable "username" is "admin"
158
     * Example: And variable "limit" is 2
159
     * Example: And variable "visible" is true
160
     * Example: And variable "orderBy" is { [{field:'login', direction: 'DESC'}] }
161
     *
162
     * @Given /^variable "([^"]*)" is "?([^"]*)"?$/
163
     */
164
    public function setVariableEqualTo($path, $value)
165
    {
166
        $accessor = new PropertyAccessor();
167
        $variables = $this->client->getVariables();
168
        $accessor->setValue($variables, sprintf("[%s]", $path), $value);
169
        $this->client->setVariables($variables);
170
    }
171
172
    /**
173
     * Allow set multiple variables using YAML syntax
174
     *
175
     * Example:
176
     * And variables:
177
     *       """
178
     *       input:
179
     *          clientMutationId: "'{faker.randomNumber}'"
180
     *          status: PUBLISHED
181
     *          title: "{faker.sentence}"
182
     *          body: "{faker.paragraph}"
183
     *          tags: ['asd', 'asdsd']
184
     *          categories:
185
     *              - "#category1"
186
     *              - "#category2"
187
     *       """
188
     *
189
     * @Given /^variables:$/
190
     */
191
    public function variables(YamlStringNode $variables)
192
    {
193
        $this->client->setVariables($variables->toArray());
194
    }
195
196
    /**
197
     * Print helpful debug information for latest executed query
198
     *
199
     * @Then debug last query
200
     */
201
    public function debugLastQuery()
202
    {
203
        if ($this->client->getGraphQL()) {
204
205
            /** @var Response $response */
206
            $response = $this->client->getResponse();
207
208
            $content = $response->getContent();
209
            $json = @json_decode($content, true);
210
211
            $error = $response->getStatusCode() >= 400;
212
            if ($json && isset($json['errors'])) {
213
                $error = true;
214
            }
215
216
            $bg = $error ? 41 : 42;
217
            print_r("\n\n");
218
            print_r("\033[{$bg}m-------------------- RESPONSE ----------------------\033[0m\n\n");
219
            print_r(sprintf("STATUS: [%s] %s \n", $response->getStatusCode(), Response::$statusTexts[$response->getStatusCode()] ?? 'Unknown Status'));
220
221
            if ($json) {
222
                $output = json_encode($json, JSON_PRETTY_PRINT);
223
            } else {
224
                $output = $content;
225
            }
226
227
            print_r($output);
228
229
            print_r("\n\n");
230
            print_r("\033[46m------------------- VARIABLES-----------------------\033[0m\n\n");
231
            $variables = $this->client->getVariables() ?? null;
232
            print_r(json_encode($variables, JSON_PRETTY_PRINT));
233
234
            $query = $this->client->getGraphQL() ?? null;
235
            $type = 'QUERY';
236
            if (preg_match('/^\s*mutation/', $query)) {
237
                $type = 'MUTATION';
238
            }
239
240
            print_r("\n\n\033[43m----------------------- $type ---------------------\033[0m\n\n");
241
            print_r($query ?? null);
242
            print_r("\n\n");
243
            print_r("-----------------------------------------------------\n\n");
244
            ob_flush();
245
        } else {
246
            throw new \RuntimeException('Does not exist any executed query on current test, try use this method after "send" the query.');
247
        }
248
    }
249
250
251
}
252