├── .gitattributes
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── README.md
├── composer.json
├── phpcs.xml
├── phpunit-printer-context.png
├── phpunit-printer-logs.png
├── phpunit.xml.dist
└── src
├── Functions
└── helpers.php
├── Printer.php
├── Printer6.php
├── Printer7.php
├── Printer8.php
└── Printer9.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 | test/ export-ignore
3 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | types: [opened, reopened, synchronize]
9 | jobs:
10 | run:
11 | env:
12 | ACTIONS_STEP_DEBUG: true
13 | runs-on: ${{ matrix.os }}
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | os: [ubuntu-latest, windows-latest, macos-latest]
18 | php-versions: ["7.3", "7.4"]
19 | phpunit-version: ["6", "7", "8", "9"]
20 | name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.os }} (PHPUnit ${{ matrix.phpunit-version }})
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v2
24 |
25 | - name: Setup PHP
26 | uses: shivammathur/setup-php@v2
27 | with:
28 | php-version: ${{ matrix.php-versions }}
29 | extensions: mbstring
30 | coverage: xdebug2
31 |
32 | - name: Set PHPUnit Version
33 | run: |
34 | $content = Get-Content -Path 'composer.json' | ConvertFrom-Json
35 | $content.{require-dev}.{phpunit/phpunit} = "^${{ matrix.phpunit-version }}"
36 | $content | ConvertTo-Json | Set-Content -Path 'composer.json'
37 | shell: pwsh
38 |
39 | - name: Composer dependencies
40 | run: composer install --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
41 |
42 | - name: Set correct phpt file name
43 | id: phpt-filename
44 | run: |
45 | If (${{matrix.phpunit-version}} -eq 6) {
46 | '##[set-output name=version;]-phpunit6'
47 | } Else {
48 | '##[set-output name=version;]'
49 | }
50 | shell: pwsh
51 |
52 | - name: Configure tests for the current PHPUnit version
53 | run: |
54 | $V = (.\vendor\bin\phpunit --version | Out-String).trim()
55 | $content = Get-Content -Path 'test/states-test${{ steps.phpt-filename.outputs.version}}.phpt'
56 | $newContent = $content -replace '%%VERSION%%', $V
57 | $newContent | Set-Content -Path 'test/states-test${{ steps.phpt-filename.outputs.version}}.phpt'
58 | shell: pwsh
59 |
60 | - name: Run phpunit
61 | run: ./vendor/bin/phpunit
62 |
63 | - name: Run phpcs
64 | run: ./vendor/bin/phpcs
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.lock
3 | .phpunit.result.cache
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## phpunit-github-actions-printer
2 |
3 | > There's a zero-config way to achieve this at [mheap/phpunit-matcher-action](https://github.com/mheap/phpunit-matcher-action)
4 |
5 | This is a PHPUnit printer that uses the `::error` and `::warning` functionality of GitHub Actions to add annotiations for failing test runs. It's main differentiator to the above is that it supports adding warnings in addition to errors.
6 |
7 | 
8 |
9 | 
10 |
11 | > If you're interested in learning more about GitHub Actions, [sign up here](https://michaelheap.com/building-github-actions/)
12 |
13 | ## Usage
14 |
15 | Add this printer to your project
16 |
17 | ```bash
18 | composer require --dev mheap/phpunit-github-actions-printer
19 | ```
20 |
21 | When you run your tests, specify `mheap\GithubActionsReporter\Printer` as the printer to use
22 |
23 | ```bash
24 | ./vendor/bin/phpunit --printer mheap\\GithubActionsReporter\\Printer /path/to/tests
25 | ```
26 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mheap/phpunit-github-actions-printer",
3 | "description": "PHPUnit Printer for adding test failures as annotations on GitHub Actions",
4 | "type": "library",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Michael Heap",
9 | "email": "m@michaelheap.com"
10 | }
11 | ],
12 | "autoload": {
13 | "files": ["src/Functions/helpers.php"],
14 | "psr-4": {
15 | "mheap\\GithubActionsReporter\\": "src"
16 | }
17 | },
18 | "require": {},
19 | "require-dev": {
20 | "phpunit/phpunit": "^9",
21 | "squizlabs/php_codesniffer": "3.*"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | PSR12
4 | src
5 |
6 |
7 |
--------------------------------------------------------------------------------
/phpunit-printer-context.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheap/phpunit-github-actions-printer/19b34d79a6abfaa362debcd25dd7bd11e82576b8/phpunit-printer-context.png
--------------------------------------------------------------------------------
/phpunit-printer-logs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheap/phpunit-github-actions-printer/19b34d79a6abfaa362debcd25dd7bd11e82576b8/phpunit-printer-logs.png
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 | ./test/
16 |
17 |
18 | ./test/Unit/
19 |
20 |
21 |
22 |
23 | ./src
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/Functions/helpers.php:
--------------------------------------------------------------------------------
1 | =') == true &&
32 | ($upperVersion === true || version_compare($version, $upperVersion, '<=') == true)
33 | ) {
34 | return $class;
35 | }
36 | }
37 |
38 | return null;
39 | }
40 |
41 | /**
42 | * @param TestFailure $defect
43 | * @param string $defectType
44 | *
45 | * @return string
46 | * @throws \ReflectionException
47 | * @internal
48 | */
49 | function printDefectTrace($defect, $defectType)
50 | {
51 | $e = $defect->thrownException();
52 |
53 | $errorLines = array_filter(
54 | explode("\n", (string)$e),
55 | static function ($l) {
56 | return $l;
57 | }
58 | );
59 |
60 | $error = end($errorLines);
61 | $lineIndex = strrpos($error, ":");
62 | $path = substr($error, 0, $lineIndex);
63 | $line = substr($error, $lineIndex + 1);
64 |
65 | list($reflectedPath, $reflectedLine) = getReflectionFromTest(
66 | $defect->getTestName()
67 | );
68 |
69 | if ($path !== $reflectedPath) {
70 | $path = $reflectedPath;
71 | $line = $reflectedLine;
72 | }
73 |
74 | $message = explode("\n", $defect->getExceptionAsString());
75 | $message = implode('%0A', $message);
76 |
77 | // Some messages might contain paths. Let's convert thost to relative paths too
78 | $message = relativePath($message);
79 | $message = preg_replace('/%0A$/', '', $message);
80 |
81 | $path = relativePath($path);
82 | $file = "file={$path}";
83 | $line = "line={$line}";
84 |
85 | return "::{$defectType} $file,$line::{$message}\n";
86 | }
87 |
88 | /**
89 | * @param string $path
90 | *
91 | * @return mixed
92 | * @internal
93 | */
94 | function relativePath($path)
95 | {
96 | $relative = str_replace(getcwd() . DIRECTORY_SEPARATOR, '', $path);
97 |
98 | // Translate \ in to / for Windows
99 | return str_replace('\\', '/', $relative);
100 | }
101 |
102 | /**
103 | * @param string $name
104 | *
105 | * @return array
106 | * @throws \ReflectionException
107 | * @internal
108 | */
109 | function getReflectionFromTest($name)
110 | {
111 | list($klass, $method) = explode('::', $name);
112 |
113 | // Handle data providers
114 | $parts = explode(" ", $method, 2);
115 | if (count($parts) > 1) {
116 | $method = $parts[0];
117 | }
118 |
119 | $c = new ReflectionClass($klass);
120 | $m = $c->getMethod($method);
121 |
122 | return [$m->getFileName(), $m->getStartLine()];
123 | }
124 |
--------------------------------------------------------------------------------
/src/Printer.php:
--------------------------------------------------------------------------------
1 | currentType = (in_array($type, ['error', 'failure']) === true) ? 'error' : 'warning';
33 |
34 | foreach ($defects as $i => $defect) {
35 | $this->printDefect($defect, $i);
36 | }
37 | }
38 |
39 | protected function printDefectHeader(TestFailure $defect, $count): void
40 | {
41 | }
42 |
43 | /**
44 | * @throws \ReflectionException
45 | */
46 | protected function printDefectTrace(TestFailure $defect): void
47 | {
48 | $this->write(printDefectTrace($defect, $this->currentType));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Printer7.php:
--------------------------------------------------------------------------------
1 | currentType = (in_array($type, ['error', 'failure']) === true) ? 'error' : 'warning';
33 |
34 | foreach ($defects as $i => $defect) {
35 | $this->printDefect($defect, $i);
36 | }
37 | }
38 |
39 | protected function printDefectHeader(TestFailure $defect, int $count): void
40 | {
41 | }
42 |
43 | protected function printDefectTrace(TestFailure $defect): void
44 | {
45 | $this->write(printDefectTrace($defect, $this->currentType));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Printer8.php:
--------------------------------------------------------------------------------
1 | currentType = (in_array($type, ['error', 'failure']) === true) ? 'error' : 'warning';
35 |
36 | foreach ($defects as $i => $defect) {
37 | $this->printDefect($defect, $i);
38 | }
39 | }
40 |
41 | protected function printDefectHeader(TestFailure $defect, int $count): void
42 | {
43 | }
44 |
45 | protected function printDefectTrace(TestFailure $defect): void
46 | {
47 | $this->write(printDefectTrace($defect, $this->currentType));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Printer9.php:
--------------------------------------------------------------------------------
1 | currentType = (in_array($type, ['error', 'failure']) === true) ? 'error' : 'warning';
33 |
34 | foreach ($defects as $i => $defect) {
35 | $this->printDefect($defect, $i);
36 | }
37 | }
38 |
39 | protected function printDefectHeader(TestFailure $defect, int $count): void
40 | {
41 | }
42 |
43 | protected function printDefectTrace(TestFailure $defect): void
44 | {
45 | $this->write(printDefectTrace($defect, $this->currentType));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------