| 148 |
lars |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
declare(strict_types=1);
|
|
|
4 |
|
|
|
5 |
namespace NunoMaduro\Collision\Adapters\Phpunit;
|
|
|
6 |
|
|
|
7 |
use NunoMaduro\Collision\Exceptions\ShouldNotHappen;
|
|
|
8 |
use NunoMaduro\Collision\Writer;
|
|
|
9 |
use PHPUnit\Framework\AssertionFailedError;
|
|
|
10 |
use PHPUnit\Framework\ExceptionWrapper;
|
|
|
11 |
use PHPUnit\Framework\ExpectationFailedException;
|
|
|
12 |
use Symfony\Component\Console\Output\ConsoleOutput;
|
|
|
13 |
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
|
|
14 |
use Throwable;
|
|
|
15 |
use Whoops\Exception\Inspector;
|
|
|
16 |
|
|
|
17 |
/**
|
|
|
18 |
* @internal
|
|
|
19 |
*/
|
|
|
20 |
final class Style
|
|
|
21 |
{
|
|
|
22 |
/**
|
|
|
23 |
* @var ConsoleOutput
|
|
|
24 |
*/
|
|
|
25 |
private $output;
|
|
|
26 |
|
|
|
27 |
/**
|
|
|
28 |
* Style constructor.
|
|
|
29 |
*/
|
|
|
30 |
public function __construct(ConsoleOutputInterface $output)
|
|
|
31 |
{
|
|
|
32 |
if (! $output instanceof ConsoleOutput) {
|
|
|
33 |
throw new ShouldNotHappen();
|
|
|
34 |
}
|
|
|
35 |
|
|
|
36 |
$this->output = $output;
|
|
|
37 |
}
|
|
|
38 |
|
|
|
39 |
/**
|
|
|
40 |
* Prints the content.
|
|
|
41 |
*/
|
|
|
42 |
public function write(string $content): void
|
|
|
43 |
{
|
|
|
44 |
$this->output->write($content);
|
|
|
45 |
}
|
|
|
46 |
|
|
|
47 |
/**
|
|
|
48 |
* Prints the content similar too:.
|
|
|
49 |
*
|
|
|
50 |
* ```
|
|
|
51 |
* PASS Unit\ExampleTest
|
|
|
52 |
* ✓ basic test
|
|
|
53 |
* ```
|
|
|
54 |
*/
|
|
|
55 |
public function writeCurrentTestCaseSummary(State $state): void
|
|
|
56 |
{
|
|
|
57 |
if ($state->testCaseTestsCount() === 0) {
|
|
|
58 |
return;
|
|
|
59 |
}
|
|
|
60 |
|
|
|
61 |
if (! $state->headerPrinted) {
|
|
|
62 |
$this->output->writeln($this->titleLineFrom(
|
|
|
63 |
$state->getTestCaseTitle() === 'FAIL' ? 'white' : 'black',
|
|
|
64 |
$state->getTestCaseTitleColor(),
|
|
|
65 |
$state->getTestCaseTitle(),
|
|
|
66 |
$state->testCaseName
|
|
|
67 |
));
|
|
|
68 |
$state->headerPrinted = true;
|
|
|
69 |
}
|
|
|
70 |
|
|
|
71 |
$state->eachTestCaseTests(function (TestResult $testResult) {
|
|
|
72 |
$this->output->writeln($this->testLineFrom(
|
|
|
73 |
$testResult->color,
|
|
|
74 |
$testResult->icon,
|
|
|
75 |
$testResult->description,
|
|
|
76 |
$testResult->warning
|
|
|
77 |
));
|
|
|
78 |
});
|
|
|
79 |
}
|
|
|
80 |
|
|
|
81 |
/**
|
|
|
82 |
* Prints the content similar too:.
|
|
|
83 |
*
|
|
|
84 |
* ```
|
|
|
85 |
* PASS Unit\ExampleTest
|
|
|
86 |
* ✓ basic test
|
|
|
87 |
* ```
|
|
|
88 |
*/
|
|
|
89 |
public function writeErrorsSummary(State $state, bool $onFailure): void
|
|
|
90 |
{
|
|
|
91 |
$errors = array_filter($state->suiteTests, function (TestResult $testResult) {
|
|
|
92 |
return $testResult->type === TestResult::FAIL;
|
|
|
93 |
});
|
|
|
94 |
|
|
|
95 |
if (! $onFailure) {
|
|
|
96 |
$this->output->writeln(['', " \e[2m---\e[22m", '']);
|
|
|
97 |
}
|
|
|
98 |
|
|
|
99 |
array_map(function (TestResult $testResult) use ($onFailure) {
|
|
|
100 |
if (! $onFailure) {
|
|
|
101 |
$this->output->write(sprintf(
|
|
|
102 |
' <fg=red;options=bold>• %s </>> <fg=red;options=bold>%s</>',
|
|
|
103 |
$testResult->testCaseName,
|
|
|
104 |
$testResult->description
|
|
|
105 |
));
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
if (! $testResult->throwable instanceof Throwable) {
|
|
|
109 |
throw new ShouldNotHappen();
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
$this->writeError($testResult->throwable);
|
|
|
113 |
}, $errors);
|
|
|
114 |
}
|
|
|
115 |
|
|
|
116 |
/**
|
|
|
117 |
* Writes the final recap.
|
|
|
118 |
*/
|
|
|
119 |
public function writeRecap(State $state, Timer $timer = null): void
|
|
|
120 |
{
|
|
|
121 |
$types = [TestResult::FAIL, TestResult::WARN, TestResult::RISKY, TestResult::INCOMPLETE, TestResult::SKIPPED, TestResult::PASS];
|
|
|
122 |
foreach ($types as $type) {
|
|
|
123 |
if (($countTests = $state->countTestsInTestSuiteBy($type)) !== 0) {
|
|
|
124 |
$color = TestResult::makeColor($type);
|
|
|
125 |
$tests[] = "<fg=$color;options=bold>$countTests $type</>";
|
|
|
126 |
}
|
|
|
127 |
}
|
|
|
128 |
|
|
|
129 |
$pending = $state->suiteTotalTests - $state->testSuiteTestsCount();
|
|
|
130 |
if ($pending !== 0) {
|
|
|
131 |
$tests[] = "\e[2m$pending pending\e[22m";
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
if (! empty($tests)) {
|
|
|
135 |
$this->output->write([
|
|
|
136 |
"\n",
|
|
|
137 |
sprintf(
|
|
|
138 |
' <fg=white;options=bold>Tests: </><fg=default>%s</>',
|
|
|
139 |
implode(', ', $tests)
|
|
|
140 |
),
|
|
|
141 |
]);
|
|
|
142 |
}
|
|
|
143 |
|
|
|
144 |
if ($timer !== null) {
|
|
|
145 |
$timeElapsed = number_format($timer->result(), 2, '.', '');
|
|
|
146 |
$this->output->writeln([
|
|
|
147 |
'',
|
|
|
148 |
sprintf(
|
|
|
149 |
' <fg=white;options=bold>Time: </><fg=default>%ss</>',
|
|
|
150 |
$timeElapsed
|
|
|
151 |
),
|
|
|
152 |
]
|
|
|
153 |
);
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
$this->output->writeln('');
|
|
|
157 |
}
|
|
|
158 |
|
|
|
159 |
/**
|
|
|
160 |
* Displays a warning message.
|
|
|
161 |
*/
|
|
|
162 |
public function writeWarning(string $message): void
|
|
|
163 |
{
|
|
|
164 |
$this->output->writeln($this->testLineFrom('yellow', $message, ''));
|
|
|
165 |
}
|
|
|
166 |
|
|
|
167 |
/**
|
|
|
168 |
* Displays the error using Collision's writer
|
|
|
169 |
* and terminates with exit code === 1.
|
|
|
170 |
*/
|
|
|
171 |
public function writeError(Throwable $throwable): void
|
|
|
172 |
{
|
|
|
173 |
$writer = (new Writer())->setOutput($this->output);
|
|
|
174 |
|
|
|
175 |
if ($throwable instanceof AssertionFailedError) {
|
|
|
176 |
$writer->showTitle(false);
|
|
|
177 |
$this->output->write('', true);
|
|
|
178 |
}
|
|
|
179 |
|
|
|
180 |
$writer->ignoreFilesIn([
|
|
|
181 |
'/vendor\/bin\/pest/',
|
|
|
182 |
'/bin\/pest/',
|
|
|
183 |
'/vendor\/pestphp\/pest/',
|
|
|
184 |
'/vendor\/phpspec\/prophecy-phpunit/',
|
|
|
185 |
'/vendor\/phpspec\/prophecy/',
|
|
|
186 |
'/vendor\/phpunit\/phpunit\/src/',
|
|
|
187 |
'/vendor\/mockery\/mockery/',
|
|
|
188 |
'/vendor\/laravel\/dusk/',
|
|
|
189 |
'/vendor\/laravel\/framework\/src\/Illuminate\/Testing/',
|
|
|
190 |
'/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Testing/',
|
|
|
191 |
'/vendor\/symfony\/framework-bundle\/Test/',
|
|
|
192 |
'/vendor\/symfony\/phpunit-bridge/',
|
|
|
193 |
'/vendor\/symfony\/dom-crawler/',
|
|
|
194 |
'/vendor\/symfony\/browser-kit/',
|
|
|
195 |
'/vendor\/symfony\/css-selector/',
|
|
|
196 |
'/vendor\/bin\/.phpunit/',
|
|
|
197 |
'/bin\/.phpunit/',
|
|
|
198 |
'/vendor\/bin\/simple-phpunit/',
|
|
|
199 |
'/bin\/phpunit/',
|
|
|
200 |
'/vendor\/coduo\/php-matcher\/src\/PHPUnit/',
|
|
|
201 |
'/vendor\/sulu\/sulu\/src\/Sulu\/Bundle\/TestBundle\/Testing/',
|
|
|
202 |
'/vendor\/webmozart\/assert/',
|
|
|
203 |
]);
|
|
|
204 |
|
|
|
205 |
if ($throwable instanceof ExceptionWrapper && $throwable->getOriginalException() !== null) {
|
|
|
206 |
$throwable = $throwable->getOriginalException();
|
|
|
207 |
}
|
|
|
208 |
|
|
|
209 |
$inspector = new Inspector($throwable);
|
|
|
210 |
|
|
|
211 |
$writer->write($inspector);
|
|
|
212 |
|
|
|
213 |
if ($throwable instanceof ExpectationFailedException && $comparisionFailure = $throwable->getComparisonFailure()) {
|
|
|
214 |
$diff = $comparisionFailure->getDiff();
|
|
|
215 |
$lines = explode(PHP_EOL, $diff);
|
|
|
216 |
$diff = '';
|
|
|
217 |
foreach ($lines as $line) {
|
|
|
218 |
if (0 === strpos($line, '-')) {
|
|
|
219 |
$line = '<fg=red>'.$line.'</>';
|
|
|
220 |
} elseif (0 === strpos($line, '+')) {
|
|
|
221 |
$line = '<fg=green>'.$line.'</>';
|
|
|
222 |
}
|
|
|
223 |
|
|
|
224 |
$diff .= $line.PHP_EOL;
|
|
|
225 |
}
|
|
|
226 |
|
|
|
227 |
$diff = trim((string) preg_replace("/\r|\n/", "\n ", $diff));
|
|
|
228 |
|
|
|
229 |
$this->output->write(" $diff");
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
$this->output->writeln('');
|
|
|
233 |
}
|
|
|
234 |
|
|
|
235 |
/**
|
|
|
236 |
* Returns the title contents.
|
|
|
237 |
*/
|
|
|
238 |
private function titleLineFrom(string $fg, string $bg, string $title, string $testCaseName): string
|
|
|
239 |
{
|
|
|
240 |
return sprintf(
|
|
|
241 |
"\n <fg=%s;bg=%s;options=bold> %s </><fg=default> %s</>",
|
|
|
242 |
$fg,
|
|
|
243 |
$bg,
|
|
|
244 |
$title,
|
|
|
245 |
$testCaseName
|
|
|
246 |
);
|
|
|
247 |
}
|
|
|
248 |
|
|
|
249 |
/**
|
|
|
250 |
* Returns the test contents.
|
|
|
251 |
*/
|
|
|
252 |
private function testLineFrom(string $fg, string $icon, string $description, string $warning = null): string
|
|
|
253 |
{
|
|
|
254 |
if (! empty($warning)) {
|
|
|
255 |
$warning = sprintf(
|
|
|
256 |
' → %s',
|
|
|
257 |
$warning
|
|
|
258 |
);
|
|
|
259 |
}
|
|
|
260 |
|
|
|
261 |
return sprintf(
|
|
|
262 |
" <fg=%s;options=bold>%s</><fg=default> \e[2m%s\e[22m</><fg=yellow>%s</>",
|
|
|
263 |
$fg,
|
|
|
264 |
$icon,
|
|
|
265 |
$description,
|
|
|
266 |
$warning
|
|
|
267 |
);
|
|
|
268 |
}
|
|
|
269 |
}
|