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

tempestphp / tempest-framework / 11714560237

06 Nov 2024 06:46PM UTC coverage: 82.608% (+0.003%) from 82.605%
11714560237

push

github

web-flow
feat(container): support injecting properties using `#[Inject]` (#690)

6 of 6 new or added lines in 2 files covered. (100.0%)

95 existing lines in 9 files now uncovered.

7210 of 8728 relevant lines covered (82.61%)

49.34 hits per line

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

78.17
/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 Exception;
9
use Fiber;
10
use PHPUnit\Framework\Assert;
11
use Tempest\Console\Actions\ExecuteConsoleCommand;
12
use Tempest\Console\Components\InteractiveComponentRenderer;
13
use Tempest\Console\Console;
14
use Tempest\Console\ConsoleCommand;
15
use Tempest\Console\Exceptions\ConsoleErrorHandler;
16
use Tempest\Console\ExitCode;
17
use Tempest\Console\GenericConsole;
18
use Tempest\Console\Input\ConsoleArgumentBag;
19
use Tempest\Console\Input\MemoryInputBuffer;
20
use Tempest\Console\InputBuffer;
21
use Tempest\Console\Key;
22
use Tempest\Console\Output\MemoryOutputBuffer;
23
use Tempest\Console\OutputBuffer;
24
use Tempest\Container\Container;
25
use Tempest\Core\AppConfig;
26
use Tempest\Highlight\Highlighter;
27
use Tempest\Reflection\MethodReflector;
28

29
final class ConsoleTester
30
{
31
    private (OutputBuffer&MemoryOutputBuffer)|null $output = null;
32

33
    private (InputBuffer&MemoryInputBuffer)|null $input = null;
34

35
    private ?InteractiveComponentRenderer $componentRenderer = null;
36

37
    private ?ExitCode $exitCode = null;
38

39
    private bool $withPrompting = true;
40

41
    public function __construct(
339✔
42
        private readonly Container $container,
43
    ) {
44
    }
339✔
45

46
    public function call(string|Closure|array $command): self
83✔
47
    {
48
        $clone = clone $this;
83✔
49

50
        $memoryOutputBuffer = new MemoryOutputBuffer();
83✔
51
        $clone->container->singleton(OutputBuffer::class, $memoryOutputBuffer);
83✔
52

53
        $memoryInputBuffer = new MemoryInputBuffer();
83✔
54
        $clone->container->singleton(InputBuffer::class, $memoryInputBuffer);
83✔
55

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

64
        if ($this->withPrompting === false) {
83✔
65
            $console->disablePrompting();
8✔
66
        }
67

68
        if ($this->componentRenderer !== null) {
83✔
UNCOV
69
            $console->setComponentRenderer($this->componentRenderer);
×
70
        }
71

72
        $clone->container->singleton(Console::class, $console);
83✔
73

74
        $appConfig = $this->container->get(AppConfig::class);
83✔
75
        $appConfig->errorHandlers[] = $clone->container->get(ConsoleErrorHandler::class);
83✔
76

77
        $clone->output = $memoryOutputBuffer;
83✔
78
        $clone->input = $memoryInputBuffer;
83✔
79

80
        if ($command instanceof Closure) {
83✔
81
            $fiber = new Fiber(function () use ($clone, $command, $console): void {
32✔
82
                $clone->exitCode = $command($console) ?? ExitCode::SUCCESS;
32✔
83
            });
32✔
84
        } else {
85
            if (is_string($command) && class_exists($command)) {
51✔
86
                $command = [$command, '__invoke'];
2✔
87
            }
88

89
            if (is_array($command) || class_exists($command)) {
51✔
90
                $handler = MethodReflector::fromParts(...$command);
4✔
91

92
                $attribute = $handler->getAttribute(ConsoleCommand::class);
4✔
93

94
                if ($attribute === null) {
4✔
UNCOV
95
                    throw new Exception("Could not resolve console command from {$command[0]}::{$command[1]}");
×
96
                }
97

98
                $attribute->setHandler($handler);
4✔
99

100
                $command = $attribute->getName();
4✔
101
            }
102

103
            $fiber = new Fiber(function () use ($command, $clone): void {
51✔
104
                $argumentBag = new ConsoleArgumentBag(['tempest', ...explode(' ', $command)]);
51✔
105

106
                $clone->container->singleton(ConsoleArgumentBag::class, $argumentBag);
51✔
107

108
                $clone->exitCode = ($this->container->get(ExecuteConsoleCommand::class))($argumentBag->getCommandName());
51✔
109
            });
51✔
110
        }
111

112
        $fiber->start();
83✔
113

114
        if ($clone->componentRenderer !== null) {
83✔
UNCOV
115
            $clone->input("\e[1;1R"); // Set cursor for interactive testing
×
116
        }
117

118
        return $clone;
83✔
119
    }
120

121
    public function complete(?string $command = null): self
6✔
122
    {
123
        if ($command) {
6✔
124
            $input = explode(' ', $command);
5✔
125

126
            $inputString = implode(' ', array_map(
5✔
127
                fn (string $item) => "--input=\"{$item}\"",
5✔
128
                $input
5✔
129
            ));
5✔
130
        } else {
131
            $inputString = '';
1✔
132
        }
133

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

137
    public function input(int|string|Key $input): self
21✔
138
    {
139
        $this->output->clear();
21✔
140

141
        $this->input->add($input);
21✔
142

143
        return $this;
21✔
144
    }
145

146
    public function submit(int|string $input = ''): self
17✔
147
    {
148
        $input = (string)$input;
17✔
149

150
        $this->input($input . Key::ENTER->value);
17✔
151

152
        return $this;
17✔
153
    }
154

UNCOV
155
    public function print(): self
×
156
    {
UNCOV
157
        echo "OUTPUT:" . PHP_EOL;
×
158
        echo $this->output->asUnformattedString();
×
159

160
        return $this;
×
161
    }
162

UNCOV
163
    public function printFormatted(): self
×
164
    {
UNCOV
165
        echo $this->output->asFormattedString();
×
166

UNCOV
167
        return $this;
×
168
    }
169

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

174
        $this->output->clear();
1✔
175

176
        if ($callback !== null) {
1✔
177
            return $callback($buffer);
1✔
178
        }
179

180
        return $buffer;
×
181
    }
182

UNCOV
183
    public function useInteractiveTerminal(): self
×
184
    {
UNCOV
185
        $this->componentRenderer = new InteractiveComponentRenderer();
×
186

UNCOV
187
        return $this;
×
188
    }
189

190
    public function assertSee(string $text): self
10✔
191
    {
192
        return $this->assertContains($text);
10✔
193
    }
194

195
    public function assertNotSee(string $text): self
4✔
196
    {
197
        return $this->assertDoesNotContain($text);
4✔
198
    }
199

200
    public function assertContains(string $text, bool $ignoreLineEndings = true): self
69✔
201
    {
202
        $method = $ignoreLineEndings ? 'assertStringContainsStringIgnoringLineEndings' : 'assertStringContainsString';
69✔
203
        Assert::$method(
69✔
204
            $text,
69✔
205
            $this->output->asUnformattedString(),
69✔
206
            sprintf(
69✔
207
                'Failed to assert that console output included text: %s. These lines were printed: %s',
69✔
208
                $text,
69✔
209
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
69✔
210
            ),
69✔
211
        );
69✔
212

213
        return $this;
69✔
214
    }
215

216
    public function assertDoesNotContain(string $text): self
10✔
217
    {
218
        Assert::assertStringNotContainsString(
10✔
219
            $text,
10✔
220
            $this->output->asUnformattedString(),
10✔
221
            sprintf(
10✔
222
                'Failed to assert that console output did not include text: %s. These lines were printed: %s',
10✔
223
                $text,
10✔
224
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
10✔
225
            ),
10✔
226
        );
10✔
227

228
        return $this;
10✔
229
    }
230

231
    public function assertContainsFormattedText(string $text): self
×
232
    {
233
        Assert::assertStringContainsString(
×
234
            $text,
×
UNCOV
235
            $this->output->asFormattedString(),
×
236
            sprintf(
×
UNCOV
237
                'Failed to assert that console output included formatted text: %s. These lines were printed: %s',
×
UNCOV
238
                $text,
×
UNCOV
239
                PHP_EOL . $this->output->asFormattedString(),
×
UNCOV
240
            ),
×
UNCOV
241
        );
×
242

UNCOV
243
        return $this;
×
244
    }
245

246
    public function assertExitCode(ExitCode $exitCode): self
6✔
247
    {
248
        Assert::assertNotNull($this->exitCode, "Expected {$exitCode->name}, but instead no exit code was set — maybe you missed providing some input?");
6✔
249

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

252
        return $this;
6✔
253
    }
254

255
    public function assertSuccess(): self
3✔
256
    {
257
        $this->assertExitCode(ExitCode::SUCCESS);
3✔
258

259
        return $this;
3✔
260
    }
261

262
    public function assertError(): self
1✔
263
    {
264
        $this->assertExitCode(ExitCode::ERROR);
1✔
265

266
        return $this;
1✔
267
    }
268

269
    public function assertCancelled(): self
1✔
270
    {
271
        $this->assertExitCode(ExitCode::CANCELLED);
1✔
272

273
        return $this;
1✔
274
    }
275

276
    public function assertInvalid(): self
1✔
277
    {
278
        $this->assertExitCode(ExitCode::INVALID);
1✔
279

280
        return $this;
1✔
281
    }
282

283
    public function withoutPrompting(): self
8✔
284
    {
285
        $this->withPrompting = false;
8✔
286

287
        return $this;
8✔
288
    }
289

UNCOV
290
    public function withPrompting(): self
×
291
    {
UNCOV
292
        $this->withPrompting = true;
×
293

UNCOV
294
        return $this;
×
295
    }
296

UNCOV
297
    public function dd(): self
×
298
    {
UNCOV
299
        ld($this->output->asFormattedString());
×
300

UNCOV
301
        return $this;
×
302
    }
303
}
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

© 2026 Coveralls, Inc