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

move-elevator / composer-translation-validator / 16119287783

07 Jul 2025 02:08PM UTC coverage: 94.526% (+1.3%) from 93.207%
16119287783

Pull #18

github

jackd248
fix: remove unnecessary blank lines in validation output files
Pull Request #18: refactor: implement object-oriented validation architecture with unified rendering system

228 of 230 new or added lines in 11 files covered. (99.13%)

5 existing lines in 1 file now uncovered.

777 of 822 relevant lines covered (94.53%)

5.23 hits per line

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

98.33
/src/Result/ValidationResultCliRenderer.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace MoveElevator\ComposerTranslationValidator\Result;
6

7
use MoveElevator\ComposerTranslationValidator\Utility\PathUtility;
8
use MoveElevator\ComposerTranslationValidator\Validator\ResultType;
9
use MoveElevator\ComposerTranslationValidator\Validator\ValidatorInterface;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Symfony\Component\Console\Style\SymfonyStyle;
13

14
class ValidationResultCliRenderer
15
{
16
    private SymfonyStyle $io;
17

18
    public function __construct(
10✔
19
        private OutputInterface $output,
20
        private InputInterface $input,
21
        private bool $dryRun = false,
22
        private bool $strict = false,
23
    ) {
24
        $this->io = new SymfonyStyle($this->input, $this->output);
10✔
25
    }
26

27
    public function render(ValidationResult $validationResult): int
10✔
28
    {
29
        $this->renderValidatorResults($validationResult);
10✔
30
        $this->renderSummary($validationResult->getOverallResult());
10✔
31

32
        return $validationResult->getOverallResult()->resolveErrorToCommandExitCode($this->dryRun, $this->strict);
10✔
33
    }
34

35
    private function renderValidatorResults(ValidationResult $validationResult): void
10✔
36
    {
37
        $validatorPairs = $validationResult->getValidatorFileSetPairs();
10✔
38

39
        if (empty($validatorPairs)) {
10✔
40
            return;
4✔
41
        }
42

43
        // Group by validator class for rendering
44
        $groupedByValidator = [];
6✔
45
        foreach ($validatorPairs as $pair) {
6✔
46
            $validator = $pair['validator'];
6✔
47
            $fileSet = $pair['fileSet'];
6✔
48

49
            if (!$validator->hasIssues()) {
6✔
NEW
50
                continue;
×
51
            }
52

53
            $validatorClass = $validator::class;
6✔
54
            if (!isset($groupedByValidator[$validatorClass])) {
6✔
55
                $groupedByValidator[$validatorClass] = [
6✔
56
                    'validator' => $validator,
6✔
57
                    'paths' => [],
6✔
58
                ];
6✔
59
            }
60

61
            $path = $fileSet->getPath();
6✔
62
            if (!isset($groupedByValidator[$validatorClass]['paths'][$path])) {
6✔
63
                $groupedByValidator[$validatorClass]['paths'][$path] = [];
6✔
64
            }
65

66
            $groupedByValidator[$validatorClass]['paths'][$path][] = $validator->getIssues();
6✔
67
        }
68

69
        foreach ($groupedByValidator as $data) {
6✔
70
            $this->renderValidatorSectionWithPaths($data['validator'], $data['paths']);
6✔
71
        }
72
    }
73

74
    /**
75
     * @param array<string, array<array<Issue>>> $pathsWithIssues
76
     */
77
    private function renderValidatorSectionWithPaths(ValidatorInterface $validator, array $pathsWithIssues): void
6✔
78
    {
79
        $validatorClass = $validator::class;
6✔
80
        $this->io->section(sprintf('Validator: <fg=cyan>%s</>', $validatorClass));
6✔
81

82
        if ($this->output->isVerbose()) {
6✔
83
            $this->io->writeln(sprintf('Explanation: %s', $validator->explain()));
1✔
84
        }
85

86
        foreach ($pathsWithIssues as $path => $issueArrays) {
6✔
87
            $this->io->writeln(sprintf('<fg=gray>Folder Path: %s</>', PathUtility::normalizeFolderPath($path)));
6✔
88
            $this->io->newLine();
6✔
89

90
            // Flatten all issues for this path
91
            $allIssues = [];
6✔
92
            foreach ($issueArrays as $issueArray) {
6✔
93
                $allIssues = [...$allIssues, ...$issueArray];
6✔
94
            }
95

96
            // Convert issues back to legacy format for existing renderIssueSets method
97
            $legacyFormat = $this->convertToLegacyFormat($allIssues);
6✔
98
            $validator->renderIssueSets($this->input, $this->output, $legacyFormat);
6✔
99

100
            $this->io->newLine();
6✔
101
            $this->io->newLine();
6✔
102
        }
103
    }
104

105
    /**
106
     * @param array<Issue> $issues
107
     *
108
     * @return array<string, array<int, array<mixed>>>
109
     */
110
    private function convertToLegacyFormat(array $issues): array
6✔
111
    {
112
        $legacy = [];
6✔
113

114
        foreach ($issues as $issue) {
6✔
115
            $legacy[''][] = $issue->toArray();
1✔
116
        }
117

118
        return $legacy;
6✔
119
    }
120

121
    private function renderSummary(ResultType $resultType): void
10✔
122
    {
123
        if ($resultType->notFullySuccessful()) {
10✔
124
            $this->io->newLine();
7✔
125
            $this->io->{$this->dryRun || ResultType::WARNING === $resultType ? 'warning' : 'error'}(
7✔
126
                $this->dryRun
7✔
127
                    ? 'Language validation failed and completed in dry-run mode.'
1✔
128
                    : 'Language validation failed.'
7✔
129
            );
7✔
130
        } else {
131
            $message = 'Language validation succeeded.';
3✔
132
            $this->output->isVerbose()
3✔
133
                ? $this->io->success($message)
1✔
134
                : $this->output->writeln('<fg=green>'.$message.'</>');
2✔
135
        }
136
    }
137
}
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