|
1 |
| -import { renderMentionTags, unwrapMentions, wrapMentions } from '../mentions'; |
| 1 | +import { |
| 2 | + renderMentionTags, |
| 3 | + unwrapMentions, |
| 4 | + wrapMentions, |
| 5 | + getContainingMentionOffsets, |
| 6 | + termBeforePosition, |
| 7 | +} from '../mentions'; |
2 | 8 |
|
3 | 9 | const mentionTag = (username, authority) =>
|
4 | 10 | `<a data-hyp-mention="" data-userid="acct:${username}@${authority}">@${username}</a>`;
|
@@ -40,16 +46,35 @@ look at ${mentionTag('foo', 'example.com')} comment`,
|
40 | 46 | },
|
41 | 47 | // Multiple mentions
|
42 | 48 | {
|
43 |
| - text: 'Hey @jane look at this quote from @rob', |
| 49 | + text: 'Hey @jane, look at this quote from @rob', |
44 | 50 | authority: 'example.com',
|
45 |
| - textWithTags: `Hey ${mentionTag('jane', 'example.com')} look at this quote from ${mentionTag('rob', 'example.com')}`, |
| 51 | + textWithTags: `Hey ${mentionTag('jane', 'example.com')}, look at this quote from ${mentionTag('rob', 'example.com')}`, |
| 52 | + }, |
| 53 | + // Mentions wrapped in punctuation chars |
| 54 | + { |
| 55 | + text: '(@jane) {@rob} and @john?', |
| 56 | + authority: 'example.com', |
| 57 | + textWithTags: `(${mentionTag('jane', 'example.com')}) {${mentionTag('rob', 'example.com')}} and ${mentionTag('john', 'example.com')}?`, |
| 58 | + }, |
| 59 | + // username-like patterns with invalid chars should be ignored |
| 60 | + { |
| 61 | + text: 'Hello @not+a/user=name', |
| 62 | + authority: 'example.com', |
| 63 | + textWithTags: `Hello @not+a/user=name`, |
46 | 64 | },
|
47 | 65 | // Email addresses should be ignored
|
48 | 66 | {
|
49 | 67 | text: 'Ignore email: [email protected]',
|
50 | 68 | authority: 'example.com',
|
51 | 69 | textWithTags: 'Ignore email: [email protected]',
|
52 | 70 | },
|
| 71 | + // Trailing dots should not be considered part of the mention, but dots |
| 72 | + // in-between should |
| 73 | + { |
| 74 | + text: 'Hello @jane.doe.', |
| 75 | + authority: 'example.com', |
| 76 | + textWithTags: `Hello ${mentionTag('jane.doe', 'example.com')}.`, |
| 77 | + }, |
53 | 78 | ].forEach(({ text, authority, textWithTags }) => {
|
54 | 79 | describe('wrapMentions', () => {
|
55 | 80 | it('wraps every mention in a mention tag', () => {
|
@@ -119,3 +144,127 @@ describe('renderMentionTags', () => {
|
119 | 144 | assert.equal(fourthMention, '@user_id_missing');
|
120 | 145 | });
|
121 | 146 | });
|
| 147 | + |
| 148 | +// To make these tests more predictable, we place the `$` sign in the position |
| 149 | +// to be checked. That way it's easier to see what is the "word" preceding it. |
| 150 | +// The test will then get the `$` sign index and remove it from the text |
| 151 | +// before passing it to `termBeforePosition`. |
| 152 | +[ |
| 153 | + // First and last positions |
| 154 | + { |
| 155 | + text: '$Hello world', |
| 156 | + expectedTerm: '', |
| 157 | + expectedOffsets: { start: 0, end: 5 }, |
| 158 | + }, |
| 159 | + { |
| 160 | + text: 'Hello world$', |
| 161 | + expectedTerm: 'world', |
| 162 | + expectedOffsets: { start: 6, end: 11 }, |
| 163 | + }, |
| 164 | + |
| 165 | + // Position in the middle of words |
| 166 | + { |
| 167 | + text: 'Hell$o world', |
| 168 | + expectedTerm: 'Hell', |
| 169 | + expectedOffsets: { start: 0, end: 5 }, |
| 170 | + }, |
| 171 | + { |
| 172 | + text: 'Hello wor$ld', |
| 173 | + expectedTerm: 'wor', |
| 174 | + expectedOffsets: { start: 6, end: 11 }, |
| 175 | + }, |
| 176 | + |
| 177 | + // Position preceded by "empty space" |
| 178 | + { |
| 179 | + text: 'Hello $world', |
| 180 | + expectedTerm: '', |
| 181 | + expectedOffsets: { start: 6, end: 11 }, |
| 182 | + }, |
| 183 | + { |
| 184 | + text: `Text with |
| 185 | + multiple |
| 186 | + $ |
| 187 | + lines |
| 188 | + `, |
| 189 | + expectedTerm: '', |
| 190 | + expectedOffsets: { start: 31, end: 31 }, |
| 191 | + }, |
| 192 | + |
| 193 | + // Position preceded by/in the middle of a word for multi-line text |
| 194 | + { |
| 195 | + text: `Text with$ |
| 196 | + multiple |
| 197 | +
|
| 198 | + lines |
| 199 | + `, |
| 200 | + expectedTerm: 'with', |
| 201 | + expectedOffsets: { start: 5, end: 9 }, |
| 202 | + }, |
| 203 | + { |
| 204 | + text: `Text with |
| 205 | + multiple |
| 206 | +
|
| 207 | + li$nes |
| 208 | + `, |
| 209 | + expectedTerm: 'li', |
| 210 | + expectedOffsets: { start: 32, end: 37 }, |
| 211 | + }, |
| 212 | + |
| 213 | + // Including punctuation characters |
| 214 | + ...[ |
| 215 | + ',', |
| 216 | + '.', |
| 217 | + ';', |
| 218 | + ':', |
| 219 | + '|', |
| 220 | + '?', |
| 221 | + '!', |
| 222 | + "'", |
| 223 | + '"', |
| 224 | + '-', |
| 225 | + '(', |
| 226 | + ')', |
| 227 | + '[', |
| 228 | + ']', |
| 229 | + '{', |
| 230 | + '}', |
| 231 | + ].flatMap(char => [ |
| 232 | + { |
| 233 | + text: `Foo${char}$ bar`, |
| 234 | + expectedTerm: '', |
| 235 | + expectedOffsets: { start: 4, end: 4 }, |
| 236 | + }, |
| 237 | + { |
| 238 | + text: `${char}Foo$ bar`, |
| 239 | + expectedTerm: 'Foo', |
| 240 | + expectedOffsets: { start: 1, end: 4 }, |
| 241 | + }, |
| 242 | + { |
| 243 | + text: `hello ${char}fo$o${char} bar`, |
| 244 | + expectedTerm: 'fo', |
| 245 | + expectedOffsets: { start: 7, end: 10 }, |
| 246 | + }, |
| 247 | + ]), |
| 248 | +].forEach(({ text, expectedTerm, expectedOffsets }) => { |
| 249 | + // Get the position of the `$` sign in the text, then remove it |
| 250 | + const position = text.indexOf('$'); |
| 251 | + const textWithoutDollarSign = text.replace('$', ''); |
| 252 | + |
| 253 | + describe('termBeforePosition', () => { |
| 254 | + it('returns the term right before provided position', () => { |
| 255 | + assert.equal( |
| 256 | + termBeforePosition(textWithoutDollarSign, position), |
| 257 | + expectedTerm, |
| 258 | + ); |
| 259 | + }); |
| 260 | + }); |
| 261 | + |
| 262 | + describe('getContainingMentionOffsets', () => { |
| 263 | + it('returns expected offsets', () => { |
| 264 | + assert.deepEqual( |
| 265 | + getContainingMentionOffsets(textWithoutDollarSign, position), |
| 266 | + expectedOffsets, |
| 267 | + ); |
| 268 | + }); |
| 269 | + }); |
| 270 | +}); |
0 commit comments