• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tempestphp / tempest-framework / 14140550176

28 Mar 2025 10:29PM UTC coverage: 80.716% (+1.4%) from 79.334%
14140550176

push

github

web-flow
feat(support): support `$default` on array `first` and `last` methods (#1096)

11 of 12 new or added lines in 2 files covered. (91.67%)

135 existing lines in 16 files now uncovered.

10941 of 13555 relevant lines covered (80.72%)

100.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.88
/src/Tempest/Console/src/Testing/ConsoleTester.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Console\Testing;
6

7
use Closure;
8
use Fiber;
9
use PHPUnit\Framework\Assert;
10
use Tempest\Console\Actions\ExecuteConsoleCommand;
11
use Tempest\Console\Components\InteractiveComponentRenderer;
12
use Tempest\Console\Console;
13
use Tempest\Console\Exceptions\ConsoleErrorHandler;
14
use Tempest\Console\ExitCode;
15
use Tempest\Console\GenericConsole;
16
use Tempest\Console\Input\ConsoleArgumentBag;
17
use Tempest\Console\Input\MemoryInputBuffer;
18
use Tempest\Console\InputBuffer;
19
use Tempest\Console\Key;
20
use Tempest\Console\Output\MemoryOutputBuffer;
21
use Tempest\Console\OutputBuffer;
22
use Tempest\Container\Container;
23
use Tempest\Core\AppConfig;
24
use Tempest\Highlight\Highlighter;
25

26
final class ConsoleTester
27
{
28
    private (OutputBuffer&MemoryOutputBuffer)|null $output = null;
29

30
    private (InputBuffer&MemoryInputBuffer)|null $input = null;
31

32
    private ?InteractiveComponentRenderer $componentRenderer = null;
33

34
    private ?ExitCode $exitCode = null;
35

36
    private bool $withPrompting = true;
37

38
    private (Console&GenericConsole)|null $console = null;
39

40
    public function __construct(
705✔
41
        private readonly Container $container,
42
    ) {}
705✔
43

44
    public function call(string|Closure|array $command, string|array $arguments = []): self
181✔
45
    {
46
        $clone = clone $this;
181✔
47

48
        $this->output ??= new MemoryOutputBuffer();
181✔
49
        $this->output->clear();
181✔
50
        $memoryOutputBuffer = $this->output;
181✔
51
        $clone->container->singleton(OutputBuffer::class, $memoryOutputBuffer);
181✔
52

53
        $this->input ??= new MemoryInputBuffer();
181✔
54
        $this->input->clear();
181✔
55
        $memoryInputBuffer = $this->input;
181✔
56
        $clone->container->singleton(InputBuffer::class, $memoryInputBuffer);
181✔
57

58
        $this->console ??= new GenericConsole(
181✔
59
            output: $memoryOutputBuffer,
181✔
60
            input: $memoryInputBuffer,
181✔
61
            highlighter: $clone->container->get(Highlighter::class, 'console'),
181✔
62
            executeConsoleCommand: $clone->container->get(ExecuteConsoleCommand::class),
181✔
63
            argumentBag: $clone->container->get(ConsoleArgumentBag::class),
181✔
64
        );
181✔
65

66
        $console = $this->console;
181✔
67

68
        if ($this->withPrompting === false) {
181✔
69
            $console->disablePrompting();
39✔
70
        }
71

72
        if ($this->componentRenderer !== null) {
181✔
UNCOV
73
            $console->setComponentRenderer($this->componentRenderer);
×
74
        }
75

76
        $clone->container->singleton(Console::class, $console);
181✔
77

78
        $appConfig = $this->container->get(AppConfig::class);
181✔
79
        $appConfig->errorHandlers[] = $clone->container->get(ConsoleErrorHandler::class);
181✔
80

81
        $clone->output = $memoryOutputBuffer;
181✔
82
        $clone->input = $memoryInputBuffer;
181✔
83

84
        if ($command instanceof Closure) {
181✔
85
            $fiber = new Fiber(function () use ($clone, $command, $console): void {
69✔
86
                $clone->exitCode = $command($console) ?? ExitCode::SUCCESS;
69✔
87
            });
69✔
88
        } else {
89
            $fiber = new Fiber(function () use ($command, $arguments, $clone): void {
112✔
90
                $clone->container->singleton(ConsoleArgumentBag::class, new ConsoleArgumentBag(['tempest']));
112✔
91
                $clone->exitCode = $this->container->invoke(
112✔
92
                    ExecuteConsoleCommand::class,
112✔
93
                    command: $command,
112✔
94
                    arguments: $arguments,
112✔
95
                );
112✔
96
            });
112✔
97
        }
98

99
        $fiber->start();
181✔
100

101
        if ($clone->componentRenderer !== null) {
181✔
UNCOV
102
            $clone->input("\e[1;1R"); // Set cursor for interactive testing
×
103
        }
104

105
        return $clone;
181✔
106
    }
107

108
    public function complete(?string $command = null): self
6✔
109
    {
110
        if ($command) {
6✔
111
            $input = explode(' ', $command);
5✔
112

113
            $inputString = implode(' ', array_map(
5✔
114
                fn (string $item) => "--input=\"{$item}\"",
5✔
115
                $input,
5✔
116
            ));
5✔
117
        } else {
118
            $inputString = '';
1✔
119
        }
120

121
        return $this->call("_complete --current=0 --input=\"./tempest\" {$inputString}");
6✔
122
    }
123

124
    public function input(int|string|Key $input): self
70✔
125
    {
126
        $this->output->clear();
70✔
127

128
        $this->input->add($input);
70✔
129

130
        return $this;
70✔
131
    }
132

133
    public function submit(int|string $input = ''): self
65✔
134
    {
135
        $input = (string) $input;
65✔
136

137
        $this->input($input . Key::ENTER->value);
65✔
138

139
        return $this;
65✔
140
    }
141

UNCOV
142
    public function confirm(): self
×
143
    {
144
        return $this->submit('yes');
×
145
    }
146

147
    public function deny(): self
×
148
    {
149
        return $this->submit('no');
×
150
    }
151

152
    public function print(): self
×
153
    {
154
        echo 'OUTPUT:' . PHP_EOL;
×
UNCOV
155
        echo $this->output->asUnformattedString();
×
156

UNCOV
157
        return $this;
×
158
    }
159

UNCOV
160
    public function printFormatted(): self
×
161
    {
UNCOV
162
        echo $this->output->asFormattedString();
×
163

UNCOV
164
        return $this;
×
165
    }
166

167
    public function getBuffer(?callable $callback = null): array
1✔
168
    {
169
        $buffer = array_map('trim', $this->output->getBufferWithoutFormatting());
1✔
170

171
        $this->output->clear();
1✔
172

173
        if ($callback !== null) {
1✔
174
            return $callback($buffer);
1✔
175
        }
176

UNCOV
177
        return $buffer;
×
178
    }
179

UNCOV
180
    public function useInteractiveTerminal(): self
×
181
    {
UNCOV
182
        $this->componentRenderer = new InteractiveComponentRenderer();
×
183

UNCOV
184
        return $this;
×
185
    }
186

187
    public function assertSee(string $text): self
11✔
188
    {
189
        return $this->assertContains($text);
11✔
190
    }
191

192
    public function assertSeeCount(string $text, int $expectedCount): self
1✔
193
    {
194
        $actualCount = substr_count($this->output->asUnformattedString(), $text);
1✔
195

196
        Assert::assertSame(
1✔
197
            $expectedCount,
1✔
198
            $actualCount,
1✔
199
            sprintf(
1✔
200
                'Failed to assert that console output counted: %s exactly %d times. These lines were printed: %s',
1✔
201
                $text,
1✔
202
                $expectedCount,
1✔
203
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
1✔
204
            ),
1✔
205
        );
1✔
206

207
        return $this;
1✔
208
    }
209

210
    public function assertNotSee(string $text): self
3✔
211
    {
212
        return $this->assertDoesNotContain($text);
3✔
213
    }
214

215
    public function assertContains(string $text, bool $ignoreLineEndings = true): self
86✔
216
    {
217
        $method = $ignoreLineEndings ? 'assertStringContainsStringIgnoringLineEndings' : 'assertStringContainsString';
86✔
218

219
        Assert::$method(
86✔
220
            $text,
86✔
221
            $this->output->asUnformattedString(),
86✔
222
            sprintf(
86✔
223
                'Failed to assert that console output included text: %s. These lines were printed: %s',
86✔
224
                $text,
86✔
225
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
86✔
226
            ),
86✔
227
        );
86✔
228

229
        return $this;
86✔
230
    }
231

232
    public function assertDoesNotContain(string $text): self
11✔
233
    {
234
        Assert::assertStringNotContainsString(
11✔
235
            $text,
11✔
236
            $this->output->asUnformattedString(),
11✔
237
            sprintf(
11✔
238
                'Failed to assert that console output did not include text: %s. These lines were printed: %s',
11✔
239
                $text,
11✔
240
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
11✔
241
            ),
11✔
242
        );
11✔
243

244
        return $this;
11✔
245
    }
246

247
    public function assertContainsFormattedText(string $text): self
×
248
    {
249
        Assert::assertStringContainsString(
×
UNCOV
250
            $text,
×
251
            $this->output->asFormattedString(),
×
UNCOV
252
            sprintf(
×
UNCOV
253
                'Failed to assert that console output included formatted text: %s. These lines were printed: %s',
×
UNCOV
254
                $text,
×
UNCOV
255
                PHP_EOL . $this->output->asFormattedString(),
×
UNCOV
256
            ),
×
UNCOV
257
        );
×
258

UNCOV
259
        return $this;
×
260
    }
261

262
    public function assertJson(): self
2✔
263
    {
264
        Assert::assertJson($this->output->asUnformattedString());
2✔
265

266
        return $this;
2✔
267
    }
268

269
    public function assertExitCode(ExitCode $exitCode): self
15✔
270
    {
271
        Assert::assertNotNull($this->exitCode, "Expected {$exitCode->name}, but instead no exit code was set — maybe you missed providing some input?");
15✔
272

273
        Assert::assertSame($exitCode, $this->exitCode, "Expected the exit code to be {$exitCode->name}, instead got {$this->exitCode->name}");
15✔
274

275
        return $this;
15✔
276
    }
277

278
    public function assertSuccess(): self
4✔
279
    {
280
        $this->assertExitCode(ExitCode::SUCCESS);
4✔
281

282
        return $this;
4✔
283
    }
284

285
    public function assertError(): self
1✔
286
    {
287
        $this->assertExitCode(ExitCode::ERROR);
1✔
288

289
        return $this;
1✔
290
    }
291

292
    public function assertCancelled(): self
1✔
293
    {
294
        $this->assertExitCode(ExitCode::CANCELLED);
1✔
295

296
        return $this;
1✔
297
    }
298

299
    public function assertInvalid(): self
1✔
300
    {
301
        $this->assertExitCode(ExitCode::INVALID);
1✔
302

303
        return $this;
1✔
304
    }
305

306
    public function withoutPrompting(): self
39✔
307
    {
308
        $this->withPrompting = false;
39✔
309

310
        return $this;
39✔
311
    }
312

UNCOV
313
    public function withPrompting(): self
×
314
    {
UNCOV
315
        $this->withPrompting = true;
×
316

UNCOV
317
        return $this;
×
318
    }
319

UNCOV
320
    public function dd(): self
×
321
    {
UNCOV
322
        ld($this->output->asFormattedString());
×
323

UNCOV
324
        return $this;
×
325
    }
326
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc