| 1: | <?php |
| 2: | |
| 3: | declare(strict_types=1); |
| 4: | |
| 5: | namespace Atk4\Ui\Behat; |
| 6: | |
| 7: | trait JsCoverageContextTrait |
| 8: | { |
| 9: | |
| 10: | private array $jsCoverage = []; |
| 11: | |
| 12: | protected function isJsCoverageEnabled(): bool |
| 13: | { |
| 14: | return is_dir(__DIR__ . '/../../coverage/js'); |
| 15: | } |
| 16: | |
| 17: | public function __destruct() |
| 18: | { |
| 19: | if (!$this->isJsCoverageEnabled()) { |
| 20: | return; |
| 21: | } |
| 22: | |
| 23: | $outputFile = __DIR__ . '/../../coverage/js/' . hash('sha256', microtime(true) . random_bytes(64)) . '.json'; |
| 24: | file_put_contents($outputFile, json_encode($this->jsCoverage, \JSON_THROW_ON_ERROR)); |
| 25: | } |
| 26: | |
| 27: | protected function saveJsCoverage(): void |
| 28: | { |
| 29: | if (!$this->isJsCoverageEnabled()) { |
| 30: | return; |
| 31: | } |
| 32: | |
| 33: | $seenPaths = array_keys($this->jsCoverage); |
| 34: | $coverages = $this->getSession()->evaluateScript(<<<'EOF' |
| 35: | return (function (seenPaths) { |
| 36: | seenPaths = new Set(seenPaths); |
| 37: | |
| 38: | const windowCoverage = window.__coverage__; |
| 39: | if (typeof windowCoverage !== 'object') { |
| 40: | throw new Error('"window.__coverage__" is not defined'); |
| 41: | } |
| 42: | |
| 43: | const transformCoverageFx = function (istanbulCoverage) { |
| 44: | const res = {}; |
| 45: | Object.entries(istanbulCoverage).forEach(([path, data]) => { |
| 46: | const resSingle = {}; |
| 47: | Object.entries(data).forEach(([k, v]) => { |
| 48: | if (['statementMap', 'fnMap', 'branchMap'].includes(k) && seenPaths.has(path)) { |
| 49: | return; |
| 50: | } |
| 51: | |
| 52: | if (typeof v === 'object') { |
| 53: | const vKeys = Object.keys(v); |
| 54: | if (JSON.stringify(vKeys) === JSON.stringify(vKeys.map((v, k) => k.toString()))) { |
| 55: | v = [...Object.values(v)]; |
| 56: | } |
| 57: | } |
| 58: | resSingle[k] = v; |
| 59: | }); |
| 60: | res[path] = resSingle; |
| 61: | }); |
| 62: | |
| 63: | return res; |
| 64: | }; |
| 65: | |
| 66: | if (window.__coverage_beforeunload__ !== true) { |
| 67: | window.addEventListener('beforeunload', () => { |
| 68: | const navigateCoverages = JSON.parse(window.sessionStorage.getItem('__coverage_navigate__') ?? '[]'); |
| 69: | navigateCoverages.push(transformCoverageFx(window.__coverage__)); |
| 70: | window.sessionStorage.setItem('__coverage_navigate__', JSON.stringify(navigateCoverages)); |
| 71: | }); |
| 72: | window.__coverage_beforeunload__ = true; |
| 73: | } |
| 74: | const navigateCoverages = JSON.parse(window.sessionStorage.getItem('__coverage_navigate__') ?? '[]'); |
| 75: | window.sessionStorage.removeItem('__coverage_navigate__'); |
| 76: | |
| 77: | const res = []; |
| 78: | for (const coverage of [windowCoverage, ...navigateCoverages]) { |
| 79: | res.push(transformCoverageFx(coverage)); |
| 80: | } |
| 81: | |
| 82: | return res; |
| 83: | })(arguments[0]); |
| 84: | EOF, [$seenPaths]); |
| 85: | |
| 86: | foreach ($coverages as $coverage) { |
| 87: | foreach ($coverage as $path => $data) { |
| 88: | if (!isset($this->jsCoverage[$path])) { |
| 89: | $this->jsCoverage[$path] = $data; |
| 90: | } else { |
| 91: | if ($this->jsCoverage[$path]['hash'] !== $data['hash'] |
| 92: | || $this->jsCoverage[$path]['_coverageSchema'] !== $data['_coverageSchema'] |
| 93: | || count($this->jsCoverage[$path]['s']) !== count($data['s']) |
| 94: | || count($this->jsCoverage[$path]['f']) !== count($data['f']) |
| 95: | || count($this->jsCoverage[$path]['b']) !== count($data['b']) |
| 96: | ) { |
| 97: | throw new \Exception('Unexpected JS coverage hash change'); |
| 98: | } |
| 99: | |
| 100: | foreach (['s', 'f', 'b'] as $k) { |
| 101: | foreach ($data[$k] as $i => $n) { |
| 102: | if ($k === 'b') { |
| 103: | foreach ($n as $nI => $nN) { |
| 104: | $this->jsCoverage[$path][$k][$i][$nI] += $nN; |
| 105: | } |
| 106: | } else { |
| 107: | $this->jsCoverage[$path][$k][$i] += $n; |
| 108: | } |
| 109: | } |
| 110: | } |
| 111: | } |
| 112: | } |
| 113: | } |
| 114: | } |
| 115: | } |
| 116: | |