diff --git a/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/Configuration.java b/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/Configuration.java
index f9d4a627..9de94a7a 100644
--- a/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/Configuration.java
+++ b/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/Configuration.java
@@ -51,7 +51,7 @@ public class Configuration {
@Builder.Default
Set excludes = new HashSet<>();
@Builder.Default
- Set indexFilenames = defaultIndeFilenames();
+ Set indexFilenames = defaultIndexFilenames();
/*
* Explanation for configuring http status codes:
@@ -77,13 +77,13 @@ public Configuration() {
this.ignoreIPAddresses = false;// warning if numerical IP addresses
this.ignoreLocalhost = false;// warning if localhost-URLs
this.indexFilenames
- = defaultIndeFilenames();
+ = defaultIndexFilenames();
this.prefixOnlyHrefExtensions = Web.POSSIBLE_EXTENSIONS;
this.checksToExecute = AllCheckers.CHECKER_CLASSES;
}
- private static Set defaultIndeFilenames() {
+ private static Set defaultIndexFilenames() {
return Arrays.stream("index.html,index.htm".split(",")).collect(Collectors.toSet());
}
diff --git a/htmlSanityCheck-gradle-plugin/README.adoc b/htmlSanityCheck-gradle-plugin/README.adoc
index 66873cd8..3ac67528 100644
--- a/htmlSanityCheck-gradle-plugin/README.adoc
+++ b/htmlSanityCheck-gradle-plugin/README.adoc
@@ -92,7 +92,7 @@ Type: Directory.
+
Type: `org.gradle.api.file.FileCollection`.
+
-Default: All files in `+{sourceDir}+` whose names end with `.html`.
+Default: All files in `+{sourceDir}+` whose names end with `.html` or `.htm`.
`checkingResultsDir` (optional):: Directory where the checking results are written to.
+
diff --git a/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTask.groovy b/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTask.groovy
index bdead7ac..5088bb31 100644
--- a/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTask.groovy
+++ b/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTask.groovy
@@ -117,6 +117,7 @@ class HtmlSanityCheckTask extends DefaultTask {
if (sourceDocuments == null) {
sourceDocuments = project.fileTree(sourceDir)
sourceDocuments.include('**/*.html')
+ sourceDocuments.include('**/*.htm')
}
}
diff --git a/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTaskSpec.groovy b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTaskSpec.groovy
index 82c569ec..84baa903 100644
--- a/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTaskSpec.groovy
+++ b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/gradle/HtmlSanityCheckTaskSpec.groovy
@@ -62,4 +62,32 @@ class HtmlSanityCheckTaskSpec extends HtmlSanityCheckBaseSpec {
e.message.contains("Your build configuration included 'failOnErrors=true', and 1 error(s) were found on all checked pages.")
}
+ def "should include .htm files when auto-populating sourceDocuments"() {
+ given:
+ // Create .htm file
+ def htmFile = new File(sourceDir, "document.htm")
+ htmFile << VALID_HTML
+
+ // Create .html file
+ htmlFile << VALID_HTML
+
+ // Create subdirectory with .htm file
+ def subDir = new File(sourceDir, "docs")
+ subDir.mkdirs()
+ def nestedHtmFile = new File(subDir, "nested.htm")
+ nestedHtmFile << VALID_HTML
+
+ when:
+ task.setSourceDir(testProjectDir.root)
+
+ then:
+ task.sourceDocuments != null
+ // Verify that sourceDocuments includes both .html and .htm files
+ def fileNames = task.sourceDocuments.files.collect { it.name }
+ fileNames.contains("test.html")
+ fileNames.contains("document.htm")
+ fileNames.contains("nested.htm")
+ task.sourceDocuments.files.size() == 3
+ }
+
}
\ No newline at end of file
diff --git a/htmlSanityCheck-maven-plugin/README.adoc b/htmlSanityCheck-maven-plugin/README.adoc
index 15ce403e..058d3336 100644
--- a/htmlSanityCheck-maven-plugin/README.adoc
+++ b/htmlSanityCheck-maven-plugin/README.adoc
@@ -41,7 +41,7 @@ Use the following snippet inside a Maven build file:
- src/file-to-test.html <2>
+ src/file-to-test.html <2>
src <3>
@@ -79,7 +79,7 @@ Type: Directory.
+
Type: `org.maven.api.file.FileCollection`.
+
-Default: All files in `+{sourceDir}+` whose names end with `.html`.
+Default: All files in `+{sourceDir}+` whose names end with `.html` or `.htm`.
`checkingResultsDir` (optional):: Directory where the checking results are written to.
+
diff --git a/htmlSanityCheck-maven-plugin/src/main/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojo.java b/htmlSanityCheck-maven-plugin/src/main/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojo.java
index 4461d283..d884b2f1 100644
--- a/htmlSanityCheck-maven-plugin/src/main/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojo.java
+++ b/htmlSanityCheck-maven-plugin/src/main/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojo.java
@@ -270,9 +270,16 @@ void logBuildParameter(Configuration myConfig) {
}
protected Configuration setupConfiguration() {
+ // Auto-populate sourceDocuments from sourceDir if not specified
+ // (same pattern as Gradle plugin and CLI)
+ Set documentsToCheck = sourceDocuments;
+ if (documentsToCheck == null && sourceDir != null) {
+ documentsToCheck = findHtmlFiles(sourceDir);
+ }
+
Configuration result = Configuration.builder()
.excludes(excludes.stream().map(Pattern::compile).collect(Collectors.toSet()))
- .sourceDocuments(sourceDocuments)
+ .sourceDocuments(documentsToCheck)
.sourceDir(sourceDir)
.checkingResultsDir(checkingResultsDir)
.junitResultsDir(junitResultsDir)
@@ -301,6 +308,33 @@ protected Configuration setupConfiguration() {
return result;
}
+
+ /**
+ * Recursively finds all HTML files in the given directory.
+ * Searches for files with .html and .htm extensions.
+ *
+ * @param directory the directory to search
+ * @return set of HTML files found
+ */
+ private Set findHtmlFiles(File directory) {
+ Set htmlFiles = new HashSet<>();
+ if (directory == null || !directory.exists() || !directory.isDirectory()) {
+ return htmlFiles;
+ }
+
+ File[] files = directory.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ // Recursively search subdirectories
+ htmlFiles.addAll(findHtmlFiles(file));
+ } else if (file.isFile() && (file.getName().endsWith(".html") || file.getName().endsWith(".htm"))) {
+ htmlFiles.add(file);
+ }
+ }
+ }
+ return htmlFiles;
+ }
}
/*========================================================================
diff --git a/htmlSanityCheck-maven-plugin/src/test/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojoTest.java b/htmlSanityCheck-maven-plugin/src/test/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojoTest.java
index c708755a..e2af2944 100644
--- a/htmlSanityCheck-maven-plugin/src/test/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojoTest.java
+++ b/htmlSanityCheck-maven-plugin/src/test/java/org/aim42/htmlsanitycheck/maven/HtmlSanityCheckMojoTest.java
@@ -36,6 +36,68 @@ void setupConfiguration() {
Assertions.assertThat(config.getFailOnErrors()).isFalse();
}
+ @Test
+ void setupConfigurationWithHttpSuccessCodes() throws Exception {
+ // Create mojo with custom HTTP success codes
+ Set customSuccessCodes = new HashSet<>();
+ customSuccessCodes.add(299);
+
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "httpSuccessCodes", customSuccessCodes);
+
+ Configuration config = mojo.setupConfiguration();
+
+ Assertions.assertThat(config).isNotNull();
+ Assertions.assertThat(config.getHttpSuccessCodes()).contains(299);
+ }
+
+ @Test
+ void setupConfigurationWithHttpErrorCodes() throws Exception {
+ // Create mojo with custom HTTP error codes
+ Set customErrorCodes = new HashSet<>();
+ customErrorCodes.add(599);
+
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "httpErrorCodes", customErrorCodes);
+
+ Configuration config = mojo.setupConfiguration();
+
+ Assertions.assertThat(config).isNotNull();
+ Assertions.assertThat(config.getHttpErrorCodes()).contains(599);
+ }
+
+ @Test
+ void setupConfigurationWithHttpWarningCodes() throws Exception {
+ // Create mojo with custom HTTP warning codes
+ Set customWarningCodes = new HashSet<>();
+ customWarningCodes.add(199);
+
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "httpWarningCodes", customWarningCodes);
+
+ Configuration config = mojo.setupConfiguration();
+
+ Assertions.assertThat(config).isNotNull();
+ Assertions.assertThat(config.getHttpWarningCodes()).contains(199);
+ }
+
+ @Test
+ void setupConfigurationWithEmptyHttpStatusCodesShouldNotOverride() throws Exception {
+ // Create mojo with empty HTTP status code sets (should not override defaults)
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "httpSuccessCodes", new HashSet());
+ setField(mojo, "httpErrorCodes", new HashSet());
+ setField(mojo, "httpWarningCodes", new HashSet());
+
+ Configuration config = mojo.setupConfiguration();
+
+ // Verify that default codes are still present (not overridden by empty sets)
+ Assertions.assertThat(config).isNotNull();
+ Assertions.assertThat(config.getHttpSuccessCodes()).contains(200); // Default success code
+ Assertions.assertThat(config.getHttpErrorCodes()).contains(404); // Default error code
+ Assertions.assertThat(config.getHttpWarningCodes()).contains(301); // Default warning code (redirect)
+ }
+
@Test
void logBuildParameter() {
@@ -190,6 +252,188 @@ void execuuserte() throws IOException, MojoExecutionException {
deleteDirectory(resultDir.toFile());
}
+ @Test
+ void executeWithOnlySourceDir_ShouldSucceed() throws IOException, MojoExecutionException {
+ // Setup: Create temp directories
+ Path junitDir = Files.createTempDirectory("MojoJunit");
+ Path resultDir = Files.createTempDirectory("MojoResult");
+ Path sourceDir = Files.createTempDirectory("MojoSource");
+
+ // Create HTML file in root of sourceDir
+ File rootHtmlFile = new File(sourceDir.toFile(), "root.html");
+ Files.write(rootHtmlFile.toPath(), VALID_HTML.getBytes(StandardCharsets.UTF_8));
+
+ // Create subdirectory with another HTML file
+ File subDir = new File(sourceDir.toFile(), "subdir");
+ boolean mkdirSuccess = subDir.mkdirs();
+ Assertions.assertThat(mkdirSuccess).isTrue();
+ File subHtmlFile = new File(subDir, "nested.html");
+ Files.write(subHtmlFile.toPath(), VALID_HTML.getBytes(StandardCharsets.UTF_8));
+
+ // Create Mojo and set only sourceDir field (NOT sourceDocuments)
+ // This simulates a Maven pom.xml with only configured
+ HtmlSanityCheckMojo mojo = new TestableHtmlSanityCheckMojo(
+ sourceDir.toFile(),
+ null, // sourceDocuments explicitly NOT set
+ resultDir.toFile(),
+ junitDir.toFile()
+ );
+
+ // This should succeed - setupConfiguration() will auto-populate sourceDocuments
+ mojo.execute();
+
+ // Clean up
+ deleteDirectory(sourceDir.toFile());
+ deleteDirectory(junitDir.toFile());
+ deleteDirectory(resultDir.toFile());
+ }
+
+ @Test
+ void findHtmlFilesWithNullDirectory() throws Exception {
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "sourceDir", null);
+ setField(mojo, "sourceDocuments", null);
+
+ Configuration config = mojo.setupConfiguration();
+
+ // When both sourceDir and sourceDocuments are null, the config should have null sourceDocuments
+ // (This will be caught by validation)
+ Assertions.assertThat(config.getSourceDocuments()).isNull();
+ }
+
+ @Test
+ void findHtmlFilesWithNonExistentDirectory() throws Exception {
+ Path nonExistentDir = java.nio.file.Paths.get("/tmp/this-directory-does-not-exist-" + System.currentTimeMillis());
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "sourceDir", nonExistentDir.toFile());
+ setField(mojo, "sourceDocuments", null);
+
+ Configuration config = mojo.setupConfiguration();
+
+ // Should return empty set when directory doesn't exist
+ Assertions.assertThat(config.getSourceDocuments()).isEmpty();
+ }
+
+ @Test
+ void findHtmlFilesWithEmptyDirectory() throws Exception {
+ Path emptyDir = Files.createTempDirectory("MojoEmpty");
+
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "sourceDir", emptyDir.toFile());
+ setField(mojo, "sourceDocuments", null);
+
+ Configuration config = mojo.setupConfiguration();
+
+ // Should return empty set when directory is empty
+ Assertions.assertThat(config.getSourceDocuments()).isEmpty();
+
+ // Clean up
+ Files.deleteIfExists(emptyDir);
+ }
+
+ @Test
+ void findHtmlFilesIgnoresNonHtmlFiles() throws Exception {
+ Path sourceDir = Files.createTempDirectory("MojoSource");
+
+ // Create various non-HTML files
+ File txtFile = new File(sourceDir.toFile(), "readme.txt");
+ Files.write(txtFile.toPath(), "Text content".getBytes(StandardCharsets.UTF_8));
+
+ File pdfFile = new File(sourceDir.toFile(), "document.pdf");
+ Files.write(pdfFile.toPath(), "PDF content".getBytes(StandardCharsets.UTF_8));
+
+ File xmlFile = new File(sourceDir.toFile(), "config.xml");
+ Files.write(xmlFile.toPath(), "".getBytes(StandardCharsets.UTF_8));
+
+ // Create one HTML file
+ File htmlFile = new File(sourceDir.toFile(), "page.html");
+ Files.write(htmlFile.toPath(), VALID_HTML.getBytes(StandardCharsets.UTF_8));
+
+ HtmlSanityCheckMojo mojo = new HtmlSanityCheckMojo();
+ setField(mojo, "sourceDir", sourceDir.toFile());
+ setField(mojo, "sourceDocuments", null);
+
+ Configuration config = mojo.setupConfiguration();
+
+ // Should only find the HTML file
+ Assertions.assertThat(config.getSourceDocuments()).hasSize(1);
+ Assertions.assertThat(config.getSourceDocuments())
+ .extracting(File::getName)
+ .containsExactly("page.html");
+
+ // Clean up
+ deleteDirectory(sourceDir.toFile());
+ }
+
+ @Test
+ void executeWithOnlySourceDir_ShouldIncludeHtmFiles() throws IOException, MojoExecutionException {
+ // Setup: Create temp directories
+ Path junitDir = Files.createTempDirectory("MojoJunit");
+ Path resultDir = Files.createTempDirectory("MojoResult");
+ Path sourceDir = Files.createTempDirectory("MojoSource");
+
+ // Create .htm file in root
+ File rootHtmFile = new File(sourceDir.toFile(), "document.htm");
+ Files.write(rootHtmFile.toPath(), VALID_HTML.getBytes(StandardCharsets.UTF_8));
+
+ // Create .html file for comparison
+ File rootHtmlFile = new File(sourceDir.toFile(), "page.html");
+ Files.write(rootHtmlFile.toPath(), VALID_HTML.getBytes(StandardCharsets.UTF_8));
+
+ // Create subdirectory with .htm file
+ File subDir = new File(sourceDir.toFile(), "docs");
+ boolean mkdirSuccess = subDir.mkdirs();
+ Assertions.assertThat(mkdirSuccess).isTrue();
+ File nestedHtmFile = new File(subDir, "nested.htm");
+ Files.write(nestedHtmFile.toPath(), VALID_HTML.getBytes(StandardCharsets.UTF_8));
+
+ // Create Mojo and set only sourceDir field
+ HtmlSanityCheckMojo mojo = new TestableHtmlSanityCheckMojo(
+ sourceDir.toFile(),
+ null, // sourceDocuments explicitly NOT set
+ resultDir.toFile(),
+ junitDir.toFile()
+ );
+
+ // Get the configuration to verify sourceDocuments includes .htm files
+ Configuration config = mojo.setupConfiguration();
+
+ // Verify that both .html and .htm files are discovered
+ Assertions.assertThat(config.getSourceDocuments()).isNotNull();
+ Assertions.assertThat(config.getSourceDocuments()).hasSize(3);
+ Assertions.assertThat(config.getSourceDocuments())
+ .extracting(File::getName)
+ .containsExactlyInAnyOrder("document.htm", "page.html", "nested.htm");
+
+ // Execute should succeed with both file types
+ mojo.execute();
+
+ // Clean up
+ deleteDirectory(sourceDir.toFile());
+ deleteDirectory(junitDir.toFile());
+ deleteDirectory(resultDir.toFile());
+ }
+
+ /**
+ * Helper class to allow setting private fields for testing
+ */
+ static class TestableHtmlSanityCheckMojo extends HtmlSanityCheckMojo {
+ TestableHtmlSanityCheckMojo(File sourceDir, Set sourceDocuments,
+ File checkingResultsDir, File junitResultsDir) {
+ // Use reflection to set private fields
+ try {
+ HtmlSanityCheckMojoTest.setField(this, "sourceDir", sourceDir);
+ HtmlSanityCheckMojoTest.setField(this, "sourceDocuments", sourceDocuments);
+ HtmlSanityCheckMojoTest.setField(this, "checkingResultsDir", checkingResultsDir);
+ HtmlSanityCheckMojoTest.setField(this, "junitResultsDir", junitResultsDir);
+ HtmlSanityCheckMojoTest.setField(this, "checkerClasses", AllCheckers.CHECKER_CLASSES);
+ HtmlSanityCheckMojoTest.setField(this, "excludes", new HashSet());
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to set fields", e);
+ }
+ }
+ }
+
// Helper functions
@@ -203,5 +447,15 @@ void deleteDirectory(File directoryToBeDeleted) throws IOException {
Files.deleteIfExists(directoryToBeDeleted.toPath());
}
+ /**
+ * Helper method to set private fields on HtmlSanityCheckMojo for testing
+ */
+ private static void setField(Object target, String fieldName, Object value)
+ throws NoSuchFieldException, IllegalAccessException {
+ java.lang.reflect.Field field = HtmlSanityCheckMojo.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(target, value);
+ }
+
}