Skip to content

Commit 4b7ec2b

Browse files
committed
Unit Tests for getNoteTags and setNoteTags and tag validation
1 parent 9ba9a25 commit 4b7ec2b

File tree

4 files changed

+111
-0
lines changed

4 files changed

+111
-0
lines changed

AnkiDroid/src/main/assets/scripts/js-api.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ Object.keys(jsApiList).forEach(method => {
130130
}
131131
if (method === "ankiSetNoteTags") {
132132
AnkiDroidJS.prototype[method] = async function (noteId, tags) {
133+
let hasSpaces = false;
134+
for (let i = 0; i < tags.length; i++) {
135+
if (tags[i].trim().includes(" ") || tags[i].trim().includes("\u3000")) {
136+
hasSpaces = true;
137+
break;
138+
}
139+
}
140+
if (hasSpaces) {
141+
console.warn("Tags with spaces will have them converted to underscores.");
142+
}
133143
const endpoint = jsApiList[method];
134144
const data = JSON.stringify({ noteId, tags });
135145
return await this.handleRequest(endpoint, data);

AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidJsAPI.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,11 @@ open class AnkiDroidJsAPI(
381381
val jsonObject = JSONObject(apiParams)
382382
val noteId = jsonObject.getLong("noteId")
383383
val tags = jsonObject.getJSONArray("tags")
384+
for (i in 0 until tags.length()) {
385+
val element = tags.getString(i).trim()
386+
val modifiedElement = element.replace(" ", "_").replace("\u3000", "_")
387+
tags.put(i, modifiedElement)
388+
}
384389
withCol {
385390
fun Note.setTagsFromList(tagList: List<String>) {
386391
val tagsAsString = this@withCol.tags.join(tagList)

AnkiDroid/src/test/java/com/ichi2/anki/AnkiDroidJsAPITest.kt

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.ichi2.utils.BASIC_MODEL_NAME
2727
import net.ankiweb.rsdroid.withoutUnicodeIsolation
2828
import org.hamcrest.CoreMatchers.equalTo
2929
import org.hamcrest.MatcherAssert.assertThat
30+
import org.json.JSONArray
3031
import org.json.JSONObject
3132
import org.junit.Ignore
3233
import org.junit.Test
@@ -417,6 +418,78 @@ class AnkiDroidJsAPITest : RobolectricTest() {
417418
assertEquals(CardType.New, cardAfterReset.type, "Card type after reset")
418419
}
419420

421+
@Test
422+
fun ankiGetNoteTagsTest() =
423+
runTest {
424+
val n =
425+
addBasicNote("Front", "Back").update {
426+
tags = mutableListOf("tag1", "tag2", "tag3")
427+
}
428+
429+
val reviewer: Reviewer = startReviewer()
430+
waitForAsyncTasksToComplete()
431+
432+
val jsapi = reviewer.jsApi
433+
434+
// test get tags for note
435+
val expectedTags = n.tags
436+
val response = getDataFromRequest("getNoteTags", jsapi, jsonObjectOf("noteId" to n.id))
437+
val jsonResponse = JSONObject(response)
438+
val actualTags = JSONArray(jsonResponse.getString("value"))
439+
440+
assertEquals(expectedTags.size, actualTags.length())
441+
for (i in 0 until actualTags.length()) {
442+
assertEquals(expectedTags[i], actualTags.getString(i))
443+
}
444+
}
445+
446+
@Test
447+
fun ankiSetNoteTagsTest() =
448+
runTest {
449+
val n =
450+
addBasicNote("Front", "Back").update {
451+
tags = mutableListOf("tag1", "tag2", "tag3")
452+
}
453+
454+
val reviewer: Reviewer = startReviewer()
455+
waitForAsyncTasksToComplete()
456+
457+
val jsapi = reviewer.jsApi
458+
459+
// test set tags for note
460+
val newTags =
461+
JSONArray().apply {
462+
put(" tag4") // leading whitespace
463+
put("tag5 ") // trailing whitespace
464+
put("tag 6 ") // spaces in tag
465+
}
466+
467+
val newTagsList =
468+
(0 until newTags.length()).map { index ->
469+
newTags
470+
.getString(index)
471+
.trim()
472+
.replace(" ", "_")
473+
.replace("\u3000", "_")
474+
}
475+
476+
assertThat(
477+
getDataFromRequest("setNoteTags", jsapi, jsonObjectOf("noteId" to n.id, "tags" to newTags)),
478+
equalTo(formatApiResult(true)),
479+
)
480+
waitForAsyncTasksToComplete()
481+
482+
// Reload the note to ensure the tags are updated
483+
val updatedNote = col.getNote(n.id)
484+
println(updatedNote.tags)
485+
486+
// Verify the tags are updated
487+
assertEquals(newTagsList.size, updatedNote.tags.size)
488+
newTagsList.zip(updatedNote.tags).forEach { (newTag, updatedTag) ->
489+
assertEquals(newTag, updatedTag)
490+
}
491+
}
492+
420493
companion object {
421494
fun jsApiContract(data: String = ""): ByteArray =
422495
JSONObject()
@@ -450,5 +523,21 @@ class AnkiDroidJsAPITest : RobolectricTest() {
450523
jsAPI
451524
.handleJsApiRequest(methodName, jsApiContract(apiData), false)
452525
.decodeToString()
526+
527+
suspend fun getDataFromRequest(
528+
methodName: String,
529+
jsAPI: AnkiDroidJsAPI,
530+
apiData: JSONObject,
531+
): String =
532+
jsAPI
533+
.handleJsApiRequest(methodName, jsApiContract(apiData.toString()), false)
534+
.decodeToString()
453535
}
454536
}
537+
538+
private fun jsonObjectOf(vararg pairs: Pair<String, Any>): JSONObject =
539+
JSONObject().apply {
540+
for ((key, value) in pairs) {
541+
put(key, value)
542+
}
543+
}

AnkiDroid/src/test/java/com/ichi2/testutils/TestClass.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,13 @@ interface TestClass {
241241
col.decks.save(deckConfig)
242242
}
243243

244+
/** Helper method to update a note */
245+
fun Note.update(block: Note.() -> Unit): Note {
246+
block(this)
247+
col.updateNote(this)
248+
return this
249+
}
250+
244251
/** Helper method to all cards of a note */
245252
fun Note.updateCards(update: Card.() -> Unit): Note {
246253
cards().forEach { it.update(update) }

0 commit comments

Comments
 (0)