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.
95 lines
2.7 KiB
C#
95 lines
2.7 KiB
C#
using QuestionsApi.Services.Flashcards;
|
|
using Xunit;
|
|
|
|
namespace QuestionsApi.Tests;
|
|
|
|
public sealed class FlashcardClozeParserTests
|
|
{
|
|
[Fact]
|
|
public void Parse_ExtractsAnswerAndHint()
|
|
{
|
|
var deletions = FlashcardClozeParser.Parse("The capital of France is {{c1::Paris::country capital}}.");
|
|
|
|
var deletion = Assert.Single(deletions);
|
|
Assert.Equal(1, deletion.Number);
|
|
Assert.Equal("Paris", deletion.Answer);
|
|
Assert.Equal("country capital", deletion.Hint);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_WithoutHint_LeavesHintNull()
|
|
{
|
|
var deletions = FlashcardClozeParser.Parse("{{c1::Paris}}");
|
|
|
|
Assert.Null(Assert.Single(deletions).Hint);
|
|
}
|
|
|
|
[Fact]
|
|
public void DistinctClozeNumbers_DeduplicatesRepeatedNumbersAndSorts()
|
|
{
|
|
var text = "{{c2::b}} some text {{c1::a}} more {{c1::a-again}} {{c3::c}}";
|
|
|
|
var numbers = FlashcardClozeParser.DistinctClozeNumbers(text);
|
|
|
|
Assert.Equal([1, 2, 3], numbers);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("no cloze markers here")]
|
|
[InlineData("{{c0::invalid, number must be >= 1}}")]
|
|
[InlineData("{{cX::not a number}}")]
|
|
[InlineData("{{c1::}}")]
|
|
public void HasValidCloze_IsFalse_ForMalformedOrMissingMarkers(string text)
|
|
{
|
|
Assert.False(FlashcardClozeParser.HasValidCloze(text));
|
|
}
|
|
|
|
[Fact]
|
|
public void HasValidCloze_IsTrue_WhenAtLeastOneValidMarkerExists()
|
|
{
|
|
Assert.True(FlashcardClozeParser.HasValidCloze("prefix {{c1::answer}} suffix"));
|
|
}
|
|
|
|
[Fact]
|
|
public void RenderFront_HidesTargetNumber_RevealsOthers()
|
|
{
|
|
var text = "{{c1::Paris}} is the capital of {{c2::France}}";
|
|
|
|
var front1 = FlashcardClozeParser.RenderFront(text, 1);
|
|
var front2 = FlashcardClozeParser.RenderFront(text, 2);
|
|
|
|
Assert.Equal("[...] is the capital of France", front1);
|
|
Assert.Equal("Paris is the capital of [...]", front2);
|
|
}
|
|
|
|
[Fact]
|
|
public void RenderFront_UsesHintWhenPresent()
|
|
{
|
|
var text = "{{c1::Paris::capital city}} is in France";
|
|
|
|
var front = FlashcardClozeParser.RenderFront(text, 1);
|
|
|
|
Assert.Equal("[capital city] is in France", front);
|
|
}
|
|
|
|
[Fact]
|
|
public void RenderBack_RevealsAllAnswers()
|
|
{
|
|
var text = "{{c1::Paris}} is the capital of {{c2::France::hint}}";
|
|
|
|
var back = FlashcardClozeParser.RenderBack(text, 1);
|
|
|
|
Assert.Equal("Paris is the capital of France", back);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_SupportsMultilineClozeContent()
|
|
{
|
|
var text = "Line one\n{{c1::multi\nline\nanswer}}\nLine two";
|
|
|
|
var deletion = Assert.Single(FlashcardClozeParser.Parse(text));
|
|
|
|
Assert.Equal("multi\nline\nanswer", deletion.Answer);
|
|
}
|
|
}
|