First stable release. Merging this bumps versionName to 1.0.0 and triggers the release pipeline (F-Droid, Codeberg, Play). **App** - CalDAV sync built in, with Agendula's own task store; OpenTasks / tasks.org stay available and can be copied over in Settings → Storage - repeating tasks, several reminders per task, lists managed in the app, iCalendar import/export, widget and Quick Settings tile - a list can be kept out of the smart lists (#18) and gets its own notification channel (#17) - duplicate a task with its subtasks (#16) - HTML descriptions shown as plain text (#15) - relative day words in reminder notifications (#14) - asks for exact-alarm access instead of claiming USE_EXACT_ALARM, and re-arms reminders when that access changes **Release plumbing** - floret-kit bumped to v0.4.0; the old pin was never pushed, so a clean clone couldn't check out the submodule. 0.4.0 drops CrashConfig.issueTitle (crash issues are always filed in English) - prebuilt .so files ship unstripped, so the build no longer depends on whether an NDK is installed; now checked by check_reproducible_release.sh - official F-Droid recipe in docs/fdroid-official/, to submit to fdroiddata once v1.0.0 is tagged - Google Play: fastlane uploads the AAB and every locale's What's New after the F-Droid release; a separate listing lane pushes text and graphics from the fastlane tree, which CI now checks against Play's limits - store listing: title "Agendula: Tasks" in every locale, icon, feature graphic, screenshots and 1.0.0 changelogs in en-US, en-GB, de-DE and pt-BR crash_report_issue_title is now unused but stays until Weblate removes the translated copies. Closes #14, closes #15, closes #16, closes #17, closes #18 Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/20
59 lines
2.1 KiB
Kotlin
59 lines
2.1 KiB
Kotlin
package de.jeanlucmakiola.agendula.domain
|
|
|
|
import com.google.common.truth.Truth.assertThat
|
|
import org.junit.jupiter.api.Test
|
|
|
|
class HtmlTextTest {
|
|
|
|
@Test
|
|
fun `plain text with angle brackets is not html`() {
|
|
assertThat("a < b and c > d".looksLikeHtml()).isFalse()
|
|
assertThat("x <3".readableDescription()).isEqualTo("x <3")
|
|
assertThat("if x<b and y>c".looksLikeHtml()).isFalse()
|
|
assertThat("Mail <b.smith@example.com>".looksLikeHtml()).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun `text that only resembles a tag survives inside real html`() {
|
|
assertThat("<p>Mail <b.smith@example.com></p>".htmlToPlainText()).isEqualTo("Mail <b.smith@example.com>")
|
|
}
|
|
|
|
@Test
|
|
fun `paragraphs and breaks become lines`() {
|
|
val html = "<p>First line<br>second</p>\n<p>Next & last</p>"
|
|
assertThat(html.htmlToPlainText()).isEqualTo("First line\nsecond\nNext & last")
|
|
}
|
|
|
|
@Test
|
|
fun `list items become bullets`() {
|
|
val html = "<ul>\n <li>Milk</li>\n <li><b>Eggs</b></li>\n</ul>"
|
|
assertThat(html.htmlToPlainText()).isEqualTo("• Milk\n• Eggs")
|
|
}
|
|
|
|
@Test
|
|
fun `a link keeps its address`() {
|
|
assertThat("""See <a href="https://example.org">the docs</a>""".htmlToPlainText())
|
|
.isEqualTo("See the docs (https://example.org)")
|
|
assertThat("""<a href="https://example.org">https://example.org</a>""".htmlToPlainText())
|
|
.isEqualTo("https://example.org")
|
|
assertThat("""<a href="mailto:a@b.de">a@b.de</a>""".htmlToPlainText()).isEqualTo("a@b.de")
|
|
}
|
|
|
|
@Test
|
|
fun `inline markup keeps the source line breaks`() {
|
|
assertThat("one <b>bold</b>\ntwo".htmlToPlainText()).isEqualTo("one bold\ntwo")
|
|
}
|
|
|
|
@Test
|
|
fun `entities decode and bogus ones stay`() {
|
|
assertThat("<span><tag> ä é � &bogus;</span>".htmlToPlainText())
|
|
.isEqualTo("<tag> ä é � &bogus;")
|
|
}
|
|
|
|
@Test
|
|
fun `head and style are dropped`() {
|
|
val html = "<html><head><style>p { color: red }</style></head><body><p>Hi</p></body></html>"
|
|
assertThat(html.htmlToPlainText()).isEqualTo("Hi")
|
|
}
|
|
}
|