Add flashcards feature with spaced repetition scheduling
Adds a full flashcards system: FSRS-based scheduling, cloze/basic card types, CSV import/export, media storage, and study UI, wired into navigation and the dashboard alongside existing question flows.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using QuestionsApi.Services.Flashcards;
|
||||
using Xunit;
|
||||
|
||||
namespace QuestionsApi.Tests;
|
||||
|
||||
public sealed class FlashcardCsvTests
|
||||
{
|
||||
[Fact]
|
||||
public void ParseRows_HandlesQuotedMultilineFieldsAndEmbeddedCommas()
|
||||
{
|
||||
var csv = "stable_id,deck,type,prompt,answer,extra,tags,source_question_external_id\n"
|
||||
+ "abc-1,Cardio,basic,\"What is the first line,\nsecond line?\",\"Answer, with comma\",,tag1;tag2,q-1\n";
|
||||
|
||||
var rows = FlashcardCsv.ParseRows(csv);
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
var data = rows[1];
|
||||
Assert.Equal("abc-1", data[0]);
|
||||
Assert.Equal("What is the first line,\nsecond line?", data[3]);
|
||||
Assert.Equal("Answer, with comma", data[4]);
|
||||
Assert.Equal("tag1;tag2", data[6]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseRows_HandlesEscapedDoubleQuotes()
|
||||
{
|
||||
var csv = "a,b\n\"she said \"\"hi\"\"\",plain\n";
|
||||
|
||||
var rows = FlashcardCsv.ParseRows(csv);
|
||||
|
||||
Assert.Equal("she said \"hi\"", rows[1][0]);
|
||||
Assert.Equal("plain", rows[1][1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseRows_SupportsUnicodeContent()
|
||||
{
|
||||
var csv = "a,b\néè中文,\U0001F600\n";
|
||||
|
||||
var rows = FlashcardCsv.ParseRows(csv);
|
||||
|
||||
Assert.Equal("éè中文", rows[1][0]);
|
||||
Assert.Equal("😀", rows[1][1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseRows_HandlesFileWithoutTrailingNewline()
|
||||
{
|
||||
var csv = "a,b\n1,2";
|
||||
|
||||
var rows = FlashcardCsv.ParseRows(csv);
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
Assert.Equal(["1", "2"], rows[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRow_QuotesFieldsContainingSpecialCharacters()
|
||||
{
|
||||
var row = FlashcardCsv.WriteRow(["plain", "has,comma", "has\"quote", "has\nnewline"]);
|
||||
|
||||
Assert.Equal("plain,\"has,comma\",\"has\"\"quote\",\"has\nnewline\"", row);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteThenParse_RoundTrips()
|
||||
{
|
||||
var original = new[] { "id-1", "Deck, Name", "basic", "prompt with \"quotes\" and\nnewline", "answer", "", "a;b", "q-1" };
|
||||
var line = FlashcardCsv.WriteRow(original);
|
||||
|
||||
var rows = FlashcardCsv.ParseRows("header\n" + line + "\n");
|
||||
|
||||
Assert.Equal(original, rows[1]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user