Skip to content

Commit

Permalink
Improved HTML export in tests (#706)
Browse files Browse the repository at this point in the history
  • Loading branch information
javipacheco authored Mar 27, 2024
1 parent 7a7093d commit e8a62e9
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ package com.xebia.functional.xef.evaluator

import com.xebia.functional.openai.models.CreateChatCompletionRequestModel
import com.xebia.functional.xef.AI
import com.xebia.functional.xef.evaluator.errors.FileNotFound
import com.xebia.functional.xef.evaluator.models.ItemResult
import com.xebia.functional.xef.evaluator.models.OutputResponse
import com.xebia.functional.xef.evaluator.models.OutputResult
import com.xebia.functional.xef.evaluator.models.SuiteResults
import com.xebia.functional.xef.evaluator.output.Html
import java.io.File
import kotlin.jvm.JvmSynthetic
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer

class SuiteBuilder(
private val description: String,
Expand Down Expand Up @@ -57,7 +57,6 @@ data class SuiteSpec(
ItemResult(item.input, outputResults)
}
val suiteResults = SuiteResults(description, model.value, E::class.simpleName, items)
export(Json.encodeToString(suiteResults))
return suiteResults
}

Expand All @@ -68,39 +67,18 @@ data class SuiteSpec(
model: CreateChatCompletionRequestModel,
block: suspend SuiteBuilder.() -> Unit
): SuiteSpec = SuiteBuilder(description, model).apply { block() }.build()
}

fun export(content: String): Boolean {
return arrow.core.raise.recover({
// Read the content of `index.html` inside resources folder
val indexHTML =
SuiteSpec::class.java.getResource("/web/index.html")?.readText()
?: raise(FileNotFound("index.html"))
val scriptJS =
SuiteSpec::class.java.getResource("/web/script.js")?.readText()
?: raise(FileNotFound("script.js"))
val styleCSS =
SuiteSpec::class.java.getResource("/web/style.css")?.readText()
?: raise(FileNotFound("style.css"))
val contentJS = "const testData = $content;"

// Copy all the files inside build folder
inline fun <reified E> toHtml(
result: SuiteResults<E>,
htmlFilename: String = "index.html"
) where E : AI.PromptClassifier, E : Enum<E> {
val content = Json.encodeToString(SuiteResults.serializer(serializer<E>()), result)
// Copy file inside build folder
val outputPath = System.getProperty("user.dir") + "/build/testSuite"
File(outputPath).mkdirs()
File("$outputPath/index.html").writeText(indexHTML)
File("$outputPath/script.js").writeText(scriptJS)
File("$outputPath/style.css").writeText(styleCSS)
File("$outputPath/content.js").writeText(contentJS)
val url = File("$outputPath/index.html").toURI()
println("Test suite exported to $url")
true
}) {
when (it) {
else -> {
println(it.message("File not found"))
false
}
}
val htmlFile = File("$outputPath/$htmlFilename")
htmlFile.writeText(Html.get(content))
println("Test suite exported to ${htmlFile.absoluteFile}")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package com.xebia.functional.xef.evaluator.output

class Html {

companion object {

// language=javascript
private val jsContent =
"""
document.addEventListener('DOMContentLoaded', function() {
const container = document.getElementById('test-container');
const headerDiv = document.createElement('div');
headerDiv.classList.add('test-block');
const header = document.createElement('h1');
header.classList.add('test-header');
header.textContent = "Suite test";
const suiteDescription = document.createElement('p');
suiteDescription.textContent = 'Description: ' + testData.description;
const model = document.createElement('p');
model.textContent = 'Model: ' + testData.model;
const metric = document.createElement('p');
metric.textContent = 'Metric: ' + testData.metric;
headerDiv.appendChild(header);
headerDiv.appendChild(suiteDescription);
headerDiv.appendChild(model);
headerDiv.appendChild(metric);
container.appendChild(headerDiv);
testData.items.forEach(block => {
const blockDiv = document.createElement('div');
blockDiv.classList.add('test-block');
const title = document.createElement('h2');
title.classList.add('test-title');
title.textContent = 'Input: ' + block.description;
blockDiv.appendChild(title);
block.items.forEach(test => {
const itemDescription = document.createElement('div');
itemDescription.textContent = 'Description: ' + test.description;
blockDiv.appendChild(itemDescription);
const context = document.createElement('div');
context.textContent = 'Context: ' + test.contextDescription;
blockDiv.appendChild(context);
const outputDiv = document.createElement('pre');
outputDiv.classList.add('output');
outputDiv.innerText = 'Output: ' + test.output;
outputDiv.addEventListener('click', function() {
this.classList.toggle('expanded');
});
blockDiv.appendChild(outputDiv);
const result = document.createElement('div');
result.classList.add('score', test.success ? 'score-passed' : 'score-failed');
result.textContent = 'Result: ' + test.result;
blockDiv.appendChild(result);
blockDiv.appendChild(document.createElement('br'));
});
container.appendChild(blockDiv);
});
});
"""
.trimIndent()

// language=css
private val cssContent =
"""
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
#test-container {
width: 80%;
margin: 20px auto;
padding: 15px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.test-block {
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
}
.test-title {
font-size: 1.2em;
color: #333;
}
.input, .output {
margin: 5px 0;
}
.input-passed {
margin-top: 25px;
color: green;
font-weight: bold;
}
.input-failed {
margin-top: 25px;
color: red;
font-weight: bold;
}
.output {
color: #666;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.output.expanded {
white-space: normal;
}
.score {
font-weight: bold;
}
.score-passed {
margin-bottom: 25px;
color: #008000;
}
.score-failed {
margin-bottom: 25px;
color: red;
}
.avg-score, .test-info {
font-size: 1.2em;
color: #d35400;
margin-top: 10px;
}
.test-summary {
background-color: #e7e7e7;
padding: 15px;
margin-top: 20px;
border-radius: 8px;
}
.test-summary h3 {
font-size: 1.1em;
color: #555;
margin-top: 0;
}
"""
.trimIndent()

fun get(contentJson: String): String {
// language=html
return """
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Tests</title>
<style>$cssContent</style>
<script>
$jsContent
const testData = $contentJson;
</script>
</head>
<body>
<div id="test-container"></div>
</body>
</html>
"""
.trimIndent()
}
}
}

0 comments on commit e8a62e9

Please sign in to comment.