Passed
Pull Request — master (#9)
by Rafael
03:29
created

GraphQLContext::variables()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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