Passed
Pull Request — master (#43)
by Baptiste
02:06
created

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