Skip to content

Commit 6ac141b

Browse files
authored
Merge pull request #23 from IonBazan/feature/hyperlinks
Add hyperlinks support
2 parents 87063f9 + 41537c3 commit 6ac141b

14 files changed

+277
-61
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,21 @@ jobs:
5454
restore-keys: ${{ runner.os }}-composer-
5555
- name: Install Composer dependencies
5656
run: composer update -n --prefer-dist ${{ matrix.composer-flags }}
57+
- name: Set default branch for tests
58+
run: git config --global init.defaultBranch main
5759
- name: Run Tests
58-
run: vendor/bin/simple-phpunit --coverage-clover coverage.xml
60+
run: vendor/bin/simple-phpunit --coverage-clover coverage.xml --coverage-text
5961
- name: Upload coverage to Codecov
6062
uses: codecov/codecov-action@v1
6163
- name: Run mutation tests
62-
if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }}
64+
if: ${{ matrix.php-versions == 8.1 && matrix.operating-system == 'ubuntu-latest' }}
6365
env:
6466
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
6567
run: |
6668
composer req infection/infection -W
6769
vendor/bin/infection --ignore-msi-with-no-mutations --min-covered-msi=100 --min-msi=100 -s -j4
6870
- name: Run phpstan
69-
if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }}
71+
if: ${{ matrix.php-versions == 8.1 && matrix.operating-system == 'ubuntu-latest' }}
7072
run: |
7173
composer req phpstan/phpstan
7274
vendor/bin/phpstan

infection.json.dist

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"logs": {
88
"text": "infection.log",
99
"stryker": {
10-
"badge": "master"
10+
"badge": "main"
1111
}
1212
},
1313
"mutators": {
@@ -16,6 +16,11 @@
1616
"ignore": [
1717
"IonBazan\\ComposerDiff\\Formatter\\JsonFormatter::format"
1818
]
19+
},
20+
"Ternary": {
21+
"ignore": [
22+
"IonBazan\\ComposerDiff\\Formatter\\AbstractFormatter::terminalLink"
23+
]
1924
}
2025
}
2126
}

src/Diff/DiffEntry.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Composer\DependencyResolver\Operation\OperationInterface;
77
use Composer\DependencyResolver\Operation\UninstallOperation;
88
use Composer\DependencyResolver\Operation\UpdateOperation;
9+
use Composer\Package\PackageInterface;
910

1011
class DiffEntry
1112
{
@@ -83,6 +84,24 @@ public function isChange()
8384
return self::TYPE_CHANGE === $this->type;
8485
}
8586

87+
/**
88+
* @return PackageInterface|null
89+
*/
90+
public function getPackage()
91+
{
92+
$operation = $this->getOperation();
93+
94+
if ($operation instanceof UpdateOperation) {
95+
return $operation->getInitialPackage();
96+
}
97+
98+
if ($operation instanceof InstallOperation || $operation instanceof UninstallOperation) {
99+
return $operation->getPackage();
100+
}
101+
102+
return null;
103+
}
104+
86105
/**
87106
* @return string
88107
*/

src/Formatter/AbstractFormatter.php

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use Composer\DependencyResolver\Operation\OperationInterface;
77
use Composer\DependencyResolver\Operation\UninstallOperation;
88
use Composer\DependencyResolver\Operation\UpdateOperation;
9-
use Composer\Package\PackageInterface;
109
use IonBazan\ComposerDiff\Diff\DiffEntry;
1110
use IonBazan\ComposerDiff\Url\GeneratorContainer;
1211
use Symfony\Component\Console\Output\OutputInterface;
@@ -37,11 +36,11 @@ public function getUrl(DiffEntry $entry)
3736
$operation = $entry->getOperation();
3837

3938
if ($operation instanceof UpdateOperation) {
40-
return $this->getCompareUrl($operation->getInitialPackage(), $operation->getTargetPackage());
39+
return $this->generators->getCompareUrl($operation->getInitialPackage(), $operation->getTargetPackage());
4140
}
4241

4342
if ($operation instanceof InstallOperation || $operation instanceof UninstallOperation) {
44-
return $this->getReleaseUrl($operation->getPackage());
43+
return $this->generators->getReleaseUrl($operation->getPackage());
4544
}
4645

4746
return null;
@@ -64,40 +63,35 @@ public function getProjectUrl(OperationInterface $operation)
6463
return null;
6564
}
6665

67-
$generator = $this->generators->get($package);
68-
69-
if (!$generator) {
70-
return null;
71-
}
72-
73-
return $generator->getProjectUrl($package);
66+
return $this->generators->getProjectUrl($package);
7467
}
7568

7669
/**
77-
* @return string|null
70+
* @return string
7871
*/
79-
private function getCompareUrl(PackageInterface $basePackage, PackageInterface $targetPackage)
72+
protected function getDecoratedPackageName(DiffEntry $entry)
8073
{
81-
$generator = $this->generators->get($targetPackage);
74+
$package = $entry->getPackage();
8275

83-
if (!$generator) {
84-
return null;
76+
if (null === $package) {
77+
return '';
8578
}
8679

87-
return $generator->getCompareUrl($basePackage, $targetPackage);
80+
return $this->terminalLink($this->getProjectUrl($entry->getOperation()), $package->getName());
8881
}
8982

9083
/**
91-
* @return string|null
84+
* @param string|null $url
85+
* @param string $title
86+
*
87+
* @return string
9288
*/
93-
private function getReleaseUrl(PackageInterface $package)
89+
private function terminalLink($url, $title)
9490
{
95-
$generator = $this->generators->get($package);
96-
97-
if (!$generator) {
98-
return null;
91+
if (null === $url) {
92+
return $title;
9993
}
10094

101-
return $generator->getReleaseUrl($package);
95+
return method_exists('Symfony\Component\Console\Formatter\OutputFormatterStyle', 'setHref') ? sprintf('<href=%s>%s</>', $url, $title) : $title;
10296
}
10397
}

src/Formatter/MarkdownTableFormatter.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ public function renderSingle(DiffEntries $entries, $title, $withUrls)
6060
private function getTableRow(DiffEntry $entry, $withUrls)
6161
{
6262
$operation = $entry->getOperation();
63-
if ($operation instanceof InstallOperation) {
64-
$packageName = $operation->getPackage()->getName();
65-
$packageUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName;
63+
$packageName = $this->getDecoratedPackageName($entry);
64+
$packageUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName;
6665

66+
if ($operation instanceof InstallOperation) {
6767
return array(
6868
$packageUrl ?: $packageName,
6969
'<fg=green>New</>',
@@ -73,21 +73,15 @@ private function getTableRow(DiffEntry $entry, $withUrls)
7373
}
7474

7575
if ($operation instanceof UpdateOperation) {
76-
$packageName = $operation->getInitialPackage()->getName();
77-
$projectUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName;
78-
7976
return array(
80-
$projectUrl ?: $packageName,
77+
$packageUrl ?: $packageName,
8178
$entry->isChange() ? '<fg=magenta>Changed</>' : ($entry->isUpgrade() ? '<fg=cyan>Upgraded</>' : '<fg=yellow>Downgraded</>'),
8279
$operation->getInitialPackage()->getFullPrettyVersion(),
8380
$operation->getTargetPackage()->getFullPrettyVersion(),
8481
);
8582
}
8683

8784
if ($operation instanceof UninstallOperation) {
88-
$packageName = $operation->getPackage()->getName();
89-
$packageUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName;
90-
9185
return array(
9286
$packageUrl ?: $packageName,
9387
'<fg=red>Removed</>',

src/Url/GeneratorContainer.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Composer\Package\PackageInterface;
66

7-
class GeneratorContainer
7+
class GeneratorContainer implements UrlGenerator
88
{
99
/**
1010
* @var UrlGenerator[]
@@ -42,4 +42,36 @@ public function get(PackageInterface $package)
4242

4343
return null;
4444
}
45+
46+
public function supportsPackage(PackageInterface $package)
47+
{
48+
return null !== $this->get($package);
49+
}
50+
51+
public function getCompareUrl(PackageInterface $initialPackage, PackageInterface $targetPackage)
52+
{
53+
if (!$generator = $this->get($targetPackage)) {
54+
return null;
55+
}
56+
57+
return $generator->getCompareUrl($initialPackage, $targetPackage);
58+
}
59+
60+
public function getReleaseUrl(PackageInterface $package)
61+
{
62+
if (!$generator = $this->get($package)) {
63+
return null;
64+
}
65+
66+
return $generator->getReleaseUrl($package);
67+
}
68+
69+
public function getProjectUrl(PackageInterface $package)
70+
{
71+
if (!$generator = $this->get($package)) {
72+
return null;
73+
}
74+
75+
return $generator->getProjectUrl($package);
76+
}
4577
}

tests/Formatter/FormatterTest.php

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace IonBazan\ComposerDiff\Tests\Formatter;
44

55
use Composer\DependencyResolver\Operation\InstallOperation;
6+
use Composer\DependencyResolver\Operation\OperationInterface;
67
use Composer\DependencyResolver\Operation\UninstallOperation;
78
use Composer\DependencyResolver\Operation\UpdateOperation;
89
use Composer\Package\PackageInterface;
@@ -43,29 +44,22 @@ public function testGetProjectUrlReturnsNullForInvalidOperation()
4344

4445
/**
4546
* @param bool $withUrls
47+
* @param bool $decorated
4648
*
4749
* @testWith [false]
4850
* [true]
51+
* [false, true]
52+
* [true, true]
4953
*/
50-
public function testItRendersTheListOfOperations($withUrls)
54+
public function testItRendersTheListOfOperations($withUrls, $decorated = false)
5155
{
52-
$output = new StreamOutput(fopen('php://memory', 'wb', false));
53-
$formatter = $this->getFormatter($output, $this->getGenerators());
54-
$prodPackages = array(
55-
new InstallOperation($this->getPackage('a/package-1', '1.0.0')),
56-
new InstallOperation($this->getPackage('a/no-link-1', '1.0.0')),
57-
new UpdateOperation($this->getPackage('a/package-2', '1.0.0'), $this->getPackage('a/package-2', '1.2.0')),
58-
new UpdateOperation($this->getPackage('a/package-3', '2.0.0'), $this->getPackage('a/package-3', '1.1.1')),
59-
new UpdateOperation($this->getPackage('a/no-link-2', '2.0.0'), $this->getPackage('a/no-link-2', '1.1.1')),
60-
new UpdateOperation($this->getPackage('php', '>=7.4.6'), $this->getPackage('php', '^8.0')),
56+
$output = new StreamOutput(fopen('php://memory', 'wb', false), OutputInterface::VERBOSITY_NORMAL, $decorated);
57+
$this->getFormatter($output, $this->getGenerators())->render(
58+
$this->getEntries($this->getSampleProdOperations()),
59+
$this->getEntries($this->getSampleDevOperations()),
60+
$withUrls
6161
);
62-
$devPackages = array(
63-
new UpdateOperation($this->getPackage('a/package-5', 'dev-master', 'dev-master 1234567'), $this->getPackage('a/package-5', '1.1.1')),
64-
new UninstallOperation($this->getPackage('a/package-4', '0.1.1')),
65-
new UninstallOperation($this->getPackage('a/no-link-2', '0.1.1')),
66-
);
67-
$formatter->render($this->getEntries($prodPackages), $this->getEntries($devPackages), $withUrls);
68-
$this->assertSame($this->getSampleOutput($withUrls), $this->getDisplay($output));
62+
$this->assertSame($this->getSampleOutput($withUrls, $decorated), $this->getDisplay($output));
6963
}
7064

7165
public function testItFailsWithInvalidOperation()
@@ -84,10 +78,11 @@ abstract protected function getFormatter(OutputInterface $output, GeneratorConta
8478

8579
/**
8680
* @param bool $withUrls
81+
* @param bool $decorated
8782
*
8883
* @return string
8984
*/
90-
abstract protected function getSampleOutput($withUrls);
85+
abstract protected function getSampleOutput($withUrls, $decorated);
9186

9287
/**
9388
* @return string
@@ -107,6 +102,14 @@ protected function getDisplay(OutputInterface $output)
107102
return stream_get_contents($output->getStream());
108103
}
109104

105+
/**
106+
* @return bool
107+
*/
108+
protected function supportsLinks()
109+
{
110+
return method_exists('Symfony\Component\Console\Formatter\OutputFormatterStyle', 'setHref');
111+
}
112+
110113
/**
111114
* @return MockObject|GeneratorContainer
112115
*/
@@ -125,6 +128,7 @@ protected function getGenerators()
125128

126129
$generators = $this->getMockBuilder('IonBazan\ComposerDiff\Url\GeneratorContainer')
127130
->disableOriginalConstructor()
131+
->setMethods(array('get'))
128132
->getMock();
129133
$generators->method('get')
130134
->willReturnCallback(function (PackageInterface $package) use ($generator) {
@@ -137,4 +141,31 @@ protected function getGenerators()
137141

138142
return $generators;
139143
}
144+
145+
/**
146+
* @return OperationInterface[]
147+
*/
148+
private function getSampleProdOperations()
149+
{
150+
return array(
151+
new InstallOperation($this->getPackage('a/package-1', '1.0.0')),
152+
new InstallOperation($this->getPackage('a/no-link-1', '1.0.0')),
153+
new UpdateOperation($this->getPackage('a/package-2', '1.0.0'), $this->getPackage('a/package-2', '1.2.0')),
154+
new UpdateOperation($this->getPackage('a/package-3', '2.0.0'), $this->getPackage('a/package-3', '1.1.1')),
155+
new UpdateOperation($this->getPackage('a/no-link-2', '2.0.0'), $this->getPackage('a/no-link-2', '1.1.1')),
156+
new UpdateOperation($this->getPackage('php', '>=7.4.6'), $this->getPackage('php', '^8.0')),
157+
);
158+
}
159+
160+
/**
161+
* @return OperationInterface[]
162+
*/
163+
private function getSampleDevOperations()
164+
{
165+
return array(
166+
new UpdateOperation($this->getPackage('a/package-5', 'dev-master', 'dev-master 1234567'), $this->getPackage('a/package-5', '1.1.1')),
167+
new UninstallOperation($this->getPackage('a/package-4', '0.1.1')),
168+
new UninstallOperation($this->getPackage('a/no-link-2', '0.1.1')),
169+
);
170+
}
140171
}

tests/Formatter/GitHubFormatterTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
class GitHubFormatterTest extends FormatterTest
1010
{
11-
protected function getSampleOutput($withUrls)
11+
protected function getSampleOutput($withUrls, $decorated)
1212
{
1313
if ($withUrls) {
1414
return <<<OUTPUT

tests/Formatter/JsonFormatterTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public function testRenderSingle()
8888
)), $this->getDisplay($output));
8989
}
9090

91-
protected function getSampleOutput($withUrls)
91+
protected function getSampleOutput($withUrls, $decorated)
9292
{
9393
if ($withUrls) {
9494
return self::formatOutput(array(

0 commit comments

Comments
 (0)