"""
This module is indended to provide a pluggable way to add assertions about
the rendered content of XBlocks.

For each view on the XBlock, this module defines a @singledispatch function
that can be used to test the contents of the rendered html.

The functions are of the form:

    @singledispatch
    def assert_student_view_valid_html(block, html):
        '''
        block: The block that rendered the HTML
        html: An lxml.html parse of the HTML for this block
        '''
        ...
        assert foo
        ...
        for child in children:
            assert_xblock_html(child, child_html)

    @singledispatch
    def assert_student_view_invalid_html(block, html):
        '''
        block: The block that rendered the HTML
        html: A string of unparsable html
        '''
        ...
        assert foo
        ...
        for child in children:
            assert_xblock_html(child, child_html)
        ...

A further extension would be to provide a companion set of functions that
resources that are provided to the Fragment
"""
import lxml.html
import lxml.etree

from singledispatch import singledispatch


@singledispatch
def assert_student_view_valid_html(block, html):
    """
    Asserts that the html generated by the `student_view` view is correct for
    the supplied block

    :param block: The :class:`XBlock` that generated the html
    :param html: The generated html as parsed by lxml.html
    """
    pass


@singledispatch
def assert_studio_view_valid_html(block, html):
    """
    Asserts that the html generated by the `studio_view` view is correct for
    the supplied block

    :param block: The :class:`XBlock` that generated the html
    :param html: The generated html as parsed by lxml.html
    """
    pass


@singledispatch
def assert_student_view_invalid_html(block, html):
    """
    Asserts that the html generated by the `student_view` view is correct for
    the supplied block, given that html wasn't parsable

    :param block: The :class:`XBlock` that generated the html
    :param html: A string, not parseable as html
    """
    assert False, "student_view should produce valid html"


@singledispatch
def assert_studio_view_invalid_html(block, html):
    """
    Asserts that the html generated by the `studio_view` view is correct for
    the supplied block

    :param block: The :class:`XBlock` that generated the html
    :param html: A string, not parseable as html
    """
    assert False, "studio_view should produce valid html"


def assert_student_view(block, fragment):
    """
    Helper function to assert that the `fragment` is valid output
    the specified `block`s `student_view`
    """
    try:
        html = lxml.html.fragment_fromstring(fragment.content)
    except lxml.etree.ParserError:
        assert_student_view_invalid_html(block, fragment.content)
    else:
        assert_student_view_valid_html(block, html)


def assert_studio_view(block, fragment):
    """
    Helper function to assert that the `fragment` is valid output
    the specified `block`s `studio_view`
    """
    try:
        html = lxml.html.fragment_fromstring(fragment.content)
    except lxml.etree.ParserError:
        assert_studio_view_invalid_html(block, fragment.content)
    else:
        assert_studio_view_valid_html(block, html)
