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

tempestphp / tempest-framework / 14049246919

24 Mar 2025 09:42PM UTC coverage: 79.353% (-0.04%) from 79.391%
14049246919

push

github

web-flow
feat(support): support array parameters in string manipulations (#1073)

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

735 existing lines in 126 files now uncovered.

10492 of 13222 relevant lines covered (79.35%)

90.78 hits per line

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

78.21
/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
    public function __construct(
658✔
39
        private readonly Container $container,
40
    ) {}
658✔
41

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

46
        $memoryOutputBuffer = new MemoryOutputBuffer();
172✔
47
        $clone->container->singleton(OutputBuffer::class, $memoryOutputBuffer);
172✔
48

49
        $memoryInputBuffer = new MemoryInputBuffer();
172✔
50
        $clone->container->singleton(InputBuffer::class, $memoryInputBuffer);
172✔
51

52
        $console = new GenericConsole(
172✔
53
            output: $memoryOutputBuffer,
172✔
54
            input: $memoryInputBuffer,
172✔
55
            highlighter: $clone->container->get(Highlighter::class, 'console'),
172✔
56
            executeConsoleCommand: $clone->container->get(ExecuteConsoleCommand::class),
172✔
57
            argumentBag: $clone->container->get(ConsoleArgumentBag::class),
172✔
58
        );
172✔
59

60
        if ($this->withPrompting === false) {
172✔
61
            $console->disablePrompting();
39✔
62
        }
63

64
        if ($this->componentRenderer !== null) {
172✔
UNCOV
65
            $console->setComponentRenderer($this->componentRenderer);
×
66
        }
67

68
        $clone->container->singleton(Console::class, $console);
172✔
69

70
        $appConfig = $this->container->get(AppConfig::class);
172✔
71
        $appConfig->errorHandlers[] = $clone->container->get(ConsoleErrorHandler::class);
172✔
72

73
        $clone->output = $memoryOutputBuffer;
172✔
74
        $clone->input = $memoryInputBuffer;
172✔
75

76
        if ($command instanceof Closure) {
172✔
77
            $fiber = new Fiber(function () use ($clone, $command, $console): void {
69✔
78
                $clone->exitCode = $command($console) ?? ExitCode::SUCCESS;
69✔
79
            });
69✔
80
        } else {
81
            $fiber = new Fiber(function () use ($command, $arguments, $clone): void {
103✔
82
                $clone->container->singleton(ConsoleArgumentBag::class, new ConsoleArgumentBag(['tempest']));
103✔
83
                $clone->exitCode = $this->container->invoke(
103✔
84
                    ExecuteConsoleCommand::class,
103✔
85
                    command: $command,
103✔
86
                    arguments: $arguments,
103✔
87
                );
103✔
88
            });
103✔
89
        }
90

91
        $fiber->start();
172✔
92

93
        if ($clone->componentRenderer !== null) {
172✔
UNCOV
94
            $clone->input("\e[1;1R"); // Set cursor for interactive testing
×
95
        }
96

97
        return $clone;
172✔
98
    }
99

100
    public function complete(?string $command = null): self
6✔
101
    {
102
        if ($command) {
6✔
103
            $input = explode(' ', $command);
5✔
104

105
            $inputString = implode(' ', array_map(
5✔
106
                fn (string $item) => "--input=\"{$item}\"",
5✔
107
                $input,
5✔
108
            ));
5✔
109
        } else {
110
            $inputString = '';
1✔
111
        }
112

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

116
    public function input(int|string|Key $input): self
70✔
117
    {
118
        $this->output->clear();
70✔
119

120
        $this->input->add($input);
70✔
121

122
        return $this;
70✔
123
    }
124

125
    public function submit(int|string $input = ''): self
65✔
126
    {
127
        $input = (string) $input;
65✔
128

129
        $this->input($input . Key::ENTER->value);
65✔
130

131
        return $this;
65✔
132
    }
133

UNCOV
134
    public function confirm(): self
×
135
    {
UNCOV
136
        return $this->submit('yes');
×
137
    }
138

UNCOV
139
    public function deny(): self
×
140
    {
UNCOV
141
        return $this->submit('no');
×
142
    }
143

UNCOV
144
    public function print(): self
×
145
    {
UNCOV
146
        echo 'OUTPUT:' . PHP_EOL;
×
147
        echo $this->output->asUnformattedString();
×
148

UNCOV
149
        return $this;
×
150
    }
151

UNCOV
152
    public function printFormatted(): self
×
153
    {
UNCOV
154
        echo $this->output->asFormattedString();
×
155

UNCOV
156
        return $this;
×
157
    }
158

159
    public function getBuffer(?callable $callback = null): array
1✔
160
    {
161
        $buffer = array_map('trim', $this->output->getBufferWithoutFormatting());
1✔
162

163
        $this->output->clear();
1✔
164

165
        if ($callback !== null) {
1✔
166
            return $callback($buffer);
1✔
167
        }
168

UNCOV
169
        return $buffer;
×
170
    }
171

UNCOV
172
    public function useInteractiveTerminal(): self
×
173
    {
UNCOV
174
        $this->componentRenderer = new InteractiveComponentRenderer();
×
175

UNCOV
176
        return $this;
×
177
    }
178

179
    public function assertSee(string $text): self
11✔
180
    {
181
        return $this->assertContains($text);
11✔
182
    }
183

184
    public function assertSeeCount(string $text, int $expectedCount): self
1✔
185
    {
186
        $actualCount = substr_count($this->output->asUnformattedString(), $text);
1✔
187

188
        Assert::assertSame(
1✔
189
            $expectedCount,
1✔
190
            $actualCount,
1✔
191
            sprintf(
1✔
192
                'Failed to assert that console output counted: %s exactly %d times. These lines were printed: %s',
1✔
193
                $text,
1✔
194
                $expectedCount,
1✔
195
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
1✔
196
            ),
1✔
197
        );
1✔
198

199
        return $this;
1✔
200
    }
201

202
    public function assertNotSee(string $text): self
3✔
203
    {
204
        return $this->assertDoesNotContain($text);
3✔
205
    }
206

207
    public function assertContains(string $text, bool $ignoreLineEndings = true): self
77✔
208
    {
209
        $method = $ignoreLineEndings ? 'assertStringContainsStringIgnoringLineEndings' : 'assertStringContainsString';
77✔
210

211
        Assert::$method(
77✔
212
            $text,
77✔
213
            $this->output->asUnformattedString(),
77✔
214
            sprintf(
77✔
215
                'Failed to assert that console output included text: %s. These lines were printed: %s',
77✔
216
                $text,
77✔
217
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
77✔
218
            ),
77✔
219
        );
77✔
220

221
        return $this;
77✔
222
    }
223

224
    public function assertDoesNotContain(string $text): self
11✔
225
    {
226
        Assert::assertStringNotContainsString(
11✔
227
            $text,
11✔
228
            $this->output->asUnformattedString(),
11✔
229
            sprintf(
11✔
230
                'Failed to assert that console output did not include text: %s. These lines were printed: %s',
11✔
231
                $text,
11✔
232
                PHP_EOL . PHP_EOL . $this->output->asUnformattedString() . PHP_EOL,
11✔
233
            ),
11✔
234
        );
11✔
235

236
        return $this;
11✔
237
    }
238

UNCOV
239
    public function assertContainsFormattedText(string $text): self
×
240
    {
UNCOV
241
        Assert::assertStringContainsString(
×
242
            $text,
×
243
            $this->output->asFormattedString(),
×
244
            sprintf(
×
245
                'Failed to assert that console output included formatted text: %s. These lines were printed: %s',
×
246
                $text,
×
247
                PHP_EOL . $this->output->asFormattedString(),
×
248
            ),
×
249
        );
×
250

UNCOV
251
        return $this;
×
252
    }
253

254
    public function assertJson(): self
2✔
255
    {
256
        Assert::assertJson($this->output->asUnformattedString());
2✔
257

258
        return $this;
2✔
259
    }
260

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

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

267
        return $this;
6✔
268
    }
269

270
    public function assertSuccess(): self
3✔
271
    {
272
        $this->assertExitCode(ExitCode::SUCCESS);
3✔
273

274
        return $this;
3✔
275
    }
276

277
    public function assertError(): self
1✔
278
    {
279
        $this->assertExitCode(ExitCode::ERROR);
1✔
280

281
        return $this;
1✔
282
    }
283

284
    public function assertCancelled(): self
1✔
285
    {
286
        $this->assertExitCode(ExitCode::CANCELLED);
1✔
287

288
        return $this;
1✔
289
    }
290

291
    public function assertInvalid(): self
1✔
292
    {
293
        $this->assertExitCode(ExitCode::INVALID);
1✔
294

295
        return $this;
1✔
296
    }
297

298
    public function withoutPrompting(): self
39✔
299
    {
300
        $this->withPrompting = false;
39✔
301

302
        return $this;
39✔
303
    }
304

UNCOV
305
    public function withPrompting(): self
×
306
    {
UNCOV
307
        $this->withPrompting = true;
×
308

UNCOV
309
        return $this;
×
310
    }
311

UNCOV
312
    public function dd(): self
×
313
    {
UNCOV
314
        ld($this->output->asFormattedString());
×
315

UNCOV
316
        return $this;
×
317
    }
318
}
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