From b3a667e460025a8d8c76dc684ed7ec3bbada518a Mon Sep 17 00:00:00 2001 From: Petter Olofsson Date: Mon, 15 Jan 2024 21:38:00 +0100 Subject: [PATCH 1/2] Add JUnit reporter --- .../src/main/scala/com/karumi/shot/Shot.scala | 20 +- .../shot/reports/ExecutionReporter.scala | 202 +---------------- .../shot/reports/HtmlExecutionReporter.scala | 210 ++++++++++++++++++ .../shot/reports/JunitExecutionReporter.scala | 131 +++++++++++ .../test/scala/com/karumi/shot/ShotSpec.scala | 6 +- gradle.properties | 2 +- .../scala/com/karumi/shot/tasks/Tasks.scala | 4 +- 7 files changed, 363 insertions(+), 212 deletions(-) create mode 100644 core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala create mode 100644 core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala diff --git a/core/src/main/scala/com/karumi/shot/Shot.scala b/core/src/main/scala/com/karumi/shot/Shot.scala index 63bfbb3f..6d60d6b8 100644 --- a/core/src/main/scala/com/karumi/shot/Shot.scala +++ b/core/src/main/scala/com/karumi/shot/Shot.scala @@ -4,7 +4,7 @@ import com.karumi.shot.android.Adb import com.karumi.shot.domain._ import com.karumi.shot.domain.model.{AppId, FilePath, Folder, ScreenshotsSuite} import com.karumi.shot.json.ScreenshotsComposeSuiteJsonParser -import com.karumi.shot.reports.{ConsoleReporter, ExecutionReporter} +import com.karumi.shot.reports.{ConsoleReporter, HtmlExecutionReporter, ExecutionReporter} import com.karumi.shot.screenshots.{ ScreenshotsComparator, ScreenshotsDiffGenerator, @@ -33,7 +33,7 @@ class Shot( screenshotsDiffGenerator: ScreenshotsDiffGenerator, screenshotsSaver: ScreenshotsSaver, console: Console, - reporter: ExecutionReporter, + reporters: List[ExecutionReporter], consoleReporter: ConsoleReporter, envVars: EnvVars ) { @@ -54,7 +54,8 @@ class Shot( } else { val screenshots = regularScreenshotSuite.get ++ composeScreenshotSuite.get console.show("😃 Screenshots recorded and saved at: " + shotFolder.screenshotsFolder()) - reporter.generateRecordReport(appId, screenshots, shotFolder) + for (reporter <- reporters) + reporter.generateRecordReport(appId, screenshots, shotFolder) console.show( "🤓 You can review the execution report here: " + shotFolder.reportFolder() + "index.html" ) @@ -121,12 +122,13 @@ class Shot( } else { console.showSuccess("✅ Yeah!!! Your tests are passing.") } - reporter.generateVerificationReport( - appId, - comparison, - shotFolder, - showOnlyFailingTestsInReports - ) + for (reporter <- reporters) + reporter.generateVerificationReport( + appId, + comparison, + shotFolder, + showOnlyFailingTestsInReports + ) console.show( "🤓 You can review the execution report here: " + shotFolder .verificationReportFolder() + "index.html" diff --git a/core/src/main/scala/com/karumi/shot/reports/ExecutionReporter.scala b/core/src/main/scala/com/karumi/shot/reports/ExecutionReporter.scala index 2cdc289f..ef9111af 100644 --- a/core/src/main/scala/com/karumi/shot/reports/ExecutionReporter.scala +++ b/core/src/main/scala/com/karumi/shot/reports/ExecutionReporter.scala @@ -1,211 +1,19 @@ package com.karumi.shot.reports +import com.karumi.shot.domain.{ScreenshotsComparisionResult, ShotFolder} +import com.karumi.shot.domain.model.{AppId, ScreenshotsSuite} -import java.io.{File, FileWriter} - -import com.karumi.shot.domain._ -import com.karumi.shot.domain.model.{AppId, Folder, ScreenshotComparisionErrors, ScreenshotsSuite} -import com.karumi.shot.templates.RecordIndexTemplate.recordIndexTemplate -import com.karumi.shot.templates.VerificationIndexTemplate.verificationIndexTemplate - -class ExecutionReporter { +trait ExecutionReporter { def generateRecordReport( appId: AppId, screenshots: ScreenshotsSuite, shotFolder: ShotFolder - ) = { - val reportFileContents = populateRecordTemplate(appId, screenshots) - resetVerificationReport(shotFolder) - val reportFolder = shotFolder.recordingReportFolder() - writeReport(reportFileContents, reportFolder) - } + ): Unit def generateVerificationReport( appId: AppId, comparision: ScreenshotsComparisionResult, shotFolder: ShotFolder, showOnlyFailingTestsInReports: Boolean = false - ) = { - val reportFileContents = - populateVerificationTemplate(appId, comparision, showOnlyFailingTestsInReports) - resetVerificationReport(shotFolder) - val reportFolder = shotFolder.verificationReportFolder() - writeReport(reportFileContents, reportFolder) - } - - private def writeReport( - fileContents: String, - reportFolder: String - ) = { - val indexFile = new File(reportFolder + "index.html") - new File(reportFolder).mkdirs() - val writer = new FileWriter(indexFile) - writer.write(fileContents) - writer.close() - } - - private def resetVerificationReport(shotFolder: ShotFolder) = { - val file = new File(shotFolder.reportFolder() + "index.html") - if (file.exists()) { - file.delete() - } - } - - private def populateRecordTemplate( - appId: AppId, - screenshots: ScreenshotsSuite - ): String = { - val title = s"Record results: $appId" - val numberOfTests = screenshots.size - val summaryResults = - s"$numberOfTests screenshot tests recorded." - val summaryTableBody = generateRecordSummaryTableBody(screenshots) - recordIndexTemplate( - title = title, - summaryResult = summaryResults, - summaryTableBody = summaryTableBody - ) - } - - private def generateRecordSummaryTableBody(screenshots: ScreenshotsSuite): String = { - screenshots - .map { (screenshot: Screenshot) => - val testClass = screenshot.testClass - val testName = screenshot.testName - val originalScreenshot = "./images/recorded/" + screenshot.name + ".png" - val width = (screenshot.screenshotDimension.width * 0.2).toInt - val screenshotName = screenshot.name - "" + - s"

Test class: $testClass

" + - s"

Test name: $testName

" + - s"

Screenshot name: $screenshotName

" + - s" " + - "" - } - .mkString("\n") - } - - private def populateVerificationTemplate( - appId: AppId, - comparision: ScreenshotsComparisionResult, - showOnlyFailingTestsInReports: Boolean - ): String = { - val title = s"Verification results: $appId" - val screenshots = comparision.screenshots - val numberOfTests = screenshots.size - val failedNumber = comparision.errors.size - val successNumber = numberOfTests - failedNumber - val summaryResults = - s"$numberOfTests screenshot tests executed. $successNumber passed and $failedNumber failed." - val summaryTableBody = - generateVerificationSummaryTableBody(comparision, showOnlyFailingTestsInReports) - val screenshotsTableBody = - generateScreenshotsTableBody(comparision, showOnlyFailingTestsInReports) - verificationIndexTemplate( - title = title, - summaryResult = summaryResults, - summaryTableBody = summaryTableBody, - screenshotsTableBody = screenshotsTableBody - ) - } - - private def getSortedByResultScreenshots(comparison: ScreenshotsComparisionResult) = - comparison.screenshots - .map { (screenshot: Screenshot) => - val error = findError(screenshot, comparison.errors) - (screenshot, error) - } - .sortBy(_._2.isEmpty) - - private def generateVerificationSummaryTableBody( - comparision: ScreenshotsComparisionResult, - showOnlyFailingTestsInReports: Boolean - ): String = { - getSortedByResultScreenshots(comparision) - .map { case (screenshot, error) => - val isFailedTest = error.isDefined - val testClass = screenshot.testClass - val testName = screenshot.testName - val result = if (isFailedTest) "❌" else "✅" - val reason = generateReasonMessage(error) - val color = if (isFailedTest) "red-text" else "green-text" - val id = screenshot.name.replace(".", "") - val screenshotName = screenshot.name - - if (showOnlyFailingTestsInReports && isFailedTest || !showOnlyFailingTestsInReports) { - "" + - s"$result" + - s"

Test class: $testClass

" + - s"

Test name: $testName

" + - s"

Screenshot name: $screenshotName

" + - s"$reason" + - "" - } else { - "" - } - } - .mkString("\n") - } - - private def generateScreenshotsTableBody( - comparision: ScreenshotsComparisionResult, - showOnlyFailingTestsInReports: Boolean - ): String = { - getSortedByResultScreenshots(comparision) - .map { case (screenshot, error) => - val isFailedTest = error.isDefined - val testClass = screenshot.testClass - val testName = screenshot.testName - val originalScreenshot = "./images/recorded/" + screenshot.name + ".png" - val newScreenshot = "./images/" + screenshot.name + ".png" - val diff = if (error.exists(_.isInstanceOf[DifferentScreenshots])) { - screenshot.getDiffScreenshotPath("./images/") - } else { - "" - } - val color = if (isFailedTest) "red-text" else "green-text" - val width = (screenshot.screenshotDimension.width * 0.2).toInt - val id = screenshot.name.replace(".", "") - val screenshotName = screenshot.name - - if (showOnlyFailingTestsInReports && isFailedTest || !showOnlyFailingTestsInReports) { - "" + - s"

Test class: $testClass

" + - s"

Test name: $testName

" + - s"

Screenshot name: $screenshotName

" + - s" " + - s" " + - s" " + - "" - } else { - "" - } - } - .mkString("\n") - } - - private def findError( - screenshot: Screenshot, - errors: ScreenshotComparisionErrors - ): Option[ScreenshotComparisonError] = - errors.find { - case ScreenshotNotFound(error) => screenshot == error - case DifferentImageDimensions(error, _, _) => screenshot == error - case DifferentScreenshots(error, _) => screenshot == error - case _ => false - } - - private def generateReasonMessage(error: Option[ScreenshotComparisonError]): String = - error - .map { - case ScreenshotNotFound(_) => - "

🔎 Recorded screenshot not found.

" - case DifferentScreenshots(_, _) => - "

🤔 The application UI has been modified.

" - case DifferentImageDimensions(_, _, _) => - "

📱 The size of the screenshot taken has changed.

" - case _ => - "

😞 Ups! Something went wrong while comparing your screenshots but we couldn't identify the cause. If you think you've found a bug, please open an issue at https://github.com/karumi/shot.

" - } - .getOrElse("") + ): Unit } diff --git a/core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala b/core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala new file mode 100644 index 00000000..629d63a9 --- /dev/null +++ b/core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala @@ -0,0 +1,210 @@ +package com.karumi.shot.reports + +import java.io.{File, FileWriter} + +import com.karumi.shot.domain._ +import com.karumi.shot.domain.model.{AppId, Folder, ScreenshotComparisionErrors, ScreenshotsSuite} +import com.karumi.shot.templates.RecordIndexTemplate.recordIndexTemplate +import com.karumi.shot.templates.VerificationIndexTemplate.verificationIndexTemplate + +class HtmlExecutionReporter extends ExecutionReporter { + + def generateRecordReport( + appId: AppId, + screenshots: ScreenshotsSuite, + shotFolder: ShotFolder + ): Unit = { + val reportFileContents = populateRecordTemplate(appId, screenshots) + resetVerificationReport(shotFolder) + val reportFolder = shotFolder.recordingReportFolder() + writeReport(reportFileContents, reportFolder) + } + + def generateVerificationReport( + appId: AppId, + comparision: ScreenshotsComparisionResult, + shotFolder: ShotFolder, + showOnlyFailingTestsInReports: Boolean = false + ) = { + val reportFileContents = + populateVerificationTemplate(appId, comparision, showOnlyFailingTestsInReports) + resetVerificationReport(shotFolder) + val reportFolder = shotFolder.verificationReportFolder() + writeReport(reportFileContents, reportFolder) + } + private def writeReport( + fileContents: String, + reportFolder: String + ) = { + val indexFile = new File(reportFolder + "index.html") + new File(reportFolder).mkdirs() + val writer = new FileWriter(indexFile) + writer.write(fileContents) + writer.close() + } + + private def resetVerificationReport(shotFolder: ShotFolder) = { + val file = new File(shotFolder.reportFolder() + "index.html") + if (file.exists()) { + file.delete() + } + } + + private def populateRecordTemplate( + appId: AppId, + screenshots: ScreenshotsSuite + ): String = { + val title = s"Record results: $appId" + val numberOfTests = screenshots.size + val summaryResults = + s"$numberOfTests screenshot tests recorded." + val summaryTableBody = generateRecordSummaryTableBody(screenshots) + recordIndexTemplate( + title = title, + summaryResult = summaryResults, + summaryTableBody = summaryTableBody + ) + } + + private def generateRecordSummaryTableBody(screenshots: ScreenshotsSuite): String = { + screenshots + .map { (screenshot: Screenshot) => + val testClass = screenshot.testClass + val testName = screenshot.testName + val originalScreenshot = "./images/recorded/" + screenshot.name + ".png" + val width = (screenshot.screenshotDimension.width * 0.2).toInt + val screenshotName = screenshot.name + "" + + s"

Test class: $testClass

" + + s"

Test name: $testName

" + + s"

Screenshot name: $screenshotName

" + + s" " + + "" + } + .mkString("\n") + } + + private def populateVerificationTemplate( + appId: AppId, + comparision: ScreenshotsComparisionResult, + showOnlyFailingTestsInReports: Boolean + ): String = { + val title = s"Verification results: $appId" + val screenshots = comparision.screenshots + val numberOfTests = screenshots.size + val failedNumber = comparision.errors.size + val successNumber = numberOfTests - failedNumber + val summaryResults = + s"$numberOfTests screenshot tests executed. $successNumber passed and $failedNumber failed." + val summaryTableBody = + generateVerificationSummaryTableBody(comparision, showOnlyFailingTestsInReports) + val screenshotsTableBody = + generateScreenshotsTableBody(comparision, showOnlyFailingTestsInReports) + verificationIndexTemplate( + title = title, + summaryResult = summaryResults, + summaryTableBody = summaryTableBody, + screenshotsTableBody = screenshotsTableBody + ) + } + + private def getSortedByResultScreenshots(comparison: ScreenshotsComparisionResult) = + comparison.screenshots + .map { (screenshot: Screenshot) => + val error = findError(screenshot, comparison.errors) + (screenshot, error) + } + .sortBy(_._2.isEmpty) + + private def generateVerificationSummaryTableBody( + comparision: ScreenshotsComparisionResult, + showOnlyFailingTestsInReports: Boolean + ): String = { + getSortedByResultScreenshots(comparision) + .map { case (screenshot, error) => + val isFailedTest = error.isDefined + val testClass = screenshot.testClass + val testName = screenshot.testName + val result = if (isFailedTest) "❌" else "✅" + val reason = generateReasonMessage(error) + val color = if (isFailedTest) "red-text" else "green-text" + val id = screenshot.name.replace(".", "") + val screenshotName = screenshot.name + + if (showOnlyFailingTestsInReports && isFailedTest || !showOnlyFailingTestsInReports) { + "" + + s"$result" + + s"

Test class: $testClass

" + + s"

Test name: $testName

" + + s"

Screenshot name: $screenshotName

" + + s"$reason" + + "" + } else { + "" + } + } + .mkString("\n") + } + + private def generateScreenshotsTableBody( + comparision: ScreenshotsComparisionResult, + showOnlyFailingTestsInReports: Boolean + ): String = { + getSortedByResultScreenshots(comparision) + .map { case (screenshot, error) => + val isFailedTest = error.isDefined + val testClass = screenshot.testClass + val testName = screenshot.testName + val originalScreenshot = "./images/recorded/" + screenshot.name + ".png" + val newScreenshot = "./images/" + screenshot.name + ".png" + val diff = if (error.exists(_.isInstanceOf[DifferentScreenshots])) { + screenshot.getDiffScreenshotPath("./images/") + } else { + "" + } + val color = if (isFailedTest) "red-text" else "green-text" + val width = (screenshot.screenshotDimension.width * 0.2).toInt + val id = screenshot.name.replace(".", "") + val screenshotName = screenshot.name + + if (showOnlyFailingTestsInReports && isFailedTest || !showOnlyFailingTestsInReports) { + "" + + s"

Test class: $testClass

" + + s"

Test name: $testName

" + + s"

Screenshot name: $screenshotName

" + + s" " + + s" " + + s" " + + "" + } else { + "" + } + } + .mkString("\n") + } + + private def findError( + screenshot: Screenshot, + errors: ScreenshotComparisionErrors + ): Option[ScreenshotComparisonError] = + errors.find { + case ScreenshotNotFound(error) => screenshot == error + case DifferentImageDimensions(error, _, _) => screenshot == error + case DifferentScreenshots(error, _) => screenshot == error + case _ => false + } + + private def generateReasonMessage(error: Option[ScreenshotComparisonError]): String = + error + .map { + case ScreenshotNotFound(_) => + "

🔎 Recorded screenshot not found.

" + case DifferentScreenshots(_, _) => + "

🤔 The application UI has been modified.

" + case DifferentImageDimensions(_, _, _) => + "

📱 The size of the screenshot taken has changed.

" + case _ => + "

😞 Ups! Something went wrong while comparing your screenshots but we couldn't identify the cause. If you think you've found a bug, please open an issue at https://github.com/karumi/shot.

" + } + .getOrElse("") +} diff --git a/core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala b/core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala new file mode 100644 index 00000000..eb9abee7 --- /dev/null +++ b/core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala @@ -0,0 +1,131 @@ +package com.karumi.shot.reports + +import com.karumi.shot.domain.{DifferentImageDimensions, DifferentScreenshots, Screenshot, ScreenshotComparisonError, ScreenshotNotFound, ScreenshotsComparisionResult, ShotFolder} +import com.karumi.shot.domain.model.{AppId, ScreenshotComparisionErrors, ScreenshotsSuite} + +import java.io.{File, FileWriter} +import scala.collection.IterableOnce.iterableOnceExtensionMethods +import scala.language.postfixOps + +class JunitExecutionReporter extends ExecutionReporter { + + def generateRecordReport( + appId: AppId, + screenshots: ScreenshotsSuite, + shotFolder: ShotFolder + ): Unit = () + + def generateVerificationReport( + appId: AppId, + comparision: ScreenshotsComparisionResult, + shotFolder: ShotFolder, + showOnlyFailingTestsInReports: Boolean = false + ): Unit = { + val reportFileContents = + populateVerificationTemplate(appId, comparision) + resetVerificationReport(shotFolder) + val reportFolder = shotFolder.verificationReportFolder() + writeReport(reportFileContents, reportFolder) + } + + private def writeReport( + fileContents: String, + reportFolder: String + ): Unit = { + val indexFile = new File(reportFolder + "TEST-Shot.xml") + new File(reportFolder).mkdirs() + val writer = new FileWriter(indexFile) + writer.write(fileContents) + writer.close() + } + + private def resetVerificationReport(shotFolder: ShotFolder) = { + val file = new File(shotFolder.reportFolder() + "TEST-Shot.xml") + if (file.exists()) { + file.delete() + } + } + + private def populateVerificationTemplate( + appId: AppId, + comparision: ScreenshotsComparisionResult + ): String = { + val title = s"Screenshot results: $appId" + val summaryTableBody = + generateVerificationSummaryTableBody(comparision) + report( + title, + summaryTableBody + ) + } + + private def report(title: String, testResults: String): String = { + s"""| + | + | $testResults + |""".stripMargin + } + + private def generateVerificationSummaryTableBody( + comparisionResult: ScreenshotsComparisionResult + + ): String = { + val groupedScreenshots = + comparisionResult + .screenshots + .groupBy { (screenshot: Screenshot) => + screenshot.testClass + } + groupedScreenshots + .map { case (testSuite: String, screenshots: Seq[Screenshot]) => + val tests = screenshots + .map((screenshot) => { + val error = findError(screenshot = screenshot, comparisionResult.errors) + val isFailedTest = error.isDefined + val testClass = screenshot.testClass + val testName = screenshot.fileName + val reason = generateReasonMessage(error) + + val failureString = if (isFailedTest) { + s"""""" + } else { + "" + } + + s""" + | $failureString + |""".stripMargin + }).mkString("\n") + + s""" + | ${tests} + | + |""".stripMargin + }.mkString("\n") + } + + private def findError( + screenshot: Screenshot, + errors: ScreenshotComparisionErrors + ): Option[ScreenshotComparisonError] = + errors.find { + case ScreenshotNotFound(error) => screenshot == error + case DifferentImageDimensions(error, _, _) => screenshot == error + case DifferentScreenshots(error, _) => screenshot == error + case _ => false + } + + private def generateReasonMessage(error: Option[ScreenshotComparisonError]): String = + error + .map { + case ScreenshotNotFound(_) => + "Recorded screenshot not found." + case DifferentScreenshots(_, _) => + "The application UI has been modified." + case DifferentImageDimensions(_, _, _) => + "The size of the screenshot taken has changed." + case _ => + "Ups! Something went wrong while comparing your screenshots but we couldn't identify the cause. If you think you've found a bug, please open an issue at https://github.com/karumi/shot." + } + .getOrElse("") +} diff --git a/core/src/test/scala/com/karumi/shot/ShotSpec.scala b/core/src/test/scala/com/karumi/shot/ShotSpec.scala index e5c66185..93f829b3 100644 --- a/core/src/test/scala/com/karumi/shot/ShotSpec.scala +++ b/core/src/test/scala/com/karumi/shot/ShotSpec.scala @@ -3,7 +3,7 @@ package com.karumi.shot import com.karumi.shot.android.Adb import com.karumi.shot.domain.model.AppId import com.karumi.shot.mothers.{AppIdMother, ProjectFolderMother, ProjectNameMother} -import com.karumi.shot.reports.{ConsoleReporter, ExecutionReporter} +import com.karumi.shot.reports.{ConsoleReporter, HtmlExecutionReporter} import com.karumi.shot.screenshots.{ ScreenshotsComparator, ScreenshotsDiffGenerator, @@ -33,7 +33,7 @@ class ShotSpec private val screenshotsComparator = mock[ScreenshotsComparator] private val screenshotsDiffGenerator = mock[ScreenshotsDiffGenerator] private val screenshotsSaver = mock[ScreenshotsSaver] - private val reporter = mock[ExecutionReporter] + private val reporter = mock[HtmlExecutionReporter] private val consoleReporter = mock[ConsoleReporter] private val envVars = mock[EnvVars] @@ -45,7 +45,7 @@ class ShotSpec screenshotsDiffGenerator, screenshotsSaver, console, - reporter, + List(reporter), consoleReporter, envVars ) diff --git a/gradle.properties b/gradle.properties index 3e689f53..149de2cd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=6.0.0 +VERSION_NAME=6.1.0 VERSION_CODE=600000 GROUP=com.karumi POM_DESCRIPTION=Gradle plugin developed to facilitate screenshot testing for Android. diff --git a/shot/src/main/scala/com/karumi/shot/tasks/Tasks.scala b/shot/src/main/scala/com/karumi/shot/tasks/Tasks.scala index c7bcf639..29896fed 100644 --- a/shot/src/main/scala/com/karumi/shot/tasks/Tasks.scala +++ b/shot/src/main/scala/com/karumi/shot/tasks/Tasks.scala @@ -4,7 +4,7 @@ import com.android.builder.model.BuildType import com.karumi.shot.android.Adb import com.karumi.shot.base64.Base64Encoder import com.karumi.shot.domain.ShotFolder -import com.karumi.shot.reports.{ConsoleReporter, ExecutionReporter} +import com.karumi.shot.reports.{ConsoleReporter, HtmlExecutionReporter, JunitExecutionReporter} import com.karumi.shot.screenshots.{ ScreenshotsComparator, ScreenshotsDiffGenerator, @@ -40,7 +40,7 @@ abstract class ShotTask extends DefaultTask { new ScreenshotsDiffGenerator(new Base64Encoder), new ScreenshotsSaver, console, - new ExecutionReporter, + List(new HtmlExecutionReporter, new JunitExecutionReporter), new ConsoleReporter(console), new EnvVars() ) From 30dcf81cc2949f1fa5e4b82f6774113aba4954bb Mon Sep 17 00:00:00 2001 From: Petter Olofsson Date: Wed, 7 Feb 2024 22:07:32 +0100 Subject: [PATCH 2/2] Reformat code --- .../shot/reports/HtmlExecutionReporter.scala | 31 +++---- .../shot/reports/JunitExecutionReporter.scala | 88 +++++++++---------- .../ScreenshotsDiffGenerator.scala | 7 +- .../shot/screenshots/ScreenshotsSaver.scala | 74 ++++++++-------- gradle.properties | 2 +- 5 files changed, 104 insertions(+), 98 deletions(-) diff --git a/core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala b/core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala index 629d63a9..823a4a77 100644 --- a/core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala +++ b/core/src/main/scala/com/karumi/shot/reports/HtmlExecutionReporter.scala @@ -1,12 +1,12 @@ package com.karumi.shot.reports -import java.io.{File, FileWriter} - import com.karumi.shot.domain._ -import com.karumi.shot.domain.model.{AppId, Folder, ScreenshotComparisionErrors, ScreenshotsSuite} +import com.karumi.shot.domain.model.{AppId, ScreenshotComparisionErrors, ScreenshotsSuite} import com.karumi.shot.templates.RecordIndexTemplate.recordIndexTemplate import com.karumi.shot.templates.VerificationIndexTemplate.verificationIndexTemplate +import java.io.{File, FileWriter} + class HtmlExecutionReporter extends ExecutionReporter { def generateRecordReport( @@ -20,18 +20,6 @@ class HtmlExecutionReporter extends ExecutionReporter { writeReport(reportFileContents, reportFolder) } - def generateVerificationReport( - appId: AppId, - comparision: ScreenshotsComparisionResult, - shotFolder: ShotFolder, - showOnlyFailingTestsInReports: Boolean = false - ) = { - val reportFileContents = - populateVerificationTemplate(appId, comparision, showOnlyFailingTestsInReports) - resetVerificationReport(shotFolder) - val reportFolder = shotFolder.verificationReportFolder() - writeReport(reportFileContents, reportFolder) - } private def writeReport( fileContents: String, reportFolder: String @@ -84,6 +72,19 @@ class HtmlExecutionReporter extends ExecutionReporter { .mkString("\n") } + def generateVerificationReport( + appId: AppId, + comparision: ScreenshotsComparisionResult, + shotFolder: ShotFolder, + showOnlyFailingTestsInReports: Boolean = false + ) = { + val reportFileContents = + populateVerificationTemplate(appId, comparision, showOnlyFailingTestsInReports) + resetVerificationReport(shotFolder) + val reportFolder = shotFolder.verificationReportFolder() + writeReport(reportFileContents, reportFolder) + } + private def populateVerificationTemplate( appId: AppId, comparision: ScreenshotsComparisionResult, diff --git a/core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala b/core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala index eb9abee7..5677f6b9 100644 --- a/core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala +++ b/core/src/main/scala/com/karumi/shot/reports/JunitExecutionReporter.scala @@ -1,26 +1,25 @@ package com.karumi.shot.reports -import com.karumi.shot.domain.{DifferentImageDimensions, DifferentScreenshots, Screenshot, ScreenshotComparisonError, ScreenshotNotFound, ScreenshotsComparisionResult, ShotFolder} +import com.karumi.shot.domain._ import com.karumi.shot.domain.model.{AppId, ScreenshotComparisionErrors, ScreenshotsSuite} import java.io.{File, FileWriter} -import scala.collection.IterableOnce.iterableOnceExtensionMethods import scala.language.postfixOps class JunitExecutionReporter extends ExecutionReporter { def generateRecordReport( - appId: AppId, - screenshots: ScreenshotsSuite, - shotFolder: ShotFolder - ): Unit = () + appId: AppId, + screenshots: ScreenshotsSuite, + shotFolder: ShotFolder + ): Unit = () def generateVerificationReport( - appId: AppId, - comparision: ScreenshotsComparisionResult, - shotFolder: ShotFolder, - showOnlyFailingTestsInReports: Boolean = false - ): Unit = { + appId: AppId, + comparision: ScreenshotsComparisionResult, + shotFolder: ShotFolder, + showOnlyFailingTestsInReports: Boolean = false + ): Unit = { val reportFileContents = populateVerificationTemplate(appId, comparision) resetVerificationReport(shotFolder) @@ -29,9 +28,9 @@ class JunitExecutionReporter extends ExecutionReporter { } private def writeReport( - fileContents: String, - reportFolder: String - ): Unit = { + fileContents: String, + reportFolder: String + ): Unit = { val indexFile = new File(reportFolder + "TEST-Shot.xml") new File(reportFolder).mkdirs() val writer = new FileWriter(indexFile) @@ -47,9 +46,9 @@ class JunitExecutionReporter extends ExecutionReporter { } private def populateVerificationTemplate( - appId: AppId, - comparision: ScreenshotsComparisionResult - ): String = { + appId: AppId, + comparision: ScreenshotsComparisionResult + ): String = { val title = s"Screenshot results: $appId" val summaryTableBody = generateVerificationSummaryTableBody(comparision) @@ -67,52 +66,53 @@ class JunitExecutionReporter extends ExecutionReporter { } private def generateVerificationSummaryTableBody( - comparisionResult: ScreenshotsComparisionResult - - ): String = { + comparisionResult: ScreenshotsComparisionResult + ): String = { val groupedScreenshots = - comparisionResult - .screenshots - .groupBy { (screenshot: Screenshot) => - screenshot.testClass - } + comparisionResult.screenshots + .groupBy { (screenshot: Screenshot) => + screenshot.testClass + } groupedScreenshots .map { case (testSuite: String, screenshots: Seq[Screenshot]) => - val tests = screenshots - .map((screenshot) => { - val error = findError(screenshot = screenshot, comparisionResult.errors) + val tests: String = screenshots + .map(f = screenshot => { + val error = + findError(screenshot = screenshot, comparisionResult.errors) val isFailedTest = error.isDefined - val testClass = screenshot.testClass - val testName = screenshot.fileName - val reason = generateReasonMessage(error) + val testClass = screenshot.testClass + val testName = screenshot.fileName + val reason = generateReasonMessage(error) val failureString = if (isFailedTest) { - s"""""" + s"""""" } else { "" } s""" - | $failureString - |""".stripMargin - }).mkString("\n") + | $failureString + |""".stripMargin + }) + .mkString("\n") - s""" - | ${tests} + s""" + | $tests | |""".stripMargin - }.mkString("\n") + } + .mkString("\n") } private def findError( - screenshot: Screenshot, - errors: ScreenshotComparisionErrors - ): Option[ScreenshotComparisonError] = + screenshot: Screenshot, + errors: ScreenshotComparisionErrors + ): Option[ScreenshotComparisonError] = errors.find { - case ScreenshotNotFound(error) => screenshot == error + case ScreenshotNotFound(error) => screenshot == error case DifferentImageDimensions(error, _, _) => screenshot == error - case DifferentScreenshots(error, _) => screenshot == error - case _ => false + case DifferentScreenshots(error, _) => screenshot == error + case _ => false } private def generateReasonMessage(error: Option[ScreenshotComparisonError]): String = diff --git a/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsDiffGenerator.scala b/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsDiffGenerator.scala index 5e7b0a99..248f45be 100644 --- a/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsDiffGenerator.scala +++ b/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsDiffGenerator.scala @@ -1,6 +1,5 @@ package com.karumi.shot.screenshots -import java.io.File import com.karumi.shot.base64.Base64Encoder import com.karumi.shot.domain.model.ScreenshotComparisionErrors import com.karumi.shot.domain.{DifferentScreenshots, ScreenshotsComparisionResult} @@ -9,6 +8,7 @@ import com.sksamuel.scrimage.composite.RedComposite import com.sksamuel.scrimage.nio.PngWriter import java.awt.image.BufferedImage +import java.io.File import scala.collection.parallel.CollectionConverters._ class ScreenshotsDiffGenerator(base64Encoder: Base64Encoder) { @@ -40,7 +40,10 @@ class ScreenshotsDiffGenerator(base64Encoder: Base64Encoder) { .fromFile(new File(originalImagePath)) .copy(BufferedImage.TYPE_INT_ARGB) val newImage = - ImmutableImage.loader().fromFile(new File(newImagePath)).copy(BufferedImage.TYPE_INT_ARGB) + ImmutableImage + .loader() + .fromFile(new File(newImagePath)) + .copy(BufferedImage.TYPE_INT_ARGB) val diff = newImage.composite(new RedComposite(1d), originalImage) val outputFilePath = screenshot.getDiffScreenshotPath(outputFolder) diff.output(PngWriter.MaxCompression, outputFilePath) diff --git a/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsSaver.scala b/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsSaver.scala index 550416af..f789f991 100644 --- a/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsSaver.scala +++ b/core/src/main/scala/com/karumi/shot/screenshots/ScreenshotsSaver.scala @@ -1,22 +1,45 @@ package com.karumi.shot.screenshots -import java.io.File -import com.karumi.shot.domain.{Dimension, Screenshot, ScreenshotsComparisionResult, ShotFolder} import com.karumi.shot.domain.model.{FilePath, Folder, ScreenshotsSuite} +import com.karumi.shot.domain.{Dimension, Screenshot, ScreenshotsComparisionResult, ShotFolder} import com.sksamuel.scrimage.ImmutableImage import com.sksamuel.scrimage.nio.PngWriter import org.apache.commons.io.FileUtils +import java.io.File + class ScreenshotsSaver { def saveRecordedScreenshots( to: FilePath, screenshots: ScreenshotsSuite - ) = { + ): Unit = { deleteFile(to) saveScreenshots(screenshots, to) } + private def saveScreenshots(screenshots: ScreenshotsSuite, folder: Folder) = { + val screenshotsFolder = new File(folder) + if (!screenshotsFolder.exists()) { + screenshotsFolder.mkdirs() + } + screenshots.foreach { screenshot => + val outputFile = new File(folder + screenshot.fileName) + if (!outputFile.exists()) { + outputFile.createNewFile() + } + val image = ScreenshotComposer.composeNewScreenshot(screenshot) + image.output(PngWriter.MaxCompression, outputFile) + } + } + + private def deleteFile(path: String): Unit = { + val folder = new File(path) + if (folder.exists()) { + folder.delete() + } + } + def saveTemporalScreenshots( screenshots: ScreenshotsSuite, projectName: String, @@ -29,6 +52,10 @@ class ScreenshotsSaver { saveScreenshots(screenshots, reportFolder) } + private def deleteOldTemporalScreenshots(projectName: String, shotFolder: ShotFolder): Unit = { + deleteFile(shotFolder.screenshotsTemporalBuildPath() + "/") + } + def copyRecordedScreenshotsToTheReportFolder( from: FilePath, to: FilePath @@ -45,6 +72,11 @@ class ScreenshotsSaver { deleteFile(destinyFolder) } + private def copyFile(screenshot: Screenshot, destinyFolder: Folder): Unit = { + val existingScreenshot = new File(screenshot.recordedScreenshotPath) + FileUtils.copyFile(existingScreenshot, new File(destinyFolder + existingScreenshot.getName)) + } + def removeNonFailingReferenceImages( verificationReferenceImagesFolder: Folder, screenshotsResult: ScreenshotsComparisionResult @@ -53,44 +85,14 @@ class ScreenshotsSaver { deleteFile(verificationReferenceImagesFolder + screenshot.fileName) ) - private def copyFile(screenshot: Screenshot, destinyFolder: Folder): Unit = { - val existingScreenshot = new File(screenshot.recordedScreenshotPath) - FileUtils.copyFile(existingScreenshot, new File(destinyFolder + existingScreenshot.getName)) - } - def getScreenshotDimension( shotFolder: ShotFolder, screenshot: Screenshot ): Dimension = { - val screenshotPath = shotFolder.pulledScreenshotsFolder() + screenshot.name + ".png" - val image = ImmutableImage.loader().fromFile(new File(screenshotPath)) + val screenshotPath = shotFolder + .pulledScreenshotsFolder() + screenshot.name + ".png" + val image = ImmutableImage.loader().fromFile(new File(screenshotPath)) Dimension(image.width, image.height) } - private def deleteOldTemporalScreenshots(projectName: String, shotFolder: ShotFolder): Unit = { - deleteFile(shotFolder.screenshotsTemporalBuildPath() + "/") - } - - private def deleteFile(path: String): Unit = { - val folder = new File(path) - if (folder.exists()) { - folder.delete() - } - } - - private def saveScreenshots(screenshots: ScreenshotsSuite, folder: Folder) = { - val screenshotsFolder = new File(folder) - if (!screenshotsFolder.exists()) { - screenshotsFolder.mkdirs() - } - screenshots.foreach { screenshot => - val outputFile = new File(folder + screenshot.fileName) - if (!outputFile.exists()) { - outputFile.createNewFile() - } - val image = ScreenshotComposer.composeNewScreenshot(screenshot) - image.output(PngWriter.MaxCompression, outputFile) - } - } - } diff --git a/gradle.properties b/gradle.properties index 149de2cd..3e689f53 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=6.1.0 +VERSION_NAME=6.0.0 VERSION_CODE=600000 GROUP=com.karumi POM_DESCRIPTION=Gradle plugin developed to facilitate screenshot testing for Android.