From dc7bcbe9653969a4fa58be1eefda75c93acecc6d Mon Sep 17 00:00:00 2001 From: RoGamerES10 Date: Tue, 29 Sep 2026 02:11:37 -0300 Subject: [PATCH 1/3] feat: generate migration report --- .../migrator/MigrationReport.java | 117 ++++++++++++++++++ .../com/seleniumboot/migrator/Migrator.java | 1 + .../migrator/MigrationReportTest.java | 66 ++++++++++ .../seleniumboot/migrator/MigratorTest.java | 5 + src/test/resources/migration-report.md | 41 ++++++ 5 files changed, 230 insertions(+) create mode 100644 src/main/java/com/seleniumboot/migrator/MigrationReport.java create mode 100644 src/test/java/com/seleniumboot/migrator/MigrationReportTest.java create mode 100644 src/test/resources/migration-report.md diff --git a/src/main/java/com/seleniumboot/migrator/MigrationReport.java b/src/main/java/com/seleniumboot/migrator/MigrationReport.java new file mode 100644 index 0000000..00cee02 --- /dev/null +++ b/src/main/java/com/seleniumboot/migrator/MigrationReport.java @@ -0,0 +1,117 @@ +package com.seleniumboot.migrator; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.TreeMap; + +final class MigrationReport { + + private MigrationReport() { + } + + static Path write(Path directory, Report report) throws IOException { + Path reportPath = directory.resolve("MIGRATION_REPORT.md"); + Files.writeString(reportPath, render(report)); + return reportPath; + } + + static String render(Report report) { + StringBuilder output = new StringBuilder("# Selenium Boot Migration Report\n\n"); + + output.append("## Summary\n\n"); + output.append("- Files found: ").append(report.filesFound()).append('\n'); + output.append("- Files parsed: ").append(report.filesParsed()).append('\n'); + output.append("- Automatic findings: ") + .append(report.count(Finding.Status.AUTO)).append('\n'); + output.append("- Manual findings: ") + .append(report.count(Finding.Status.MANUAL)).append('\n'); + output.append("- Unparsable files: ") + .append(report.unparsable().size()).append("\n\n"); + + output.append("## Detected technologies\n\n"); + if (report.detectedTechnologies().isEmpty()) { + output.append("- No supported build descriptor found\n"); + } else { + report.detectedTechnologies() + .forEach(technology -> output.append("- ").append(technology).append('\n')); + } + + output.append("\n## Recognized technologies\n\n"); + if (report.recognizedTechnologies().isEmpty()) { + output.append("- No supported test technologies detected\n"); + } else { + report.recognizedTechnologies() + .forEach(technology -> output.append("- ").append(technology).append('\n')); + } + + output.append("\n## Rules applied per file\n\n"); + Map findingsByFile = new TreeMap<>(); + report.findings().forEach(finding -> findingsByFile + .computeIfAbsent(finding.file(), file -> new StringBuilder()) + .append("- ") + .append(finding.ruleId()) + .append(" at line ") + .append(finding.line()) + .append(": ") + .append(finding.detected()) + .append('\n')); + + if (findingsByFile.isEmpty()) { + output.append("- No migration rules detected\n"); + } else { + findingsByFile.forEach((file, findings) -> output + .append("### `") + .append(file) + .append("`\n\n") + .append(findings) + .append('\n')); + } + + output.append("## Warnings\n\n"); + var manualFindings = report.findings().stream() + .filter(finding -> finding.status() == Finding.Status.MANUAL) + .toList(); + + if (manualFindings.isEmpty() && report.unparsable().isEmpty()) { + output.append("- None\n"); + } else { + manualFindings.forEach(finding -> output + .append("- `") + .append(finding.file()) + .append(':') + .append(finding.line()) + .append("`: Detected: ") + .append(finding.detected()) + .append(". Why not automatic: manual review is required. Suggested approach: ") + .append(finding.advice()) + .append('\n')); + + report.unparsable().forEach(file -> output + .append("- `") + .append(file) + .append("` could not be parsed\n")); + } + + output.append("\n## Manual actions\n\n"); + if (manualFindings.isEmpty()) { + output.append("- None\n"); + } else { + manualFindings.forEach(finding -> output + .append("- `") + .append(finding.file()) + .append(':') + .append(finding.line()) + .append("`: ") + .append(finding.advice()) + .append('\n')); + } + + output.append("\n## Estimated migration confidence\n\n"); + output.append(report.estimatedConfidence()) + .append("% estimated migration confidence. This is an estimate, not a guarantee.\n"); + + return output.toString(); + } +} diff --git a/src/main/java/com/seleniumboot/migrator/Migrator.java b/src/main/java/com/seleniumboot/migrator/Migrator.java index b783efe..444a7e0 100644 --- a/src/main/java/com/seleniumboot/migrator/Migrator.java +++ b/src/main/java/com/seleniumboot/migrator/Migrator.java @@ -64,6 +64,7 @@ public Result migrate(Path project, Path output) throws IOException { Path parent = destination.getParent(); if (parent != null) Files.createDirectories(parent); copyProject(source, destination); + MigrationReport.write(destination, sourceAnalysis); List applied = new ArrayList<>(); List notes = new ArrayList<>(); diff --git a/src/test/java/com/seleniumboot/migrator/MigrationReportTest.java b/src/test/java/com/seleniumboot/migrator/MigrationReportTest.java new file mode 100644 index 0000000..c09e5fd --- /dev/null +++ b/src/test/java/com/seleniumboot/migrator/MigrationReportTest.java @@ -0,0 +1,66 @@ +package com.seleniumboot.migrator; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class MigrationReportTest { + + @Test + void matchesSnapshot() throws Exception { + var report = fixtureReport(); + + String expected = Files.readString( + Path.of("src/test/resources/migration-report.md") + ); + + assertEquals(expected, MigrationReport.render(report)); + } + + @Test + void writesMigrationReport(@TempDir Path directory) throws Exception { + var report = fixtureReport(); + + Path reportPath = MigrationReport.write(directory, report); + + assertEquals(directory.resolve("MIGRATION_REPORT.md"), reportPath); + assertTrue(Files.exists(reportPath)); + assertEquals(MigrationReport.render(report), Files.readString(reportPath)); + } + + private static Report fixtureReport() { + return new Report( + 3, + 2, + List.of("Broken.java"), + List.of( + new Finding( + "MIG-002", + Finding.Status.AUTO, + "Auto.java", + 4, + "WebDriverManager", + "Delete; Selenium Manager fetches drivers automatically." + ), + new Finding( + "MIG-003", + Finding.Status.MANUAL, + "Page.java", + 8, + "WebDriverWait / ExpectedConditions", + "Use $(locator) auto-wait or getWait(); review the condition by hand." + ) + ), + List.of("Build system: Maven"), + List.of("Selenium", "JUnit 5"), + Map.of("By.id", 2L) + ); + } +} diff --git a/src/test/java/com/seleniumboot/migrator/MigratorTest.java b/src/test/java/com/seleniumboot/migrator/MigratorTest.java index 4ce1129..fb4601f 100644 --- a/src/test/java/com/seleniumboot/migrator/MigratorTest.java +++ b/src/test/java/com/seleniumboot/migrator/MigratorTest.java @@ -80,6 +80,11 @@ class Utility { int value; } Migrator.Result result = new Migrator().migrate(project, output); + assertTrue(Files.isRegularFile(output.resolve("MIGRATION_REPORT.md"))); + String migrationReport = Files.readString(output.resolve("MIGRATION_REPORT.md")); + assertTrue(migrationReport.contains("# Selenium Boot Migration Report")); + assertTrue(migrationReport.contains("## Rules applied per file")); + assertTrue(migrationReport.contains("Estimated migration confidence")); assertEquals(original.keySet(), snapshot(project).keySet()); original.forEach((path, bytes) -> assertArrayEquals(bytes, read(project.resolve(path)))); assertFalse(Files.exists(output.resolve("src/main/java/fixture/DriverFactory.java"))); diff --git a/src/test/resources/migration-report.md b/src/test/resources/migration-report.md new file mode 100644 index 0000000..e4f902c --- /dev/null +++ b/src/test/resources/migration-report.md @@ -0,0 +1,41 @@ +# Selenium Boot Migration Report + +## Summary + +- Files found: 3 +- Files parsed: 2 +- Automatic findings: 1 +- Manual findings: 1 +- Unparsable files: 1 + +## Detected technologies + +- Build system: Maven + +## Recognized technologies + +- Selenium +- JUnit 5 + +## Rules applied per file + +### `Auto.java` + +- MIG-002 at line 4: WebDriverManager + +### `Page.java` + +- MIG-003 at line 8: WebDriverWait / ExpectedConditions + +## Warnings + +- `Page.java:8`: Detected: WebDriverWait / ExpectedConditions. Why not automatic: manual review is required. Suggested approach: Use $(locator) auto-wait or getWait(); review the condition by hand. +- `Broken.java` could not be parsed + +## Manual actions + +- `Page.java:8`: Use $(locator) auto-wait or getWait(); review the condition by hand. + +## Estimated migration confidence + +50% estimated migration confidence. This is an estimate, not a guarantee. From 735f7999575a9d69a9eac120fba73e08c7c5349f Mon Sep 17 00:00:00 2001 From: Pushpak-Jaiswal Date: Thu, 1 Oct 2026 16:34:31 +0530 Subject: [PATCH 2/3] fix: issue-7-migration-report changes --- .../migrator/MigrationReport.java | 2 +- .../com/seleniumboot/migrator/Migrator.java | 9 ++-- .../migrator/MigrationReportTest.java | 45 ++++++++++++++++++- .../seleniumboot/migrator/MigratorTest.java | 11 ++++- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/seleniumboot/migrator/MigrationReport.java b/src/main/java/com/seleniumboot/migrator/MigrationReport.java index 00cee02..d0b88ec 100644 --- a/src/main/java/com/seleniumboot/migrator/MigrationReport.java +++ b/src/main/java/com/seleniumboot/migrator/MigrationReport.java @@ -59,7 +59,7 @@ static String render(Report report) { .append('\n')); if (findingsByFile.isEmpty()) { - output.append("- No migration rules detected\n"); + output.append("- No migration rules detected\n\n"); } else { findingsByFile.forEach((file, findings) -> output .append("### `") diff --git a/src/main/java/com/seleniumboot/migrator/Migrator.java b/src/main/java/com/seleniumboot/migrator/Migrator.java index 444a7e0..0bca348 100644 --- a/src/main/java/com/seleniumboot/migrator/Migrator.java +++ b/src/main/java/com/seleniumboot/migrator/Migrator.java @@ -64,7 +64,6 @@ public Result migrate(Path project, Path output) throws IOException { Path parent = destination.getParent(); if (parent != null) Files.createDirectories(parent); copyProject(source, destination); - MigrationReport.write(destination, sourceAnalysis); List applied = new ArrayList<>(); List notes = new ArrayList<>(); @@ -76,8 +75,9 @@ public Result migrate(Path project, Path output) throws IOException { applied.add("pom.xml: replaced selenium-java with io.github.seleniumboot:selenium-boot:" + SELENIUM_BOOT_VERSION); } Report outputAnalysis = new Analyzer().analyze(destination); - return new Result(destination, List.copyOf(applied), List.copyOf(notes), - includeSourceManualFindings(sourceAnalysis, outputAnalysis, danglingReferences)); + Report remaining = includeSourceManualFindings(sourceAnalysis, outputAnalysis, danglingReferences); + MigrationReport.write(destination, remaining); + return new Result(destination, List.copyOf(applied), List.copyOf(notes), remaining); } private static Report includeSourceManualFindings(Report source, Report output, List additionalFindings) { @@ -87,7 +87,8 @@ private static Report includeSourceManualFindings(Report source, Report output, additionalFindings.stream().filter(finding -> !findings.contains(finding)).forEach(findings::add); Set unparsable = new LinkedHashSet<>(source.unparsable()); unparsable.addAll(output.unparsable()); - return new Report(output.filesFound(), output.filesParsed(), List.copyOf(unparsable), List.copyOf(findings)); + return new Report(output.filesFound(), output.filesParsed(), List.copyOf(unparsable), List.copyOf(findings), + source.detectedTechnologies(), source.recognizedTechnologies(), source.locatorCounts()); } private static void copyProject(Path source, Path destination) throws IOException { diff --git a/src/test/java/com/seleniumboot/migrator/MigrationReportTest.java b/src/test/java/com/seleniumboot/migrator/MigrationReportTest.java index c09e5fd..f2c0c14 100644 --- a/src/test/java/com/seleniumboot/migrator/MigrationReportTest.java +++ b/src/test/java/com/seleniumboot/migrator/MigrationReportTest.java @@ -19,7 +19,50 @@ void matchesSnapshot() throws Exception { String expected = Files.readString( Path.of("src/test/resources/migration-report.md") - ); + ).replace("\r\n", "\n"); + + assertEquals(expected, MigrationReport.render(report)); + } + + @Test + void rendersReportWithZeroFindings() { + var report = new Report(0, 0, List.of(), List.of()); + + String expected = """ + # Selenium Boot Migration Report + + ## Summary + + - Files found: 0 + - Files parsed: 0 + - Automatic findings: 0 + - Manual findings: 0 + - Unparsable files: 0 + + ## Detected technologies + + - No supported build descriptor found + + ## Recognized technologies + + - No supported test technologies detected + + ## Rules applied per file + + - No migration rules detected + + ## Warnings + + - None + + ## Manual actions + + - None + + ## Estimated migration confidence + + 100% estimated migration confidence. This is an estimate, not a guarantee. + """; assertEquals(expected, MigrationReport.render(report)); } diff --git a/src/test/java/com/seleniumboot/migrator/MigratorTest.java b/src/test/java/com/seleniumboot/migrator/MigratorTest.java index fb4601f..2b73891 100644 --- a/src/test/java/com/seleniumboot/migrator/MigratorTest.java +++ b/src/test/java/com/seleniumboot/migrator/MigratorTest.java @@ -82,6 +82,7 @@ class Utility { int value; } assertTrue(Files.isRegularFile(output.resolve("MIGRATION_REPORT.md"))); String migrationReport = Files.readString(output.resolve("MIGRATION_REPORT.md")); + assertEquals(MigrationReport.render(result.remaining()), migrationReport); assertTrue(migrationReport.contains("# Selenium Boot Migration Report")); assertTrue(migrationReport.contains("## Rules applied per file")); assertTrue(migrationReport.contains("Estimated migration confidence")); @@ -157,7 +158,15 @@ class Caller { && finding.file().endsWith("Caller.java")) .count()); assertTrue(Files.readString(output.resolve("pom.xml")).contains("3.5.0")); - } + + assertTrue(Files.isRegularFile(output.resolve("MIGRATION_REPORT.md"))); + String migrationReport = Files.readString(output.resolve("MIGRATION_REPORT.md")); + assertEquals(MigrationReport.render(result.remaining()), migrationReport); + assertTrue(migrationReport.contains("MIG-017")); + assertTrue(migrationReport.contains("Caller.java")); + assertFalse(migrationReport.contains("### `src/main/java/fixture/DriverFactory.java`")); + assertFalse(migrationReport.contains("### `src/main/java/fixture/RetryAnalyzer.java`")); + } @Test void refusesToWriteIntoSourceOrOverwriteExistingOutput() throws Exception { From 52fe8b9b404b2cc92de8171e3cd0fca2d1d74054 Mon Sep 17 00:00:00 2001 From: Pushpak-Jaiswal Date: Thu, 1 Oct 2026 16:50:46 +0530 Subject: [PATCH 3/3] fix: test failure condition --- src/test/java/com/seleniumboot/migrator/MigratorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/seleniumboot/migrator/MigratorTest.java b/src/test/java/com/seleniumboot/migrator/MigratorTest.java index 2b73891..824e006 100644 --- a/src/test/java/com/seleniumboot/migrator/MigratorTest.java +++ b/src/test/java/com/seleniumboot/migrator/MigratorTest.java @@ -164,8 +164,8 @@ class Caller { assertEquals(MigrationReport.render(result.remaining()), migrationReport); assertTrue(migrationReport.contains("MIG-017")); assertTrue(migrationReport.contains("Caller.java")); - assertFalse(migrationReport.contains("### `src/main/java/fixture/DriverFactory.java`")); - assertFalse(migrationReport.contains("### `src/main/java/fixture/RetryAnalyzer.java`")); + assertFalse(migrationReport.contains("MIG-004")); + assertFalse(migrationReport.contains("MIG-001")); } @Test