|
21 | 21 | use CodeIgniter\Test\CIUnitTestCase; |
22 | 22 | use CodeIgniter\Test\Mock\MockCLIConfig; |
23 | 23 | use CodeIgniter\Test\Mock\MockCodeIgniter; |
| 24 | +use CodeIgniter\Test\Mock\MockInputOutput; |
24 | 25 | use CodeIgniter\Test\StreamFilterTrait; |
25 | 26 | use PHPUnit\Framework\Attributes\CoversClass; |
26 | 27 | use PHPUnit\Framework\Attributes\Group; |
@@ -54,6 +55,25 @@ protected function tearDown(): void |
54 | 55 | CLI::reset(); |
55 | 56 | } |
56 | 57 |
|
| 58 | + private function getUndecoratedBuffer(): string |
| 59 | + { |
| 60 | + return preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()) ?? ''; |
| 61 | + } |
| 62 | + |
| 63 | + private function getUndecoratedIoOutput(MockInputOutput $io): string |
| 64 | + { |
| 65 | + return preg_replace('/\e\[[^m]+m/', '', $io->getOutput()) ?? ''; |
| 66 | + } |
| 67 | + |
| 68 | + private function useInputs(string ...$inputs): MockInputOutput |
| 69 | + { |
| 70 | + $io = new MockInputOutput(); |
| 71 | + $io->setInputs($inputs); |
| 72 | + CLI::setInputOutput($io); |
| 73 | + |
| 74 | + return $io; |
| 75 | + } |
| 76 | + |
57 | 77 | public function testHeaderShowsNormally(): void |
58 | 78 | { |
59 | 79 | $this->initializeConsole(); |
@@ -121,6 +141,142 @@ public function testBadCommand(): void |
121 | 141 | $this->assertStringContainsString('Command "bogus" not found', $this->getStreamFilterBuffer()); |
122 | 142 | } |
123 | 143 |
|
| 144 | + public function testUnknownCommandRunsConfirmedSuggestion(): void |
| 145 | + { |
| 146 | + $this->initializeConsole('app:inf', '--no-header'); |
| 147 | + $io = $this->useInputs('y'); |
| 148 | + |
| 149 | + $console = new Console(); |
| 150 | + $exitCode = $console->run(); |
| 151 | + |
| 152 | + $this->assertSame(EXIT_SUCCESS, $exitCode); |
| 153 | + $this->assertSame('app:info', $console->getCommand()); |
| 154 | + $this->assertSame( |
| 155 | + sprintf( |
| 156 | + <<<'EOT' |
| 157 | +
|
| 158 | + Command "app:inf" not found. |
| 159 | +
|
| 160 | + Run "app:info" instead? [y, n]: y |
| 161 | + CodeIgniter Version: %s |
| 162 | + |
| 163 | + EOT, |
| 164 | + CodeIgniter::CI_VERSION, |
| 165 | + ), |
| 166 | + $this->getUndecoratedIoOutput($io), |
| 167 | + ); |
| 168 | + } |
| 169 | + |
| 170 | + public function testUnknownCommandDeclinedSuggestionExitsWithError(): void |
| 171 | + { |
| 172 | + $this->initializeConsole('app:inf', '--no-header'); |
| 173 | + $io = $this->useInputs('n'); |
| 174 | + |
| 175 | + $console = new Console(); |
| 176 | + $exitCode = $console->run(); |
| 177 | + |
| 178 | + $this->assertSame(EXIT_ERROR, $exitCode); |
| 179 | + $this->assertSame('app:inf', $console->getCommand()); |
| 180 | + $this->assertSame( |
| 181 | + <<<'EOT' |
| 182 | +
|
| 183 | + Command "app:inf" not found. |
| 184 | +
|
| 185 | + Run "app:info" instead? [y, n]: n |
| 186 | + |
| 187 | + EOT, |
| 188 | + $this->getUndecoratedIoOutput($io), |
| 189 | + ); |
| 190 | + } |
| 191 | + |
| 192 | + public function testUnknownCommandRunsSelectedSuggestion(): void |
| 193 | + { |
| 194 | + $this->initializeConsole('clear', '--no-header'); |
| 195 | + $io = $this->useInputs('0'); |
| 196 | + |
| 197 | + $console = new Console(); |
| 198 | + $exitCode = $console->run(); |
| 199 | + |
| 200 | + $this->assertSame(EXIT_SUCCESS, $exitCode); |
| 201 | + $this->assertSame('cache:clear', $console->getCommand()); |
| 202 | + $this->assertSame( |
| 203 | + <<<'EOT' |
| 204 | +
|
| 205 | + Command "clear" not found. |
| 206 | +
|
| 207 | + Select a command to run instead: |
| 208 | + [0] cache:clear |
| 209 | + [1] debugbar:clear |
| 210 | + [2] logs:clear |
| 211 | + [3] none of these |
| 212 | +
|
| 213 | + [0, 1, 2, 3]: 0 |
| 214 | + Cache cleared using the "file" driver. |
| 215 | + |
| 216 | + EOT, |
| 217 | + $this->getUndecoratedIoOutput($io), |
| 218 | + ); |
| 219 | + } |
| 220 | + |
| 221 | + public function testUnknownCommandSelectingNoneExitsWithError(): void |
| 222 | + { |
| 223 | + $this->initializeConsole('clear', '--no-header'); |
| 224 | + $io = $this->useInputs('3'); |
| 225 | + |
| 226 | + $console = new Console(); |
| 227 | + $exitCode = $console->run(); |
| 228 | + |
| 229 | + $this->assertSame(EXIT_ERROR, $exitCode); |
| 230 | + $this->assertSame('clear', $console->getCommand()); |
| 231 | + $this->assertSame( |
| 232 | + <<<'EOT' |
| 233 | +
|
| 234 | + Command "clear" not found. |
| 235 | +
|
| 236 | + Select a command to run instead: |
| 237 | + [0] cache:clear |
| 238 | + [1] debugbar:clear |
| 239 | + [2] logs:clear |
| 240 | + [3] none of these |
| 241 | +
|
| 242 | + [0, 1, 2, 3]: 3 |
| 243 | + |
| 244 | + EOT, |
| 245 | + $this->getUndecoratedIoOutput($io), |
| 246 | + ); |
| 247 | + } |
| 248 | + |
| 249 | + public function testUnknownCommandDoesNotPromptWhenNotInteractive(): void |
| 250 | + { |
| 251 | + $this->initializeConsole('lst', '--no-header', '--no-interaction'); |
| 252 | + $exitCode = (new Console())->run(); |
| 253 | + |
| 254 | + $this->assertSame(EXIT_ERROR, $exitCode); |
| 255 | + $this->assertSame( |
| 256 | + <<<'EOT' |
| 257 | +
|
| 258 | + Command "lst" not found. |
| 259 | +
|
| 260 | + Did you mean this? |
| 261 | + list |
| 262 | + |
| 263 | + EOT, |
| 264 | + $this->getUndecoratedBuffer(), |
| 265 | + ); |
| 266 | + } |
| 267 | + |
| 268 | + public function testUnknownCommandDoesNotPromptWithNullInputOutput(): void |
| 269 | + { |
| 270 | + $this->initializeConsole('lst', '--no-header'); |
| 271 | + CLI::setInputOutput(new NullInputOutput()); |
| 272 | + |
| 273 | + $console = new Console(); |
| 274 | + $exitCode = $console->run(); |
| 275 | + |
| 276 | + $this->assertSame(EXIT_ERROR, $exitCode); |
| 277 | + $this->assertSame('lst', $console->getCommand()); |
| 278 | + } |
| 279 | + |
124 | 280 | public function testHelpCommandDetails(): void |
125 | 281 | { |
126 | 282 | $this->initializeConsole('help', 'make:migration'); |
|
0 commit comments