<?php
use App\Prompts\LiveSuggestPrompt;
use Illuminate\Process\PendingProcess;
use Illuminate\Support\Facades\Process;
use Laravel\Prompts\Key;
use Laravel\Prompts\MultiSelectPrompt;
it('lists known intent tags', function () {
Process::fake(function (PendingProcess $process) {
return match ($process->command) {
['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
['message' => 'feat(pijul-qol): first'],
['message' => 'feat(init): second'],
['message' => 'plain message'],
['message' => 'feat(pijul-qol): duplicate'],
], JSON_THROW_ON_ERROR)),
default => Process::result('', 'Unexpected command.', 1),
};
});
$this->artisan('pijul:record-with-intent-tags', ['--list-tags' => true])
->expectsOutput('feat(pijul-qol)')
->expectsOutput('feat(init)')
->assertExitCode(0);
Process::assertDidntRun(fn (PendingProcess $process) => is_array($process->command)
&& ($process->command[1] ?? null) === 'record');
});
it('records a change with a reused intent tag', function () {
Process::fake(function (PendingProcess $process) {
return match (true) {
$process->command === ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
['message' => 'feat(pijul-qol): first'],
['message' => 'feat(init): second'],
], JSON_THROW_ON_ERROR)),
$process->command === ['pijul', 'record', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture' => tap(
Process::result('', 'Captured Pijul hunks.', 1),
function () use ($process): void {
file_put_contents(
(string) $process->environment['ANI_PIJUL_CAPTURE_PATH'],
<<<'TEXT'
message = ""
# Hunks
1. Edit in "README.md"
+ README change
2. Edit in "app/Console.php"
+ Console change
TEXT
);
},
),
$process->command === ['pijul', 'record', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
&& ($process->environment['ANI_PIJUL_MESSAGE'] ?? null) === 'feat(pijul-qol): add laravel command'
&& ($process->environment['ANI_PIJUL_SELECTED_HUNKS'] ?? null) === '[1]' => Process::result(''),
default => Process::result('', 'Unexpected command.', 1),
};
});
LiveSuggestPrompt::fake(['f', 'e', 'a', 't', '(', 'p', 'i', 'j', 'u', 'l', '-', 'q', 'o', 'l', ')', Key::ENTER, Key::SPACE, Key::ENTER]);
$this->artisan('pijul:record-with-intent-tags', ['prefixes' => ['app']])
->expectsOutputToContain('Known tags: feat(pijul-qol), feat(init)')
->expectsQuestion('message', 'add laravel command')
->assertExitCode(0);
Process::assertRanTimes(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app'], 2);
Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture'
&& ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
&& ! $process->tty
&& $process->timeout === null);
Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
&& ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
&& ! $process->tty
&& $process->timeout === null);
});
it('records selected hunks when an explicit message is provided interactively', function () {
Process::fake(function (PendingProcess $process) {
return match (true) {
$process->command === ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
['message' => 'feat(pijul-qol): first'],
], JSON_THROW_ON_ERROR)),
$process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture' => tap(
Process::result('', 'Captured Pijul hunks.', 1),
function () use ($process): void {
file_put_contents(
(string) $process->environment['ANI_PIJUL_CAPTURE_PATH'],
<<<'TEXT'
message = ""
# Hunks
1. Edit in "README.md"
+ README change
TEXT
);
},
),
$process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
&& ($process->environment['ANI_PIJUL_MESSAGE'] ?? null) === 'ship it'
&& ($process->environment['ANI_PIJUL_SELECTED_HUNKS'] ?? null) === '[1]' => Process::result(''),
default => Process::result('', 'Unexpected command.', 1),
};
});
MultiSelectPrompt::fake([Key::ENTER]);
$this->artisan('pijul:record-with-intent-tags', [
'--message' => 'ship it',
'--all' => true,
'--description' => 'desc',
'prefixes' => ['app'],
])
->expectsOutputToContain('Known tags: feat(pijul-qol)')
->assertExitCode(0);
Process::assertRanTimes(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app'], 2);
Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture'
&& ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
&& ! $process->tty
&& $process->timeout === null);
Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
&& ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
&& ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
&& ! $process->tty
&& $process->timeout === null);
});
it('still forwards the message directly when the command runs non-interactively', function () {
Process::fake(function (PendingProcess $process) {
return match ($process->command) {
['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
['message' => 'feat(pijul-qol): first'],
], JSON_THROW_ON_ERROR)),
['pijul', 'record', '-m', 'ship it', '--all', '--description', 'desc', 'app'] => Process::result(''),
default => Process::result('', 'Unexpected command.', 1),
};
});
$kernel = app(\Illuminate\Contracts\Console\Kernel::class);
$artisan = new ReflectionMethod($kernel, 'getArtisan');
$artisan->setAccessible(true);
$command = $artisan->invoke($kernel)->find('pijul:record-with-intent-tags');
$input = new Symfony\Component\Console\Input\ArrayInput([
'command' => 'pijul:record-with-intent-tags',
'--message' => 'ship it',
'--all' => true,
'--description' => 'desc',
'prefixes' => ['app'],
]);
$input->setInteractive(false);
$exitCode = $command->run($input, new Symfony\Component\Console\Output\BufferedOutput);
expect($exitCode)->toBe(0);
Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '-m', 'ship it', '--all', '--description', 'desc', 'app']
&& $process->environment === []
&& ! $process->tty);
});