motion-markdown-it-plugins 8.4.1 → 8.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/lib/motion-markdown-it-plugins.rb +12 -4
- data/lib/motion-markdown-it-plugins/emoji/emoji.rb +34 -0
- data/lib/motion-markdown-it-plugins/emoji/emoji_light.rb +18 -0
- data/lib/motion-markdown-it-plugins/emoji/plugin/data/full.rb +1490 -0
- data/lib/motion-markdown-it-plugins/emoji/plugin/data/light.rb +161 -0
- data/lib/motion-markdown-it-plugins/emoji/plugin/data/shortcuts.rb +36 -0
- data/lib/motion-markdown-it-plugins/emoji/plugin/normalize_opts.rb +72 -0
- data/lib/motion-markdown-it-plugins/emoji/plugin/render.rb +9 -0
- data/lib/motion-markdown-it-plugins/emoji/plugin/replace.rb +101 -0
- data/lib/motion-markdown-it-plugins/version.rb +1 -1
- data/spec/emoji/emoji_spec.rb +115 -0
- data/spec/testgen_helper.rb +1 -1
- metadata +14 -4
@@ -0,0 +1,161 @@
|
|
1
|
+
module MotionMarkdownItPlugins
|
2
|
+
module EmojiPlugin
|
3
|
+
module Data
|
4
|
+
module Light
|
5
|
+
EMOJIES_DEF_LIGHT = {
|
6
|
+
"grinning" => "😀",
|
7
|
+
"smiley" => "😃",
|
8
|
+
"smile" => "😄",
|
9
|
+
"grin" => "😁",
|
10
|
+
"laughing" => "😆",
|
11
|
+
"satisfied" => "😆",
|
12
|
+
"sweat_smile" => "😅",
|
13
|
+
"joy" => "😂",
|
14
|
+
"blush" => "😊",
|
15
|
+
"innocent" => "😇",
|
16
|
+
"wink" => "😉",
|
17
|
+
"relieved" => "😌",
|
18
|
+
"heart_eyes" => "😍",
|
19
|
+
"kissing_heart" => "😘",
|
20
|
+
"kissing" => "😗",
|
21
|
+
"kissing_smiling_eyes" => "😙",
|
22
|
+
"kissing_closed_eyes" => "😚",
|
23
|
+
"yum" => "😋",
|
24
|
+
"stuck_out_tongue_winking_eye" => "😜",
|
25
|
+
"stuck_out_tongue_closed_eyes" => "😝",
|
26
|
+
"stuck_out_tongue" => "😛",
|
27
|
+
"sunglasses" => "😎",
|
28
|
+
"smirk" => "😏",
|
29
|
+
"unamused" => "😒",
|
30
|
+
"disappointed" => "😞",
|
31
|
+
"pensive" => "😔",
|
32
|
+
"worried" => "😟",
|
33
|
+
"confused" => "😕",
|
34
|
+
"persevere" => "😣",
|
35
|
+
"confounded" => "😖",
|
36
|
+
"tired_face" => "😫",
|
37
|
+
"weary" => "😩",
|
38
|
+
"angry" => "😠",
|
39
|
+
"rage" => "😡",
|
40
|
+
"pout" => "😡",
|
41
|
+
"no_mouth" => "😶",
|
42
|
+
"neutral_face" => "😐",
|
43
|
+
"expressionless" => "😑",
|
44
|
+
"hushed" => "😯",
|
45
|
+
"frowning" => "😦",
|
46
|
+
"anguished" => "😧",
|
47
|
+
"open_mouth" => "😮",
|
48
|
+
"astonished" => "😲",
|
49
|
+
"dizzy_face" => "😵",
|
50
|
+
"flushed" => "😳",
|
51
|
+
"scream" => "😱",
|
52
|
+
"fearful" => "😨",
|
53
|
+
"cold_sweat" => "😰",
|
54
|
+
"cry" => "😢",
|
55
|
+
"disappointed_relieved" => "😥",
|
56
|
+
"sob" => "😭",
|
57
|
+
"sweat" => "😓",
|
58
|
+
"sleepy" => "😪",
|
59
|
+
"sleeping" => "😴",
|
60
|
+
"mask" => "😷",
|
61
|
+
"smiling_imp" => "😈",
|
62
|
+
"smiley_cat" => "😺",
|
63
|
+
"smile_cat" => "😸",
|
64
|
+
"joy_cat" => "😹",
|
65
|
+
"heart_eyes_cat" => "😻",
|
66
|
+
"smirk_cat" => "😼",
|
67
|
+
"kissing_cat" => "😽",
|
68
|
+
"scream_cat" => "🙀",
|
69
|
+
"crying_cat_face" => "😿",
|
70
|
+
"pouting_cat" => "😾",
|
71
|
+
"fist_raised" => "✊",
|
72
|
+
"fist" => "✊",
|
73
|
+
"v" => "✌️",
|
74
|
+
"point_up" => "☝️",
|
75
|
+
"hand" => "✋",
|
76
|
+
"raised_hand" => "✋",
|
77
|
+
"cat" => "🐱",
|
78
|
+
"mouse" => "🐭",
|
79
|
+
"cow" => "🐮",
|
80
|
+
"monkey_face" => "🐵",
|
81
|
+
"star" => "⭐️",
|
82
|
+
"sparkles" => "✨",
|
83
|
+
"zap" => "⚡️",
|
84
|
+
"sunny" => "☀️",
|
85
|
+
"cloud" => "☁️",
|
86
|
+
"snowflake" => "❄️",
|
87
|
+
"umbrella" => "☔️",
|
88
|
+
"coffee" => "☕️",
|
89
|
+
"airplane" => "✈️",
|
90
|
+
"anchor" => "⚓️",
|
91
|
+
"watch" => "⌚️",
|
92
|
+
"phone" => "☎️",
|
93
|
+
"telephone" => "☎️",
|
94
|
+
"hourglass" => "⌛️",
|
95
|
+
"email" => "✉️",
|
96
|
+
"envelope" => "✉️",
|
97
|
+
"scissors" => "✂️",
|
98
|
+
"black_nib" => "✒️",
|
99
|
+
"pencil2" => "✏️",
|
100
|
+
"heart" => "❤️",
|
101
|
+
"aries" => "♈️",
|
102
|
+
"taurus" => "♉️",
|
103
|
+
"gemini" => "♊️",
|
104
|
+
"cancer" => "♋️",
|
105
|
+
"leo" => "♌️",
|
106
|
+
"virgo" => "♍️",
|
107
|
+
"libra" => "♎️",
|
108
|
+
"scorpius" => "♏️",
|
109
|
+
"sagittarius" => "♐️",
|
110
|
+
"capricorn" => "♑️",
|
111
|
+
"aquarius" => "♒️",
|
112
|
+
"pisces" => "♓️",
|
113
|
+
"eight_pointed_black_star" => "✴️",
|
114
|
+
"x" => "❌",
|
115
|
+
"hotsprings" => "♨️",
|
116
|
+
"exclamation" => "❗️",
|
117
|
+
"heavy_exclamation_mark" => "❗️",
|
118
|
+
"grey_exclamation" => "❕",
|
119
|
+
"question" => "❓",
|
120
|
+
"grey_question" => "❔",
|
121
|
+
"bangbang" => "‼️",
|
122
|
+
"interrobang" => "⁉️",
|
123
|
+
"part_alternation_mark" => "〽️",
|
124
|
+
"warning" => "⚠️",
|
125
|
+
"recycle" => "♻️",
|
126
|
+
"white_check_mark" => "✅",
|
127
|
+
"sparkle" => "❇️",
|
128
|
+
"eight_spoked_asterisk" => "✳️",
|
129
|
+
"negative_squared_cross_mark" => "❎",
|
130
|
+
"m" => "Ⓜ️",
|
131
|
+
"wheelchair" => "♿️",
|
132
|
+
"information_source" => "ℹ️",
|
133
|
+
"heavy_plus_sign" => "➕",
|
134
|
+
"heavy_minus_sign" => "➖",
|
135
|
+
"heavy_division_sign" => "➗",
|
136
|
+
"heavy_multiplication_x" => "✖️",
|
137
|
+
"tm" => "™️",
|
138
|
+
"copyright" => "©️",
|
139
|
+
"registered" => "®️",
|
140
|
+
"wavy_dash" => "〰️",
|
141
|
+
"curly_loop" => "➰",
|
142
|
+
"loop" => "➿",
|
143
|
+
"heavy_check_mark" => "✔️",
|
144
|
+
"ballot_box_with_check" => "☑️",
|
145
|
+
"white_circle" => "⚪️",
|
146
|
+
"black_circle" => "⚫️",
|
147
|
+
"black_small_square" => "▪️",
|
148
|
+
"white_small_square" => "▫️",
|
149
|
+
"black_medium_small_square" => "◾️",
|
150
|
+
"white_medium_small_square" => "◽️",
|
151
|
+
"black_medium_square" => "◼️",
|
152
|
+
"white_medium_square" => "◻️",
|
153
|
+
"black_large_square" => "⬛️",
|
154
|
+
"white_large_square" => "⬜️",
|
155
|
+
"black_joker" => "🃏",
|
156
|
+
"mahjong" => "🀄️"
|
157
|
+
}.freeze
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MotionMarkdownItPlugins
|
2
|
+
module EmojiPlugin
|
3
|
+
module Data
|
4
|
+
module Shortcuts
|
5
|
+
EMOJIES_DEF_SHORTCUTS = {
|
6
|
+
'angry' => [ '>:(', '>:-(' ],
|
7
|
+
'blush' => [ ':")', ':-")' ],
|
8
|
+
'broken_heart' => [ '</3', '<\\3' ],
|
9
|
+
# :\ and :-\ not used because of conflict with markdown escaping
|
10
|
+
'confused' => [ ':/', ':-/' ], # twemoji shows question
|
11
|
+
'cry' => [ ":'(", ":'-(", ':,(', ':,-(' ],
|
12
|
+
'frowning' => [ ':(', ':-(' ],
|
13
|
+
'heart' => [ '<3' ],
|
14
|
+
'imp' => [ ']:(', ']:-(' ],
|
15
|
+
'innocent' => [ 'o:)', 'O:)', 'o:-)', 'O:-)', '0:)', '0:-)' ],
|
16
|
+
'joy' => [ ":')", ":'-)", ':,)', ':,-)', ":'D", ":'-D", ':,D', ':,-D' ],
|
17
|
+
'kissing' => [ ':*', ':-*' ],
|
18
|
+
'laughing' => [ 'x-)', 'X-)' ],
|
19
|
+
'neutral_face' => [ ':|', ':-|' ],
|
20
|
+
'open_mouth' => [ ':o', ':-o', ':O', ':-O' ],
|
21
|
+
'rage' => [ ':@', ':-@' ],
|
22
|
+
'smile' => [ ':D', ':-D' ],
|
23
|
+
'smiley' => [ ':)', ':-)' ],
|
24
|
+
'smiling_imp' => [ ']:)', ']:-)' ],
|
25
|
+
'sob' => [ ":,'(", ":,'-(", ';(', ';-(' ],
|
26
|
+
'stuck_out_tongue' => [ ':P', ':-P' ],
|
27
|
+
'sunglasses' => [ '8-)', 'B-)' ],
|
28
|
+
'sweat' => [ ',:(', ',:-(' ],
|
29
|
+
'sweat_smile' => [ ',:)', ',:-)' ],
|
30
|
+
'unamused' => [ ':s', ':-S', ':z', ':-Z', ':$', ':-$' ],
|
31
|
+
'wink' => [ ';)', ';-)' ]
|
32
|
+
}.freeze
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module MotionMarkdownItPlugins
|
2
|
+
module EmojiPlugin
|
3
|
+
module NormalizeOpts
|
4
|
+
#------------------------------------------------------------------------------
|
5
|
+
def quoteRE(str)
|
6
|
+
str.gsub(/([.?*+^$\[\]\\(){}|-])/, '\\\\\1')
|
7
|
+
end
|
8
|
+
|
9
|
+
#------------------------------------------------------------------------------
|
10
|
+
def normalize_opts(options)
|
11
|
+
emojies = stringify_keys(options[:defs])
|
12
|
+
shortcuts = stringify_keys(options[:shortcuts])
|
13
|
+
|
14
|
+
# Filter emojies by whitelist, if needed
|
15
|
+
if options[:enabled].length > 0
|
16
|
+
emojies = emojies.keys.reduce({}) do |acc, key|
|
17
|
+
if options[:enabled].include?(key)
|
18
|
+
acc[key] = emojies[key]
|
19
|
+
end
|
20
|
+
|
21
|
+
acc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Flatten shortcuts to simple object: { alias: emoji_name }
|
26
|
+
shortcuts = shortcuts.keys.reduce({}) do |acc, key|
|
27
|
+
# Skip aliases for filtered emojies, to reduce regexp
|
28
|
+
next acc if !emojies[key]
|
29
|
+
|
30
|
+
if shortcuts[key].is_a?(Array)
|
31
|
+
shortcuts[key].each do |alias_value|
|
32
|
+
acc[alias_value] = key
|
33
|
+
end
|
34
|
+
next acc
|
35
|
+
end
|
36
|
+
|
37
|
+
acc[shortcuts[key]] = key
|
38
|
+
acc
|
39
|
+
end
|
40
|
+
|
41
|
+
# Compile regexp
|
42
|
+
names = emojies.keys
|
43
|
+
.map { |name| ':' + name + ':' }
|
44
|
+
.concat(shortcuts.keys)
|
45
|
+
.sort
|
46
|
+
.reverse
|
47
|
+
.map { |name| quoteRE(name) }
|
48
|
+
.join('|')
|
49
|
+
|
50
|
+
scanRE = Regexp.new(names)
|
51
|
+
replaceRE = Regexp.new(names)
|
52
|
+
|
53
|
+
return {
|
54
|
+
defs: emojies,
|
55
|
+
shortcuts: shortcuts,
|
56
|
+
scanRE: scanRE,
|
57
|
+
replaceRE: replaceRE
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# convert keys from symbols into strings
|
64
|
+
#------------------------------------------------------------------------------
|
65
|
+
def stringify_keys(symbol_hash)
|
66
|
+
result = {}
|
67
|
+
symbol_hash.each_key { |key| result[key.to_s] = symbol_hash[key] }
|
68
|
+
result
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# Emojies & shortcuts replacement logic.
|
2
|
+
#
|
3
|
+
# Note: In theory, it could be faster to parse :smile: in inline chain and
|
4
|
+
# leave only shortcuts here. But, who care...
|
5
|
+
#------------------------------------------------------------------------------
|
6
|
+
module MotionMarkdownItPlugins
|
7
|
+
module EmojiPlugin
|
8
|
+
module Replace
|
9
|
+
Z_RE_SRC = UCMicro::Categories::Z::REGEX.source
|
10
|
+
P_RE_SRC = UCMicro::Categories::P::REGEX.source
|
11
|
+
CC_RE_SRC = UCMicro::Categories::Cc::REGEX.source
|
12
|
+
ZPCC_RE = Regexp.new([Z_RE_SRC, P_RE_SRC, CC_RE_SRC].join('|'))
|
13
|
+
|
14
|
+
#------------------------------------------------------------------------------
|
15
|
+
def create_rule(md, emojies, shortcuts, scanRE, replaceRE)
|
16
|
+
@emojies = emojies
|
17
|
+
@shortcuts = shortcuts
|
18
|
+
@scanRE = scanRE
|
19
|
+
@replaceRE = replaceRE
|
20
|
+
|
21
|
+
return lambda { |state| emoji_replace(state) }
|
22
|
+
end
|
23
|
+
|
24
|
+
#------------------------------------------------------------------------------
|
25
|
+
def splitTextToken(text, level, token_class)
|
26
|
+
last_pos = 0
|
27
|
+
nodes = []
|
28
|
+
|
29
|
+
text.gsub(@replaceRE) do |match|
|
30
|
+
match_data = Regexp.last_match
|
31
|
+
offset = match_data.offset(0)[0]
|
32
|
+
src = match_data.string
|
33
|
+
|
34
|
+
# Validate emoji name
|
35
|
+
if @shortcuts.include?(match)
|
36
|
+
# replace shortcut with full name
|
37
|
+
emoji_name = @shortcuts[match]
|
38
|
+
|
39
|
+
# Don't allow letters before any shortcut (as in no ":/" in http://)
|
40
|
+
next if offset > 0 && !(ZPCC_RE =~ src[offset - 1])
|
41
|
+
|
42
|
+
# Don't allow letters after any shortcut
|
43
|
+
next if (offset + match.length < src.length) && !(ZPCC_RE =~ src[offset + match.length])
|
44
|
+
else
|
45
|
+
emoji_name = match.slice(1...-1)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add new tokens to pending list
|
49
|
+
if offset > last_pos
|
50
|
+
token = token_class.new('text', '', 0)
|
51
|
+
token.content = text.slice(last_pos...offset)
|
52
|
+
nodes.push(token)
|
53
|
+
end
|
54
|
+
|
55
|
+
token = token_class.new('emoji', '', 0)
|
56
|
+
token.markup = emoji_name
|
57
|
+
token.content = @emojies[emoji_name]
|
58
|
+
nodes.push(token)
|
59
|
+
|
60
|
+
last_pos = offset + match.length
|
61
|
+
end
|
62
|
+
|
63
|
+
if last_pos < text.length
|
64
|
+
token = token_class.new('text', '', 0)
|
65
|
+
token.content = text.slice(last_pos..-1)
|
66
|
+
nodes.push(token)
|
67
|
+
end
|
68
|
+
|
69
|
+
return nodes
|
70
|
+
end
|
71
|
+
|
72
|
+
#------------------------------------------------------------------------------
|
73
|
+
def emoji_replace(state)
|
74
|
+
blockTokens = state.tokens
|
75
|
+
autolinkLevel = 0
|
76
|
+
|
77
|
+
0.upto(blockTokens.length - 1) do |j|
|
78
|
+
next if blockTokens[j].type != 'inline'
|
79
|
+
tokens = blockTokens[j].children
|
80
|
+
|
81
|
+
# We scan from the end, to keep position when new tags added.
|
82
|
+
# Use reversed logic in links start/end match
|
83
|
+
(tokens.length - 1).downto(0) do |i|
|
84
|
+
token = tokens[i]
|
85
|
+
|
86
|
+
if token.type == 'link_open' || token.type == 'link_close'
|
87
|
+
autolinkLevel -= token.nesting if token.info == 'auto'
|
88
|
+
end
|
89
|
+
|
90
|
+
if token.type == 'text' && autolinkLevel == 0 && @scanRE =~ token.content
|
91
|
+
# replace current node
|
92
|
+
blockTokens[j].children = tokens = arrayReplaceAt(
|
93
|
+
tokens, i, splitTextToken(token.content, token.level, MarkdownIt::Token)
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
fixture_dir = fixture_path('emoji/fixtures')
|
2
|
+
|
3
|
+
#------------------------------------------------------------------------------
|
4
|
+
describe 'markdown-it-emoji' do
|
5
|
+
describe 'default' do
|
6
|
+
md = MarkdownIt::Parser.new
|
7
|
+
md.use(MotionMarkdownItPlugins::Emoji)
|
8
|
+
|
9
|
+
generate(File.join(fixture_dir, 'default/emoji.txt'), md)
|
10
|
+
generate(File.join(fixture_dir, 'default/aliases.txt'), md)
|
11
|
+
generate(File.join(fixture_dir, 'full.txt'), md)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'options' do
|
15
|
+
md = MarkdownIt::Parser.new
|
16
|
+
md.use(MotionMarkdownItPlugins::Emoji, {
|
17
|
+
defs: {
|
18
|
+
one: '!!!one!!!',
|
19
|
+
fifty: '!!50!!'
|
20
|
+
},
|
21
|
+
shortcuts: {
|
22
|
+
fifty: [ ':50', '|50' ],
|
23
|
+
one: ':uno'
|
24
|
+
}
|
25
|
+
})
|
26
|
+
|
27
|
+
generate(File.join(fixture_dir, 'options.txt'), md)
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'whitelist' do
|
31
|
+
md = MarkdownIt::Parser.new
|
32
|
+
md.use(MotionMarkdownItPlugins::Emoji, { enabled: [ 'smile', 'grin' ] })
|
33
|
+
|
34
|
+
generate(File.join(fixture_dir, 'whitelist.txt'), md)
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'autolinks' do
|
38
|
+
md = MarkdownIt::Parser.new({ linkify: true })
|
39
|
+
md.use(MotionMarkdownItPlugins::Emoji)
|
40
|
+
|
41
|
+
generate(File.join(fixture_dir, 'autolinks.txt'), md)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'markdown-it-emoji-light' do
|
46
|
+
describe 'default' do
|
47
|
+
md = MarkdownIt::Parser.new
|
48
|
+
md.use(MotionMarkdownItPlugins::EmojiLight)
|
49
|
+
|
50
|
+
generate(File.join(fixture_dir, 'default/emoji.txt'), md)
|
51
|
+
generate(File.join(fixture_dir, 'default/aliases.txt'), md)
|
52
|
+
generate(File.join(fixture_dir, 'light.txt'), md)
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'options' do
|
56
|
+
md = MarkdownIt::Parser.new
|
57
|
+
md.use(MotionMarkdownItPlugins::EmojiLight, {
|
58
|
+
defs: {
|
59
|
+
one: '!!!one!!!',
|
60
|
+
fifty: '!!50!!'
|
61
|
+
},
|
62
|
+
shortcuts: {
|
63
|
+
fifty: [ ':50', '|50' ],
|
64
|
+
one: ':uno'
|
65
|
+
}
|
66
|
+
})
|
67
|
+
|
68
|
+
generate(File.join(fixture_dir, 'options.txt'), md)
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'whitelist' do
|
72
|
+
md = MarkdownIt::Parser.new
|
73
|
+
md.use(MotionMarkdownItPlugins::EmojiLight, { enabled: [ 'smile', 'grin' ] })
|
74
|
+
|
75
|
+
generate(File.join(fixture_dir, 'whitelist.txt'), md)
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'autolinks' do
|
79
|
+
md = MarkdownIt::Parser.new({ linkify: true })
|
80
|
+
md.use(MotionMarkdownItPlugins::EmojiLight)
|
81
|
+
|
82
|
+
generate(File.join(fixture_dir, 'autolinks.txt'), md)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'integrity' do
|
87
|
+
it 'all shortcuts should exist' do
|
88
|
+
MotionMarkdownItPlugins::EmojiPlugin::Data::Shortcuts::EMOJIES_DEF_SHORTCUTS.keys.each do |name|
|
89
|
+
expect(MotionMarkdownItPlugins::EmojiPlugin::Data::Full::EMOJIES_DEF_FULL[name]).not_to eq nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'no chars with "uXXXX" names allowed' do
|
94
|
+
MotionMarkdownItPlugins::EmojiPlugin::Data::Full::EMOJIES_DEF_FULL.keys.each do |name|
|
95
|
+
expect(/^u[0-9a-b]{4,}$/ =~ name).to eq nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'all light chars should exist' do
|
100
|
+
|
101
|
+
visible = File.read(File.join(fixture_dir, 'visible.txt'))
|
102
|
+
|
103
|
+
available = MotionMarkdownItPlugins::EmojiPlugin::Data::Light::EMOJIES_DEF_LIGHT.keys.map do |k|
|
104
|
+
MotionMarkdownItPlugins::EmojiPlugin::Data::Light::EMOJIES_DEF_LIGHT[k].gsub(/\uFE0F/, '')
|
105
|
+
end
|
106
|
+
|
107
|
+
missed = ''
|
108
|
+
|
109
|
+
visible.each_char do |ch|
|
110
|
+
missed << ch unless available.include?(ch)
|
111
|
+
end
|
112
|
+
|
113
|
+
expect(missed.length).to eq 0
|
114
|
+
end
|
115
|
+
end
|