test_link_preview_text.js (9072B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const { LinkPreviewModel } = ChromeUtils.importESModule( 5 "moz-src:///browser/components/genai/LinkPreviewModel.sys.mjs" 6 ); 7 const { SentencePostProcessor } = ChromeUtils.importESModule( 8 "moz-src:///browser/components/genai/LinkPreviewModel.sys.mjs" 9 ); 10 11 const { sinon } = ChromeUtils.importESModule( 12 "resource://testing-common/Sinon.sys.mjs" 13 ); 14 15 const { BlockListManager } = ChromeUtils.importESModule( 16 "chrome://global/content/ml/Utils.sys.mjs" 17 ); 18 19 /** 20 * Test that text is processed for ai. 21 */ 22 add_task(function test_text_processing() { 23 const text = "This is a test sentence. "; 24 Assert.equal( 25 LinkPreviewModel.preprocessText(text), 26 "This is a test sentence.", 27 "basic sentence should be trimmed" 28 ); 29 Assert.equal( 30 LinkPreviewModel.preprocessText("Too short."), 31 "", 32 "short sentence should be removed" 33 ); 34 Assert.equal( 35 LinkPreviewModel.preprocessText("Today is Mar. 12, 2025."), 36 "Today is Mar. 12, 2025.", 37 "abbreviations don't break sentences" 38 ); 39 Assert.equal( 40 LinkPreviewModel.preprocessText("Thisisatestsentence but fewwords."), 41 "", 42 "needs enough words" 43 ); 44 Assert.equal( 45 LinkPreviewModel.preprocessText( 46 "This is a test sentence without punctuation" 47 ), 48 "", 49 "needs punctuation" 50 ); 51 Assert.equal( 52 LinkPreviewModel.preprocessText(`${text} 53 Too short. 54 Today is Mar. 12, 2025. 55 Thisisatestsentence but fewwords. 56 This is a test sentence without punctuation`), 57 "This is a test sentence. Today is Mar. 12, 2025.", 58 "multiple sentences should be processed" 59 ); 60 61 Assert.equal( 62 LinkPreviewModel.preprocessText(`${text} 63 64 Short \t\t\t\t\t\t\t\t\t\t without spaces. 65 66 67 68 It has \t \t \t multiple consecutive spaces in between words. 69 They should all go away 70 `), 71 "This is a test sentence. It has multiple consecutive spaces in between words.", 72 "Remove consecutive spaces by single." 73 ); 74 75 Assert.equal( 76 LinkPreviewModel.preprocessText(text.repeat(100)), 77 text.repeat(6).trim(), 78 "restrict to 6 sentences" 79 ); 80 81 Assert.equal( 82 LinkPreviewModel.preprocessText( 83 "This is a test sentence with an emoji \ud83e\udd14." 84 ), 85 "This is a test sentence with an emoji .", 86 "emoji should be removed from the sentence" 87 ); 88 }); 89 90 /** 91 * Test that prefs affect text processing. 92 */ 93 add_task(function test_text_processing_prefs() { 94 const text = "This is a test sentence. "; 95 Services.prefs.setIntPref("browser.ml.linkPreview.inputSentences", 3); 96 Assert.equal( 97 LinkPreviewModel.preprocessText(text.repeat(100)), 98 text.repeat(3).trim(), 99 "restrict to 3 sentences" 100 ); 101 102 Services.prefs.setIntPref( 103 "browser.ml.linkPreview.minWordsPerOutputSentences", 104 3 105 ); 106 let processor = new SentencePostProcessor(); 107 let output = ""; 108 output += processor.put("I am writing unit test.").sentence; 109 110 output += processor.put(" So great.").sentence; 111 112 output += processor.put(" I like it.").sentence; 113 114 output += processor.put(" Done. ").sentence; 115 116 Assert.equal( 117 output, 118 "I am writing unit test. I like it. ", 119 "ignore too short sentences" 120 ); 121 122 Services.prefs.setIntPref( 123 "browser.ml.linkPreview.minWordsPerOutputSentences", 124 4 125 ); 126 processor = new SentencePostProcessor(); 127 output = ""; 128 output += processor.put("I am writing unit test.").sentence; 129 130 output += processor.put(" So great.").sentence; 131 132 output += processor.put(" I like it.").sentence; 133 134 output += processor.put(" Done. ").sentence; 135 136 Assert.equal( 137 output, 138 "I am writing unit test. ", 139 "ignore too short sentences" 140 ); 141 142 Services.prefs.clearUserPref("browser.ml.linkPreview.inputSentences"); 143 Services.prefs.clearUserPref( 144 "browser.ml.linkPreview.minWordsPerOutputSentences" 145 ); 146 }); 147 148 /** 149 * Test that post processor provides sentences. 150 */ 151 add_task(function test_sentence_post_processor() { 152 const processor = new SentencePostProcessor(); 153 Assert.deepEqual( 154 processor.put("Hello "), 155 { sentence: "", abort: false }, 156 "no sentence yet" 157 ); 158 Assert.deepEqual( 159 processor.put("world. "), 160 { sentence: "", abort: false }, 161 "sentence complete but not next" 162 ); 163 Assert.deepEqual( 164 processor.put("How "), 165 { sentence: "Hello world. ", abort: false }, 166 "previous sentence complete" 167 ); 168 Assert.deepEqual( 169 processor.put("are you today? I'm"), 170 { sentence: "How are you today? ", abort: false }, 171 "question complete" 172 ); 173 Assert.deepEqual( 174 processor.put(" fine. And"), 175 { sentence: "I'm fine. ", abort: true }, 176 "response complete" 177 ); 178 Assert.deepEqual( 179 processor.put("you? Good"), 180 { sentence: "", abort: true }, 181 "hit limit" 182 ); 183 Assert.equal(processor.flush(), "", "still nothing at limit"); 184 }); 185 186 /** 187 * Test that generateTextAI works properly with the blocklist 188 */ 189 add_task(async function test_generateAI_with_blocklist() { 190 // Mocked ML Engine 191 const engine = { 192 async *runWithGenerator() { 193 const preview = 194 "Hello world, I am here and would like to make a sentence. Now, more sentences are coming. Even more are raining here. Bye"; 195 196 for (const text of preview) { 197 yield { text }; 198 } 199 }, 200 201 terminate() {}, 202 }; 203 204 // Mocked Blocked List Manager 205 let manager = new BlockListManager({ 206 blockNgrams: [BlockListManager.encodeBase64("hello")], 207 language: "en", 208 }); 209 210 // Disable block list 211 Services.prefs.setBoolPref("browser.ml.linkPreview.blockListEnabled", false); 212 213 let createEngineStub = sinon 214 .stub(LinkPreviewModel, "createEngine") 215 .returns(engine); 216 217 let managerStub = sinon 218 .stub(BlockListManager, "initializeFromRemoteSettings") 219 .returns(manager); 220 221 let numOutputs = 0; 222 223 await LinkPreviewModel.generateTextAI( 224 "This is the big article. Now give me its preview. Please.", 225 { 226 onText: () => { 227 numOutputs += 1; 228 }, 229 } 230 ); 231 232 Assert.equal( 233 numOutputs, 234 3, 235 "should output all sentences when block list is disabled" 236 ); 237 238 // Enable block list 239 Services.prefs.setBoolPref("browser.ml.linkPreview.blockListEnabled", true); 240 241 numOutputs = 0; 242 243 await LinkPreviewModel.generateTextAI( 244 "This is the big article. Now give me its preview. Please.", 245 { 246 onText: () => { 247 numOutputs += 1; 248 }, 249 } 250 ); 251 252 Assert.equal( 253 numOutputs, 254 0, 255 "Should output no sentences when 1st sentence contains block word and block list enabled." 256 ); 257 258 managerStub.restore(); 259 manager = new BlockListManager({ 260 blockNgrams: [BlockListManager.encodeBase64("coming")], 261 language: "en", 262 }); 263 managerStub = sinon 264 .stub(BlockListManager, "initializeFromRemoteSettings") 265 .returns(manager); 266 267 // Force link preview to reload blockList manager 268 Services.prefs.setBoolPref("browser.ml.linkPreview.blockListEnabled", false); 269 await LinkPreviewModel.generateTextAI( 270 "This is the big article. Now give me its preview. Please." 271 ); 272 273 Assert.equal(LinkPreviewModel.blockListManager, null); 274 275 // Now re-enable block list 276 Services.prefs.setBoolPref("browser.ml.linkPreview.blockListEnabled", true); 277 278 numOutputs = 0; 279 await LinkPreviewModel.generateTextAI( 280 "This is the big article. Now give me its preview. Please.", 281 { 282 onText: () => { 283 numOutputs += 1; 284 }, 285 } 286 ); 287 288 Assert.equal( 289 numOutputs, 290 1, 291 "Should output 1 sentence when blocked word found 1st time in 2nd sentence and block list enabled." 292 ); 293 294 Services.prefs.setBoolPref("browser.ml.linkPreview.blockListEnabled", false); 295 296 numOutputs = 0; 297 298 await LinkPreviewModel.generateTextAI( 299 "This is the big article. Now give me its preview. Please.", 300 { 301 onText: () => { 302 numOutputs += 1; 303 }, 304 } 305 ); 306 307 Assert.equal( 308 numOutputs, 309 3, 310 "all sentences should be outputted when block list disabled" 311 ); 312 313 Services.prefs.clearUserPref("browser.ml.linkPreview.blockListEnabled"); 314 createEngineStub.restore(); 315 managerStub.restore(); 316 }); 317 318 /** 319 * Test post processor respects limits. 320 */ 321 add_task(function test_sentence_post_processor_limits() { 322 const processor = new SentencePostProcessor({ 323 maxNumOutputSentences: 1, 324 }); 325 Assert.deepEqual( 326 processor.put("Hi. There. "), 327 { sentence: "Hi. ", abort: true }, 328 "first sentence" 329 ); 330 Assert.deepEqual( 331 processor.put("Nope."), 332 { sentence: "", abort: true }, 333 "no more sentences" 334 ); 335 336 Services.prefs.setIntPref("browser.ml.linkPreview.outputSentences", 2); 337 const viaPref = new SentencePostProcessor(); 338 Assert.deepEqual( 339 viaPref.put("Hi. There. "), 340 { sentence: "Hi. ", abort: false }, 341 "first sentence" 342 ); 343 Assert.deepEqual( 344 viaPref.put("Yup. "), 345 { sentence: "There. ", abort: true }, 346 "second sentence" 347 ); 348 Assert.deepEqual( 349 viaPref.put("Nope."), 350 { sentence: "", abort: true }, 351 "no more sentences" 352 ); 353 Services.prefs.clearUserPref("browser.ml.linkPreview.outputSentences"); 354 });