Skip to content

Commit a072b29

Browse files
committed
adds junit tests for format
1 parent 03ab7b1 commit a072b29

File tree

1 file changed

+309
-0
lines changed
  • core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api

1 file changed

+309
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
package org.jetbrains.kotlinx.dataframe.api
2+
3+
import io.kotest.matchers.shouldBe
4+
import io.kotest.matchers.shouldNotBe
5+
import io.kotest.matchers.string.shouldContain
6+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.attr
7+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.background
8+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.black
9+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.blue
10+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.bold
11+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.green
12+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.italic
13+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.linear
14+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.red
15+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.rgb
16+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.textColor
17+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.underline
18+
import org.jetbrains.kotlinx.dataframe.api.FormattingDsl.white
19+
import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration
20+
import org.jetbrains.kotlinx.dataframe.samples.api.TestBase
21+
import org.jetbrains.kotlinx.dataframe.samples.api.age
22+
import org.jetbrains.kotlinx.dataframe.samples.api.firstName
23+
import org.jetbrains.kotlinx.dataframe.samples.api.isHappy
24+
import org.jetbrains.kotlinx.dataframe.samples.api.name
25+
import org.jetbrains.kotlinx.dataframe.samples.api.weight
26+
import org.junit.Ignore
27+
import org.junit.Test
28+
29+
class FormatTests : TestBase() {
30+
31+
@Test
32+
fun `basic format with background color`() {
33+
val formatted = df.format { age }.with { background(red) }
34+
val html = formatted.toHtml().toString()
35+
36+
// Should contain CSS background-color styling
37+
html shouldContain "background-color:#ff0000"
38+
// Format operation should produce a FormattedFrame
39+
formatted::class.simpleName shouldBe "FormattedFrame"
40+
}
41+
42+
@Test
43+
fun `format with text color`() {
44+
val formatted = df.format { age }.with { textColor(blue) }
45+
val html = formatted.toHtml().toString()
46+
47+
html shouldContain "color:#0000ff"
48+
formatted::class.simpleName shouldBe "FormattedFrame"
49+
}
50+
51+
@Test
52+
fun `format with multiple attributes using and`() {
53+
val formatted = df.format { age }.with { background(white) and textColor(black) and bold }
54+
val html = formatted.toHtml().toString()
55+
56+
html shouldContain "background-color:#ffffff"
57+
html shouldContain "color"
58+
html shouldContain "font-weight"
59+
html shouldContain "bold"
60+
}
61+
62+
@Test
63+
fun `format with italic and underline`() {
64+
val formatted = df.format { age }.with { italic and underline }
65+
val html = formatted.toHtml().toString()
66+
67+
html shouldContain "font-style"
68+
html shouldContain "italic"
69+
html shouldContain "text-decoration"
70+
html shouldContain "underline"
71+
}
72+
73+
// TODO #1356
74+
@Ignore
75+
@Test
76+
fun `format with italic and underline in nested group`() {
77+
val formatted = df.format { name.firstName }.with { italic and underline }
78+
val html = formatted.toHtml().toString()
79+
80+
html shouldContain "font-style"
81+
html shouldContain "italic"
82+
html shouldContain "text-decoration"
83+
html shouldContain "underline"
84+
}
85+
86+
@Test
87+
fun `format with custom rgb color`() {
88+
val customColor = rgb(128, 64, 192)
89+
val formatted = df.format { age }.with { background(customColor) }
90+
val html = formatted.toHtml().toString()
91+
92+
// Custom color should be applied
93+
html shouldContain "background-color:#8040c0"
94+
}
95+
96+
@Test
97+
fun `format with custom attribute`() {
98+
val formatted = df.format { age }.with { attr("text-align", "center") }
99+
val html = formatted.toHtml().toString()
100+
101+
val occurrences = html.split("text-align:center").size - 1
102+
occurrences shouldBe 7
103+
}
104+
105+
@Test
106+
fun `format with where clause`() {
107+
val formatted = df.format { age }.where { it > 30 }.with { background(red) }
108+
val html = formatted.toHtml().toString()
109+
110+
// Should contain styling but only for cells where age > 30
111+
val occurrences = html.split("background-color:#ff0000").size - 1
112+
occurrences shouldBe 2 // Two cells where age > 30
113+
}
114+
115+
@Test
116+
fun `format with at specific rows`() {
117+
val formatted = df.format { age }.at(0, 2, 4, 9999).with { background(green) }
118+
val html = formatted.toHtml().toString()
119+
120+
val occurrences = html.split("background-color:#00ff00").size - 1
121+
occurrences shouldBe 3
122+
}
123+
124+
@Test
125+
fun `format with at range`() {
126+
val formatted = df.format { age }.at(1..3).with { background(blue) }
127+
val html = formatted.toHtml().toString()
128+
129+
val occurrences = html.split("background-color:#0000ff").size - 1
130+
occurrences shouldBe 3
131+
}
132+
133+
@Test
134+
fun `format with notNull filter`() {
135+
val formatted = df.format { weight }.notNull().with { background(green) }
136+
val html = formatted.toHtml().toString()
137+
138+
// Should only format non-null weight values
139+
val occurrences = html.split("background-color:#00ff00").size - 1
140+
occurrences shouldBe 5
141+
}
142+
143+
@Test
144+
fun `format with notNull shorthand`() {
145+
val formatted = df.format { weight }.notNull { background(red) and bold }
146+
val html = formatted.toHtml().toString()
147+
148+
html.split("background-color:#ff0000").size - 1 shouldBe 5
149+
html.split("font-weight:bold").size - 1 shouldBe 5
150+
}
151+
152+
@Test
153+
fun `format with perRowCol`() {
154+
val formatted = df.format { age }.perRowCol { row, col ->
155+
if (col[row] > 25) background(red) else background(green)
156+
}
157+
val html = formatted.toHtml().toString()
158+
159+
// Should contain formatting based on age values
160+
val occurrences = html.split("background-color:#00ff00").size - 1
161+
occurrences shouldBe 3
162+
163+
formatted::class.simpleName shouldBe "FormattedFrame"
164+
}
165+
166+
@Test
167+
fun `format with linearBg`() {
168+
val formatted = df.format { age }.linearBg(15 to blue, 45 to red)
169+
val html = formatted.toHtml().toString()
170+
171+
html shouldContain "background-color:#0000ff"
172+
html shouldContain "background-color:#2a00d4"
173+
html shouldContain "background-color:#d4002a"
174+
html shouldContain "background-color:#7f007f"
175+
html shouldContain "background-color:#2a00d4"
176+
html shouldContain "background-color:#7f007f"
177+
html shouldContain "background-color:#ff0000"
178+
formatted::class.simpleName shouldBe "FormattedFrame"
179+
}
180+
181+
@Test
182+
fun `format with linear color interpolation`() {
183+
val formatted = df.format { age }.with { value ->
184+
textColor(linear(value, 15 to blue, 45 to red))
185+
}
186+
val html = formatted.toHtml().toString()
187+
188+
html shouldContain "color:#0000ff"
189+
html shouldContain "color:#2a00d4"
190+
html shouldContain "color:#d4002a"
191+
html shouldContain "color:#7f007f"
192+
html shouldContain "color:#2a00d4"
193+
html shouldContain "color:#7f007f"
194+
html shouldContain "color:#ff0000"
195+
formatted::class.simpleName shouldBe "FormattedFrame"
196+
}
197+
198+
@Test
199+
fun `chained format operations`() {
200+
val formatted = df
201+
.format().with { background(white) and textColor(black) }
202+
.format { age }.with { background(red) }
203+
.format { isHappy }.with { background(if (it) green else red) }
204+
205+
val html = formatted.toHtml().toString()
206+
207+
// Should contain all applied styles
208+
html.split("background-color:#ffffff").size - 1 shouldBe 35
209+
html.split("background-color:#ff0000").size - 1 shouldBe 9
210+
html.split("background-color:#00ff00").size - 1 shouldBe 5
211+
html.split("color:#000000").size - 1 shouldBe 98 // includes attributes outside cells
212+
formatted::class.simpleName shouldBe "FormattedFrame"
213+
}
214+
215+
@Test
216+
fun `format all columns`() {
217+
val formatted = df.format().with { bold and textColor(black) }
218+
formatted.toStandaloneHtml().openInBrowser()
219+
val html = formatted.toHtml().toString()
220+
221+
html.split("font-weight:bold").size - 1 shouldBe 49 // All cells formatted
222+
html.split("color:#000000").size - 1 shouldBe 98 // includes attributes outside cells
223+
}
224+
225+
@Test
226+
fun `format by column names`() {
227+
val formatted = df.format("age", "weight").with { background(blue) }
228+
val html = formatted.toHtml().toString()
229+
230+
html.split("background-color:#0000ff").size - 1 shouldBe 14 // 7 rows * 2 columns (age, weight)
231+
}
232+
233+
@Test
234+
fun `format with complex perRowCol logic`() {
235+
val formatted = df.format { age }.perRowCol { row, col ->
236+
val value = col[row]
237+
when {
238+
value < 20 -> textColor(blue)
239+
value < 30 -> textColor(green)
240+
else -> textColor(red)
241+
}
242+
}
243+
val html = formatted.toHtml().toString()
244+
245+
// Ages: 15(blue), 45(red), 20(green), 40(red), 30(green), 20(green), 30(green)
246+
html.split("color:#0000ff").size - 1 shouldBe 2 // blue: age < 20
247+
html.split("color:#00ff00").size - 1 shouldBe 4 // green: 20 <= age < 30
248+
html.split("color:#ff0000").size - 1 shouldBe 8 // red: age >= 30
249+
formatted::class.simpleName shouldBe "FormattedFrame"
250+
}
251+
252+
@Test
253+
fun `toStandaloneHtml includes CSS definitions`() {
254+
val formatted = df.format { age }.with { background(red) }
255+
val standaloneHtml = formatted.toStandaloneHtml().toString()
256+
val regularHtml = formatted.toHtml().toString()
257+
258+
// Standalone should be longer and contain more CSS/script definitions
259+
standaloneHtml.length shouldNotBe regularHtml.length
260+
standaloneHtml.split("<!DOCTYPE html>").size - 1 shouldBe 0
261+
standaloneHtml.split("<html>").size - 1 shouldBe 1
262+
standaloneHtml.split("<head>").size - 1 shouldBe 1
263+
standaloneHtml.split("<style>").size - 1 shouldBe 0
264+
}
265+
266+
@Test
267+
fun `format with custom display configuration`() {
268+
val config = DisplayConfiguration.DEFAULT.copy(rowsLimit = 3)
269+
val formatted = df.format { age }.with { background(red) }
270+
val html = formatted.toHtml(config).toString()
271+
272+
html.split("background-color:#ff0000").size - 1 shouldBe 3 // Limited to 3 rows by config
273+
}
274+
275+
@Test
276+
fun `documentation example - simple formatting`() {
277+
// Simple formatting example
278+
val formatted = df
279+
.format { age }.with { background(red) }
280+
.format { weight }.notNull().with { textColor(blue) }
281+
282+
val html = formatted.toHtml().toString()
283+
284+
// Should contain both background and text color formatting
285+
html.split("background-color:#ff0000").size - 1 shouldBe 7 // age column, 7 rows
286+
html.split("color:#0000ff").size - 1 shouldBe 10 // weight column, actual count from test
287+
formatted::class.simpleName shouldBe "FormattedFrame"
288+
}
289+
290+
@Test
291+
fun `format returns FormattedFrame`() {
292+
val formatted = df.format { age }.with { background(red) }
293+
294+
// Should be a FormattedFrame, not a regular DataFrame
295+
formatted::class.simpleName shouldBe "FormattedFrame"
296+
}
297+
298+
@Test
299+
fun `format with null values handled correctly`() {
300+
val formatted = df.format { weight }.with { value ->
301+
if (value != null) background(green) else null
302+
}
303+
val html = formatted.toHtml().toString()
304+
305+
// Should handle null values gracefully
306+
html.split("background-color:#00ff00").size - 1 shouldBe 5 // Only non-null weight values get formatted
307+
formatted::class.simpleName shouldBe "FormattedFrame"
308+
}
309+
}

0 commit comments

Comments
 (0)