Skip to content

Commit

Permalink
Collect js coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Oct 7, 2022
1 parent 05e4738 commit b93e892
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 4 deletions.
29 changes: 26 additions & 3 deletions .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,13 @@ jobs:
diff -ru template.orig template
rm -r public.orig template.orig
- name: Instrument & recompile JS files (only for coverage)
if: env.LOG_COVERAGE
run: |
rm public/*.js
(cd js && npm install --package-lock-only --save-dev nyc && npm ci --loglevel=error)
(cd js && npx nyc instrument --in-place . && npx webpack --env production)
- name: Install PHP dependencies
run: |
composer remove --no-interaction --no-update phpunit/phpunit johnkary/phpunit-speedtrap --dev
Expand All @@ -351,7 +358,7 @@ jobs:
php -r '(new PDO("mysql:host=mariadb", "root", "atk4_pass_root"))->exec("ALTER USER '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");'
php -r '(new PDO("pgsql:host=postgres;dbname=atk4_test", "atk4_test_user", "atk4_pass"))->exec("ALTER ROLE atk4_test_user CONNECTION LIMIT 1");'
/usr/lib/oracle/setup.sh
if [ -n "$LOG_COVERAGE" ]; then mkdir coverage; fi
if [ -n "$LOG_COVERAGE" ]; then mkdir coverage coverage/js; fi
ci_wait_until () { timeout 30 sh -c "until { $1 2> /dev/null; }; do sleep 0.02; done" || timeout 15 sh -c "$1" || { echo "health timeout: $1"; exit 1; }; }
php -d opcache.enable_cli=1 -S 127.0.0.1:8888 > /dev/null 2>&1 &
ci_wait_until 'nc -w 1 127.0.0.1 8888'
Expand Down Expand Up @@ -424,15 +431,31 @@ jobs:
php demos/_demo-data/create-db.php
vendor/bin/behat -vv --config behat.yml.dist
- name: Upload coverage logs 1/2 (only for latest Chrome)
- name: Upload PHP coverage logs 1/2 (only for coverage)
if: env.LOG_COVERAGE
run: |
ls -l coverage | wc -l
php -d memory_limit=2G vendor/bin/phpcov merge coverage/ --clover coverage/merged.xml
- name: Upload coverage logs 2/2 (only for latest Chrome)
- name: Upload PHP coverage logs 2/2 (only for coverage)
if: env.LOG_COVERAGE
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage/merged.xml

- name: Upload JS coverage logs 1/2 (only for coverage)
if: env.LOG_COVERAGE
run: |
ls -l coverage/js | wc -l
(cd js && npx nyc report --temp-dir ../coverage/js --report-dir ../coverage/js --reporter=clover)
# fix never reached condition is rendered to clover with falsecount = 2 (or even higher if condition has multiple statements)
# https://github.com/istanbuljs/istanbuljs/issues/695
sed -i -E 's~count="0" type="cond" truecount="0" falsecount="[1-9]+"~count="0" type="cond" truecount="0" falsecount="0"~' coverage/js/clover.xml
- name: Upload JS coverage logs 2/2 (only for coverage)
if: env.LOG_COVERAGE
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage/js/clover.xml
6 changes: 5 additions & 1 deletion src/Behat/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

class Context extends RawMinkContext implements BehatContext
{
use JsCoverageContextTrait;
use WarnDynamicPropertyTrait;

public function getSession($name = null): MinkSession
Expand Down Expand Up @@ -73,10 +74,13 @@ public function waitUntilLoadingAndAnimationFinished(AfterStepScope $event): voi
{
$this->jqueryWait();
$this->disableAnimations();

if (!str_contains($this->getScenario($event)->getTitle() ?? '', 'exception is displayed')) {
$this->assertNoException();
}
$this->assertNoDuplicateId();

$this->saveJsCoverage();
}

protected function getFinishedScript(): string
Expand Down Expand Up @@ -139,7 +143,7 @@ protected function disableAnimations(): void
]);

$this->getSession()->executeScript(
'if (Array.prototype.filter.call(document.getElementsByTagName("style"), e => e.getAttribute("about") === "atk-test-behat").length === 0) {'
'if (Array.prototype.filter.call(document.getElementsByTagName("style"), (e) => e.getAttribute("about") === "atk-test-behat").length === 0) {'
. ' $(\'<style about="atk-test-behat">' . $css . '</style>\').appendTo(\'head\');'
. ' }'
. 'jQuery.fx.off = true;'
Expand Down
93 changes: 93 additions & 0 deletions src/Behat/JsCoverageContextTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace Atk4\Ui\Behat;

use Exception;

trait JsCoverageContextTrait
{
/** @var array<string, array<string, mixed>> */
private array $jsCoverage = [];

protected function isJsCoverageEnabled(): bool
{
return is_dir(__DIR__ . '/../../coverage/js');
}

public function __destruct()
{
if (!$this->isJsCoverageEnabled()) {
return;
}

$outputFile = __DIR__ . '/../../coverage/js/' . hash('sha256', microtime(true) . random_bytes(64)) . '.json';
file_put_contents($outputFile, json_encode($this->jsCoverage, \JSON_THROW_ON_ERROR));
}

protected function saveJsCoverage(): void
{
if (!$this->isJsCoverageEnabled()) {
return;
}

$seenPaths = array_keys($this->jsCoverage);
$coverageAll = $this->getSession()->evaluateScript(<<<'EOF'
return (function (seenPaths) {
seenPaths = new Set(seenPaths);
const istanbulCoverage = window.__coverage__;
if (typeof istanbulCoverage !== 'object') {
throw '"window.__coverage__" is not defined';
}
const resAll = {};
Object.entries(istanbulCoverage).forEach(([path, data]) => {
const res = {};
Object.entries(data).forEach(([k, v]) => {
if (['statementMap', 'fnMap', 'branchMap'].includes(k) && seenPaths.has(path)) {
return;
}
if (typeof v === 'object') {
const vKeys = Object.keys(v);
if (JSON.stringify(vKeys) === JSON.stringify(vKeys.map((v, k) => k.toString()))) {
v = [...Object.values(v)];
}
}
res[k] = v;
});
resAll[path] = res;
});
return resAll;
})(arguments[0]);
EOF, [$seenPaths]);

foreach ($coverageAll as $path => $data) {
if (!isset($this->jsCoverage[$path])) {
$this->jsCoverage[$path] = $data;
} else {
if ($this->jsCoverage[$path]['hash'] !== $data['hash']
|| $this->jsCoverage[$path]['_coverageSchema'] !== $data['_coverageSchema']
|| count($this->jsCoverage[$path]['s']) !== count($data['s'])
|| count($this->jsCoverage[$path]['f']) !== count($data['f'])
|| count($this->jsCoverage[$path]['b']) !== count($data['b'])
) {
throw new Exception('Unexpected JS coverage hash change');
}

foreach (['s', 'f', 'b'] as $k) {
foreach ($data[$k] as $i => $n) {
if ($k === 'b') {
foreach ($n as $nI => $nN) {
$this->jsCoverage[$path][$k][$i][$nI] += $nN;
}
} else {
$this->jsCoverage[$path][$k][$i] += $n;
}
}
}
}
}
}
}

0 comments on commit b93e892

Please sign in to comment.