Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read external resources locally when generating html report #404

Merged
merged 14 commits into from
Jun 18, 2024
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ ktor-serialization-kotlinx-xml = "2.3.11"
kotlinx-serialization = "1.6.3"
squareup-okhttp = "5.0.0-alpha.11"
kotlinx-io = "0.3.3"
webjar-material-design-icons = "4.0.0"
webjar-materialize = "1.0.0"
webjars-locator-lite = "0.0.4"

[libraries]
roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi-for-replacing-by-include-build" }
Expand Down Expand Up @@ -88,3 +91,6 @@ squareup-okhttp-coroutines = { module = "com.squareup.okhttp3:okhttp-coroutines"
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
robolectric-android-all = { module = "org.robolectric:android-all", version.ref = "robolectric-android-all" }
kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" }
webjars-material-design-icons = { module = "org.webjars:material-design-icons", version.ref = "webjar-material-design-icons" }
webjars-materialize = { module = "org.webjars:materializecss", version.ref = "webjar-materialize" }
webjars-locator-lite = { module = "org.webjars:webjars-locator-lite", version.ref = "webjars-locator-lite" }
Original file line number Diff line number Diff line change
Expand Up @@ -43,114 +43,4 @@ object RoborazziReportConst {
}
}
}

const val reportHtml = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Roborazzi report</title>
<!-- Compiled and minified CSS -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@materializecss/materialize/dist/css/materialize.min.css"
/>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<style>
.container {
width: 90%;
}

h3 {
color: orange;
}

a, .menu {
color: white;
}

th a, td a {
display: block;
color: black;
}

.material-icons {
color: #29b6f6;
}

.us {
color: #ffcc80;
}

#imageBottomSheet {
max-height: 100%;
top: 15%;
}

#modalImage {
max-width: 100%;
}
</style>
</head>
<body>
<nav role="navigation" class="light-blue lighten-1">
<div class="nav-wrapper container">
<a href="#" class="brand-logo">Roborazzi report</a>
<a href="#" data-target="nav-mobile" class="sidenav-trigger"
><i class="material-icons menu">menu</i></a
>
</div>
</nav>
<div class="section">
<div class="container">
<br><br>
REPORT_TEMPLATE_BODY
<br><br>
</div>
</div>

<div id="imageBottomSheet" class="modal bottom-sheet max-height">
<div class="modal-content center-align">
<img id="modalImage" src="" alt="">
</div>
</div>

<footer class="page-footer orange">
<div class="container">
<a class="us" href="https://github.com/takahirom/roborazzi" target="_blank"
rel="noopener noreferrer">Roborazzi</a>
<br>
<br>
</div>
</footer>
<!-- Compiled and minified JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize/dist/js/materialize.min.js"></script>
<script>
M.AutoInit();
document.addEventListener('DOMContentLoaded', function() {
M.Tabs.getInstance(document.getElementsByClassName("tabs")[0]).select('results');
var modalInstance = M.Modal.init(document.getElementById('imageBottomSheet'), {});
var modal = document.getElementById('imageBottomSheet');
var modalImage = document.getElementById('modalImage');
var modalTriggers = document.querySelectorAll('.modal-trigger');
modalTriggers.forEach(function(trigger) {
trigger.addEventListener('click', function() {
var src = this.getAttribute('src');
var alt = this.getAttribute('data-alt');
modalImage.setAttribute('src', src);
modalImage.setAttribute('alt', alt);
var instance = M.Modal.getInstance(modal);
instance.open();
});
});
});
</script>
</body>
</html>
"""
}
3 changes: 3 additions & 0 deletions include-build/roborazzi-gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ dependencies {
exclude group: 'junit', module: 'junit'
}
implementation libs.kotlinx.serialization.json
implementation libs.webjars.locator.lite
implementation libs.webjars.materialize
implementation libs.webjars.material.design.icons
integrationTestDepImplementation libs.android.tools.build.gradle
integrationTestDepImplementation libs.kotlin.gradle.plugin
integrationTestDepImplementation libs.compose.gradle.plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.targets.native.KotlinNativeBinaryTestRun
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
import org.webjars.WebJarVersionLocator
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import org.webjars.WebJarVersionLocator

import java.io.FileNotFoundException
import java.util.Locale
import javax.inject.Inject
import kotlin.reflect.KClass
Expand All @@ -50,6 +52,9 @@ open class RoborazziExtension @Inject constructor(objects: ObjectFactory) {
@Suppress("unused")
// From Paparazzi: https://github.com/cashapp/paparazzi/blob/a76702744a7f380480f323ffda124e845f2733aa/paparazzi/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt
abstract class RoborazziPlugin : Plugin<Project> {

private val webJarVersionLocator = WebJarVersionLocator()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this can be removed now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It can be removed now. Thanks for catching it


Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private val webJarVersionLocator = WebJarVersionLocator()

val AbstractTestTask.systemProperties: MutableMap<String, Any?>
get() = when (this) {
is Test -> systemProperties
Expand Down Expand Up @@ -255,9 +260,12 @@ abstract class RoborazziPlugin : Plugin<Project> {
resultsSummaryFile.parentFile.mkdirs()
resultsSummaryFile.writeText(jsonResult)
val reportFile = reportFileProperty.get().asFile

reportFile.parentFile.mkdirs()
WebAssets.create().writeWebAssets(reportFile.parentFile)
val reportHtml = readIndexHtmlFile() ?: throw FileNotFoundException("index.html not found in resources")
reportFile.writeText(
RoborazziReportConst.reportHtml.replace(
reportHtml.replace(
oldValue = "REPORT_TEMPLATE_BODY",
newValue = roborazziResults.toHtml(reportFile.parentFile.absolutePath)
)
Expand Down Expand Up @@ -464,6 +472,10 @@ abstract class RoborazziPlugin : Plugin<Project> {
}
}

private fun readIndexHtmlFile(): String? {
return this::class.java.classLoader.getResource("META-INF/index.html")?.readText()
}

private fun String.capitalizeUS() =
replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.github.takahirom.roborazzi

import org.webjars.WebJarVersionLocator
import java.io.File

class WebAssets private constructor(private val webJarVersionLocator: WebJarVersionLocator) {
Copy link
Owner

@takahirom takahirom Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. The CSS from the webjar packing seems a little old. The styling is a little off from main Looking to see if there is a recently one published with webjar.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed it, but it's a subtle change. So I think it is ok

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, however not all the tabs shows. That I think needs to be fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved it with 9864d9d. All the tabs shows now though we lost the fixed sized tabs. I believe it should be okay?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can now scroll through the tabs! Thank you!
image

private val materializeCss = "materializecss"
private val materialIcons = "material-design-icons"
private val webJarResource = "resources"

fun writeWebAssets(reportDir: File) {
writeLocalAssetsToRoborazziReportsDir(reportDir)
writeWebJarAssetsToRoborazziReportsDir(reportDir)
}

private fun writeLocalAssetsToRoborazziReportsDir(reportDir: File) {
writeAssets(
assetName = "report-style.css",
exactPath = "assets/report-style.css",
reportDir = reportDir
)
}

private fun writeWebJarAssetsToRoborazziReportsDir(reportDir: File) {
mapOf(
materializeCss to listOf(
"css/materialize.min.css",
"js/materialize.min.js",
),
materialIcons to listOf(
"material-icons.css",
"MaterialIcons-Regular.ttf",
)
).forEach { (key, value) ->
value.forEach { exactPath ->
webJarVersionLocator.locate(key, exactPath)?.let {
writeAssets(
assetName = exactPath.substringAfterLast("/"),
exactPath = "$webJarResource/$it",
reportDir = reportDir
)
}
}
}
}

private fun outputFile(directory: File, filename: String): File {
return File(directory, filename).apply {
parentFile.apply { if (!exists()) mkdirs() }
}
}

private fun WebJarVersionLocator.locate(webJarName: String, exactPath: String) = run {
path(webJarName, exactPath)?.let { "webjars/$it" }
}

private fun writeAssets(
assetName: String,
exactPath: String,
reportDir: File
) {
val assetsDirectory = File(reportDir, "assets")
val asset = this::class.java
.classLoader
.getResource("META-INF/$exactPath")?.readBytes()
if (asset != null) {
val assetFile = outputFile(assetsDirectory, assetName)
assetFile.writeBytes(asset)
}
}

companion object {

fun create(
webJarVersionLocator: WebJarVersionLocator = WebJarVersionLocator()
): WebAssets = WebAssets(webJarVersionLocator)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.container {
width: 90%;
}

h3 {
color: orange;
}

a, .menu {
color: white;
}

th a, td a {
display: block;
color: black;
}

.material-icons {
color: #29b6f6;
}

.us {
color: #ffcc80;
}

#imageBottomSheet {
max-height: 100%;
top: 15%;
}

#modalImage {
max-width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Roborazzi report</title>
<!-- Compiled and minified CSS -->
<link href="assets/materialize.min.css" rel="stylesheet" />
<link href="assets/material-icons.css" rel="stylesheet" />

<link href="assets/report-style.css" rel="stylesheet"/>
</head>
<body>
<nav role="navigation" class="light-blue lighten-1">
<div class="nav-wrapper container">
<a href="#" class="brand-logo">Roborazzi report</a>
<a href="#" data-target="nav-mobile" class="sidenav-trigger">
<i class="material-icons menu">menu</i>
</a>
</div>
</nav>
<div class="section">
<div class="container">
<p>REPORT_TEMPLATE_BODY</p>
</div>
</div>

<div id="imageBottomSheet" class="modal bottom-sheet max-height">
<div class="modal-content center-align">
<img id="modalImage" src="" alt="">
</div>
</div>

<footer class="page-footer orange">
<div class="container">
<a class="us" href="https://github.com/takahirom/roborazzi" target="_blank"
rel="noopener noreferrer">Roborazzi</a>
<br>
<br>
</div>
</footer>
<!-- Compiled and minified JavaScript -->
<script src="assets/materialize.min.js"></script>
<script>
M.AutoInit();
document.addEventListener('DOMContentLoaded', function() {
M.Tabs.getInstance(document.getElementsByClassName("tabs")[0]).select('results');
var modalInstance = M.Modal.init(document.getElementById('imageBottomSheet'), {});
var modal = document.getElementById('imageBottomSheet');
var modalImage = document.getElementById('modalImage');
var modalTriggers = document.querySelectorAll('.modal-trigger');
modalTriggers.forEach(function(trigger) {
trigger.addEventListener('click', function() {
var src = this.getAttribute('src');
var alt = this.getAttribute('data-alt');
modalImage.setAttribute('src', src);
modalImage.setAttribute('alt', alt);
var instance = M.Modal.getInstance(modal);
instance.open();
});
});
});
</script>
</body>
</html>
Loading