def test_parse_suffix_section(app): source = """ ## Section 1 - [1A](./1A.md) - [1B](./1B.md) --- [2A](./2A.md) [2B](./2B.md) [2C](./2C.md) """ parser = SummaryParser(app, source) actual = parser.parse_sections() expected = [ Section(title='Section 1', parts=[ Part('1A', source='./1A.md', level=0), Part('1B', source='./1B.md', level=0), ]), Section(parts=[ Part('2A', source='./2A.md'), Part('2B', source='./2B.md'), Part('2C', source='./2C.md'), ]), ] assert actual == expected
def test_create_part_raises_value_error_when_link_is_empty(app): src = "[Empty]()\n" parser = SummaryParser(app, src) element = parser.stream.parse_tree.find('.//a') with pytest.raises(ValueError): parser.create_part(element)
def test_parse_sections_with_multiple_sections_where_first_section_has_no_heading( app): source = """ - [1A](./1A.md) - [1B](./1B.md) - [1C](./1C.md) ## Section 2 - [2A](./2A.md) """ parser = SummaryParser(app, source) actual = parser.parse_sections() expected = [ Section(parts=[ Part('1A', source='./1A.md', level=0), Part('1B', source='./1B.md', level=0), Part('1C', source='./1C.md', level=0), ]), Section(title='Section 2', parts=[ Part('2A', source='./2A.md', level=0), ]), ] assert actual == expected
def test_parse_sections_with_one_section_and_no_heading(app): source = """ - [1A](./1A.md) - [1B](./1B.md) - [1C](./1C.md) """ parser = SummaryParser(app, source) actual = parser.parse_sections() expected = [ Section(parts=[ Part('1A', source='./1A.md', level=0), Part('1B', source='./1B.md', level=0), Part('1C', source='./1C.md', level=0), ]) ] assert actual == expected
def test_parse_section_with_one_section_excluding_non_links(app): src = """ - [First](./first.md) - [Second](./second.md) - Item 1 - Item 2 - [Third](./third.md)""" parser = SummaryParser(app, src) actual = parser.parse_sections() expected = [ Section(parts=[ Part('First', source='./first.md', level=0), Part('Second', source='./second.md', level=0), Part('Third', source='./third.md', level=0), ]) ] assert actual == expected
def test_parse_sections_with_multiple_sections(app): source = """ ## Section 1 - [1A](./1A.md) - [1B](./1B.md) - [1C](./1C.md) --- - [2A](./2A.md) - [2B](./2B.md) - [2C](./2C.md) - [2D](./2D.md) #### Section 3 - [3A](./3A.md) """ parser = SummaryParser(app, source) actual = parser.parse_sections() expected = [ Section(title='Section 1', parts=[ Part('1A', source='./1A.md', level=0), Part('1B', source='./1B.md', level=0), Part('1C', source='./1C.md', level=0), ]), Section(parts=[ Part('2A', source='./2A.md', level=0), Part('2B', source='./2B.md', level=0), Part('2C', source='./2C.md', level=0), Part('2D', source='./2D.md', level=0), ]), Section(title='Section 3', parts=[ Part('3A', source='./3A.md', level=0), ]), ] assert actual == expected
def parse_summary(book): """Parse the text to create a ``Summary`` object. The ``text``, read from a ``SUMMARY.md`` file, is parsed into a ``Summary`` object, which acts as a sort of "recipe" to be used when loading a book's contents from disk. It represents the structure of the book, in-so-far as how the chapters will be compiled and in what order. Summary Format -------------- The format of the ``SUMMARY.md`` might contain the following elements: - **Title:** It's common practice to begin with a title, e.g. ``# Summary``. It's not mandatory and the parser (currently) ignores it, so you can too if you feel like it. - **Prefix Chapters:** Before the main numbered chapters you can add one or more chapter elements that will not be numbered. This is useful for forewords, introductions, etc. There are however some constraints: (1) You can not nest prefix chapters, they should all be on the root level, and (2) you can not add prefix chapters once you have added numbered chapters.:: [Title of prefix element](relative/path/to/markdown.md) - **Numbered Chapter:** Numbered chapters are the main content of the book, they will be numbered and can be nested, resulting in a nice hierarchy (chapters, sub-chapters, etc.).:: - [Title of the Chapter](relative/path/to/markdown.md) You can either use - or * to indicate a numbered chapter, the parser doesn't care but you'll probably want to stay consistent. - **Suffix Chapter:** After the numbered chapters you can add a couple of non-numbered chapters. They are the same as prefix chapters but come after the numbered chapters instead of before. All other elements are unsupported and will be ignored at best, or result in an error at worst. Summary Example --------------- :: # Table of Contents [Introduction](./indtroduction.md) [Preface](./preface.md) - [Chapter 1](./chapter1.md) - [Chapter 2](./chapter2.md) - [Chapter 3](./chapter3.md) - [Chapter 4](./chapter4.md) [Appendix A](./appendix-a.md) [Appendix B](./appendix-b.md) """ if not book: raise InvalidBookError('Unable to parse summary for unknown book.') summary = None app = book.app summary_file = parse_structure_file(book, 'summary') if not summary_file: app.logger.warn('no summary file in this book') summary = Summary() else: app.logger.debug('summary file found at: {}'.format(summary_file)) with open(summary_file) as f: summary_content = f.read() if not summary_content: summary = Summary() else: parser = SummaryParser(app, summary_content, book=book) summary = parser.parse() # Insert README as first entry if not in SUMMARY.md # var readmeArticle = summary.getByPath(readmeFile.getPath()); # if (readmeFile.exists() && !readmeArticle) { # summary = SummaryModifier.unshiftArticle(summary, { # title: 'Introduction', # ref: readmeFile.getPath() # }); # } book.summary = summary return summary
def test_parse_sections_with_no_sections(app): parser = SummaryParser(app, '') actual = parser.parse_sections() assert actual == []
def test_parse_initial_title(app, source, expected): parser = SummaryParser(app, source) actual = parser.parse_title() assert actual == expected
def test_create_part(app, source, expected): parser = SummaryParser(app, source) element = parser.stream.parse_tree.find('.//a') actual = parser.create_part(element) assert actual == expected