test_Tools_GetOpenTabs.js (8399B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const { getOpenTabs } = ChromeUtils.importESModule( 6 "moz-src:///browser/components/aiwindow/models/Tools.sys.mjs" 7 ); 8 9 const { sinon } = ChromeUtils.importESModule( 10 "resource://testing-common/Sinon.sys.mjs" 11 ); 12 13 function createFakeTab(url, title, lastAccessed) { 14 return { 15 linkedBrowser: { 16 currentURI: { 17 spec: url, 18 }, 19 }, 20 label: title, 21 lastAccessed, 22 }; 23 } 24 25 function createFakeWindow(tabs, closed = false, isAIWindow = true) { 26 return { 27 closed, 28 gBrowser: { 29 tabs, 30 }, 31 document: { 32 documentElement: { 33 hasAttribute: attr => attr === "ai-window" && isAIWindow, 34 }, 35 }, 36 }; 37 } 38 39 function setupPageDataServiceMock(sandbox, descriptionMap = {}) { 40 const PageDataService = ChromeUtils.importESModule( 41 "moz-src:///browser/components/pagedata/PageDataService.sys.mjs" 42 ).PageDataService; 43 44 sandbox.stub(PageDataService, "getCached").callsFake(url => { 45 if (url in descriptionMap) { 46 return { description: descriptionMap[url] }; 47 } 48 return null; 49 }); 50 51 sandbox.stub(PageDataService, "fetchPageData").callsFake(async url => { 52 if (url in descriptionMap) { 53 return { description: descriptionMap[url] }; 54 } 55 return null; 56 }); 57 } 58 59 add_task(async function test_getOpenTabs_basic() { 60 const BrowserWindowTracker = ChromeUtils.importESModule( 61 "resource:///modules/BrowserWindowTracker.sys.mjs" 62 ).BrowserWindowTracker; 63 64 const sb = sinon.createSandbox(); 65 66 try { 67 const fakeWindow = createFakeWindow([ 68 createFakeTab("https://example.com", "Example", 1000), 69 createFakeTab("https://mozilla.org", "Mozilla", 2000), 70 createFakeTab("https://firefox.com", "Firefox", 3000), 71 ]); 72 73 sb.stub(BrowserWindowTracker, "orderedWindows").get(() => [fakeWindow]); 74 setupPageDataServiceMock(sb, { 75 "https://firefox.com": "Firefox browser homepage", 76 "https://mozilla.org": "Mozilla organization site", 77 }); 78 79 const tabs = await getOpenTabs(); 80 81 Assert.equal(tabs.length, 3, "Should return all 3 tabs"); 82 Assert.equal(tabs[0].url, "https://firefox.com", "Most recent tab first"); 83 Assert.equal(tabs[0].title, "Firefox", "Title should match"); 84 // @todo Bug2009194 85 // Assert.equal( 86 // tabs[0].description, 87 // "Firefox browser homepage", 88 // "Description should be fetched" 89 // ); 90 Assert.equal(tabs[1].url, "https://mozilla.org", "Second most recent tab"); 91 // @todo Bug2009194 92 // Assert.equal( 93 // tabs[1].description, 94 // "Mozilla organization site", 95 // "Description should be fetched" 96 // ); 97 Assert.equal(tabs[2].url, "https://example.com", "Least recent tab"); 98 Assert.equal( 99 tabs[2].description, 100 "", 101 "Description should be empty when not available" 102 ); 103 } finally { 104 sb.restore(); 105 } 106 }); 107 108 add_task(async function test_getOpenTabs_filters_about_urls() { 109 const BrowserWindowTracker = ChromeUtils.importESModule( 110 "resource:///modules/BrowserWindowTracker.sys.mjs" 111 ).BrowserWindowTracker; 112 113 const sb = sinon.createSandbox(); 114 115 try { 116 const fakeWindow = createFakeWindow([ 117 createFakeTab("https://example.com", "Example", 1000), 118 createFakeTab("about:preferences", "Preferences", 2000), 119 createFakeTab("about:config", "Config", 3000), 120 createFakeTab("https://mozilla.org", "Mozilla", 4000), 121 createFakeTab("about:blank", "Blank", 5000), 122 ]); 123 124 sb.stub(BrowserWindowTracker, "orderedWindows").get(() => [fakeWindow]); 125 setupPageDataServiceMock(sb); 126 127 const tabs = await getOpenTabs(); 128 129 Assert.equal( 130 tabs.length, 131 2, 132 "Should only return non-about: tabs (filtered 3)" 133 ); 134 Assert.equal( 135 tabs[0].url, 136 "https://mozilla.org", 137 "Should return mozilla.org" 138 ); 139 Assert.equal(tabs[1].url, "https://example.com", "Should return example"); 140 Assert.ok( 141 !tabs.some(t => t.url.startsWith("about:")), 142 "No about: URLs in results" 143 ); 144 } finally { 145 sb.restore(); 146 } 147 }); 148 149 add_task(async function test_getOpenTabs_pagination() { 150 const BrowserWindowTracker = ChromeUtils.importESModule( 151 "resource:///modules/BrowserWindowTracker.sys.mjs" 152 ).BrowserWindowTracker; 153 154 const sb = sinon.createSandbox(); 155 156 try { 157 const tabs = []; 158 for (let i = 0; i < 20; i++) { 159 tabs.push( 160 createFakeTab(`https://example${i}.com`, `Example ${i}`, i * 1000) 161 ); 162 } 163 const fakeWindow = createFakeWindow(tabs); 164 165 sb.stub(BrowserWindowTracker, "orderedWindows").get(() => [fakeWindow]); 166 setupPageDataServiceMock(sb); 167 168 // Test default limit 169 const defaultResult = await getOpenTabs(); 170 Assert.equal(defaultResult.length, 15, "Should default to 15 tabs"); 171 Assert.equal( 172 defaultResult[0].url, 173 "https://example19.com", 174 "First tab should be most recent" 175 ); 176 177 // Test custom limit 178 const customResult = await getOpenTabs(10); 179 Assert.equal(customResult.length, 10, "Should return at most 10 tabs"); 180 Assert.equal( 181 customResult[9].url, 182 "https://example10.com", 183 "Last tab should be 10th most recent" 184 ); 185 } finally { 186 sb.restore(); 187 } 188 }); 189 190 add_task(async function test_getOpenTabs_filters_non_ai_windows() { 191 const BrowserWindowTracker = ChromeUtils.importESModule( 192 "resource:///modules/BrowserWindowTracker.sys.mjs" 193 ).BrowserWindowTracker; 194 195 const sb = sinon.createSandbox(); 196 197 try { 198 const aiWindow = createFakeWindow( 199 [ 200 createFakeTab("https://ai1.com", "AI Tab 1", 1000), 201 createFakeTab("https://ai2.com", "AI Tab 2", 2000), 202 ], 203 false, 204 true 205 ); 206 207 const classicWindow = createFakeWindow( 208 [ 209 createFakeTab("https://classic1.com", "Classic Tab 1", 3000), 210 createFakeTab("https://classic2.com", "Classic Tab 2", 4000), 211 ], 212 false, 213 false 214 ); 215 216 sb.stub(BrowserWindowTracker, "orderedWindows").get(() => [ 217 classicWindow, 218 aiWindow, 219 ]); 220 setupPageDataServiceMock(sb); 221 222 const tabs = await getOpenTabs(); 223 224 Assert.equal( 225 tabs.length, 226 2, 227 "Should only return tabs from AI Windows (filtered 2 classic tabs)" 228 ); 229 Assert.equal(tabs[0].url, "https://ai2.com", "Most recent AI tab"); 230 Assert.equal(tabs[1].url, "https://ai1.com", "Second AI tab"); 231 Assert.ok( 232 !tabs.some(t => t.url.includes("classic")), 233 "No classic window tabs in results" 234 ); 235 } finally { 236 sb.restore(); 237 } 238 }); 239 240 add_task(async function test_getOpenTabs_return_structure() { 241 const BrowserWindowTracker = ChromeUtils.importESModule( 242 "resource:///modules/BrowserWindowTracker.sys.mjs" 243 ).BrowserWindowTracker; 244 245 const sb = sinon.createSandbox(); 246 247 try { 248 const fakeWindow = createFakeWindow([ 249 createFakeTab("https://test.com", "Test Page", 1000), 250 ]); 251 252 sb.stub(BrowserWindowTracker, "orderedWindows").get(() => [fakeWindow]); 253 setupPageDataServiceMock(sb, { 254 "https://test.com": "A test page description", 255 }); 256 257 const tabs = await getOpenTabs(); 258 259 Assert.equal(tabs.length, 1, "Should return one tab"); 260 261 const tab = tabs[0]; 262 Assert.ok("url" in tab, "Tab should have url property"); 263 Assert.ok("title" in tab, "Tab should have title property"); 264 Assert.ok("description" in tab, "Tab should have description property"); 265 Assert.ok("lastAccessed" in tab, "Tab should have lastAccessed property"); 266 267 Assert.equal(typeof tab.url, "string", "url should be a string"); 268 Assert.equal(typeof tab.title, "string", "title should be a string"); 269 Assert.equal( 270 typeof tab.description, 271 "string", 272 "description should be a string" 273 ); 274 Assert.equal( 275 typeof tab.lastAccessed, 276 "number", 277 "lastAccessed should be a number" 278 ); 279 280 Assert.equal(tab.url, "https://test.com", "url value correct"); 281 Assert.equal(tab.title, "Test Page", "title value correct"); 282 // @todo Bug2009194 283 // Assert.equal( 284 // tab.description, 285 // "A test page description", 286 // "description should be fetched from PageDataService" 287 // ); 288 Assert.equal(tab.lastAccessed, 1000, "lastAccessed value correct"); 289 } finally { 290 sb.restore(); 291 } 292 });