@@ -80,3 +80,103 @@ def test_marks_preserves_href_from_top_level(self):
8080 assert mark ["attrs" ]["href" ] == "https://example.com"
8181 return
8282 raise AssertionError ("No link mark found in output" )
83+
84+
85+ class TestBlockquoteFromMarkdown :
86+ """Tests for blockquote parsing in from_markdown()."""
87+
88+ def test_single_blockquote_line (self ):
89+ """A single '> text' line produces a blockquote with one paragraph."""
90+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
91+ post .from_markdown ("> This is a quote" )
92+ body = json .loads (post .get_draft ()["draft_body" ])
93+ bq = body ["content" ][0 ]
94+ assert bq ["type" ] == "blockquote"
95+ assert len (bq ["content" ]) == 1
96+ assert bq ["content" ][0 ]["type" ] == "paragraph"
97+ assert bq ["content" ][0 ]["content" ][0 ]["text" ] == "This is a quote"
98+
99+ def test_multiline_blockquote_grouped (self ):
100+ """Consecutive '>' lines become a single blockquote with multiple paragraphs."""
101+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
102+ post .from_markdown ("> Line one\n > Line two\n > Line three" )
103+ body = json .loads (post .get_draft ()["draft_body" ])
104+ bq = body ["content" ][0 ]
105+ assert bq ["type" ] == "blockquote"
106+ assert len (bq ["content" ]) == 3
107+ texts = [p ["content" ][0 ]["text" ] for p in bq ["content" ]]
108+ assert texts == ["Line one" , "Line two" , "Line three" ]
109+
110+ def test_blockquote_separated_by_blank_line (self ):
111+ """A blank line between '>' groups creates two separate blockquotes."""
112+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
113+ post .from_markdown ("> First block\n \n > Second block" )
114+ body = json .loads (post .get_draft ()["draft_body" ])
115+ blockquotes = [n for n in body ["content" ] if n ["type" ] == "blockquote" ]
116+ assert len (blockquotes ) == 2
117+
118+ def test_blockquote_then_paragraph (self ):
119+ """A blockquote followed by a regular paragraph produces both node types."""
120+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
121+ post .from_markdown ("> A quote\n \n A regular paragraph" )
122+ body = json .loads (post .get_draft ()["draft_body" ])
123+ assert body ["content" ][0 ]["type" ] == "blockquote"
124+ assert body ["content" ][1 ]["type" ] == "paragraph"
125+
126+ def test_paragraph_blockquote_paragraph (self ):
127+ """Blockquote sandwiched between paragraphs preserves order."""
128+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
129+ post .from_markdown ("Before\n \n > The quote\n \n After" )
130+ body = json .loads (post .get_draft ()["draft_body" ])
131+ types = [n ["type" ] for n in body ["content" ]]
132+ assert types == ["paragraph" , "blockquote" , "paragraph" ]
133+
134+ def test_blockquote_with_inline_link (self ):
135+ """Links inside blockquotes are parsed as marks."""
136+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
137+ post .from_markdown ("> See [example](https://example.com)" )
138+ body = json .loads (post .get_draft ()["draft_body" ])
139+ bq = body ["content" ][0 ]
140+ assert bq ["type" ] == "blockquote"
141+ para = bq ["content" ][0 ]
142+ assert para ["type" ] == "paragraph"
143+
144+ def test_blockquote_adjacent_to_bullet_list (self ):
145+ """Blockquote followed immediately by bullets flushes correctly."""
146+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
147+ post .from_markdown ("> A quote\n - bullet one\n - bullet two" )
148+ body = json .loads (post .get_draft ()["draft_body" ])
149+ types = [n ["type" ] for n in body ["content" ]]
150+ assert types == ["blockquote" , "bullet_list" ]
151+
152+ def test_empty_continuation_line (self ):
153+ """A bare '>' between quoted lines keeps them in one blockquote."""
154+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
155+ post .from_markdown ("> First\n >\n > Third" )
156+ body = json .loads (post .get_draft ()["draft_body" ])
157+ blockquotes = [n for n in body ["content" ] if n ["type" ] == "blockquote" ]
158+ assert len (blockquotes ) == 1
159+ paras_with_content = [p for p in blockquotes [0 ]["content" ] if p .get ("content" )]
160+ assert len (paras_with_content ) == 2
161+
162+
163+ class TestBlockquoteMethod :
164+ """Tests for the Post.blockquote() convenience method."""
165+
166+ def test_blockquote_string (self ):
167+ """blockquote('text') wraps text in a blockquote node."""
168+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
169+ post .blockquote ("Hello world" )
170+ body = json .loads (post .get_draft ()["draft_body" ])
171+ bq = body ["content" ][0 ]
172+ assert bq ["type" ] == "blockquote"
173+ assert bq ["content" ][0 ]["content" ][0 ]["text" ] == "Hello world"
174+
175+ def test_blockquote_chaining (self ):
176+ """blockquote() returns self for method chaining."""
177+ post = Post (title = "T" , subtitle = "S" , user_id = 1 )
178+ result = post .blockquote ("one" ).blockquote ("two" )
179+ assert result is post
180+ body = json .loads (post .get_draft ()["draft_body" ])
181+ blockquotes = [n for n in body ["content" ] if n ["type" ] == "blockquote" ]
182+ assert len (blockquotes ) == 2
0 commit comments