diff --git a/__tests__/__outputs__/phpunit-test-results.md b/__tests__/__outputs__/phpunit-test-results.md
new file mode 100644
index 0000000..af8ee4a
--- /dev/null
+++ b/__tests__/__outputs__/phpunit-test-results.md
@@ -0,0 +1,38 @@
+
+## ❌ fixtures/external/java/phpunit.xml
+**12** tests were completed in **148ms** with **10** passed, **2** failed and **0** skipped.
+|Test suite|Passed|Failed|Skipped|Time|
+|:---|---:|---:|---:|---:|
+|[CLI Arguments](#r0s0)||2❌||140ms|
+|[PHPUnit\Event\CollectingDispatcherTest](#r0s1)|2✅|||4ms|
+|[PHPUnit\Event\DeferringDispatcherTest](#r0s2)|4✅|||3ms|
+|[PHPUnit\Event\DirectDispatcherTest](#r0s3)|4✅|||1ms|
+### ❌ CLI Arguments
+```
+❌ targeting-traits-with-coversclass-attribute-is-deprecated.phpt
+ targeting-traits-with-coversclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
+❌ targeting-traits-with-usesclass-attribute-is-deprecated.phpt
+ targeting-traits-with-usesclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
+```
+### ✅ PHPUnit\Event\CollectingDispatcherTest
+```
+PHPUnit.Event.CollectingDispatcherTest
+ ✅ testHasNoCollectedEventsWhenFlushedImmediatelyAfterCreation
+ ✅ testCollectsDispatchedEventsUntilFlushed
+```
+### ✅ PHPUnit\Event\DeferringDispatcherTest
+```
+PHPUnit.Event.DeferringDispatcherTest
+ ✅ testCollectsEventsUntilFlush
+ ✅ testFlushesCollectedEvents
+ ✅ testSubscriberCanBeRegistered
+ ✅ testTracerCanBeRegistered
+```
+### ✅ PHPUnit\Event\DirectDispatcherTest
+```
+PHPUnit.Event.DirectDispatcherTest
+ ✅ testDispatchesEventToKnownSubscribers
+ ✅ testDispatchesEventToTracers
+ ✅ testRegisterRejectsUnknownSubscriber
+ ✅ testDispatchRejectsUnknownEventType
+```
\ No newline at end of file
diff --git a/__tests__/__snapshots__/java-junit-phpunit.test.ts.snap b/__tests__/__snapshots__/java-junit-phpunit.test.ts.snap
new file mode 100644
index 0000000..627a936
--- /dev/null
+++ b/__tests__/__snapshots__/java-junit-phpunit.test.ts.snap
@@ -0,0 +1,188 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`java-junit phpunit tests report from phpunit test results matches snapshot 1`] = `
+TestRunResult {
+ "path": "fixtures/external/java/phpunit.xml",
+ "suites": [
+ TestSuiteResult {
+ "groups": [
+ TestGroupResult {
+ "name": "PHPUnit.Event.CollectingDispatcherTest",
+ "tests": [
+ TestCaseResult {
+ "error": undefined,
+ "name": "testHasNoCollectedEventsWhenFlushedImmediatelyAfterCreation",
+ "result": "success",
+ "time": 1.441,
+ },
+ TestCaseResult {
+ "error": undefined,
+ "name": "testCollectsDispatchedEventsUntilFlushed",
+ "result": "success",
+ "time": 2.815,
+ },
+ ],
+ },
+ ],
+ "name": "PHPUnit\\Event\\CollectingDispatcherTest",
+ "totalTime": 4.256,
+ },
+ TestSuiteResult {
+ "groups": [
+ TestGroupResult {
+ "name": "PHPUnit.Event.DeferringDispatcherTest",
+ "tests": [
+ TestCaseResult {
+ "error": undefined,
+ "name": "testCollectsEventsUntilFlush",
+ "result": "success",
+ "time": 1.6720000000000002,
+ },
+ TestCaseResult {
+ "error": undefined,
+ "name": "testFlushesCollectedEvents",
+ "result": "success",
+ "time": 0.661,
+ },
+ TestCaseResult {
+ "error": undefined,
+ "name": "testSubscriberCanBeRegistered",
+ "result": "success",
+ "time": 0.33399999999999996,
+ },
+ TestCaseResult {
+ "error": undefined,
+ "name": "testTracerCanBeRegistered",
+ "result": "success",
+ "time": 0.262,
+ },
+ ],
+ },
+ ],
+ "name": "PHPUnit\\Event\\DeferringDispatcherTest",
+ "totalTime": 2.928,
+ },
+ TestSuiteResult {
+ "groups": [
+ TestGroupResult {
+ "name": "PHPUnit.Event.DirectDispatcherTest",
+ "tests": [
+ TestCaseResult {
+ "error": undefined,
+ "name": "testDispatchesEventToKnownSubscribers",
+ "result": "success",
+ "time": 0.17,
+ },
+ TestCaseResult {
+ "error": undefined,
+ "name": "testDispatchesEventToTracers",
+ "result": "success",
+ "time": 0.248,
+ },
+ TestCaseResult {
+ "error": undefined,
+ "name": "testRegisterRejectsUnknownSubscriber",
+ "result": "success",
+ "time": 0.257,
+ },
+ TestCaseResult {
+ "error": undefined,
+ "name": "testDispatchRejectsUnknownEventType",
+ "result": "success",
+ "time": 0.11900000000000001,
+ },
+ ],
+ },
+ ],
+ "name": "PHPUnit\\Event\\DirectDispatcherTest",
+ "totalTime": 0.794,
+ },
+ TestSuiteResult {
+ "groups": [
+ TestGroupResult {
+ "name": undefined,
+ "tests": [
+ TestCaseResult {
+ "error": {
+ "details": "targeting-traits-with-coversclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
+--- Expected
++++ Actual
+@@ @@
+ PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
+ Test Runner Configured
+ Test Suite Loaded (1 test)
++Test Runner Triggered Warning (No code coverage driver available)
+ Event Facade Sealed
+ Test Runner Started
+ Test Suite Sorted
+@@ @@
+ Test Preparation Started (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
+ Test Prepared (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
+ Test Passed (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
+-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\\TestFixture\\CoveredTrait with #[CoversClass] is deprecated, please refactor your test to use #[CoversTrait] instead.)
+ Test Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
+ Test Suite Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest, 1 test)
+ Test Runner Execution Finished
+ Test Runner Finished
+-PHPUnit Finished (Shell Exit Code: 0)
++PHPUnit Finished (Shell Exit Code: 1)
+
+/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-coversclass-attribute-is-deprecated.phpt:28
+/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
+/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
+/home/matteo/OSS/phpunit/src/TextUI/Application.php:200",
+ "line": undefined,
+ "message": undefined,
+ "path": undefined,
+ },
+ "name": "targeting-traits-with-coversclass-attribute-is-deprecated.phpt",
+ "result": "failed",
+ "time": 68.151,
+ },
+ TestCaseResult {
+ "error": {
+ "details": "targeting-traits-with-usesclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
+--- Expected
++++ Actual
+@@ @@
+ PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
+ Test Runner Configured
+ Test Suite Loaded (1 test)
++Test Runner Triggered Warning (No code coverage driver available)
+ Event Facade Sealed
+ Test Runner Started
+ Test Suite Sorted
+@@ @@
+ Test Preparation Started (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
+ Test Prepared (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
+ Test Passed (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
+-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\\TestFixture\\CoveredTrait with #[UsesClass] is deprecated, please refactor your test to use #[UsesTrait] instead.)
+ Test Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
+ Test Suite Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest, 1 test)
+ Test Runner Execution Finished
+ Test Runner Finished
+-PHPUnit Finished (Shell Exit Code: 0)
++PHPUnit Finished (Shell Exit Code: 1)
+
+/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-usesclass-attribute-is-deprecated.phpt:28
+/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
+/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
+/home/matteo/OSS/phpunit/src/TextUI/Application.php:200",
+ "line": undefined,
+ "message": undefined,
+ "path": undefined,
+ },
+ "name": "targeting-traits-with-usesclass-attribute-is-deprecated.phpt",
+ "result": "failed",
+ "time": 64.268,
+ },
+ ],
+ },
+ ],
+ "name": "CLI Arguments",
+ "totalTime": 140.397,
+ },
+ ],
+ "totalTime": undefined,
+}
+`;
diff --git a/__tests__/fixtures/empty/java-junit-empty-phpunit.xml b/__tests__/fixtures/empty/java-junit-empty-phpunit.xml
new file mode 100644
index 0000000..0d3d2e2
--- /dev/null
+++ b/__tests__/fixtures/empty/java-junit-empty-phpunit.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/__tests__/fixtures/external/java/phpunit.xml b/__tests__/fixtures/external/java/phpunit.xml
new file mode 100644
index 0000000..b0e0cc0
--- /dev/null
+++ b/__tests__/fixtures/external/java/phpunit.xml
@@ -0,0 +1,79 @@
+
+
+
+
+ targeting-traits-with-coversclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
+--- Expected
++++ Actual
+@@ @@
+ PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
+ Test Runner Configured
+ Test Suite Loaded (1 test)
++Test Runner Triggered Warning (No code coverage driver available)
+ Event Facade Sealed
+ Test Runner Started
+ Test Suite Sorted
+@@ @@
+ Test Preparation Started (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
+ Test Prepared (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
+ Test Passed (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
+-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\TestFixture\CoveredTrait with #[CoversClass] is deprecated, please refactor your test to use #[CoversTrait] instead.)
+ Test Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
+ Test Suite Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest, 1 test)
+ Test Runner Execution Finished
+ Test Runner Finished
+-PHPUnit Finished (Shell Exit Code: 0)
++PHPUnit Finished (Shell Exit Code: 1)
+
+/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-coversclass-attribute-is-deprecated.phpt:28
+/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
+/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
+/home/matteo/OSS/phpunit/src/TextUI/Application.php:200
+
+
+ targeting-traits-with-usesclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
+--- Expected
++++ Actual
+@@ @@
+ PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
+ Test Runner Configured
+ Test Suite Loaded (1 test)
++Test Runner Triggered Warning (No code coverage driver available)
+ Event Facade Sealed
+ Test Runner Started
+ Test Suite Sorted
+@@ @@
+ Test Preparation Started (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
+ Test Prepared (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
+ Test Passed (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
+-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\TestFixture\CoveredTrait with #[UsesClass] is deprecated, please refactor your test to use #[UsesTrait] instead.)
+ Test Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
+ Test Suite Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest, 1 test)
+ Test Runner Execution Finished
+ Test Runner Finished
+-PHPUnit Finished (Shell Exit Code: 0)
++PHPUnit Finished (Shell Exit Code: 1)
+
+/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-usesclass-attribute-is-deprecated.phpt:28
+/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
+/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
+/home/matteo/OSS/phpunit/src/TextUI/Application.php:200
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/java-junit-phpunit.test.ts b/__tests__/java-junit-phpunit.test.ts
new file mode 100644
index 0000000..0173c85
--- /dev/null
+++ b/__tests__/java-junit-phpunit.test.ts
@@ -0,0 +1,46 @@
+import * as fs from 'fs'
+import * as path from 'path'
+
+import {JavaJunitParser} from '../src/parsers/java-junit/java-junit-parser'
+import {ParseOptions} from '../src/test-parser'
+import {getReport} from '../src/report/get-report'
+import {normalizeFilePath} from '../src/utils/path-utils'
+
+describe('java-junit phpunit tests', () => {
+ it('produces empty test run result when there are no test cases', async () => {
+ const fixturePath = path.join(__dirname, 'fixtures', 'empty', 'java-junit-empty-phpunit.xml')
+ const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
+ const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
+
+ const opts: ParseOptions = {
+ parseErrors: true,
+ trackedFiles: []
+ }
+
+ const parser = new JavaJunitParser(opts)
+ const result = await parser.parse(filePath, fileContent)
+ expect(result.tests).toBe(0)
+ expect(result.result).toBe('success')
+ })
+
+ it('report from phpunit test results matches snapshot', async () => {
+ const fixturePath = path.join(__dirname, 'fixtures', 'external', 'java', 'phpunit.xml')
+ const trackedFilesPath = path.join(__dirname, 'fixtures', 'external', 'java', 'phpunit.txt')
+ const outputPath = path.join(__dirname, '__outputs__', 'phpunit-test-results.md')
+ const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
+ const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
+
+ const opts: ParseOptions = {
+ parseErrors: true,
+ trackedFiles: []
+ }
+
+ const parser = new JavaJunitParser(opts)
+ const result = await parser.parse(filePath, fileContent)
+ expect(result).toMatchSnapshot()
+
+ const report = getReport([result])
+ fs.mkdirSync(path.dirname(outputPath), {recursive: true})
+ fs.writeFileSync(outputPath, report)
+ })
+})
diff --git a/dist/index.js b/dist/index.js
index d103be3..4d21f60 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1129,19 +1129,21 @@ class JavaJunitParser {
});
}
getTestRunResult(filePath, junit) {
- var _a;
- const suites = junit.testsuites.testsuite === undefined
- ? []
- : junit.testsuites.testsuite.map(ts => {
- const name = ts.$.name.trim();
- const time = parseFloat(ts.$.time) * 1000;
- const sr = new test_results_1.TestSuiteResult(name, this.getGroups(ts), time);
- return sr;
- });
- const seconds = parseFloat((_a = junit.testsuites.$) === null || _a === void 0 ? void 0 : _a.time);
+ var _a, _b;
+ let suites = [];
+ this.appendSuites(suites, (_a = junit.testsuites.testsuite) !== null && _a !== void 0 ? _a : []);
+ const seconds = parseFloat((_b = junit.testsuites.$) === null || _b === void 0 ? void 0 : _b.time);
const time = isNaN(seconds) ? undefined : seconds * 1000;
return new test_results_1.TestRunResult(filePath, suites, time);
}
+ appendSuites(results, testsuites) {
+ if (testsuites === undefined)
+ return;
+ for (const ts of testsuites) {
+ this.appendSuites(results, ts.testsuite);
+ results.push(new test_results_1.TestSuiteResult(ts.$.name.trim(), this.getGroups(ts), parseFloat(ts.$.time) * 1000));
+ }
+ }
getGroups(suite) {
if (suite.testcase === undefined) {
return [];
diff --git a/src/parsers/java-junit/java-junit-parser.ts b/src/parsers/java-junit/java-junit-parser.ts
index 5163f46..fac8bdb 100644
--- a/src/parsers/java-junit/java-junit-parser.ts
+++ b/src/parsers/java-junit/java-junit-parser.ts
@@ -61,21 +61,30 @@ export class JavaJunitParser implements TestParser {
}
private getTestRunResult(filePath: string, junit: JunitReport): TestRunResult {
- const suites =
- junit.testsuites.testsuite === undefined
- ? []
- : junit.testsuites.testsuite.map(ts => {
- const name = ts.$.name.trim()
- const time = parseFloat(ts.$.time) * 1000
- const sr = new TestSuiteResult(name, this.getGroups(ts), time)
- return sr
- })
+ let suites: TestSuiteResult[] = []
+
+ this.appendSuites(suites, junit.testsuites.testsuite ?? [])
const seconds = parseFloat(junit.testsuites.$?.time)
const time = isNaN(seconds) ? undefined : seconds * 1000
return new TestRunResult(filePath, suites, time)
}
+ private appendSuites(results: TestSuiteResult[], testsuites?: TestSuite[]): void
+ {
+ if (testsuites === undefined) return
+
+ for (const ts of testsuites) {
+ this.appendSuites(results, ts.testsuite)
+
+ results.push(new TestSuiteResult(
+ ts.$.name.trim(),
+ this.getGroups(ts),
+ parseFloat(ts.$.time) * 1000
+ ));
+ }
+ }
+
private getGroups(suite: TestSuite): TestGroupResult[] {
if (suite.testcase === undefined) {
return []
diff --git a/src/parsers/java-junit/java-junit-types.ts b/src/parsers/java-junit/java-junit-types.ts
index 7ca6246..cbcf9e4 100644
--- a/src/parsers/java-junit/java-junit-types.ts
+++ b/src/parsers/java-junit/java-junit-types.ts
@@ -23,7 +23,8 @@ export interface TestSuite {
time: string
timestamp?: Date
}
- testcase: TestCase[]
+ testcase?: TestCase[]
+ testsuite?: TestSuite[]
}
export interface TestCase {