<NME> fountain_test.py
<BEF> # Copyright (c) 2011 Martin Vilcans
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php

from testcompat import TestCase

from screenplain.parsers import fountain
from screenplain.types import (
    Slug, Action, Dialog, DualDialog, Transition, Section, PageBreak
)
from screenplain.richstring import plain, italic, empty_string
from io import StringIO


def parse(lines):
    content = '\n'.join(lines)
    return fountain.parse(StringIO(content))


class SlugTests(TestCase):
    def test_slug_with_prefix(self):
        paras = list(parse([
            'INT. SOMEWHERE - DAY',
            '',
            'THIS IS JUST ACTION',
        ]))
        self.assertEquals([Slug, Action], [type(p) for p in paras])

    def test_slug_must_be_single_line(self):
        paras = list(parse([
            'INT. SOMEWHERE - DAY',
            'ANOTHER LINE',
            '',
            'Some action',
        ]))
        self.assertEquals([Dialog, Action], [type(p) for p in paras])
        # What looks like a scene headingis parsed as a character name.
        # Unexpected perhaps, but that's how I interpreted the spec.
        self.assertEquals(plain('INT. SOMEWHERE - DAY'), paras[0].character)
        self.assertEquals([plain('Some action')], paras[1].lines)

    def test_action_is_not_a_slug(self):
        paras = list(parse([
            '',
            'THIS IS JUST ACTION',
        ]))
        self.assertEquals([Action], [type(p) for p in paras])

    def test_two_lines_creates_no_slug(self):
        types = [type(p) for p in parse([
            '',
            '',
            'This is a slug',
            '',
        ])]
        # This used to be Slug. Changed in the Jan 2012 version of the spec.
        self.assertEquals([Action], types)

    def test_period_creates_slug(self):
        paras = parse([
            '.SNIPER SCOPE POV',
            '',
        ])
        self.assertEquals(1, len(paras))
        self.assertEquals(Slug, type(paras[0]))
        self.assertEquals(plain('SNIPER SCOPE POV'), paras[0].line)

    def test_more_than_one_period_does_not_create_slug(self):
        paras = parse([
            '..AND THEN...',
            '',
        ])
        self.assertEquals(1, len(paras))
        self.assertEquals(Action, type(paras[0]))
        self.assertEquals(plain('..AND THEN...'), paras[0].lines[0])

    def test_scene_number_is_parsed(self):
        paras = parse(['EXT SOMEWHERE - DAY #42#'])
        self.assertEquals(plain('EXT SOMEWHERE - DAY'), paras[0].line)
        self.assertEquals(plain('42'), paras[0].scene_number)

    def test_only_last_two_hashes_in_slug_used_for_scene_number(self):
        paras = parse(['INT ROOM #237 #42#'])
        self.assertEquals(plain('42'), paras[0].scene_number)
        self.assertEquals(plain('INT ROOM #237'), paras[0].line)

    def test_scene_number_must_be_alphanumeric(self):
        paras = parse(['.SOMEWHERE #*HELLO*#'])
        self.assertIsNone(paras[0].scene_number)
        self.assertEquals(
            (plain)(u'SOMEWHERE #') + (italic)(u'HELLO') + (plain)(u'#'),
            paras[0].line
        )


class SectionTests(TestCase):
    def test_section_parsed_correctly(self):
        paras = parse([
            '# first level',
            '',
            '## second level',
        ])
        self.assertEquals([Section, Section], [type(p) for p in paras])
        self.assertEquals(1, paras[0].level)
        self.assertEquals(plain('first level'), paras[0].text)
        self.assertEquals(2, paras[1].level)
        self.assertEquals(plain('second level'), paras[1].text)

    def test_multiple_sections_in_one_paragraph(self):
        paras = parse([
            '# first level',
            '## second level',
            '# first level again'
        ])
        self.assertEquals(
            [Section, Section, Section],
            [type(p) for p in paras]
        )
        self.assertEquals(1, paras[0].level)
        self.assertEquals(plain('first level'), paras[0].text)
        self.assertEquals(2, paras[1].level)
        self.assertEquals(plain('second level'), paras[1].text)
        self.assertEquals(1, paras[2].level)
        self.assertEquals(plain('first level again'), paras[2].text)

    def test_multiple_sections_with_synopsis(self):
        paras = parse([
            '# first level',
            '= level one synopsis',
            '## second level',
        ])
        self.assertEquals([
            Section(plain(u'first level'), 1, 'level one synopsis'),
            Section(plain(u'second level'), 2, None),
        ], paras)


class DialogTests(TestCase):
    # A Character element is any line entirely in caps, with one empty
    # line before it and without an empty line after it.
    def test_all_caps_is_character(self):
        paras = [p for p in parse([
            'SOME GUY',
            'Hello',
        ])]
        self.assertEquals(1, len(paras))
        dialog = paras[0]
        self.assertEquals(Dialog, type(dialog))
        self.assertEquals(plain('SOME GUY'), dialog.character)

    # Fountain would not be able to support a character named "23". We
    # might need a syntax to force a character element.
    def test_nonalpha_character(self):
        paras = list(parse([
            '23',
            'Hello',
        ]))
        self.assertEquals([Action], [type(p) for p in paras])

    def test_twospaced_line_is_not_character(self):
        paras = list(parse([
            'SCANNING THE AISLES...  ',
            'Where is that pit boss?',
        ]))
        self.assertEquals([Action], [type(p) for p in paras])

    def test_simple_parenthetical(self):
        paras = list(parse([
            'STEEL',
            '(starting the engine)',
            'So much for retirement!',
        ]))
        self.assertEquals(1, len(paras))
        dialog = paras[0]
        self.assertEqual(2, len(dialog.blocks))
        self.assertEquals([Dialog], [type(p) for p in paras])
        self.assertEquals(plain('McCLANE'), paras[0].character)

    def test_twospaced_line_is_not_character(self):
        paras = parse([
            'SCANNING THE AISLES...  ',
            'Where is that pit boss?',
        )

    def test_twospace_keeps_dialog_together(self):
        paras = list(parse([
            'SOMEONE',
            'One',
            '  ',
            'Two',
        ]))
        self.assertEquals([Dialog], [type(p) for p in paras])
        self.assertEquals([
            (False, plain('One')),
        self.assertEqual(
            (True, plain('(starting the engine)')),
        ], paras[0].blocks)

    def test_dual_dialog(self):
        paras = list(parse([
            'BRICK',
            'Fuck retirement.',
            '',
            'STEEL ^',
            'Fuck retirement!',
        ]))
        self.assertEquals([DualDialog], [type(p) for p in paras])
        dual = paras[0]
        self.assertEquals(plain('BRICK'), dual.left.character)
        ])
        self.assertEquals([Dialog], [type(p) for p in paras])
        self.assertEquals([
            (False, plain('One')),
            (False, empty_string),
            (False, plain('Two')),
        ], paras[0].blocks)

        )

    def test_dual_dialog_without_previous_dialog_is_ignored(self):
        paras = list(parse([
            'Brick strolls down the street.',
            '',
            'BRICK ^',
            'Nice retirement.',
        ]))
        self.assertEquals([Action, Dialog], [type(p) for p in paras])
        dialog = paras[1]
        self.assertEqual(plain('BRICK ^'), dialog.character)
            [(False, plain('Fuck retirement.'))],
            dual.left.blocks
        ], dialog.blocks)

    def test_leading_and_trailing_spaces_in_dialog(self):
        paras = list(parse([
            'JULIET',
            'O Romeo, Romeo! wherefore art thou Romeo?',
            '  Deny thy father and refuse thy name;  ',
            'Or, if thou wilt not, be but sworn my love,',
            " And I'll no longer be a Capulet.",
        ]))
        self.assertEquals([Dialog], [type(p) for p in paras])
        self.assertEquals([
            (False, plain(u'O Romeo, Romeo! wherefore art thou Romeo?')),
        ])
        self.assertEquals([Action, Dialog], [type(p) for p in paras])
        dialog = paras[1]
        self.assertEqual(plain('BRICK ^'), dialog.character)
        self.assertEqual([
            (False, plain('Nice retirement.'))
class TransitionTests(TestCase):

    def test_standard_transition(self):
        paras = list(parse([
            'Jack begins to argue vociferously in Vietnamese (?)',
            '',
            'CUT TO:',
            '',
            "EXT. BRICK'S POOL - DAY",
        ]))
        self.assertEquals([Action, Transition, Slug], [type(p) for p in paras])

    def test_transition_must_end_with_to(self):
        paras = list(parse([
            'CUT TOO:',
            '',
            "EXT. BRICK'S POOL - DAY",
        ]))
        self.assertEquals([Action, Slug], [type(p) for p in paras])

    def test_transition_needs_to_be_upper_case(self):
        paras = list(parse([
            'Jack begins to argue vociferously in Vietnamese (?)',
            '',
            'cut to:',
            '',
            "EXT. BRICK'S POOL - DAY",
        ]))
        self.assertEquals([Action, Action, Slug], [type(p) for p in paras])

    def test_not_a_transition_on_trailing_whitespace(self):
        paras = list(parse([
            'Jack begins to argue vociferously in Vietnamese (?)',
            '',
            'CUT TO: ',
            '',
            "EXT. BRICK'S POOL - DAY",
        ]))
        self.assertEquals([Action, Action, Slug], [type(p) for p in paras])

    def test_transition_does_not_have_to_be_followed_by_slug(self):
        # The "followed by slug" requirement is gone from the Jan 2012 spec
        paras = list(parse([
            'Bill lights a cigarette.',
            '',
            'CUT TO:',
            '',
            'SOME GUY mowing the lawn.',
        ]))
        self.assertEquals(
            [Action, Transition, Action],
            [type(p) for p in paras]
        )

    def test_greater_than_sign_means_transition(self):
        paras = list(parse([
            'Bill blows out the match.',
            '',
            '> FADE OUT.',
            '',
            '.DARKNESS',
        ]))
        self.assertEquals([Action, Transition, Slug], [type(p) for p in paras])
        self.assertEquals(plain('FADE OUT.'), paras[1].line)

    def test_centered_text_is_not_parsed_as_transition(self):
        paras = list(parse([
            'Bill blows out the match.',
            '',
            '> THE END. <',
            '',
            'bye!'
        ]))
        self.assertEquals([Action, Action, Action], [type(p) for p in paras])

    def test_transition_at_end(self):
        paras = list(parse([
            'They stroll hand in hand down the street.',
            '',
            '> FADE OUT.',
        ]))
        self.assertEquals([Action, Transition], [type(p) for p in paras])
        self.assertEquals(plain('FADE OUT.'), paras[1].line)

    def test_centered_text_is_not_parsed_as_transition(self):
class ActionTests(TestCase):

    def test_action_preserves_leading_whitespace(self):
        paras = list(parse([
            'hello',
            '',
            '  two spaces',
            '   three spaces ',
        ]))
        self.assertEquals([Action, Action], [type(p) for p in paras])
        self.assertEquals(
            [
            '',
            '> FADE OUT.',
        ])
        )

    def test_single_centered_line(self):
        paras = list(parse(['> center me! <']))
        self.assertEquals([Action], [type(p) for p in paras])
        self.assertTrue(paras[0].centered)

        paras = parse([
            'hello',
            '',
            '  > second!   <',
            '> third!< ',
        ]
        paras = list(parse(lines))
        self.assertEquals([Action], [type(p) for p in paras])
        self.assertTrue(paras[0].centered)
        self.assertEquals([
                plain(u'   three spaces'),
            ], paras[1].lines
        )
        ], paras[0].lines)

    def test_upper_case_centered_not_parsed_as_dialog(self):
        paras = list(parse([
            '> FIRST! <',
            '  > SECOND! <',
            '> THIRD! <',
        ]))
        self.assertEquals([Action], [type(p) for p in paras])
        self.assertTrue(paras[0].centered)

        ]
        paras = parse(lines)
        self.assertEquals([Action], [type(p) for p in paras])
            '> second! <',
            'third!',
        ]
        paras = list(parse(lines))
        self.assertEquals([Action], [type(p) for p in paras])
        self.assertFalse(paras[0].centered)
        self.assertEquals([plain(line) for line in lines], paras[0].lines)
    def test_upper_case_centered_not_parsed_as_dialog(self):

class SynopsisTests(TestCase):
    def test_synopsis_after_slug_adds_synopsis_to_scene(self):
        paras = list(parse([
            "EXT. BRICK'S PATIO - DAY",
            '',
            "= Set up Brick & Steel's new life."
            '',
        ]))
        self.assertEquals([Slug], [type(p) for p in paras])
        self.assertEquals(
            "Set up Brick & Steel's new life.",
            'third!',
        )

    def test_synopsis_in_section(self):
        paras = list(parse([
            '# section one',
            '',
            '= In which we get to know our characters'
        ]))
        self.assertEquals([Section], [type(p) for p in paras])
        self.assertEquals(
            'In which we get to know our characters',
            '',
        )

    def test_synopsis_syntax_parsed_as_literal(self):
        paras = list(parse([
            'Some action',
            '',
            '= A line that just happens to look like a synopsis'
        ]))
        self.assertEquals([Action, Action], [type(p) for p in paras])
        self.assertEquals(
            [plain('= A line that just happens to look like a synopsis')],
            '# section one',
            '',
            '= In which we get to know our characters'
        ])
        self.assertEquals([Section], [type(p) for p in paras])
        self.assertEquals(
            'In which we get to know our characters',
            paras[0].synopsis
        )

    def test_synopsis_syntax_parsed_as_literal(self):
        paras = parse([
            'Some action',
            '',
            '= A line that just happens to look like a synopsis'
        ])
        self.assertEquals([Action, Action], [type(p) for p in paras])
        self.assertEquals(
            [plain('= A line that just happens to look like a synopsis')],
            paras[1].lines
        )


class TitlePageTests(TestCase):

    def test_basic_title_page(self):
        lines = [
            'Title:',
            '    _**BRICK & STEEL**_',
            '    _**FULL RETIRED**_',
            'Author: Stu Maschwitz',
        ]
        self.assertDictEqual(
            {
                'Title': ['_**BRICK & STEEL**_', '_**FULL RETIRED**_'],
                'Author': ['Stu Maschwitz'],
            },
            fountain.parse_title_page(lines)
        )

    def test_multiple_values(self):
        lines = [
            'Title: Death',
            'Title: - a love story',
            'Title:',
            '   (which happens to be true)',
        ]
        self.assertDictEqual(
            {
                'Title': [
                    'Death',
                    '- a love story',
                    '(which happens to be true)'
                ]
            },
            fountain.parse_title_page(lines)

class PageBreakTests(TestCase):
    def test_page_break_is_parsed(self):
        paras = list(parse([
            '====',
            '',
            'So here we go'
        ]))
        self.assertEquals([PageBreak, Action], [type(p) for p in paras])
            fountain.parse_title_page(lines)
        )

    def test_unparsable_title_page_returns_none(self):
        lines = [
            'Title: Inception',
            '    additional line',
        ]
        self.assertIsNone(fountain.parse_title_page(lines))


class PageBreakTests(TestCase):
    def test_page_break_is_parsed(self):
        paras = parse([
            '====',
            '',
            'So here we go'
        ])
        self.assertEquals([PageBreak, Action], [type(p) for p in paras])

<MSG> Add Screenplay class

This is to be able to access title page data.

<DFF> @@ -14,25 +14,25 @@ from StringIO import StringIO
 
 def parse(lines):
     content = '\n'.join(lines)
-    return fountain.parse(StringIO(content))
+    return list(fountain.parse(StringIO(content)))
 
 
 class SlugTests(TestCase):
     def test_slug_with_prefix(self):
-        paras = list(parse([
+        paras = parse([
             'INT. SOMEWHERE - DAY',
             '',
             'THIS IS JUST ACTION',
-        ]))
+        ])
         self.assertEquals([Slug, Action], [type(p) for p in paras])
 
     def test_slug_must_be_single_line(self):
-        paras = list(parse([
+        paras = parse([
             'INT. SOMEWHERE - DAY',
             'ANOTHER LINE',
             '',
             'Some action',
-        ]))
+        ])
         self.assertEquals([Dialog, Action], [type(p) for p in paras])
         # What looks like a scene headingis parsed as a character name.
         # Unexpected perhaps, but that's how I interpreted the spec.
@@ -40,10 +40,10 @@ class SlugTests(TestCase):
         self.assertEquals([plain('Some action')], paras[1].lines)
 
     def test_action_is_not_a_slug(self):
-        paras = list(parse([
+        paras = parse([
             '',
             'THIS IS JUST ACTION',
-        ]))
+        ])
         self.assertEquals([Action], [type(p) for p in paras])
 
     def test_two_lines_creates_no_slug(self):
@@ -151,25 +151,25 @@ class DialogTests(TestCase):
     # Fountain would not be able to support a character named "23". We
     # might need a syntax to force a character element.
     def test_nonalpha_character(self):
-        paras = list(parse([
+        paras = parse([
             '23',
             'Hello',
-        ]))
+        ])
         self.assertEquals([Action], [type(p) for p in paras])
 
     def test_twospaced_line_is_not_character(self):
-        paras = list(parse([
+        paras = parse([
             'SCANNING THE AISLES...  ',
             'Where is that pit boss?',
-        ]))
+        ])
         self.assertEquals([Action], [type(p) for p in paras])
 
     def test_simple_parenthetical(self):
-        paras = list(parse([
+        paras = parse([
             'STEEL',
             '(starting the engine)',
             'So much for retirement!',
-        ]))
+        ])
         self.assertEquals(1, len(paras))
         dialog = paras[0]
         self.assertEqual(2, len(dialog.blocks))
@@ -183,12 +183,12 @@ class DialogTests(TestCase):
         )
 
     def test_twospace_keeps_dialog_together(self):
-        paras = list(parse([
+        paras = parse([
             'SOMEONE',
             'One',
             '  ',
             'Two',
-        ]))
+        ])
         self.assertEquals([Dialog], [type(p) for p in paras])
         self.assertEquals([
             (False, plain('One')),
@@ -197,13 +197,13 @@ class DialogTests(TestCase):
         ], paras[0].blocks)
 
     def test_dual_dialog(self):
-        paras = list(parse([
+        paras = parse([
             'BRICK',
             'Fuck retirement.',
             '',
             'STEEL ^',
             'Fuck retirement!',
-        ]))
+        ])
         self.assertEquals([DualDialog], [type(p) for p in paras])
         dual = paras[0]
         self.assertEquals(plain('BRICK'), dual.left.character)
@@ -218,12 +218,12 @@ class DialogTests(TestCase):
         )
 
     def test_dual_dialog_without_previous_dialog_is_ignored(self):
-        paras = list(parse([
+        paras = parse([
             'Brick strolls down the street.',
             '',
             'BRICK ^',
             'Nice retirement.',
-        ]))
+        ])
         self.assertEquals([Action, Dialog], [type(p) for p in paras])
         dialog = paras[1]
         self.assertEqual(plain('BRICK ^'), dialog.character)
@@ -232,13 +232,13 @@ class DialogTests(TestCase):
         ], dialog.blocks)
 
     def test_leading_and_trailing_spaces_in_dialog(self):
-        paras = list(parse([
+        paras = parse([
             'JULIET',
             'O Romeo, Romeo! wherefore art thou Romeo?',
             '  Deny thy father and refuse thy name;  ',
             'Or, if thou wilt not, be but sworn my love,',
             " And I'll no longer be a Capulet.",
-        ]))
+        ])
         self.assertEquals([Dialog], [type(p) for p in paras])
         self.assertEquals([
             (False, plain(u'O Romeo, Romeo! wherefore art thou Romeo?')),
@@ -251,84 +251,84 @@ class DialogTests(TestCase):
 class TransitionTests(TestCase):
 
     def test_standard_transition(self):
-        paras = list(parse([
+        paras = parse([
             'Jack begins to argue vociferously in Vietnamese (?)',
             '',
             'CUT TO:',
             '',
             "EXT. BRICK'S POOL - DAY",
-        ]))
+        ])
         self.assertEquals([Action, Transition, Slug], [type(p) for p in paras])
 
     def test_transition_must_end_with_to(self):
-        paras = list(parse([
+        paras = parse([
             'CUT TOO:',
             '',
             "EXT. BRICK'S POOL - DAY",
-        ]))
+        ])
         self.assertEquals([Action, Slug], [type(p) for p in paras])
 
     def test_transition_needs_to_be_upper_case(self):
-        paras = list(parse([
+        paras = parse([
             'Jack begins to argue vociferously in Vietnamese (?)',
             '',
             'cut to:',
             '',
             "EXT. BRICK'S POOL - DAY",
-        ]))
+        ])
         self.assertEquals([Action, Action, Slug], [type(p) for p in paras])
 
     def test_not_a_transition_on_trailing_whitespace(self):
-        paras = list(parse([
+        paras = parse([
             'Jack begins to argue vociferously in Vietnamese (?)',
             '',
             'CUT TO: ',
             '',
             "EXT. BRICK'S POOL - DAY",
-        ]))
+        ])
         self.assertEquals([Action, Action, Slug], [type(p) for p in paras])
 
     def test_transition_does_not_have_to_be_followed_by_slug(self):
         # The "followed by slug" requirement is gone from the Jan 2012 spec
-        paras = list(parse([
+        paras = parse([
             'Bill lights a cigarette.',
             '',
             'CUT TO:',
             '',
             'SOME GUY mowing the lawn.',
-        ]))
+        ])
         self.assertEquals(
             [Action, Transition, Action],
             [type(p) for p in paras]
         )
 
     def test_greater_than_sign_means_transition(self):
-        paras = list(parse([
+        paras = parse([
             'Bill blows out the match.',
             '',
             '> FADE OUT.',
             '',
             '.DARKNESS',
-        ]))
+        ])
         self.assertEquals([Action, Transition, Slug], [type(p) for p in paras])
         self.assertEquals(plain('FADE OUT.'), paras[1].line)
 
     def test_centered_text_is_not_parsed_as_transition(self):
-        paras = list(parse([
+        paras = parse([
             'Bill blows out the match.',
             '',
             '> THE END. <',
             '',
             'bye!'
-        ]))
+        ])
         self.assertEquals([Action, Action, Action], [type(p) for p in paras])
 
     def test_transition_at_end(self):
-        paras = list(parse([
+        paras = parse([
             'They stroll hand in hand down the street.',
             '',
             '> FADE OUT.',
-        ]))
+        ])
         self.assertEquals([Action, Transition], [type(p) for p in paras])
         self.assertEquals(plain('FADE OUT.'), paras[1].line)
 
@@ -336,12 +336,12 @@ class TransitionTests(TestCase):
 class ActionTests(TestCase):
 
     def test_action_preserves_leading_whitespace(self):
-        paras = list(parse([
+        paras = parse([
             'hello',
             '',
             '  two spaces',
             '   three spaces ',
-        ]))
+        ])
         self.assertEquals([Action, Action], [type(p) for p in paras])
         self.assertEquals(
             [
@@ -351,7 +351,7 @@ class ActionTests(TestCase):
         )
 
     def test_single_centered_line(self):
-        paras = list(parse(['> center me! <']))
+        paras = parse(['> center me! <'])
         self.assertEquals([Action], [type(p) for p in paras])
         self.assertTrue(paras[0].centered)
 
@@ -361,7 +361,7 @@ class ActionTests(TestCase):
             '  > second!   <',
             '> third!< ',
         ]
-        paras = list(parse(lines))
+        paras = parse(lines)
         self.assertEquals([Action], [type(p) for p in paras])
         self.assertTrue(paras[0].centered)
         self.assertEquals([
@@ -371,11 +371,11 @@ class ActionTests(TestCase):
         ], paras[0].lines)
 
     def test_upper_case_centered_not_parsed_as_dialog(self):
-        paras = list(parse([
+        paras = parse([
             '> FIRST! <',
             '  > SECOND! <',
             '> THIRD! <',
-        ]))
+        ])
         self.assertEquals([Action], [type(p) for p in paras])
         self.assertTrue(paras[0].centered)
 
@@ -385,7 +385,7 @@ class ActionTests(TestCase):
             '> second! <',
             'third!',
         ]
-        paras = list(parse(lines))
+        paras = parse(lines)
         self.assertEquals([Action], [type(p) for p in paras])
         self.assertFalse(paras[0].centered)
         self.assertEquals([plain(line) for line in lines], paras[0].lines)
@@ -393,12 +393,12 @@ class ActionTests(TestCase):
 
 class SynopsisTests(TestCase):
     def test_synopsis_after_slug_adds_synopsis_to_scene(self):
-        paras = list(parse([
+        paras = parse([
             "EXT. BRICK'S PATIO - DAY",
             '',
             "= Set up Brick & Steel's new life."
             '',
-        ]))
+        ])
         self.assertEquals([Slug], [type(p) for p in paras])
         self.assertEquals(
             "Set up Brick & Steel's new life.",
@@ -406,11 +406,11 @@ class SynopsisTests(TestCase):
         )
 
     def test_synopsis_in_section(self):
-        paras = list(parse([
+        paras = parse([
             '# section one',
             '',
             '= In which we get to know our characters'
-        ]))
+        ])
         self.assertEquals([Section], [type(p) for p in paras])
         self.assertEquals(
             'In which we get to know our characters',
@@ -418,11 +418,11 @@ class SynopsisTests(TestCase):
         )
 
     def test_synopsis_syntax_parsed_as_literal(self):
-        paras = list(parse([
+        paras = parse([
             'Some action',
             '',
             '= A line that just happens to look like a synopsis'
-        ]))
+        ])
         self.assertEquals([Action, Action], [type(p) for p in paras])
         self.assertEquals(
             [plain('= A line that just happens to look like a synopsis')],
@@ -485,9 +485,9 @@ class TitlePageTests(TestCase):
 
 class PageBreakTests(TestCase):
     def test_page_break_is_parsed(self):
-        paras = list(parse([
+        paras = parse([
             '====',
             '',
             'So here we go'
-        ]))
+        ])
         self.assertEquals([PageBreak, Action], [type(p) for p in paras])

Add Screenplay class