Coverage for tests/test_snippets.py: 100%

44 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2026-09-13 18:40 +0000

1import typing 

2 

3import hypothesis 

4import hypothesis.strategies as st 

5import pytest 

6 

7from meta_tags_parser import parse_snippets_from_source, structs 

8 

9 

10SNIPPET_PAGE_FIXTURE: typing.Final = """ 

11<html><head> 

12<meta property="og:title" content="Meta Tags — Preview, Edit and Generate"> 

13<meta property="og:description" content="Edit and experiment with your content."> 

14<meta property="og:url" content="https://metatags.io/"> 

15<meta property="og:image" content="https://metatags.io/assets/preview.png"> 

16<meta property="og:image:width" content="1200"> 

17<meta property="og:image:height" content="630"> 

18<meta name="twitter:card" content="summary_large_image"> 

19<meta name="twitter:title" content="Privet, kak dela to vcelom?"> 

20<meta name="twitter:description" content="Twitter flavoured description."> 

21<meta name="twitter:url" content="https://metatags.io/"> 

22<meta name="twitter:image" content="https://metatags.io/assets/hm-fail.png"> 

23<meta name="twitter:image:width" content="800"> 

24<meta name="twitter:image:height" content="418"> 

25</head></html> 

26""" 

27EXPECTED_SNIPPET_GROUP: typing.Final = structs.SnippetGroup( 

28 open_graph=structs.SocialMediaSnippet( 

29 title="Meta Tags — Preview, Edit and Generate", 

30 description="Edit and experiment with your content.", 

31 image="https://metatags.io/assets/preview.png", 

32 image_width=1200, 

33 image_height=630, 

34 url="https://metatags.io/", 

35 ), 

36 twitter=structs.SocialMediaSnippet( 

37 title="Privet, kak dela to vcelom?", 

38 description="Twitter flavoured description.", 

39 image="https://metatags.io/assets/hm-fail.png", 

40 image_width=800, 

41 image_height=418, 

42 url="https://metatags.io/", 

43 ), 

44) 

45REPEATED_PARSE_COUNT: typing.Final = 3 

46MAX_GENERATED_DIMENSION: typing.Final = 100_000 

47UNICODE_DIGITS: typing.Final = st.characters(whitelist_categories=["Nd"]) 

48SAFE_ATTRIBUTE_ALPHABET: typing.Final = st.characters(blacklist_categories=("Cc", "Cs"), blacklist_characters='<>"&') 

49 

50 

51@pytest.mark.parametrize("_repeat_number", range(REPEATED_PARSE_COUNT)) 

52def test_snippets_are_stable_across_repeated_parsing(_repeat_number: int) -> None: 

53 assert parse_snippets_from_source(SNIPPET_PAGE_FIXTURE) == EXPECTED_SNIPPET_GROUP 

54 

55 

56def test_snippets_are_empty_without_social_tags() -> None: 

57 snippet_result: typing.Final[structs.SnippetGroup] = parse_snippets_from_source( 

58 '<meta name="twitter:title" content="Hi, whatsup kekeke">' 

59 ) 

60 

61 assert snippet_result.twitter.title == "Hi, whatsup kekeke" 

62 assert snippet_result.twitter.image_width == 0 

63 assert snippet_result.open_graph == structs.SocialMediaSnippet() 

64 

65 

66def test_first_duplicated_tag_wins() -> None: 

67 """Social networks treat repeated tags as a list where the first entry is the primary one.""" 

68 snippet_result: typing.Final[structs.SnippetGroup] = parse_snippets_from_source( 

69 '<meta property="og:title" content="First title">' 

70 '<meta property="og:title" content="Second title">' 

71 '<meta property="og:image" content="https://example.com/first.png">' 

72 '<meta property="og:image:width" content="640">' 

73 '<meta property="og:image" content="https://example.com/second.png">' 

74 '<meta property="og:image:width" content="1280">' 

75 ) 

76 

77 assert snippet_result.open_graph.title == "First title" 

78 assert snippet_result.open_graph.image == "https://example.com/first.png" 

79 assert snippet_result.open_graph.image_width == 640 

80 

81 

82def test_unknown_social_tags_do_not_reach_the_snippet() -> None: 

83 snippet_result: typing.Final[structs.SnippetGroup] = parse_snippets_from_source( 

84 '<meta property="og:video:duration" content="754"><meta property="og:audio" content="x.mp3">' 

85 ) 

86 

87 assert snippet_result.open_graph == structs.SocialMediaSnippet() 

88 

89 

90def test_snippets_accept_bytes() -> None: 

91 snippet_result: typing.Final[structs.SnippetGroup] = parse_snippets_from_source( 

92 '<html><head><meta charset="windows-1251"><meta property="og:title" content="Заголовок"></head></html>'.encode( 

93 "windows-1251" 

94 ) 

95 ) 

96 

97 assert snippet_result.open_graph.title == "Заголовок" 

98 

99 

100@pytest.mark.parametrize( 

101 ("dimension_text", "expected_width"), 

102 [ 

103 ("", 0), 

104 (" ", 0), 

105 ("123", 123), 

106 (" 55 ", 55), 

107 ("0", 0), 

108 ("abc", 0), 

109 ("12.5", 0), 

110 ("-12", 0), 

111 ("1200px", 0), 

112 ("١٢٣", 0), 

113 ("٥", 0), 

114 ("²", 0), 

115 ], 

116) 

117def test_image_width_examples(dimension_text: str, expected_width: int) -> None: 

118 snippet_result: typing.Final[structs.SnippetGroup] = parse_snippets_from_source( 

119 f'<meta property="twitter:image:width" content="{dimension_text}">' 

120 ) 

121 

122 assert snippet_result.twitter.image_width == expected_width 

123 

124 

125@hypothesis.given( 

126 dimension_text=st.one_of( 

127 st.integers(min_value=0, max_value=MAX_GENERATED_DIMENSION).map(str), 

128 st.text(alphabet=UNICODE_DIGITS, min_size=1, max_size=8), 

129 st.text(alphabet=SAFE_ATTRIBUTE_ALPHABET, max_size=8), 

130 ) 

131) 

132def test_image_dimensions_follow_the_ascii_digit_rule(dimension_text: str) -> None: 

133 """Anything that is not a plain ascii number (unicode digits, units, decimals) becomes a zero.""" 

134 cleaned_text: typing.Final[str] = dimension_text.strip() 

135 

136 snippet_result: typing.Final[structs.SnippetGroup] = parse_snippets_from_source( 

137 f'<meta property="og:image:width" content="{dimension_text}">' 

138 ) 

139 

140 assert snippet_result.open_graph.image_width == ( 

141 int(cleaned_text) if cleaned_text.isascii() and cleaned_text.isdigit() else 0 

142 ) 

143 

144 

145@hypothesis.settings(max_examples=50) 

146@hypothesis.given(snippet_title=st.text(alphabet=SAFE_ATTRIBUTE_ALPHABET, max_size=80)) 

147def test_any_title_survives_the_round_trip(snippet_title: str) -> None: 

148 snippet_result: typing.Final[structs.SnippetGroup] = parse_snippets_from_source( 

149 f'<meta property="og:title" content="{snippet_title}">' 

150 ) 

151 

152 assert snippet_result.open_graph.title == snippet_title