|
7 | 7 | _normalize_link_text_spacing, |
8 | 8 | _normalize_sentence_breaks, |
9 | 9 | _normalize_table_cell_padding, |
| 10 | + _normalize_empty_list_items, |
| 11 | + _normalize_consecutive_blank_lines, |
| 12 | + _normalize_blank_line_after_br, |
10 | 13 | ) |
11 | 14 |
|
12 | 15 |
|
@@ -190,6 +193,78 @@ def test_multiple_links_all_normalized(self): |
190 | 193 | assert result == "* [**A**](a) and [**B**](b)" |
191 | 194 |
|
192 | 195 |
|
| 196 | +# --- _normalize_empty_list_items 단위 테스트 --- |
| 197 | + |
| 198 | + |
| 199 | +class TestNormalizeEmptyListItems: |
| 200 | + def test_removes_number_only_line(self): |
| 201 | + """번호만 있는 리스트 항목(예: ' 12.')을 줄째 제거한다. |
| 202 | +
|
| 203 | + 재현: integrating-with-email.mdx — FC가 이미지만 포함된 <li>를 |
| 204 | + ' 12.'로 변환하지만, improved.mdx에서는 제거됨. |
| 205 | + """ |
| 206 | + text = " 11. item\n 12.\n 13. next\n" |
| 207 | + assert _normalize_empty_list_items(text) == " 11. item\n 13. next\n" |
| 208 | + |
| 209 | + def test_preserves_item_with_content(self): |
| 210 | + """내용이 있는 리스트 항목은 제거하지 않는다.""" |
| 211 | + text = " 1. first item\n 2. second item\n" |
| 212 | + assert _normalize_empty_list_items(text) == text |
| 213 | + |
| 214 | + def test_removes_with_tab_indent(self): |
| 215 | + """탭 들여쓰기인 경우에도 제거한다.""" |
| 216 | + text = "\t5.\n\t6. content\n" |
| 217 | + assert _normalize_empty_list_items(text) == "\t6. content\n" |
| 218 | + |
| 219 | + |
| 220 | +# --- _normalize_consecutive_blank_lines 단위 테스트 --- |
| 221 | + |
| 222 | + |
| 223 | +class TestNormalizeConsecutiveBlankLines: |
| 224 | + def test_triple_newline_collapsed(self): |
| 225 | + """3개 이상 연속 개행을 2개로 정규화한다. |
| 226 | +
|
| 227 | + 재현: identity-providers.mdx, user-management.mdx — FC가 블록 사이에 |
| 228 | + 추가 빈 줄을 삽입하거나, empty list item 제거 후 연속 빈 줄이 남음. |
| 229 | + """ |
| 230 | + text = "para1\n\n\npara2\n" |
| 231 | + assert _normalize_consecutive_blank_lines(text) == "para1\n\npara2\n" |
| 232 | + |
| 233 | + def test_double_newline_unchanged(self): |
| 234 | + """2개 개행(단일 빈 줄)은 변경하지 않는다.""" |
| 235 | + text = "para1\n\npara2\n" |
| 236 | + assert _normalize_consecutive_blank_lines(text) == text |
| 237 | + |
| 238 | + def test_quadruple_newline_collapsed(self): |
| 239 | + """4개 이상 연속 개행도 2개로 정규화한다.""" |
| 240 | + text = "para1\n\n\n\npara2\n" |
| 241 | + assert _normalize_consecutive_blank_lines(text) == "para1\n\npara2\n" |
| 242 | + |
| 243 | + |
| 244 | +# --- _normalize_blank_line_after_br 단위 테스트 --- |
| 245 | + |
| 246 | + |
| 247 | +class TestNormalizeBlankLineAfterBr: |
| 248 | + def test_removes_blank_after_br(self): |
| 249 | + """<br/> 뒤의 빈 줄을 제거한다. |
| 250 | +
|
| 251 | + 재현: integrating-with-email.mdx — improved.mdx에서 빈 리스트 번호 |
| 252 | + 제거 후 <br/> 바로 뒤에 빈 줄이 남음. FC는 빈 줄 없이 출력. |
| 253 | + """ |
| 254 | + text = "text<br/>\n\nnext\n" |
| 255 | + assert _normalize_blank_line_after_br(text) == "text<br/>\nnext\n" |
| 256 | + |
| 257 | + def test_preserves_br_without_blank(self): |
| 258 | + """<br/> 뒤에 빈 줄이 없으면 변경하지 않는다.""" |
| 259 | + text = "text<br/>\nnext\n" |
| 260 | + assert _normalize_blank_line_after_br(text) == text |
| 261 | + |
| 262 | + def test_handles_self_closing_variants(self): |
| 263 | + """<br /> 형태도 처리한다.""" |
| 264 | + text = "text<br />\n\nnext\n" |
| 265 | + assert _normalize_blank_line_after_br(text) == "text<br />\nnext\n" |
| 266 | + |
| 267 | + |
193 | 268 | # --- _normalize_table_cell_padding 단위 테스트 --- |
194 | 269 |
|
195 | 270 |
|
@@ -274,6 +349,44 @@ def test_minimal_norm_link_spacing_multiple_items(): |
274 | 349 | assert result.passed is True |
275 | 350 |
|
276 | 351 |
|
| 352 | +def test_minimal_norm_empty_list_item_passes(): |
| 353 | + """빈 리스트 번호 차이는 최소 정규화로 통과한다. |
| 354 | +
|
| 355 | + 재현: integrating-with-email.mdx — improved.mdx에서 '12.' 제거, |
| 356 | + FC verify.mdx에서 '12.' 출력. 정규화로 양쪽 모두 해당 줄 제거. |
| 357 | + """ |
| 358 | + result = verify_roundtrip( |
| 359 | + expected_mdx=" 11. item\n 13. next\n", |
| 360 | + actual_mdx=" 11. item\n 12.\n 13. next\n", |
| 361 | + ) |
| 362 | + assert result.passed is True |
| 363 | + |
| 364 | + |
| 365 | +def test_minimal_norm_consecutive_blank_lines_passes(): |
| 366 | + """연속 빈 줄 차이는 최소 정규화로 통과한다. |
| 367 | +
|
| 368 | + 재현: identity-providers.mdx — FC가 블록 사이에 빈 줄을 추가 삽입. |
| 369 | + """ |
| 370 | + result = verify_roundtrip( |
| 371 | + expected_mdx="para1\n\npara2\n", |
| 372 | + actual_mdx="para1\n\n\npara2\n", |
| 373 | + ) |
| 374 | + assert result.passed is True |
| 375 | + |
| 376 | + |
| 377 | +def test_minimal_norm_blank_line_after_br_passes(): |
| 378 | + """<br/> 뒤 빈 줄 차이는 최소 정규화로 통과한다. |
| 379 | +
|
| 380 | + 재현: integrating-with-email.mdx — empty list item 제거 후 |
| 381 | + <br/> 뒤에 빈 줄이 남는 차이. |
| 382 | + """ |
| 383 | + result = verify_roundtrip( |
| 384 | + expected_mdx="text<br/>\n\nnext\n", |
| 385 | + actual_mdx="text<br/>\nnext\n", |
| 386 | + ) |
| 387 | + assert result.passed is True |
| 388 | + |
| 389 | + |
277 | 390 | def test_strict_mode_still_fails_on_trailing_ws(): |
278 | 391 | """strict 모드: trailing whitespace 차이는 여전히 실패한다 (최소 정규화 대상 아님).""" |
279 | 392 | result = verify_roundtrip( |
|
0 commit comments