Passed
Pull Request — master (#30)
by Baptiste
03:36 queued 01:24
created

DebugHttp::getSubscribedEvents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
1
<?php
2
namespace Behapi\EventListener;
3
4
use Psr\Http\Message\RequestInterface;
5
use Psr\Http\Message\ResponseInterface;
6
7
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
8
9
use Behat\Testwork\Tester\Result\TestResult;
10
use Behat\Testwork\Tester\Result\TestResults;
11
use Behat\Testwork\EventDispatcher\Event\AfterTested;
12
13
use Behat\Behat\EventDispatcher\Event\OutlineTested;
14
use Behat\Behat\EventDispatcher\Event\ScenarioTested;
15
use Behat\Behat\EventDispatcher\Event\BackgroundTested;
16
use Behat\Behat\EventDispatcher\Event\GherkinNodeTested;
17
18
use Behat\Gherkin\Node\TaggedNodeInterface;
19
20
use Behapi\Tools\Debug;
21
use Behapi\Tools\HttpHistory as History;
22
23
/**
24
 * Debug http
25
 *
26
 * Allows to debug a scenario, or, if the debug is activated, to print
27
 * a message if a scenario failed for http requests.
28
 *
29
 * @author Baptiste Clavié <[email protected]>
30
 */
31
class DebugHttp implements EventSubscriberInterface
32
{
33
    // 1 - key
34
    // 2 - value
35
    const TEMPLATE = "\033[36m| \033[1m%s : \033[0;36m%s\033[0m\n";
36
37
    /** @var History */
38
    private $history;
39
40
    /** @var Debug */
41
    private $debug;
42
43
    public function __construct(Debug $debug, History $history)
44
    {
45
        $this->debug = $debug;
46
        $this->history = $history;
47
    }
48
49
    /** {@inheritDoc} */
50
    public static function getSubscribedEvents()
51
    {
52
        return [
53
            OutlineTested::AFTER => 'debugAfter',
54
            ScenarioTested::AFTER => 'debugAfter',
55
            BackgroundTested::AFTER => 'debugAfter',
56
        ];
57
    }
58
59
    public function debugAfter(GherkinNodeTested $event): void
60
    {
61
        if (!$event instanceof AfterTested) {
62
            return;
63
        }
64
65
        // no http tag... still no chocolates
66
        if (!$this->hasTag($event, 'http')) {
67
            return;
68
        }
69
70
        // debug only if tag is present (all debug) or only on test failures
71
        if ($this->hasTag($event, 'debug')) {
72
            $tuples = current($this->history);
73
74
            foreach ($tuples as $tuple) {
75
                foreach ($tuple as list($request, $response)) {
76
                    $this->debug($request, $response);
77
                }
78
            }
79
80
            return;
81
        }
82
83
        if (false === $this->debug->getStatus()) {
84
            return;
85
        }
86
87
        $result = $event->getTestResult();
88
89
        if (TestResult::FAILED !== $result->getResultCode()) {
90
            return;
91
        }
92
93
        // all ->getTestResult() returns actually TestResults even for simple
94
        // scenarios. So we have to ensure that if we are not testing against
95
        // OutlineTested, we need to wrap the result in an array
96
        if ($result instanceof TestResults && !$event instanceof OutlineTested) {
97
            $result = [$result];
98
        }
99
100
        $values = iterator_to_array($this->history);
101
        $key = -1;
102
103
        foreach ($result as $testResult) {
104
            ++$key;
105
106
            if (TestResult::FAILED !== $testResult->getResultCode()) {
107
                continue;
108
            }
109
110
            // no history created
111
            if (!isset($values[$key])) {
112
                continue;
113
            }
114
115
            foreach ($values[$key] as list($request, $response)) {
116
                $this->debug($request, $response);
117
            }
118
        }
119
    }
120
121
    private function debug(?RequestInterface $request, ?ResponseInterface $response): void
122
    {
123
        if (!$request instanceof RequestInterface) {
124
            return;
125
        }
126
127
        printf(self::TEMPLATE, 'Request', "{$request->getMethod()} {$request->getUri()}");
128
129
        foreach ($this->debug->getRequestHeaders() as $header) {
130
            printf(self::TEMPLATE, "Request {$header}", $request->getHeaderLine($header));
131
        }
132
133
        echo "\n";
134
135
        if (!$response instanceof ResponseInterface) {
136
            return;
137
        }
138
139
        printf(self::TEMPLATE, 'Response status', "{$response->getStatusCode()} {$response->getReasonPhrase()}");
140
141
        foreach ($this->debug->getResponseHeaders() as $header) {
142
            printf(self::TEMPLATE, "Response {$header}", $response->getHeaderLine($header));
143
        }
144
145
        echo "\n";
146
        echo (string) $response->getBody();
147
        echo "\n";
148
    }
149
150
    private function hasTag(GherkinNodeTested $event, string $tag): bool
151
    {
152
        $node = $event->getNode();
153
154
        // no tags, no chocolates
155
        if (!$node instanceof TaggedNodeInterface) {
156
            return false;
157
        }
158
159
        if ($node->hasTag($tag)) {
160
            return true;
161
        }
162
163
        if (!method_exists($event, 'getFeature')) {
164
            return false;
165
        }
166
167
        $feature = $event->getFeature();
168
169
        return $feature->hasTag($tag);
170
    }
171
}
172