Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/palette.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
## 2024-05-24 - Accessibility improvements to directory index files
**Learning:** Automatically generated directory indexes need standard web accessibility features like meta viewport tags, proper heading structures, ARIA roles, and lang attributes to be usable by screen readers and mobile devices.
**Action:** Add basic a11y scaffolding to generated HTML files including `lang`, `meta viewport`, skip links (if applicable, though simple here), and aria labels.
8 changes: 8 additions & 0 deletions build.gradle
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,14 @@ apply plugin: 'jacoco'

mainClassName = 'html4tree.MainKt'

jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.enabled true
}
}

defaultTasks 'build'

repositories {
Expand Down
35 changes: 35 additions & 0 deletions src/test/kotlin/html4tree/BranchTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
package html4tree

import org.junit.Test
import kotlin.test.assertTrue
import java.io.File

class BranchTest {
@Test
fun testProcessIgnoreFileNoFiles() {
// hit branch where directory is empty and doesn't match any ignored files
val tempDir = createTempDir("emptyDir")
try {
File(tempDir, ".html4ignore").writeText(".*\\.txt")
// dir is empty otherwise
val excluded = process_ignore_file(tempDir)
assertTrue(excluded.contains("index.html"))
} finally {
tempDir.deleteRecursively()
}
}

@Test
fun testGoWithFilesNoDirs() {
val tempDir = createTempDir("filesOnlyDir")
try {
File(tempDir, "file1.txt").writeText("test")
File(tempDir, "file2.txt").writeText("test")
// go will loop but find no subdirectories to push to LinkedList
go(tempDir.absolutePath, -1)
assertTrue(File(tempDir, "index.html").exists())
} finally {
tempDir.deleteRecursively()
}
}
}
28 changes: 28 additions & 0 deletions src/test/kotlin/html4tree/BranchTest2.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
package html4tree

import org.junit.Test
import kotlin.test.assertTrue
import kotlin.test.assertFalse
import java.io.File

class BranchTest2 {
@Test
fun testIndexMiddleBranch() {
val tempDir = createTempDir("testIndexMiddle")
try {
// Create .html4ignore explicitly excluding index.html to see branch behavior
File(tempDir, ".html4ignore").writeText("index\\.html")
File(tempDir, "regular_file.txt").writeText("test")
val subDir = File(tempDir, "subDir")
subDir.mkdir()

process_dir(tempDir)

val indexContent = File(tempDir, "index.html").readText()
assertTrue(indexContent.contains("regular_file.txt"))
assertTrue(indexContent.contains("subDir"))
} finally {
tempDir.deleteRecursively()
}
}
}
29 changes: 29 additions & 0 deletions src/test/kotlin/html4tree/BranchTest3.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
package html4tree

import org.junit.Test
import kotlin.test.assertTrue
import kotlin.test.assertFalse
import java.io.File

class BranchTest3 {
@Test
fun testProcessIgnoreEmptyDirectory() {
// Test go with empty directory. The while loop condition `lle != null && lle.file.isDirectory()`
// should skip if `lle` is null or it's a file, but since we always push a dir, it's about hitting
// the loop end where we pull and get null.
// Actually, we've hit these. The missed branches are likely in `process_ignore_file` where we loop over dir files
// and one branch might be `regex.matches` being false for all, or something like that.

val tempDir = createTempDir("testNoMatches")
try {
File(tempDir, ".html4ignore").writeText(".*\\.exe") // won't match anything
File(tempDir, "file1.txt").writeText("test")

val excluded = process_ignore_file(tempDir)
assertTrue(excluded.contains("index.html"))
assertFalse(excluded.contains("file1.txt"))
} finally {
tempDir.deleteRecursively()
}
}
}
105 changes: 105 additions & 0 deletions src/test/kotlin/html4tree/CoreLogicTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
package html4tree

import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.assertFalse
import java.io.File

class CoreLogicTest {

@Test
fun testProcessIgnoreFile() {
val tempDir = createTempDir("testDir")
try {
// No ignore file
val excluded1 = process_ignore_file(tempDir)
assertEquals(1, excluded1.size)
assertTrue(excluded1.contains("index.html"))

// With ignore file
File(tempDir, ".html4ignore").writeText(".*\\.txt\n.*\\.log")
File(tempDir, "file1.txt").writeText("test")
File(tempDir, "file2.log").writeText("test")
File(tempDir, "file3.md").writeText("test")

val excluded2 = process_ignore_file(tempDir)
assertTrue(excluded2.contains("index.html"))
assertTrue(excluded2.contains("file1.txt"))
assertTrue(excluded2.contains("file2.log"))
assertFalse(excluded2.contains("file3.md"))
assertFalse(excluded2.contains(".html4ignore")) // not explicitly ignored in the file

} finally {
tempDir.deleteRecursively()
}
}

@Test
fun testProcessDirAndGo() {
val tempDir = createTempDir("testGoDir")
try {
// Create some structure
File(tempDir, "file1.txt").writeText("test")
val subDir1 = File(tempDir, "subDir1")
subDir1.mkdir()
File(subDir1, "file2.txt").writeText("test")
val subDir2 = File(subDir1, "subDir2")
subDir2.mkdir()
File(subDir2, "file3.txt").writeText("test")

// Test go with maxLevel 0
go(tempDir.absolutePath, 0)
assertTrue(File(tempDir, "index.html").exists())
assertFalse(File(subDir1, "index.html").exists())
assertFalse(File(subDir2, "index.html").exists())

// check contents of generated file for maxLevel 0
val indexContent = File(tempDir, "index.html").readText()
assertTrue(indexContent.contains("<html lang=\"ko\">"))
assertTrue(indexContent.contains("<meta charset=\"UTF-8\">"))
assertTrue(indexContent.contains("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"))
assertTrue(indexContent.contains("file1.txt"))
assertTrue(indexContent.contains("subDir1"))
assertFalse(indexContent.contains(".html4ignore"))

// delete index.html
File(tempDir, "index.html").delete()

// Test go with maxLevel 1
go(tempDir.absolutePath, 1)
assertTrue(File(tempDir, "index.html").exists())
assertTrue(File(subDir1, "index.html").exists())
assertFalse(File(subDir2, "index.html").exists())

File(tempDir, "index.html").delete()
File(subDir1, "index.html").delete()

// Test go with maxLevel -1 (unlimited)
go(tempDir.absolutePath, -1)
assertTrue(File(tempDir, "index.html").exists())
assertTrue(File(subDir1, "index.html").exists())
assertTrue(File(subDir2, "index.html").exists())

} finally {
tempDir.deleteRecursively()
}
}

@Test
fun testMainAndHtml4Tree() {
val tempDir = createTempDir("testMainDir")
try {
File(tempDir, "file1.txt").writeText("test")
val subDir1 = File(tempDir, "subDir1")
subDir1.mkdir()

// Run main
main(arrayOf("--max-level", "0", tempDir.absolutePath))
assertTrue(File(tempDir, "index.html").exists())
assertFalse(File(subDir1, "index.html").exists())
} finally {
tempDir.deleteRecursively()
}
}
}
23 changes: 23 additions & 0 deletions src/test/kotlin/html4tree/ExceptionTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package html4tree

import org.junit.Test
import java.io.File
import kotlin.test.assertFailsWith

class ExceptionTest {
@Test
fun testGoRequire() {
assertFailsWith(IllegalArgumentException::class) {
go("non_existent_dir_123", 0)
}

val tempFile = File.createTempFile("test", "file")
try {
assertFailsWith(IllegalArgumentException::class) {
go(tempFile.absolutePath, 0)
}
} finally {
tempFile.delete()
}
}
}
30 changes: 30 additions & 0 deletions src/test/kotlin/html4tree/LinkedListAdvancedTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
package html4tree

import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import java.io.File

class LinkedListAdvancedTest {
@Test
fun testLinkedListFull() {
val list = LinkedList()
list.push(LinkedListEntry(File("f1"), 1))

// This is to hit specific branch cases. LinkedList logic is a bit weird.
// It acts somewhat like a queue but the pointers are called first and last.
// last = first node added
// first = last node added

list.push(LinkedListEntry(File("f2"), 2))
list.push(LinkedListEntry(File("f3"), 3))

val p1 = list.pull()
val p2 = list.pull()
val p3 = list.pull()

assertEquals("f1", p1?.file?.name)
assertEquals("f2", p2?.file?.name)
assertEquals("f3", p3?.file?.name)
}
}
34 changes: 34 additions & 0 deletions src/test/kotlin/html4tree/LinkedListEdgeTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
package html4tree

import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertNotNull
import java.io.File

class LinkedListEdgeTest {
@Test
fun testLinkedListEdge() {
val list = LinkedList()
// test push on null last
list.push(LinkedListEntry(File("f1"), 1))

// internal check
assertNotNull(list.last)
assertNotNull(list.first)

// test push on non-null last
list.push(LinkedListEntry(File("f2"), 2))

// pull first item
val pulled1 = list.pull()
assertEquals("f1", pulled1?.file?.name)

// pull second item
val pulled2 = list.pull()
assertEquals("f2", pulled2?.file?.name)

// pull on empty list again
assertNull(list.pull())
}
}
30 changes: 30 additions & 0 deletions src/test/kotlin/html4tree/LinkedListSetTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
package html4tree

import org.junit.Test
import kotlin.test.assertEquals
import java.io.File

class LinkedListSetTest {
@Test
fun testLinkedListSettersAndPush() {
val list = LinkedList()

// Use the generated setters
val e = Entry(File("test"), 0, null)
list.first = e
list.last = e

assertEquals(e, list.first)
assertEquals(e, list.last)

val list2 = LinkedList()
list2.push(LinkedListEntry(File("f1"), 1))
list2.first = null
list2.push(LinkedListEntry(File("f2"), 2))

val pulled = list2.pull()
assertEquals("f1", pulled?.file?.name)
assertEquals(1, pulled?.level)
assertEquals(null, list2.pull())
}
}
39 changes: 39 additions & 0 deletions src/test/kotlin/html4tree/StringAndHelpTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package html4tree

import org.junit.Test
import kotlin.test.assertEquals
import java.io.ByteArrayOutputStream
import java.io.PrintStream

class StringAndHelpTest {

@Test
fun testEscapeHtml() {
assertEquals("&amp;", "&".escapeHtml())
assertEquals("&lt;", "<".escapeHtml())
assertEquals("&gt;", ">".escapeHtml())
assertEquals("&quot;", "\"".escapeHtml())
assertEquals("&#x27;", "'".escapeHtml())
assertEquals("&lt;b&gt;hello &amp; world&lt;/b&gt;", "<b>hello & world</b>".escapeHtml())
}

@Test
fun testUrlEncodePath() {
assertEquals("hello%20world", "hello world".urlEncodePath())
assertEquals("test%2Bplus", "test+plus".urlEncodePath())
}

@Test
fun testHelp() {
val originalOut = System.out
val outContent = ByteArrayOutputStream()
System.setOut(PrintStream(outContent))

try {
help()
assertEquals("ERROR: help has not been written yet!${System.lineSeparator()}", outContent.toString())
} finally {
System.setOut(originalOut)
}
}
}
Loading