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)
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)
def test_complicated(self): # As reported by Stu self.assertEquals( parse_emphasis( 'You can _underline_ words, make them **bold** or *italic* ' 'or even ***bold italic.***'), (plain('You can ') + underline('underline') + plain(' words, make them ') + bold('bold') + plain(' or ') + italic('italic') + plain(' or even ') + (bold + italic)('bold italic.')))
def test_dual_dialog_without_previous_dialog_is_ignored(self): 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) self.assertEqual([(False, plain('Nice retirement.'))], dialog.blocks)
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_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_slug_must_be_single_line(self): 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. self.assertEquals(plain('INT. SOMEWHERE - DAY'), paras[0].character) self.assertEquals([plain('Some action')], paras[1].lines)
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([ plain(u' two spaces'), plain(u' three spaces'), ], paras[1].lines)
def test_slug_must_be_single_line(self): 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. self.assertEquals(plain('INT. SOMEWHERE - DAY'), paras[0].character) self.assertEquals([plain('Some action')], paras[1].lines)
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')), (False, empty_string), (False, plain('Two')), ], paras[0].blocks)
def test_dual_dialog_without_previous_dialog_is_ignored(self): 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) self.assertEqual([ (False, plain('Nice retirement.')) ], dialog.blocks)
def test_twospace_keeps_dialog_together(self): paras = parse([ 'SOMEONE', 'One', ' ', 'Two', ]) 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_simple_parenthetical(self): paras = parse([ 'STEEL', '(starting the engine)', 'So much for retirement!', ]) self.assertEquals(1, len(paras)) dialog = paras[0] self.assertEqual(2, len(dialog.blocks)) self.assertEqual((True, plain('(starting the engine)')), dialog.blocks[0]) self.assertEqual((False, plain('So much for retirement!')), dialog.blocks[1])
def test_complicated(self): # As reported by Stu self.assertEquals( parse_emphasis( 'You can _underline_ words, make them **bold** or *italic* ' 'or even ***bold italic.***' ), ( plain('You can ') + underline('underline') + plain(' words, make them ') + bold('bold') + plain(' or ') + italic('italic') + plain(' or even ') + (bold + italic)('bold italic.') ) )
def test_full_centered_paragraph(self): lines = [ '> first! <', ' > second! <', '> third!< ', ] paras = parse(lines) self.assertEquals([Action], [type(p) for p in paras]) self.assertTrue(paras[0].centered) self.assertEquals([ plain('first!'), plain('second!'), plain('third!'), ], paras[0].lines)
def test_full_centered_paragraph(self): lines = [ '> first! <', ' > second! <', '> third!< ', ] paras = list(parse(lines)) self.assertEquals([Action], [type(p) for p in paras]) self.assertTrue(paras[0].centered) self.assertEquals([ plain('first!'), plain('second!'), plain('third!'), ], paras[0].lines)
def test_action_preserves_leading_whitespace(self): paras = parse([ 'hello', '', ' two spaces', ' three spaces ', ]) self.assertEquals([Action, Action], [type(p) for p in paras]) self.assertEquals( [ plain(u' two spaces'), plain(u' three spaces'), ], paras[1].lines )
def test_leading_and_trailing_spaces_in_dialog(self): 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?')), (False, plain(u'Deny thy father and refuse thy name;')), (False, plain(u'Or, if thou wilt not, be but sworn my love,')), (False, plain(u"And I'll no longer be a Capulet.")), ], paras[0].blocks)
def test_leading_and_trailing_spaces_in_dialog(self): 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?')), (False, plain(u'Deny thy father and refuse thy name;')), (False, plain(u'Or, if thou wilt not, be but sworn my love,')), (False, plain(u"And I'll no longer be a Capulet.")), ], paras[0].blocks)
def test_at_sign_forces_dialog(self): paras = parse([ '@McCLANE', 'Yippee ki-yay', ]) self.assertEquals([Dialog], [type(p) for p in paras]) self.assertEquals(plain('McCLANE'), paras[0].character)
def test_at_sign_forces_dialog(self): paras = parse([ '@McCLANE', 'Yippee ki-yay', ]) self.assertEquals([Dialog], [type(p) for p in paras]) self.assertEquals(plain('McCLANE'), paras[0].character)
def test_sequential_styles(self): rich = plain('plain') + bold('b') + italic('i') write_text(self.out, rich, False) self.assertEqual( self.out.getvalue(), ' <Text>plain</Text>\n' ' <Text Style="Bold">b</Text>\n' ' <Text Style="Italic">i</Text>\n')
def test_dual_dialog(self): paras = parse([ 'BRICK', 'F**k retirement.', '', 'STEEL ^', 'F**k retirement!', ]) self.assertEquals([DualDialog], [type(p) for p in paras]) dual = paras[0] self.assertEquals(plain('BRICK'), dual.left.character) self.assertEquals([(False, plain('F**k retirement.'))], dual.left.blocks) self.assertEquals(plain('STEEL'), dual.right.character) self.assertEquals([(False, plain('F**k retirement!'))], dual.right.blocks)
def test_alphanumeric_character(self): paras = parse([ 'R2D2', 'Bee-bop', ]) self.assertEquals([Dialog], [type(p) for p in paras]) self.assertEquals(plain('R2D2'), paras[0].character)
def test_alphanumeric_character(self): paras = parse([ 'R2D2', 'Bee-bop', ]) self.assertEquals([Dialog], [type(p) for p in paras]) self.assertEquals(plain('R2D2'), paras[0].character)
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_concatenating_two_richstrings(self): expected = RichString(Segment('hello', ()), Segment(' there', (Bold, ))) s1 = plain('hello') s2 = bold(' there') result = s1 + s2 self.assertEquals(expected, result)
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_simple_parenthetical(self): paras = parse([ 'STEEL', '(starting the engine)', 'So much for retirement!', ]) self.assertEquals(1, len(paras)) dialog = paras[0] self.assertEqual(2, len(dialog.blocks)) self.assertEqual( (True, plain('(starting the engine)')), dialog.blocks[0] ) self.assertEqual( (False, plain('So much for retirement!')), dialog.blocks[1] )
def test_transition_at_end(self): 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)
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_transition_at_end(self): 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)
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)
def test_to_html(self): s = ( bold('bold') + plain(' normal ') + italic('italic') + underline('wonderline') ) self.assertEquals( '<strong>bold</strong> normal <em>italic</em><u>wonderline</u>', s.to_html() )
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)
def test_sequential_styles(self): rich = plain('plain') + bold('b') + italic('i') write_text(self.out, rich, False) self.assertEqual( self.out.getvalue(), ' <Text>plain</Text>\n' ' <Text Style="Bold">b</Text>\n' ' <Text Style="Italic">i</Text>\n' )
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)
def test_greater_than_sign_means_transition(self): 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_centering_marks_in_middle_of_paragraphs_are_verbatim(self): lines = [ 'first!', '> 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_centering_marks_in_middle_of_paragraphs_are_verbatim(self): lines = [ 'first!', '> second! <', 'third!', ] 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)
def test_greater_than_sign_means_transition(self): 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_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 )
def test_dual_dialog(self): paras = parse([ 'BRICK', 'F**k retirement.', '', 'STEEL ^', 'F**k retirement!', ]) self.assertEquals([DualDialog], [type(p) for p in paras]) dual = paras[0] self.assertEquals(plain('BRICK'), dual.left.character) self.assertEquals( [(False, plain('F**k retirement.'))], dual.left.blocks ) self.assertEquals(plain('STEEL'), dual.right.character) self.assertEquals( [(False, plain('F**k retirement!'))], dual.right.blocks )
def format_slug(self, slug): num = slug.scene_number with self._tag('h6'): if num: with self._tag('span', classes=['scnuml']): self.out.write(to_html(slug.scene_number)) self.out.write(to_html(slug.line)) if num: with self._tag('span', classes=['scnumr']): self.out.write(to_html(slug.scene_number)) if slug.synopsis: with self._tag('span', classes=['h6-synopsis']): self.out.write(to_html(plain(slug.synopsis)))
def format_slug(self, slug): num = slug.scene_number with self._tag('h6'): if num: with self._tag('span', classes=['scnuml']): self.out.write(to_html(slug.scene_number)) self.out.write(to_html(slug.line)) if num: with self._tag('span', classes=['scnumr']): self.out.write(to_html(slug.scene_number)) if slug.synopsis: with self._tag('span', classes=['h6-synopsis']): self.out.write(to_html(plain(slug.synopsis)))
def append_slug(self, paragraphs): if len(self.lines) != 1: return False match = slug_re.match(self.lines[0]) if not match: return False period, text = match.groups() text = text.upper() if not period and not any(regex.match(text) for regex in slug_regexes): return False match = scene_number_re.match(text) if match: text, scene_number = match.groups() paragraphs.append(Slug(_string_to_rich(text), plain(scene_number))) else: paragraphs.append(Slug(_string_to_rich(text))) return True
def append_slug(self, paragraphs): if len(self.lines) != 1: return False match = slug_re.match(self.lines[0]) if not match: return False period, text = match.groups() text = text.upper() if not period and not any(regex.match(text) for regex in slug_regexes): return False match = scene_number_re.match(text) if match: text, scene_number = match.groups() paragraphs.append(Slug(_string_to_rich(text), plain(scene_number))) else: paragraphs.append(Slug(_string_to_rich(text))) return True
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_plain_to_html(self): self.assertEquals('hello', plain('hello').to_html())
def test_unicode(self): s = bold('Hello') + plain(' there ') + bold('folks') self.assertEquals( u'Hello there folks', unicode(s) )
def test_eq(self): self.assertEquals(bold('Hello'), bold('Hello')) self.assertNotEquals(bold('Hello'), bold('Foo')) self.assertNotEquals(plain('Hello'), bold('Hello'))
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_concatenating_two_richstrings(self): expected = RichString(Segment('hello', ()), Segment(' there', (Bold,))) s1 = plain('hello') s2 = bold(' there') result = s1 + s2 self.assertEquals(expected, result)
def test_parse_without_emphasis(self): self.assertEquals( plain('Hello'), parse_emphasis('Hello'), 'Expected parse_emphasis to return a plain string')