diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eddab3..0cff427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Support configurable testsuite name, testcase classname, and testcase name. ([#105](https://github.com/cucumber/junit-xml-formatter/pull/105)) + ## [0.9.0] - 2025-09-11 ### Changed - Update dependency cucumber/query to 14.0.1 diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java b/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java index 54dd98a..891ee03 100644 --- a/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java +++ b/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java @@ -9,6 +9,7 @@ import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; +import static io.cucumber.query.NamingStrategy.ExampleName.NUMBER_AND_PICKLE_IF_PARAMETERIZED; import static io.cucumber.query.NamingStrategy.FeatureName.EXCLUDE; import static io.cucumber.query.NamingStrategy.Strategy.LONG; import static java.util.Objects.requireNonNull; @@ -22,24 +23,75 @@ */ public final class MessagesToJunitXmlWriter implements AutoCloseable { + private static final String DEFAULT_TEST_SUITE_NAME = "Cucumber"; private final OutputStreamWriter out; private final XmlReportData data; private boolean streamClosed = false; public MessagesToJunitXmlWriter(OutputStream out) { - this(NamingStrategy.ExampleName.NUMBER_AND_PICKLE_IF_PARAMETERIZED, out); + this("Cucumber", null, createNamingStrategy(NUMBER_AND_PICKLE_IF_PARAMETERIZED), out); } + @Deprecated public MessagesToJunitXmlWriter(NamingStrategy.ExampleName exampleNameStrategy, OutputStream out) { - this(createNamingStrategy(requireNonNull(exampleNameStrategy)), out); + this("Cucumber", null, createNamingStrategy(requireNonNull(exampleNameStrategy)), out); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String testSuiteName = DEFAULT_TEST_SUITE_NAME; + private String testClassName; + private NamingStrategy testNamingStrategy = NamingStrategy.strategy(LONG) + .featureName(EXCLUDE) + .exampleName(NUMBER_AND_PICKLE_IF_PARAMETERIZED) + .build(); + + private Builder() { + + } + + /** + * Sets the value for the {@code } attribute. Defaults to {@value DEFAULT_TEST_SUITE_NAME}. + */ + public Builder testSuiteName(String testSuiteName) { + this.testSuiteName = requireNonNull(testSuiteName); + return this; + } + + /** + * Sets the value for the {@code } attribute. Defaults to the name of the + * feature. + */ + public Builder testClassName(String testClassName) { + this.testClassName = testClassName; + return this; + } + + /** + * Set the naming strategy used for the {@code attribute}. Defaults to the + * {@link NamingStrategy.Strategy#LONG} strategy with {@link NamingStrategy.FeatureName#EXCLUDE} and + * {@link NamingStrategy.ExampleName#NUMBER_AND_PICKLE_IF_PARAMETERIZED}. + */ + public Builder testNamingStrategy(NamingStrategy namingStrategy) { + this.testNamingStrategy = requireNonNull(namingStrategy); + return this; + } + + public MessagesToJunitXmlWriter build(OutputStream out) { + return new MessagesToJunitXmlWriter(testSuiteName, testClassName, testNamingStrategy, requireNonNull(out)); + } } private static NamingStrategy createNamingStrategy(NamingStrategy.ExampleName exampleName) { - return NamingStrategy.strategy(LONG).featureName(EXCLUDE).exampleName(exampleName).build(); + return NamingStrategy.strategy(NamingStrategy.Strategy.LONG).featureName(NamingStrategy.FeatureName.EXCLUDE).exampleName(exampleName).build(); } - private MessagesToJunitXmlWriter(NamingStrategy namingStrategy, OutputStream out) { - this.data = new XmlReportData(namingStrategy); + private MessagesToJunitXmlWriter(String testSuiteName, String testClassName, NamingStrategy testNamingStrategy, OutputStream out) { + this.data = new XmlReportData(testSuiteName, testClassName, testNamingStrategy); this.out = new OutputStreamWriter( requireNonNull(out), StandardCharsets.UTF_8 diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java index 61bbea3..ef4ddd6 100644 --- a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java +++ b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java @@ -28,21 +28,25 @@ import static io.cucumber.messages.types.TestStepResultStatus.PASSED; import static io.cucumber.query.Repository.RepositoryFeature.INCLUDE_GHERKIN_DOCUMENTS; import static java.time.format.DateTimeFormatter.ISO_INSTANT; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; class XmlReportData { + private static final long MILLIS_PER_SECOND = SECONDS.toMillis(1L); private final Repository repository = Repository.builder() .feature(INCLUDE_GHERKIN_DOCUMENTS, true) .build(); private final Query query = new Query(repository); - private final NamingStrategy namingStrategy; - - private static final long MILLIS_PER_SECOND = SECONDS.toMillis(1L); - - XmlReportData(NamingStrategy namingStrategy) { - this.namingStrategy = namingStrategy; + private final String testSuiteName; + private final String testClassName; + private final NamingStrategy testNamingStrategy; + + XmlReportData(String testSuiteName, String testClassName, NamingStrategy testNamingStrategy) { + this.testSuiteName = requireNonNull(testSuiteName); + this.testClassName = testClassName; + this.testNamingStrategy = requireNonNull(testNamingStrategy); } void collect(Envelope envelope) { @@ -74,20 +78,27 @@ private Pickle getPickle(TestCaseStarted testCaseStarted) { .orElseThrow(() -> new IllegalStateException("No pickle for " + testCaseStarted.getId())); } - String getPickleName(TestCaseStarted testCaseStarted) { + String getTestName(TestCaseStarted testCaseStarted) { Pickle pickle = getPickle(testCaseStarted); return query.findLineageBy(pickle) - .map(lineage -> namingStrategy.reduce(lineage, pickle)) + .map(lineage -> testNamingStrategy.reduce(lineage, pickle)) .orElseGet(pickle::getName); } - String getFeatureName(TestCaseStarted testCaseStarted) { + String getTestClassName(TestCaseStarted testCaseStarted) { + if (testClassName != null) { + return testClassName; + } return query.findLineageBy(testCaseStarted) .flatMap(Lineage::feature) .map(Feature::getName) .orElseGet(() -> this.getPickle(testCaseStarted).getUri()); } + String getTestSuiteName() { + return testSuiteName; + } + List> getStepsAndResult(TestCaseStarted testCaseStarted) { return query.findTestStepFinishedAndTestStepBy(testCaseStarted) .stream() @@ -138,7 +149,7 @@ TestStepResult getTestCaseStatus(TestCaseStarted testCaseStarted) { .orElse(SCENARIO_WITH_NO_STEPS); } - public Optional getTestRunStartedAt() { + Optional getTestRunStartedAt() { return query.findTestRunStarted() .map(TestRunStarted::getTimestamp) .map(Convertor::toInstant) diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java index 040b27d..dd3f407 100644 --- a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java +++ b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java @@ -47,7 +47,7 @@ private void writeTestsuite(EscapingXmlStreamWriter writer) throws XMLStreamExce } private void writeSuiteAttributes(EscapingXmlStreamWriter writer) throws XMLStreamException { - writer.writeAttribute("name", "Cucumber"); + writer.writeAttribute("name", data.getTestSuiteName()); writer.writeAttribute("time", String.valueOf(data.getSuiteDurationInSeconds())); Map counts = data.getTestCaseStatusCounts(); @@ -85,8 +85,8 @@ private void writeTestcase(EscapingXmlStreamWriter writer, TestCaseStarted testC } private void writeTestCaseAttributes(EscapingXmlStreamWriter writer, TestCaseStarted testCaseStarted) throws XMLStreamException { - writer.writeAttribute("classname", data.getFeatureName(testCaseStarted)); - writer.writeAttribute("name", data.getPickleName(testCaseStarted)); + writer.writeAttribute("classname", data.getTestClassName(testCaseStarted)); + writer.writeAttribute("name", data.getTestName(testCaseStarted)); writer.writeAttribute("time", String.valueOf(data.getDurationInSeconds(testCaseStarted))); } diff --git a/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java b/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java index 05ef8a8..e649036 100644 --- a/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java +++ b/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java @@ -2,6 +2,8 @@ import io.cucumber.messages.NdjsonToMessageIterable; import io.cucumber.messages.types.Envelope; +import io.cucumber.query.NamingStrategy; +import io.cucumber.query.NamingStrategy.Strategy; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; @@ -24,23 +26,44 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; import java.util.stream.Stream; import static io.cucumber.junitxmlformatter.Jackson.OBJECT_MAPPER; +import static io.cucumber.query.NamingStrategy.Strategy.LONG; +import static io.cucumber.query.NamingStrategy.strategy; import static org.xmlunit.assertj.XmlAssert.assertThat; class MessagesToJunitXmlWriterAcceptanceTest { private static final NdjsonToMessageIterable.Deserializer deserializer = (json) -> OBJECT_MAPPER.readValue(json, Envelope.class); static List acceptance() throws IOException { + List testCases = new ArrayList<>(); + try (Stream paths = Files.list(Paths.get("../testdata/src"))) { - return paths + paths .filter(path -> path.getFileName().toString().endsWith(".ndjson")) - .map(TestCase::new) + .map(source -> new TestCase( + source, + "default", + MessagesToJunitXmlWriter.builder() + ) + ) .sorted(Comparator.comparing(testCase -> testCase.source)) - .collect(Collectors.toList()); + .forEach(testCases::add); } + + testCases.add( + new TestCase( + Paths.get("../testdata/src/examples-tables.ndjson"), + "custom", + MessagesToJunitXmlWriter.builder() + .testSuiteName("Cucumber Suite") + .testClassName("Cucumber Class") + .testNamingStrategy(strategy(LONG).build()) + ) + ); + + return testCases; } @ParameterizedTest @@ -94,7 +117,7 @@ void updateExpectedFiles(TestCase testCase) throws IOException { private static T writeJunitXmlReport(TestCase testCase, T out) throws IOException { try (InputStream in = Files.newInputStream(testCase.source)) { try (NdjsonToMessageIterable envelopes = new NdjsonToMessageIterable(in, deserializer)) { - try (MessagesToJunitXmlWriter writer = new MessagesToJunitXmlWriter(out)) { + try (MessagesToJunitXmlWriter writer = testCase.getBuilder().build(out)) { for (Envelope envelope : envelopes) { writer.write(envelope); } @@ -109,17 +132,25 @@ static class TestCase { private final Path expected; private final String name; + private final MessagesToJunitXmlWriter.Builder builder; + private final String strategyName; - TestCase(Path source) { + TestCase(Path source, String namingStrategyName, MessagesToJunitXmlWriter.Builder builder) { this.source = source; String fileName = source.getFileName().toString(); this.name = fileName.substring(0, fileName.lastIndexOf(".ndjson")); - this.expected = source.getParent().resolve(name + ".xml"); + this.expected = source.getParent().resolve(name + "." + namingStrategyName + ".xml"); + this.builder = builder; + this.strategyName = namingStrategyName; + } + + MessagesToJunitXmlWriter.Builder getBuilder() { + return builder; } @Override public String toString() { - return name; + return name + " -> " + strategyName; } @Override diff --git a/javascript/package-lock.json b/javascript/package-lock.json index ee64dc9..a7e3bac 100644 --- a/javascript/package-lock.json +++ b/javascript/package-lock.json @@ -74,7 +74,6 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-30.1.0.tgz", "integrity": "sha512-KxnsSjHz9EGF23GeZc3BRMK2+bagt2p87mwwNfisBK7BfuyvnXJumyBQJJN4xv5SLSzBKxH3FsZnuOf8LwsHhg==", "license": "MIT", - "peer": true, "dependencies": { "class-transformer": "0.5.1", "reflect-metadata": "0.2.2" @@ -635,7 +634,6 @@ "integrity": "sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -686,7 +684,6 @@ "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", @@ -918,7 +915,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1307,7 +1303,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-6.0.1.tgz", "integrity": "sha512-/JOoU2//6p5vCXh00FpNgtlw0LjvhGttaWc+y7wpW9yjBm3ys0dI8tSKZxIOgNruz5J0RleccatSIC3uxEZP0g==", "dev": true, - "peer": true, "engines": { "node": ">=18" } @@ -1855,7 +1850,6 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4790,7 +4784,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/javascript/src/index.spec.ts b/javascript/src/index.spec.ts index 17cc57c..c43e569 100644 --- a/javascript/src/index.spec.ts +++ b/javascript/src/index.spec.ts @@ -5,6 +5,7 @@ import util from 'node:util' import { NdjsonToMessageStream } from '@cucumber/message-streams' import { Envelope } from '@cucumber/messages' +import { namingStrategy, NamingStrategyLength } from '@cucumber/query' import { expect, use } from 'chai' import chaiXml from 'chai-xml' import { globbySync } from 'globby' @@ -22,13 +23,33 @@ describe('Acceptance Tests', async function () { absolute: true, }) - for (const ndjsonFile of ndjsonFiles) { + const testCases = ndjsonFiles.map((ndjsonFile) => { const [suiteName] = path.basename(ndjsonFile).split('.') - it(suiteName, async () => { + return { + suiteName, + source: ndjsonFile, + strategyName: 'default', + options: {}, + } + }) + + testCases.push({ + suiteName: 'examples-tables', + source: '../testdata/src/examples-tables.ndjson', + strategyName: 'custom', + options: { + suiteName: 'Cucumber Suite', + testClassName: 'Cucumber Class', + testNamingStrategy: namingStrategy(NamingStrategyLength.LONG), + }, + }) + + for (const testCase of testCases) { + it(testCase.suiteName + ' -> ' + testCase.strategyName, async () => { let emit: (message: Envelope) => void let content = '' formatter.formatter({ - options: {}, + options: testCase.options, on(type, handler) { emit = handler }, @@ -38,7 +59,7 @@ describe('Acceptance Tests', async function () { }) await asyncPipeline( - fs.createReadStream(ndjsonFile, { encoding: 'utf-8' }), + fs.createReadStream(testCase.source, { encoding: 'utf-8' }), new NdjsonToMessageStream(), new Writable({ objectMode: true, @@ -49,9 +70,12 @@ describe('Acceptance Tests', async function () { }) ) - const expectedXml = fs.readFileSync(ndjsonFile.replace('.ndjson', '.xml'), { - encoding: 'utf-8', - }) + const expectedXml = fs.readFileSync( + testCase.source.replace('.ndjson', '.' + testCase.strategyName + '.xml'), + { + encoding: 'utf-8', + } + ) expect(content).xml.to.be.valid() expect(content).xml.to.deep.eq(expectedXml) }) diff --git a/javascript/src/index.ts b/javascript/src/index.ts index b94874d..96730b1 100644 --- a/javascript/src/index.ts +++ b/javascript/src/index.ts @@ -1,5 +1,5 @@ import { Envelope } from '@cucumber/messages' -import { Query } from '@cucumber/query' +import { NamingStrategy, Query } from '@cucumber/query' import xmlbuilder from 'xmlbuilder' import { makeReport } from './makeReport.js' @@ -11,7 +11,11 @@ export default { on, write, }: { - options: { suiteName?: string } + options: { + suiteName?: string + testClassName?: string + testNamingStrategy?: NamingStrategy + } on: (type: 'message', handler: (message: Envelope) => void) => void write: (content: string) => void }) { @@ -24,7 +28,7 @@ export default { query.update(message) if (message.testRunFinished) { - const testSuite = makeReport(query) + const testSuite = makeReport(query, options.testClassName, options.testNamingStrategy) builder.att('time', testSuite.time) builder.att('tests', testSuite.tests) builder.att('skipped', testSuite.skipped) diff --git a/javascript/src/makeReport.ts b/javascript/src/makeReport.ts index ca97b5b..1c3c796 100644 --- a/javascript/src/makeReport.ts +++ b/javascript/src/makeReport.ts @@ -1,5 +1,6 @@ import { TestCaseStarted, TestStepResultStatus } from '@cucumber/messages' import { + NamingStrategy, namingStrategy, NamingStrategyExampleName, NamingStrategyFeatureName, @@ -40,7 +41,11 @@ interface ReportFailure { stack?: string } -export function makeReport(query: Query): ReportSuite { +export function makeReport( + query: Query, + testClassName: string | undefined = undefined, + customNamingStrategy: NamingStrategy = NAMING_STRATEGY +): ReportSuite { const statuses = query.countMostSevereTestStepResultStatus() return { time: durationToSeconds(query.findTestRunDuration()), @@ -51,12 +56,16 @@ export function makeReport(query: Query): ReportSuite { (status) => status !== TestStepResultStatus.PASSED && status !== TestStepResultStatus.SKIPPED ), errors: 0, - testCases: makeTestCases(query), + testCases: makeTestCases(query, testClassName, customNamingStrategy), timestamp: formatTimestamp(query.findTestRunStarted()), } } -function makeTestCases(query: Query): ReadonlyArray { +function makeTestCases( + query: Query, + testClassName: string | undefined, + testNamingStrategy: NamingStrategy +): ReadonlyArray { return query.findAllTestCaseStarted().map((testCaseStarted) => { const pickle = ensure( query.findPickleBy(testCaseStarted), @@ -65,8 +74,8 @@ function makeTestCases(query: Query): ReadonlyArray { const lineage = ensure(query.findLineageBy(pickle), 'Expected to find Lineage by Pickle') return { - classname: lineage.feature?.name ?? pickle.uri, - name: NAMING_STRATEGY.reduce(lineage, pickle), + classname: testClassName ?? lineage.feature?.name ?? pickle.uri, + name: testNamingStrategy.reduce(lineage, pickle), time: durationToSeconds(query.findTestCaseDurationBy(testCaseStarted)), failure: makeFailure(query, testCaseStarted), output: query diff --git a/testdata/src/ambiguous.xml b/testdata/src/ambiguous.default.xml similarity index 100% rename from testdata/src/ambiguous.xml rename to testdata/src/ambiguous.default.xml diff --git a/testdata/src/attachments.xml b/testdata/src/attachments.default.xml similarity index 100% rename from testdata/src/attachments.xml rename to testdata/src/attachments.default.xml diff --git a/testdata/src/backgrounds.xml b/testdata/src/backgrounds.default.xml similarity index 100% rename from testdata/src/backgrounds.xml rename to testdata/src/backgrounds.default.xml diff --git a/testdata/src/cdata.xml b/testdata/src/cdata.default.xml similarity index 100% rename from testdata/src/cdata.xml rename to testdata/src/cdata.default.xml diff --git a/testdata/src/data-tables.xml b/testdata/src/data-tables.default.xml similarity index 100% rename from testdata/src/data-tables.xml rename to testdata/src/data-tables.default.xml diff --git a/testdata/src/doc-strings.xml b/testdata/src/doc-strings.default.xml similarity index 100% rename from testdata/src/doc-strings.xml rename to testdata/src/doc-strings.default.xml diff --git a/testdata/src/empty.xml b/testdata/src/empty.default.xml similarity index 100% rename from testdata/src/empty.xml rename to testdata/src/empty.default.xml diff --git a/testdata/src/examples-tables-attachment.xml b/testdata/src/examples-tables-attachment.default.xml similarity index 100% rename from testdata/src/examples-tables-attachment.xml rename to testdata/src/examples-tables-attachment.default.xml diff --git a/testdata/src/examples-tables-undefined.xml b/testdata/src/examples-tables-undefined.default.xml similarity index 100% rename from testdata/src/examples-tables-undefined.xml rename to testdata/src/examples-tables-undefined.default.xml diff --git a/testdata/src/examples-tables.custom.xml b/testdata/src/examples-tables.custom.xml new file mode 100644 index 0000000..d2b6c69 --- /dev/null +++ b/testdata/src/examples-tables.custom.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testdata/src/examples-tables.xml b/testdata/src/examples-tables.default.xml similarity index 100% rename from testdata/src/examples-tables.xml rename to testdata/src/examples-tables.default.xml diff --git a/testdata/src/global-hooks-afterall-error.xml b/testdata/src/global-hooks-afterall-error.default.xml similarity index 100% rename from testdata/src/global-hooks-afterall-error.xml rename to testdata/src/global-hooks-afterall-error.default.xml diff --git a/testdata/src/global-hooks-attachments.xml b/testdata/src/global-hooks-attachments.default.xml similarity index 100% rename from testdata/src/global-hooks-attachments.xml rename to testdata/src/global-hooks-attachments.default.xml diff --git a/testdata/src/global-hooks-beforeall-error.xml b/testdata/src/global-hooks-beforeall-error.default.xml similarity index 100% rename from testdata/src/global-hooks-beforeall-error.xml rename to testdata/src/global-hooks-beforeall-error.default.xml diff --git a/testdata/src/global-hooks.xml b/testdata/src/global-hooks.default.xml similarity index 100% rename from testdata/src/global-hooks.xml rename to testdata/src/global-hooks.default.xml diff --git a/testdata/src/hooks-attachment.xml b/testdata/src/hooks-attachment.default.xml similarity index 100% rename from testdata/src/hooks-attachment.xml rename to testdata/src/hooks-attachment.default.xml diff --git a/testdata/src/hooks-conditional.xml b/testdata/src/hooks-conditional.default.xml similarity index 100% rename from testdata/src/hooks-conditional.xml rename to testdata/src/hooks-conditional.default.xml diff --git a/testdata/src/hooks-named.xml b/testdata/src/hooks-named.default.xml similarity index 100% rename from testdata/src/hooks-named.xml rename to testdata/src/hooks-named.default.xml diff --git a/testdata/src/hooks-undefined.xml b/testdata/src/hooks-undefined.default.xml similarity index 100% rename from testdata/src/hooks-undefined.xml rename to testdata/src/hooks-undefined.default.xml diff --git a/testdata/src/hooks.xml b/testdata/src/hooks.default.xml similarity index 100% rename from testdata/src/hooks.xml rename to testdata/src/hooks.default.xml diff --git a/testdata/src/markdown.xml b/testdata/src/markdown.default.xml similarity index 100% rename from testdata/src/markdown.xml rename to testdata/src/markdown.default.xml diff --git a/testdata/src/minimal.xml b/testdata/src/minimal.default.xml similarity index 100% rename from testdata/src/minimal.xml rename to testdata/src/minimal.default.xml diff --git a/testdata/src/multiple-features-reversed.xml b/testdata/src/multiple-features-reversed.default.xml similarity index 100% rename from testdata/src/multiple-features-reversed.xml rename to testdata/src/multiple-features-reversed.default.xml diff --git a/testdata/src/multiple-features.xml b/testdata/src/multiple-features.default.xml similarity index 100% rename from testdata/src/multiple-features.xml rename to testdata/src/multiple-features.default.xml diff --git a/testdata/src/parameter-types.xml b/testdata/src/parameter-types.default.xml similarity index 100% rename from testdata/src/parameter-types.xml rename to testdata/src/parameter-types.default.xml diff --git a/testdata/src/pending.xml b/testdata/src/pending.default.xml similarity index 100% rename from testdata/src/pending.xml rename to testdata/src/pending.default.xml diff --git a/testdata/src/regular-expression.xml b/testdata/src/regular-expression.default.xml similarity index 100% rename from testdata/src/regular-expression.xml rename to testdata/src/regular-expression.default.xml diff --git a/testdata/src/retry-ambiguous.xml b/testdata/src/retry-ambiguous.default.xml similarity index 100% rename from testdata/src/retry-ambiguous.xml rename to testdata/src/retry-ambiguous.default.xml diff --git a/testdata/src/retry-pending.xml b/testdata/src/retry-pending.default.xml similarity index 100% rename from testdata/src/retry-pending.xml rename to testdata/src/retry-pending.default.xml diff --git a/testdata/src/retry-undefined.xml b/testdata/src/retry-undefined.default.xml similarity index 100% rename from testdata/src/retry-undefined.xml rename to testdata/src/retry-undefined.default.xml diff --git a/testdata/src/retry.xml b/testdata/src/retry.default.xml similarity index 100% rename from testdata/src/retry.xml rename to testdata/src/retry.default.xml diff --git a/testdata/src/rules-backgrounds.xml b/testdata/src/rules-backgrounds.default.xml similarity index 100% rename from testdata/src/rules-backgrounds.xml rename to testdata/src/rules-backgrounds.default.xml diff --git a/testdata/src/rules.xml b/testdata/src/rules.default.xml similarity index 100% rename from testdata/src/rules.xml rename to testdata/src/rules.default.xml diff --git a/testdata/src/skipped.xml b/testdata/src/skipped.default.xml similarity index 100% rename from testdata/src/skipped.xml rename to testdata/src/skipped.default.xml diff --git a/testdata/src/stack-traces.xml b/testdata/src/stack-traces.default.xml similarity index 100% rename from testdata/src/stack-traces.xml rename to testdata/src/stack-traces.default.xml diff --git a/testdata/src/undefined.xml b/testdata/src/undefined.default.xml similarity index 100% rename from testdata/src/undefined.xml rename to testdata/src/undefined.default.xml diff --git a/testdata/src/unknown-parameter-type.xml b/testdata/src/unknown-parameter-type.default.xml similarity index 100% rename from testdata/src/unknown-parameter-type.xml rename to testdata/src/unknown-parameter-type.default.xml diff --git a/testdata/src/unused-steps.xml b/testdata/src/unused-steps.default.xml similarity index 100% rename from testdata/src/unused-steps.xml rename to testdata/src/unused-steps.default.xml