diff --git a/pylib/tests/test_models.py b/pylib/tests/test_models.py index ce5a98b51..5e45e35bd 100644 --- a/pylib/tests/test_models.py +++ b/pylib/tests/test_models.py @@ -2,6 +2,8 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # coding: utf-8 +import html +import re import time from anki.consts import MODEL_CLOZE @@ -10,6 +12,12 @@ from anki.utils import is_win, strip_html from tests.shared import getEmptyCol +def encode_attribute(s): + return "".join( + c if c.isalnum() else "&#x{:X};".format(ord(c)) for c in html.escape(s) + ) + + def test_modelDelete(): col = getEmptyCol() note = col.newNote() @@ -176,29 +184,41 @@ def test_cloze(): note = col.new_note(m) note["Text"] = "hello {{c1::world}}" assert col.addNote(note) == 1 - assert "hello [...]" in note.cards()[0].question() - assert "hello world" in note.cards()[0].answer() + assert ( + f'hello [...]' + in note.cards()[0].question() + ) + assert 'hello world' in note.cards()[0].answer() # and with a comment note = col.new_note(m) note["Text"] = "hello {{c1::world::typical}}" assert col.addNote(note) == 1 - assert "[typical]" in note.cards()[0].question() - assert "world" in note.cards()[0].answer() + assert ( + f'[typical]' + in note.cards()[0].question() + ) + assert 'world' in note.cards()[0].answer() # and with 2 clozes note = col.new_note(m) note["Text"] = "hello {{c1::world}} {{c2::bar}}" assert col.addNote(note) == 2 (c1, c2) = note.cards() - assert "[...] bar" in c1.question() - assert "world bar" in c1.answer() - assert "world [...]" in c2.question() - assert "world bar" in c2.answer() + assert ( + f'[...] bar' + in c1.question() + ) + assert 'world bar' in c1.answer() + assert ( + f'world [...]' + in c2.question() + ) + assert 'world bar' in c2.answer() # if there are multiple answers for a single cloze, they are given in a # list note = col.new_note(m) note["Text"] = "a {{c1::b}} {{c1::c}}" assert col.addNote(note) == 1 - assert "b c" in ( + assert 'b c' in ( note.cards()[0].answer() ) # if we add another cloze, a card should be generated @@ -216,16 +236,42 @@ def test_cloze_mathjax(): col = getEmptyCol() m = col.models.by_name("Cloze") note = col.new_note(m) + q1 = "ok" + q2 = "not ok" + q3 = "2" + q4 = "blah" + q5 = "text with \(x^2\) jax" note[ "Text" - ] = r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}" + ] = "{{{{c1::{}}}}} \(2^2\) {{{{c2::{}}}}} \(2^{{{{c3::{}}}}}\) \(x^3\) {{{{c4::{}}}}} {{{{c5::{}}}}}".format( + q1, + q2, + q3, + q4, + q5, + ) assert col.addNote(note) assert len(note.cards()) == 5 - assert "class=cloze" in note.cards()[0].question() - assert "class=cloze" in note.cards()[1].question() - assert "class=cloze" not in note.cards()[2].question() - assert "class=cloze" in note.cards()[3].question() - assert "class=cloze" in note.cards()[4].question() + assert ( + f'class="cloze" data-cloze="{encode_attribute(q1)}"' + in note.cards()[0].question() + ) + assert ( + f'class="cloze" data-cloze="{encode_attribute(q2)}"' + in note.cards()[1].question() + ) + assert ( + f'class="cloze" data-cloze="{encode_attribute(q3)}"' + not in note.cards()[2].question() + ) + assert ( + f'class="cloze" data-cloze="{encode_attribute(q4)}"' + in note.cards()[3].question() + ) + assert ( + f'class="cloze" data-cloze="{encode_attribute(q5)}"' + in note.cards()[4].question() + ) note = col.new_note(m) note["Text"] = r"\(a\) {{c1::b}} \[ {{c1::c}} \]" @@ -234,7 +280,7 @@ def test_cloze_mathjax(): assert ( note.cards()[0] .question() - .endswith(r"\(a\) [...] \[ [...] \]") + .endswith(r'\(a\) [...] \[ [...] \]') ) @@ -278,11 +324,12 @@ def test_chained_mods(): ) assert col.addNote(note) == 1 assert ( - "This [sentence] demonstrates [chained] clozes." + f'This [sentence]' + f' demonstrates [chained] clozes.' in note.cards()[0].question() ) assert ( - "This phrase demonstrates en chaine clozes." + f'This phrase demonstrates en chaine clozes.' in note.cards()[0].answer() ) diff --git a/rslib/src/cloze.rs b/rslib/src/cloze.rs index dcd064c2f..0612f7795 100644 --- a/rslib/src/cloze.rs +++ b/rslib/src/cloze.rs @@ -57,26 +57,30 @@ pub fn reveal_cloze_text(text: &str, cloze_ord: u16, question: bool) -> Cow .parse() .unwrap_or(0); + let text = caps.get(cloze_caps::TEXT).unwrap().as_str().to_owned(); if captured_ord != cloze_ord { // other cloze deletions are unchanged - return caps.get(cloze_caps::TEXT).unwrap().as_str().to_owned(); + return text; } else { cloze_ord_was_in_text = true; } + let text_attr; let replacement; if question { + text_attr = format!(r#" data-cloze="{}""#, htmlescape::encode_attribute(&text)); // hint provided? if let Some(hint) = caps.get(cloze_caps::HINT) { replacement = format!("[{}]", hint.as_str()); } else { - replacement = "[...]".to_string() + replacement = "[...]".to_string(); } } else { - replacement = caps.get(cloze_caps::TEXT).unwrap().as_str().to_owned(); + text_attr = "".to_string(); + replacement = text; } - format!("{}", replacement) + format!(r#"{}"#, text_attr, replacement) }); if !cloze_ord_was_in_text { diff --git a/rslib/src/template_filters.rs b/rslib/src/template_filters.rs index b799983b3..21b5b99ce 100644 --- a/rslib/src/template_filters.rs +++ b/rslib/src/template_filters.rs @@ -256,7 +256,7 @@ field assert_eq!(strip_html(&cloze_filter(text, &ctx)).as_ref(), "[...] two"); assert_eq!( cloze_filter(text, &ctx), - "[...] two" + r#"[...] two"# ); ctx.card_ord = 1;