test: add PHPUnit 11.5 unit test suite
- phpunit.xml.dist with unit suite and source coverage config - .phpunit.cache excluded in .gitignore - Initial SvgRendererTest as example test - .claude/settings.json with PostToolUse hook to auto-run phpunit on edit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write",
|
||||||
|
"command": "php bin/phpunit 2>&1 | tail -20"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,4 @@
|
|||||||
/var/
|
/var/
|
||||||
/public/bundles/
|
/public/bundles/
|
||||||
composer.lock
|
composer.lock
|
||||||
|
/.phpunit.cache
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
cacheDirectory=".phpunit.cache"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="unit">
|
||||||
|
<directory>tests/Unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">src</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Unit\Service;
|
||||||
|
|
||||||
|
use App\Service\SvgRenderer;
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
#[CoversClass(SvgRenderer::class)]
|
||||||
|
final class SvgRendererTest extends TestCase
|
||||||
|
{
|
||||||
|
private SvgRenderer $renderer;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->renderer = new SvgRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_returns_a_valid_svg_element(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([]);
|
||||||
|
|
||||||
|
$this->assertStringStartsWith('<svg', $svg);
|
||||||
|
$this->assertStringEndsWith('</svg>', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_includes_accessibility_attributes(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([]);
|
||||||
|
|
||||||
|
$this->assertStringContainsString('role="img"', $svg);
|
||||||
|
$this->assertStringContainsString('aria-label="Contribution graph"', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_applies_dark_theme_background_color(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([], 'dark');
|
||||||
|
|
||||||
|
$this->assertStringContainsString('#0d1117', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_applies_light_theme_background_color(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([], 'light');
|
||||||
|
|
||||||
|
$this->assertStringContainsString('#ffffff', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_falls_back_to_dark_theme_for_unknown_theme_names(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([], 'unknown');
|
||||||
|
|
||||||
|
$this->assertStringContainsString('#0d1117', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_shows_zero_contributions_when_no_data_is_provided(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([]);
|
||||||
|
|
||||||
|
$this->assertStringContainsString('0 contributions in the last year', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_displays_formatted_total_contribution_count(): void
|
||||||
|
{
|
||||||
|
$today = (new \DateTimeImmutable('today'))->format('Y-m-d');
|
||||||
|
|
||||||
|
$svg = $this->renderer->render([$today => 1234]);
|
||||||
|
|
||||||
|
$this->assertStringContainsString('1,234 contributions in the last year', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_renders_all_53_week_columns(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([]);
|
||||||
|
|
||||||
|
// MARGIN_X(28) + col_52 * STEP(13) = 704
|
||||||
|
$this->assertStringContainsString('x="704"', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_renders_day_of_week_labels_matching_github(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([]);
|
||||||
|
|
||||||
|
$this->assertStringContainsString('>Mon<', $svg);
|
||||||
|
$this->assertStringContainsString('>Wed<', $svg);
|
||||||
|
$this->assertStringContainsString('>Fri<', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_renders_a_legend_with_less_and_more_labels(): void
|
||||||
|
{
|
||||||
|
$svg = $this->renderer->render([]);
|
||||||
|
|
||||||
|
$this->assertStringContainsString('>Less<', $svg);
|
||||||
|
$this->assertStringContainsString('>More<', $svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function it_sums_contributions_from_multiple_dates(): void
|
||||||
|
{
|
||||||
|
$today = (new \DateTimeImmutable('today'))->format('Y-m-d');
|
||||||
|
$yesterday = (new \DateTimeImmutable('yesterday'))->format('Y-m-d');
|
||||||
|
|
||||||
|
$svg = $this->renderer->render([$today => 3, $yesterday => 7]);
|
||||||
|
|
||||||
|
$this->assertStringContainsString('10 contributions in the last year', $svg);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user