From 9b1abf7f0245be3e471cd17f9b0d42935690eecf Mon Sep 17 00:00:00 2001 From: Samih Mustafa Date: Tue, 10 Dec 2024 17:25:04 +0400 Subject: [PATCH 1/2] create manifest file --- .../zim2xapi/AddxAPIStatementUseCase.kt | 3 +- .../kotlin/com/ustadmobile/zim2xapi/App.kt | 2 +- .../zim2xapi/CreateManifestFileUseCase.kt | 43 ++++++++++++ .../zim2xapi/CreateXapiFileUseCase.kt | 2 + .../zim2xapi/CreateManifestUseCaseTest.kt | 65 +++++++++++++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt create mode 100644 app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt diff --git a/app/src/main/kotlin/com/ustadmobile/zim2xapi/AddxAPIStatementUseCase.kt b/app/src/main/kotlin/com/ustadmobile/zim2xapi/AddxAPIStatementUseCase.kt index 395f553..f7bd410 100644 --- a/app/src/main/kotlin/com/ustadmobile/zim2xapi/AddxAPIStatementUseCase.kt +++ b/app/src/main/kotlin/com/ustadmobile/zim2xapi/AddxAPIStatementUseCase.kt @@ -1,5 +1,6 @@ package com.ustadmobile.zim2xapi +import com.ustadmobile.zim2xapi.utils.FileConstants import org.jsoup.Jsoup import java.io.File @@ -27,7 +28,7 @@ class AddxAPIStatementUseCase { // if doesn't exist, presume not a khan exercise if (!perseusFile.exists()) return - val indexFile = File(zimFolder, "index.html") + val indexFile = File(zimFolder, FileConstants.INDEX_HTML_FILE) val indexDoc = Jsoup.parse(indexFile) val scoreScript = indexDoc.createElement("script") diff --git a/app/src/main/kotlin/com/ustadmobile/zim2xapi/App.kt b/app/src/main/kotlin/com/ustadmobile/zim2xapi/App.kt index 40d4a32..f5974ea 100644 --- a/app/src/main/kotlin/com/ustadmobile/zim2xapi/App.kt +++ b/app/src/main/kotlin/com/ustadmobile/zim2xapi/App.kt @@ -167,7 +167,7 @@ class DownloadTopic : CliktCommand(name = "convert") { ShrinkXapiUseCase().invoke(extractedZimFolder) // create the xApi zip file - val xapiFile = CreateXapiFileUseCase(zimDumpProcess, AddxAPIStatementUseCase(), json).invoke( + val xapiFile = CreateXapiFileUseCase(zimDumpProcess, AddxAPIStatementUseCase(), CreateManifestFileUseCase(), json).invoke( extractedZimFolder, outputDir, fileName, diff --git a/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt b/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt new file mode 100644 index 0000000..0b26fd6 --- /dev/null +++ b/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt @@ -0,0 +1,43 @@ +package com.ustadmobile.zim2xapi + +import com.ustadmobile.zim2xapi.utils.FileConstants +import org.jsoup.Jsoup +import java.io.File + +class CreateManifestFileUseCase { + + operator fun invoke( + zimFolder: File + ): File { + + val manifestFile = File(zimFolder, MANIFEST_FILE) + + manifestFile.bufferedWriter().use { writer -> + zimFolder.walkTopDown().filter { + it.isFile + }.forEach { file -> + writer.write(file.relativeTo(zimFolder).path) + writer.newLine() + } + } + + val indexHtmlFile = File(zimFolder, FileConstants.INDEX_HTML_FILE) + if (indexHtmlFile.exists()) { + val indexDoc = Jsoup.parse(indexHtmlFile) + val linkTag = indexDoc.createElement("link") + linkTag.attr("rel", "manifest") + linkTag.attr("href", MANIFEST_FILE) + } + + + return manifestFile + } + + companion object { + + const val MANIFEST_FILE = "manifest.txt" + + } + + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCase.kt b/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCase.kt index 010cf14..beea950 100644 --- a/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCase.kt +++ b/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCase.kt @@ -15,6 +15,7 @@ import java.util.zip.ZipOutputStream class CreateXapiFileUseCase( private val zimDumpProcess: ProcessBuilderUseCase, private val addXApi: AddxAPIStatementUseCase, + private val createManifestFileUseCase: CreateManifestFileUseCase, private val json: Json ) { @@ -70,6 +71,7 @@ class CreateXapiFileUseCase( ) addXApi.invoke(zimFolder, passingGrade) + createManifestFileUseCase.invoke(zimFolder) val xapiFile = File(outputFolder, "$fileName.zip") ZipOutputStream(FileOutputStream(xapiFile)).use { zipOut -> diff --git a/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt b/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt new file mode 100644 index 0000000..c58596c --- /dev/null +++ b/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt @@ -0,0 +1,65 @@ +package com.ustadmobile.zim2xapi + +import org.junit.Before +import org.junit.Test +import java.io.File +import kotlin.io.path.createTempDirectory +import kotlin.test.assertTrue + +class CreateManifestUseCaseTest { + + private val createManifestUseCase = CreateManifestFileUseCase() + + private lateinit var zimFolder: File + + @Before + fun setup(){ + zimFolder = createTemporaryFolder("zimFolder") + } + + + @Test + fun `test generate manifest`() { + val assetsFolder = File(zimFolder, "assets") + assetsFolder.mkdirs() + + val videoFolder = File(assetsFolder, "video") + videoFolder.mkdirs() + + File(assetsFolder, "script.js").writeText("console.log('Hello World')") + File(videoFolder, "style.css").writeText("body { color: red; }") + File(zimFolder, "index.html").writeText("") + + + val manifestFile = createManifestUseCase(zimFolder) + + assertTrue(manifestFile.exists()) + + val expectedPaths = listOf( + "assets/script.js", + "assets/video/style.css", + "index.html" + ) + + val actualLines = manifestFile.readLines() + + assertTrue(actualLines.containsAll(expectedPaths)) + + } + + + + + + + + + + private fun createTemporaryFolder(name: String): File { + return createTempDirectory(name).toFile() + } + + + + +} \ No newline at end of file From fd6e9bf9fc889215be214527e7362f970947b15d Mon Sep 17 00:00:00 2001 From: Samih Mustafa Date: Tue, 10 Dec 2024 17:41:10 +0400 Subject: [PATCH 2/2] fix tests, manifest wasnt added to indexhtml --- .../zim2xapi/CreateManifestFileUseCase.kt | 2 ++ .../zim2xapi/CreateManifestUseCaseTest.kt | 28 +++++++++++++------ .../zim2xapi/CreateXapiFileUseCaseTest.kt | 3 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt b/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt index 0b26fd6..46968b2 100644 --- a/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt +++ b/app/src/main/kotlin/com/ustadmobile/zim2xapi/CreateManifestFileUseCase.kt @@ -27,6 +27,8 @@ class CreateManifestFileUseCase { val linkTag = indexDoc.createElement("link") linkTag.attr("rel", "manifest") linkTag.attr("href", MANIFEST_FILE) + indexDoc.head().appendChild(linkTag) + indexHtmlFile.writeText(indexDoc.html()) } diff --git a/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt b/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt index c58596c..8ff8fe8 100644 --- a/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt +++ b/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateManifestUseCaseTest.kt @@ -1,5 +1,6 @@ package com.ustadmobile.zim2xapi +import org.jsoup.Jsoup import org.junit.Before import org.junit.Test import java.io.File @@ -28,7 +29,19 @@ class CreateManifestUseCaseTest { File(assetsFolder, "script.js").writeText("console.log('Hello World')") File(videoFolder, "style.css").writeText("body { color: red; }") - File(zimFolder, "index.html").writeText("") + + val indexHtmlFile = File(zimFolder, "index.html") + indexHtmlFile.writeText(""" + + + + My Web App + + +

Welcome to my Web App

+ + + """.trimIndent()) val manifestFile = createManifestUseCase(zimFolder) @@ -45,16 +58,13 @@ class CreateManifestUseCaseTest { assertTrue(actualLines.containsAll(expectedPaths)) - } - - - - - - - + val updatedDoc = Jsoup.parse(indexHtmlFile, "UTF-8") + val linkElement = updatedDoc.select("link[rel=manifest]").first() + assertTrue(linkElement != null, "Manifest link tag should exist") + assertTrue(linkElement.attr("href") == "manifest.txt", "Manifest link href should be manifest.txt") + } private fun createTemporaryFolder(name: String): File { return createTempDirectory(name).toFile() } diff --git a/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCaseTest.kt b/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCaseTest.kt index 9752b0f..caf82d9 100644 --- a/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCaseTest.kt +++ b/app/src/test/kotlin/com/ustadmobile/zim2xapi/CreateXapiFileUseCaseTest.kt @@ -14,7 +14,8 @@ class CreateXapiFileUseCaseTest { private val zimDumpProcess = mockk(relaxed = true) private val xapiStatement = AddxAPIStatementUseCase() - private val createXapiFileUseCase = CreateXapiFileUseCase(zimDumpProcess, xapiStatement, Json) + private val createManifestFileUseCase = CreateManifestFileUseCase() + private val createXapiFileUseCase = CreateXapiFileUseCase(zimDumpProcess, xapiStatement, createManifestFileUseCase, Json) @Test fun `invoke should create tincan xml and zip file`() {