Repository: great-expectations/great_expectations
Pull request: [BUGFIX] Missing `markown_text.j2` jinja template
Changes proposed in this pull request:
- adds missing `markdown_text.j2` jinja template
- some formatting changes for better rendering in Markdown. This includes `markdown_collapse.j2`, which now renders `collapse` content blocks as indented header blocks,  rather than just a level-4 header.
- the message in `markdown_graph.j2` template has been brought in line with the changes in `markdown_collapse.j2`. The message that `Graphs are not currently supported in markdown` is now an indented header block. 
- In `tests/render/test_default_markdown_view.py`, added fixture for `expectation_suite_to_render_with_notes` and associated tests. Also cleaned up the Markdown output. 
- closes #2098 


Problem statement:
Missing markown_text jinja template
**Describe the bug**
I am trying to render our JSON expectations to markdown. I followed a tutorial that showed how to do that for validation results but I think a jinja template is missing.

Here is the traceback:
```
~/Library/Caches/pypoetry/virtualenvs/ge-MUDrC1sS-py3.7/lib/python3.7/site-packages/great_expectations/render/view/view.py in _get_template(self, template)
    106         env.filters["add_data_context_id_to_url"] = self.add_data_context_id_to_url
    107 
--> 108         template = env.get_template(template)
    109         template.globals["now"] = lambda: datetime.datetime.now(datetime.timezone.utc)
    110 

TemplateNotFound: markdown_text.j2
```

I just hacked a template just containing:
```
{{ text | render_content_block }}
```
and it fixed the error, however the comments added to the expectations are missing!

**To Reproduce**
```
import great_expectations as ge
from great_expectations.render.renderer import ValidationResultsPageRenderer, ExpectationSuitePageRenderer
from great_expectations.render.view import DefaultMarkdownPageView
from great_expectations.validation_operators.types.validation_operator_result import (
    ValidationOperatorResult,
)

context = ge.DataContext()

def render_to_markdown(run_info_at_end, expectations):
    expectation_suite_page_renderer = ExpectationSuitePageRenderer()
    rendered_document_content_list = expectation_suite_page_renderer.render(
        expectations
    )
    return DefaultMarkdownPageView().render(rendered_document_content_list)

expectations = context.get_expectation_suite("suite-name")

render_to_markdown(True, expectations)
```

**Expected behavior**
I would expect the expectations to be rendered as markdown

**Environment (please complete the following information):**
 - OS: macOS
 - GE Version: 0.12.10

Hints from issue discussion:
Thank you for reporting this, @lccambiaghi ! We are going to triage this issue within next couple of days.
@lccambiaghi Could you please post the link to the tutorial that you followed? Thank you!

This does look like a bug. Adding to the core team's queue.
I adapted the code from this util function: https://github.com/great-expectations/great_expectations/blob/develop/great_expectations/render/page_renderer_util.py#L37

I hope the example I provided is clear enough. We have worked around it for the moment by adding the custom jinja template to a custom_views_directory (nice feature!) 

```python
custom_views_directory = "assets/jinja_templates"

def render_expectations_to_markdown(expectations):
    rendered_dcument_contents = {
        dataset_name: ExpectationSuitePageRenderer().render(expectation)
        for dataset_name, expectation in expectations.items()
    }

    markdown_rendered_documents = {
        dataset_name: DefaultMarkdownPageView(custom_views_directory=custom_views_directory).render(
            document=rendered_document_content
        )
        for dataset_name, rendered_document_content in rendered_dcument_contents.items()
    }

    return markdown_rendered_documents
```

The output of this however is badly formatted. I use the python library `mdformat` to format the markdown plus some custom `replace()` calls. Of course, I would love this functionality to be implemented upstream, maybe streamlined with the other rendering capabilities you have.

One issue with our current approach is that comments to JSON expectations are not rendered to markdown. Here is a screenshot of the result:

![Screenshot 2020-12-10 at 11 13 10](https://user-images.githubusercontent.com/15185927/101758594-cd800c00-3ad8-11eb-9d2a-5a42179c71d1.png)

I would expect the comments to be inserted within the dotted lines.

Thank you so much for looking at this!
Hi @eugmandel I was wondering if there had been any progress on this issue? I don't mean to rush you or anything, I am sure you have many exciting features coming that are more worthy of your time! 
@Aylr @eugmandel what is the status of that issue?
Hi @gardnerdev and @lccambiaghi thank you for submitting the issue, and sorry about the delay. 

I just got assigned this issue through the Triage process. Should have something for you soon :)

Commit message: Extra comment is removed now

Patch to review:
diff --git a/great_expectations/render/view/templates/markdown_collapse.j2 b/great_expectations/render/view/templates/markdown_collapse.j2
index 4f34ef0b4741..e139371461ef 100644
--- a/great_expectations/render/view/templates/markdown_collapse.j2
+++ b/great_expectations/render/view/templates/markdown_collapse.j2
@@ -1,9 +1,9 @@
 
----
+{% filter indent(width=4) %}
 {% include 'markdown_content_block_header.j2' %}
 {% for block in content_block["collapse"] %}
   {% set collapse_block_loop = loop %}
   {% set content_block_id = content_block_id ~ "-collapse-item-" ~ collapse_block_loop.index %}
-  {{ block | render_content_block(content_block_id=content_block_id) }}
+    {{ block | render_content_block(content_block_id=content_block_id) }}
 {% endfor %}
----
+{% endfilter %}
diff --git a/great_expectations/render/view/templates/markdown_graph.j2 b/great_expectations/render/view/templates/markdown_graph.j2
index 646848af6b29..8da695b1243a 100644
--- a/great_expectations/render/view/templates/markdown_graph.j2
+++ b/great_expectations/render/view/templates/markdown_graph.j2
@@ -1 +1,4 @@
-Note: Graphs are not currently supported in markdown
+{% filter indent(width=4) %}
+#### Notes:
+Graphs are not currently supported in markdown
+{% endfilter %}
diff --git a/great_expectations/render/view/templates/markdown_text.j2 b/great_expectations/render/view/templates/markdown_text.j2
new file mode 100644
index 000000000000..52f0b6276a17
--- /dev/null
+++ b/great_expectations/render/view/templates/markdown_text.j2
@@ -0,0 +1,15 @@
+{% if not content_block_body_styling and content_block.get("styling") %}
+    {% set content_block_body_styling = content_block.get("styling") | render_styling %}
+{% endif %}
+
+<div id="{{content_block_id}}-body" {{ content_block_body_styling | replace("{{section_id}}", section_id) | replace("{{content_block_id}}", content_block_id) }}>
+  {% include 'markdown_content_block_header.j2' %}
+  {% for text in content_block["text"] -%}
+      {% if text is mapping and "styling" in text -%}
+          {% set paragraph_styling = text["styling"].get("parent", {}) | render_styling -%}
+      {% else -%}
+          {% set paragraph_styling = "" -%}
+      {% endif -%}
+      <div {{ paragraph_styling }}>{{ text | render_content_block }}</div>
+    {% endfor -%}
+</div>
diff --git a/great_expectations/render/view/view.py b/great_expectations/render/view/view.py
index c37836e4055d..290cf92c370c 100644
--- a/great_expectations/render/view/view.py
+++ b/great_expectations/render/view/view.py
@@ -176,7 +176,9 @@ def render_content_block(
         content_block_type = content_block.get("content_block_type")
         if content_block_type is None:
             return content_block
-        if render_to_markdown:
+
+        if render_to_markdown and content_block_type is not "markdown":
+            # dont add `markdown_` for `markdown.j2`
             template_filename = f"markdown_{content_block_type}.j2"
         else:
             template_filename = f"{content_block_type}.j2"
@@ -504,6 +506,11 @@ def render_string_template(self, template: pTemplate) -> pTemplate:
         if not isinstance(template, (dict, OrderedDict)):
             return template
 
+        # replace and render any horizontal lines using ***
+        tag = template.get("tag", None)
+        if tag and tag == "hr":
+            template["template"] = "***"
+
         # if there are any groupings of two or more $, we need to double the groupings to account
         # for template string substitution escaping
         template["template"] = re.sub(
@@ -524,6 +531,11 @@ def render_string_template(self, template: pTemplate) -> pTemplate:
             if parameter == "html_success_icon":
                 template["params"][parameter] = ""
                 continue
+            # to escape any values that are '*' which, when combined with bold ('**') in markdown,
+            # does not give the output we want.
+            elif template["params"][parameter] == "*":
+                template["params"][parameter] = "\\*"
+                continue
 
             template["params"][parameter] = pTemplate(
                 base_param_template_string
diff --git a/tests/render/test_default_markdown_view.py b/tests/render/test_default_markdown_view.py
index 6d7a35338a42..50c53bb6f6dd 100644
--- a/tests/render/test_default_markdown_view.py
+++ b/tests/render/test_default_markdown_view.py
@@ -6,10 +6,14 @@
 
 import great_expectations as ge
 import great_expectations.render as render
+from great_expectations.core import ExpectationConfiguration, ExpectationSuite
 from great_expectations.core.expectation_validation_result import (
     ExpectationSuiteValidationResultSchema,
 )
-from great_expectations.render.renderer import ValidationResultsPageRenderer
+from great_expectations.render.renderer import (
+    ExpectationSuitePageRenderer,
+    ValidationResultsPageRenderer,
+)
 from great_expectations.render.types import (
     RenderedDocumentContent,
     RenderedHeaderContent,
@@ -40,6 +44,44 @@ def validation_operator_result():
         return validation_operator_result
 
 
+@pytest.fixture()
+def expectation_suite_to_render_with_notes():
+    expectation_suite = ExpectationSuite(
+        expectation_suite_name="default",
+        meta={"great_expectations_version": "0.13.0-test"},
+        expectations=[
+            ExpectationConfiguration(
+                expectation_type="expect_column_to_exist",
+                kwargs={"column": "infinities"},
+            ),
+            ExpectationConfiguration(
+                expectation_type="expect_column_to_exist", kwargs={"column": "nulls"}
+            ),
+            ExpectationConfiguration(
+                expectation_type="expect_column_to_exist", kwargs={"column": "naturals"}
+            ),
+            ExpectationConfiguration(
+                expectation_type="expect_column_distinct_values_to_be_in_set",
+                kwargs={"column": "irrationals", "value_set": ["*", "1st", "2nd"]},
+            ),
+            ExpectationConfiguration(
+                expectation_type="expect_column_values_to_be_unique",
+                kwargs={"column": "testings"},
+                meta={
+                    "notes": {
+                        "content": [
+                            "Example notes about this expectation. **Markdown** `Supported`.",
+                            "Second example note **with** *Markdown*",
+                        ],
+                        "format": "markdown",
+                    }
+                },
+            ),
+        ],
+    )
+    return expectation_suite
+
+
 def test_render_section_page():
     section = RenderedSectionContent(
         **{
@@ -124,65 +166,24 @@ def test_snapshot_render_section_page_with_fixture_data(validation_operator_resu
         md_str
         == """
 # Validation Results
-
-
-
-
 ## Overview
 ### **Expectation Suite:** **basic.warning**
 **Data asset:** **None**
 **Status:**  **Failed**
-
-
-
-
-
 ### Statistics
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Evaluated Expectations  | 11
 Successful Expectations  | 9
 Unsuccessful Expectations  | 2
 Success Percent  | ≈81.82%
-
-
-
-
-
 ## Table-Level Expectations
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ❌  | Must have greater than or equal to **27000** and less than or equal to **33000** rows.  | 30
 ✅  | Must have exactly **3** columns.  | 3
 ✅  | Must have these columns in this order: **Team**, ** "Payroll (millions)"**, ** "Wins"**  | ['Team', ' "Payroll (millions)"', ' "Wins"']
-
-
-
-
-
 ##  "Payroll (millions)"
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
@@ -191,10 +192,6 @@ def test_snapshot_render_section_page_with_fixture_data(validation_operator_resu
 ✅  | mean must be greater than or equal to **97.01899999999998** and less than or equal to **99.01899999999998**.  | ≈98.019
 ❌  | median must be greater than or equal to **84000.75** and less than or equal to **86000.75**.  | 85.75
 ✅  | quantiles must be within the following value ranges.
-
-
-
-
  | Quantile | Min Value | Max Value |
  | ------------  | ------------  | ------------ |
 0.05  | 54.37  | 56.37
@@ -203,10 +200,6 @@ def test_snapshot_render_section_page_with_fixture_data(validation_operator_resu
 Q3  | 116.62  | 118.62
 0.95  | 173.54  | 175.54
   |
-
-
-
-
  | Quantile | Value |
  | ------------  | ------------ |
 0.05  | 55.37
@@ -214,143 +207,49 @@ def test_snapshot_render_section_page_with_fixture_data(validation_operator_resu
 Median  | 83.31
 Q3  | 117.62
 0.95  | 174.54
-
-
-
-
-
-
 ## Team
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
 ✅  | values must always be greater than or equal to **1** characters long.  | 0% unexpected
-
-
-
-
-
-
-
 ### Info
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Great Expectations Version  | 0.11.8+4.g4ab34df3.dirty
 Run Name  | getest run
 Run Time  | 2020-07-27T17:19:32.959193+00:00
-
-
-
-
-
 ### Batch Markers
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **ge_load_time**  | **20200727T171932.954810Z**
 **pandas_data_fingerprint**  | **8c46fdaf0bd356fd58b7bcd9b2e6012d**
-
-
-
-
-
 ### Batch Kwargs
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **PandasInMemoryDF**  | **True**
 **datasource**  | **getest**
 **ge_batch_id**  | **56615f40-d02d-11ea-b6ea-acde48001122**
-
-
-
-
 -----------------------------------------------------------
 Powered by [Great Expectations](https://greatexpectations.io/)
 # Validation Results
-
-
-
-
 ## Overview
 ### **Expectation Suite:** **basic.warning**
 **Data asset:** **None**
 **Status:**  **Failed**
-
-
-
-
-
 ### Statistics
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Evaluated Expectations  | 11
 Successful Expectations  | 9
 Unsuccessful Expectations  | 2
 Success Percent  | ≈81.82%
-
-
-
-
-
 ## Table-Level Expectations
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ❌  | Must have greater than or equal to **27000** and less than or equal to **33000** rows.  | 30
 ✅  | Must have exactly **3** columns.  | 3
 ✅  | Must have these columns in this order: **Team**, ** "Payroll (millions)"**, ** "Wins"**  | ['Team', ' "Payroll (millions)"', ' "Wins"']
-
-
-
-
-
 ##  "Payroll (millions)"
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
@@ -359,10 +258,6 @@ def test_snapshot_render_section_page_with_fixture_data(validation_operator_resu
 ✅  | mean must be greater than or equal to **97.01899999999998** and less than or equal to **99.01899999999998**.  | ≈98.019
 ❌  | median must be greater than or equal to **84000.75** and less than or equal to **86000.75**.  | 85.75
 ✅  | quantiles must be within the following value ranges.
-
-
-
-
  | Quantile | Min Value | Max Value |
  | ------------  | ------------  | ------------ |
 0.05  | 54.37  | 56.37
@@ -371,10 +266,6 @@ def test_snapshot_render_section_page_with_fixture_data(validation_operator_resu
 Q3  | 116.62  | 118.62
 0.95  | 173.54  | 175.54
   |
-
-
-
-
  | Quantile | Value |
  | ------------  | ------------ |
 0.05  | 55.37
@@ -382,81 +273,28 @@ def test_snapshot_render_section_page_with_fixture_data(validation_operator_resu
 Median  | 83.31
 Q3  | 117.62
 0.95  | 174.54
-
-
-
-
-
-
 ## Team
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
 ✅  | values must always be greater than or equal to **1** characters long.  | 0% unexpected
-
-
-
-
-
-
-
 ### Info
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Great Expectations Version  | 0.11.8+4.g4ab34df3.dirty
 Run Name  | getest run
 Run Time  | 2020-07-27T17:19:32.959193+00:00
-
-
-
-
-
 ### Batch Markers
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **ge_load_time**  | **20200727T171932.954810Z**
 **pandas_data_fingerprint**  | **8c46fdaf0bd356fd58b7bcd9b2e6012d**
-
-
-
-
-
 ### Batch Kwargs
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **PandasInMemoryDF**  | **True**
 **datasource**  | **getest**
 **ge_batch_id**  | **56615f40-d02d-11ea-b6ea-acde48001122**
-
-
-
-
 -----------------------------------------------------------
 Powered by [Great Expectations](https://greatexpectations.io/)
 """.replace(
@@ -494,71 +332,30 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 
     md_str = md_str.replace(" ", "").replace("\t", "").replace("\n", "")
 
-    print(md_str)
+    # print(md_str)
 
     assert (
         md_str
         == """
 # Validation Results
-
-
-
-
 ## Overview
 ### **Expectation Suite:** **basic.warning**
 **Dataasset:** **None**
 **Status:**  **Failed**
-
-
-
-
-
 ### Statistics
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Evaluated Expectations  | 11
 Successful Expectations  | 9
 Unsuccessful Expectations  | 2
 Success Percent  | ≈81.82%
-
-
-
-
-
 ## Table-Level Expectations
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ❌  | Must have greater than or equal to **27000** and less than or equal to **33000** rows.  | 30
 ✅  | Must have exactly **3** columns.  | 3
 ✅  | Must have these columns in this order: **Team**, ** "Payroll (millions)"**, ** "Wins"**  | ['Team', ' "Payroll (millions)"', ' "Wins"']
-
-
-
-
-
 ##  "Payroll (millions)"
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
@@ -567,10 +364,6 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 ✅  | mean must be greater than or equal to **97.01899999999998** and less than or equal to **99.01899999999998**.  | ≈98.019
 ❌  | median must be greater than or equal to **84000.75** and less than or equal to **86000.75**.  | 85.75
 ✅  | quantiles must be within the following value ranges.
-
-
-
-
  | Quantile | Min Value | Max Value |
  | ------------  | ------------  | ------------ |
 0.05  | 54.37  | 56.37
@@ -579,10 +372,6 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 Q3  | 116.62  | 118.62
 0.95  | 173.54  | 175.54
   |
-
-
-
-
  | Quantile | Value |
  | ------------  | ------------ |
 0.05  | 55.37
@@ -590,143 +379,49 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 Median  | 83.31
 Q3  | 117.62
 0.95  | 174.54
-
-
-
-
-
-
 ## Team
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
 ✅  | values must always be greater than or equal to **1** characters long.  | 0% unexpected
-
-
-
-
-
-
-
 ### Info
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Great Expectations Version  | 0.11.8+4.g4ab34df3.dirty
 Run Name  | getest run
 Run Time  | 2020-07-27T17:19:32.959193+00:00
-
-
-
-
-
 ### Batch Markers
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **ge_load_time**  | **20200727T171932.954810Z**
 **pandas_data_fingerprint**  | **8c46fdaf0bd356fd58b7bcd9b2e6012d**
-
-
-
-
-
 ### Batch Kwargs
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **PandasInMemoryDF**  | **True**
 **datasource**  | **getest**
 **ge_batch_id**  | **56615f40-d02d-11ea-b6ea-acde48001122**
-
-
-
-
 -----------------------------------------------------------
 Powered by [Great Expectations](https://greatexpectations.io/)
 # Validation Results
-
-
-
-
 ## Overview
 ### **Expectation Suite:** **basic.warning**
 **Dataasset:** **None**
 **Status:**  **Failed**
-
-
-
-
-
 ### Statistics
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Evaluated Expectations  | 11
 Successful Expectations  | 9
 Unsuccessful Expectations  | 2
 Success Percent  | ≈81.82%
-
-
-
-
-
 ## Table-Level Expectations
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ❌  | Must have greater than or equal to **27000** and less than or equal to **33000** rows.  | 30
 ✅  | Must have exactly **3** columns.  | 3
 ✅  | Must have these columns in this order: **Team**, ** "Payroll (millions)"**, ** "Wins"**  | ['Team', ' "Payroll (millions)"', ' "Wins"']
-
-
-
-
-
 ##  "Payroll (millions)"
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
@@ -735,10 +430,6 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 ✅  | mean must be greater than or equal to **97.01899999999998** and less than or equal to **99.01899999999998**.  | ≈98.019
 ❌  | median must be greater than or equal to **84000.75** and less than or equal to **86000.75**.  | 85.75
 ✅  | quantiles must be within the following value ranges.
-
-
-
-
  | Quantile | Min Value | Max Value |
  | ------------  | ------------  | ------------ |
 0.05  | 54.37  | 56.37
@@ -747,10 +438,6 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 Q3  | 116.62  | 118.62
 0.95  | 173.54  | 175.54
   |
-
-
-
-
  | Quantile | Value |
  | ------------  | ------------ |
 0.05  | 55.37
@@ -758,84 +445,85 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 Median  | 83.31
 Q3  | 117.62
 0.95  | 174.54
-
-
-
-
-
-
 ## Team
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
 ✅  | values must always be greater than or equal to **1** characters long.  | 0% unexpected
-
-
-
-
-
-
-
 ### Info
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Great Expectations Version  | 0.11.8+4.g4ab34df3.dirty
 Run Name  | getest run
 Run Time  | 2020-07-27T17:19:32.959193+00:00
-
-
-
-
-
 ### Batch Markers
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **ge_load_time**  | **20200727T171932.954810Z**
 **pandas_data_fingerprint**  | **8c46fdaf0bd356fd58b7bcd9b2e6012d**
-
-
-
-
-
 ### Batch Kwargs
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **PandasInMemoryDF**  | **True**
 **datasource**  | **getest**
 **ge_batch_id**  | **56615f40-d02d-11ea-b6ea-acde48001122**
+-----------------------------------------------------------
+Powered by [Great Expectations](https://greatexpectations.io/)
+""".replace(
+            " ", ""
+        )
+        .replace("\t", "")
+        .replace("\n", "")
+    )
 
 
-
-
+def test_render_expectation_suite_for_Markdown(expectation_suite_to_render_with_notes):
+    expectation_suite_page_renderer = ExpectationSuitePageRenderer()
+    rendered_document_content_list = expectation_suite_page_renderer.render(
+        expectation_suite_to_render_with_notes
+    )
+    md_str = DefaultMarkdownPageView().render(rendered_document_content_list)
+    md_str = " ".join(md_str)
+    md_str = md_str.replace(" ", "").replace("\t", "").replace("\n", "")
+    assert (
+        md_str
+        == """
+   # Validation Results
+## Overview
+### Info
+ |  |  |
+ | ------------  | ------------ | 
+Expectation Suite Name  | default  
+Great Expectations Version  | 0.13.0-test  
+<div id="section-1-content-block-3-body" class="col-12 table-responsive mt-1" >
+### Notes
+  <div >This Expectation suite currently contains 5 total Expectations across 5 columns.</div>
+    </div>
+## infinities
+  * is a required field.
+  * ***
+## irrationals
+  * distinct values must belong to this set: \* **1st** **2nd**.
+  * ***
+## naturals
+  * is a required field.
+  * ***
+## nulls
+  * is a required field.
+  * ***
+## testings
+  * values must be unique.
+    <div id="section-6-content-block-2-collapse-item-1-body" class="col-12 mt-2 mb-2" >
+    #### Notes:
+      <div ><p>Example notes about this expectation. <strong>Markdown</strong> <code>Supported</code>.</p>
+    </div>
+        <div ><p>Second example note <strong>with</strong> <em>Markdown</em></p>
+    </div>
+        </div>
+  * ***
 -----------------------------------------------------------
 Powered by [Great Expectations](https://greatexpectations.io/)
-""".replace(
+    """.replace(
             " ", ""
         )
         .replace("\t", "")


Code review comments:
diff_hunk: @@ -0,0 +1,15 @@
+{% if not content_block_body_styling and content_block.get("styling") %}
+    {% set content_block_body_styling = content_block.get("styling") | render_styling %}
+{% endif %}
+
+<div id="{{content_block_id}}-body" {{ content_block_body_styling | replace("{{section_id}}", section_id) | replace("{{content_block_id}}", content_block_id) }}>
+  {% include 'markdown_content_block_header.j2' %}
+  {% for text in content_block["text"] -%}
+      {% if text is mapping and "styling" in text -%}
+          {% set paragraph_styling = text["styling"].get("parent", {}) | render_styling -%}
path: great_expectations/render/view/templates/markdown_text.j2
text: @user1:
I love the use of the default in .get() here

@user1:
But I think the styling functionality should be removed since it's not applicable for markdown

diff_hunk: @@ -0,0 +1,15 @@
+{% if not content_block_body_styling and content_block.get("styling") %}
+    {% set content_block_body_styling = content_block.get("styling") | render_styling %}
+{% endif %}
+
+<div id="{{content_block_id}}-body" {{ content_block_body_styling | replace("{{section_id}}", section_id) | replace("{{content_block_id}}", content_block_id) }}>
path: great_expectations/render/view/templates/markdown_text.j2
text: @user1:
I think these `<div>` tags should be removed from the markdown template.

diff_hunk: @@ -747,95 +438,92 @@ def test_render_section_page_with_fixture_data_multiple_validations(
 Q3  | 116.62  | 118.62
 0.95  | 173.54  | 175.54
   |
-
-
-
-
  | Quantile | Value |
  | ------------  | ------------ |
 0.05  | 55.37
 Q1  | 75.48
 Median  | 83.31
 Q3  | 117.62
 0.95  | 174.54
-
-
-
-
-
-
 ## Team
-
-
-
-
-
-
-
-
  | Status | Expectation | Observed Value |
  | ------------  | ------------  | ------------ |
 ✅  | values must never be null.  | 100% not null
 ✅  | values must always be greater than or equal to **1** characters long.  | 0% unexpected
-
-
-
-
-
-
-
 ### Info
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 Great Expectations Version  | 0.11.8+4.g4ab34df3.dirty
 Run Name  | getest run
 Run Time  | 2020-07-27T17:19:32.959193+00:00
-
-
-
-
-
 ### Batch Markers
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **ge_load_time**  | **20200727T171932.954810Z**
 **pandas_data_fingerprint**  | **8c46fdaf0bd356fd58b7bcd9b2e6012d**
-
-
-
-
-
 ### Batch Kwargs
-
-
-
-
-
-
  |  |  |
  | ------------  | ------------ |
 **PandasInMemoryDF**  | **True**
 **datasource**  | **getest**
 **ge_batch_id**  | **56615f40-d02d-11ea-b6ea-acde48001122**
+-----------------------------------------------------------
+Powered by [Great Expectations](https://greatexpectations.io/)
+""".replace(
+            " ", ""
+        )
+        .replace("\t", "")
+        .replace("\n", "")
+    )
 
 
-
-
+def test_render_expectation_suite_for_Markdown(expectation_suite_to_render_with_notes):
+    expectation_suite_page_renderer = ExpectationSuitePageRenderer()
+    rendered_document_content_list = expectation_suite_page_renderer.render(
+        expectation_suite_to_render_with_notes
+    )
+    md_str = DefaultMarkdownPageView().render(rendered_document_content_list)
+    md_str = " ".join(md_str)
+    md_str = md_str.replace(" ", "").replace("\t", "").replace("\n", "")
+    assert (
+        md_str
+        == """
+   # Validation Results
+## Overview
+### Info
+ |  |  |
+ | ------------  | ------------ | 
+Expectation Suite Name  | default  
+Great Expectations Version  | 0.13.0-test  
+<div id="section-1-content-block-3-body" class="col-12 table-responsive mt-1" >
path: tests/render/test_default_markdown_view.py
text: @user1:
We should not be rendering html in our markdown views.