unit-entry.js (15885B)
1 import { 2 EventEmitter, 3 FakePrefs, 4 FakensIPrefService, 5 GlobalOverrider, 6 FakeConsoleAPI, 7 FakeLogger, 8 FakeNimbusFeatures, 9 } from "test/unit/utils"; 10 import Adapter from "enzyme-adapter-react-16"; 11 import { chaiAssertions } from "test/schemas/pings"; 12 import enzyme from "enzyme"; 13 14 enzyme.configure({ adapter: new Adapter() }); 15 16 // Cause React warnings to make tests that trigger them fail 17 const origConsoleError = console.error; 18 console.error = function (msg, ...args) { 19 origConsoleError.apply(console, [msg, ...args]); 20 21 if ( 22 /(Invalid prop|Failed prop type|Check the render method|React Intl)/.test( 23 msg 24 ) 25 ) { 26 throw new Error(msg); 27 } 28 }; 29 30 const req = require.context(".", true, /\.test\.jsx?$/); 31 const files = req.keys(); 32 33 // This exposes sinon assertions to chai.assert 34 sinon.assert.expose(assert, { prefix: "" }); 35 36 chai.use(chaiAssertions); 37 38 const overrider = new GlobalOverrider(); 39 40 const RemoteSettings = name => ({ 41 get: () => { 42 if (name === "attachment") { 43 return Promise.resolve([{ attachment: {} }]); 44 } 45 return Promise.resolve([]); 46 }, 47 on: () => {}, 48 off: () => {}, 49 }); 50 RemoteSettings.pollChanges = () => {}; 51 52 class JSWindowActorParent { 53 sendAsyncMessage(name, data) { 54 return { name, data }; 55 } 56 } 57 58 class JSWindowActorChild { 59 sendAsyncMessage(name, data) { 60 return { name, data }; 61 } 62 63 sendQuery(name, data) { 64 return Promise.resolve({ name, data }); 65 } 66 67 get contentWindow() { 68 return { 69 Promise, 70 }; 71 } 72 } 73 74 class NewTabContentPing { 75 recordEvent() {} 76 scheduleSubmission() {} 77 uninit() {} 78 } 79 80 // Detect plain object passed to lazy getter APIs, and set its prototype to 81 // global object, and return the global object for further modification. 82 // Returns the object if it's not plain object. 83 // 84 // This is a workaround to make the existing testharness and testcase keep 85 // working even after lazy getters are moved to plain `lazy` object. 86 const cachedPlainObject = new Set(); 87 function updateGlobalOrObject(object) { 88 // Given this function modifies the prototype, and the following 89 // condition doesn't meet on the second call, cache the result. 90 if (cachedPlainObject.has(object)) { 91 return global; 92 } 93 94 if (Object.getPrototypeOf(object).constructor.name !== "Object") { 95 return object; 96 } 97 98 cachedPlainObject.add(object); 99 Object.setPrototypeOf(object, global); 100 return global; 101 } 102 103 const TEST_GLOBAL = { 104 JSWindowActorParent, 105 JSWindowActorChild, 106 AboutReaderParent: { 107 addMessageListener: (_messageName, _listener) => {}, 108 removeMessageListener: (_messageName, _listener) => {}, 109 }, 110 AboutWelcomeTelemetry: class { 111 submitGleanPingForPing() {} 112 }, 113 AddonManager: { 114 getActiveAddons() { 115 return Promise.resolve({ addons: [], fullData: false }); 116 }, 117 }, 118 AppConstants: { 119 MOZILLA_OFFICIAL: true, 120 MOZ_APP_VERSION: "69.0a1", 121 isPlatformAndVersionAtMost() { 122 return false; 123 }, 124 platform: "win", 125 }, 126 BrowserUtils: { 127 sendToDeviceEmailsSupported() { 128 return true; 129 }, 130 }, 131 UpdateUtils: { getUpdateChannel() {} }, 132 BasePromiseWorker: class { 133 constructor() { 134 this.ExceptionHandlers = []; 135 } 136 post() {} 137 }, 138 browserSearchRegion: "US", 139 BrowserWindowTracker: { getTopWindow() {} }, 140 ChromeUtils: { 141 defineLazyGetter(object, name, f) { 142 updateGlobalOrObject(object)[name] = f(); 143 }, 144 defineESModuleGetters: updateGlobalOrObject, 145 generateQI() { 146 return {}; 147 }, 148 importESModule() { 149 return global; 150 }, 151 }, 152 ClientEnvironment: { 153 get userId() { 154 return "foo123"; 155 }, 156 }, 157 Components: { 158 Constructor(classId) { 159 switch (classId) { 160 case "@mozilla.org/referrer-info;1": 161 return function (referrerPolicy, sendReferrer, originalReferrer) { 162 this.referrerPolicy = referrerPolicy; 163 this.sendReferrer = sendReferrer; 164 this.originalReferrer = originalReferrer; 165 }; 166 } 167 return function () {}; 168 }, 169 isSuccessCode: () => true, 170 }, 171 ConsoleAPI: FakeConsoleAPI, 172 // NB: These are functions/constructors 173 // eslint-disable-next-line object-shorthand 174 ContentSearchUIController: function () {}, 175 // eslint-disable-next-line object-shorthand 176 ContentSearchHandoffUIController: function () {}, 177 ContextId: { 178 request: () => "ContextId", 179 }, 180 Cc: { 181 "@mozilla.org/browser/nav-bookmarks-service;1": { 182 addObserver() {}, 183 getService() { 184 return this; 185 }, 186 removeObserver() {}, 187 SOURCES: {}, 188 TYPE_BOOKMARK: {}, 189 }, 190 "@mozilla.org/browser/nav-history-service;1": { 191 addObserver() {}, 192 executeQuery() {}, 193 getNewQuery() {}, 194 getNewQueryOptions() {}, 195 getService() { 196 return this; 197 }, 198 insert() {}, 199 markPageAsTyped() {}, 200 removeObserver() {}, 201 pageFrecencyThreshold() {}, 202 }, 203 "@mozilla.org/io/string-input-stream;1": { 204 createInstance() { 205 return {}; 206 }, 207 }, 208 "@mozilla.org/updates/update-checker;1": { createInstance() {} }, 209 "@mozilla.org/widget/useridleservice;1": { 210 getService() { 211 return { 212 idleTime: 0, 213 addIdleObserver() {}, 214 removeIdleObserver() {}, 215 }; 216 }, 217 }, 218 "@mozilla.org/streamConverters;1": { 219 getService() { 220 return this; 221 }, 222 }, 223 "@mozilla.org/network/stream-loader;1": { 224 createInstance() { 225 return {}; 226 }, 227 }, 228 "@mozilla.org/network/protocol;1?name=http": { 229 getService() { 230 return this; 231 }, 232 }, 233 }, 234 Ci: { 235 nsICryptoHash: {}, 236 nsIReferrerInfo: { UNSAFE_URL: 5 }, 237 nsITimer: { TYPE_ONE_SHOT: 1 }, 238 nsIWebProgressListener: { LOCATION_CHANGE_SAME_DOCUMENT: 1 }, 239 nsIDOMWindow: Object, 240 nsITrackingDBService: { 241 TRACKERS_ID: 1, 242 TRACKING_COOKIES_ID: 2, 243 CRYPTOMINERS_ID: 3, 244 FINGERPRINTERS_ID: 4, 245 SOCIAL_ID: 5, 246 }, 247 nsICookieBannerService: { 248 MODE_DISABLED: 0, 249 MODE_REJECT: 1, 250 MODE_REJECT_OR_ACCEPT: 2, 251 MODE_UNSET: 3, 252 }, 253 nsIProtocolProxyChannelFilter: {}, 254 }, 255 Cu: { 256 importGlobalProperties() {}, 257 now: () => window.performance.now(), 258 cloneInto: o => JSON.parse(JSON.stringify(o)), 259 }, 260 console: { 261 ...console, 262 error() {}, 263 }, 264 dump() {}, 265 EveryWindow: { 266 registerCallback: (_id, _init, _uninit) => {}, 267 unregisterCallback: _id => {}, 268 }, 269 setTimeout: window.setTimeout.bind(window), 270 clearTimeout: window.clearTimeout.bind(window), 271 fetch() {}, 272 // eslint-disable-next-line object-shorthand 273 Image: function () {}, // NB: This is a function/constructor 274 IOUtils: { 275 writeJSON() { 276 return Promise.resolve(0); 277 }, 278 readJSON() { 279 return Promise.resolve({}); 280 }, 281 read() { 282 return Promise.resolve(new Uint8Array()); 283 }, 284 makeDirectory() { 285 return Promise.resolve(0); 286 }, 287 write() { 288 return Promise.resolve(0); 289 }, 290 exists() { 291 return Promise.resolve(0); 292 }, 293 remove() { 294 return Promise.resolve(0); 295 }, 296 stat() { 297 return Promise.resolve(0); 298 }, 299 }, 300 NewTabUtils: { 301 activityStreamProvider: { 302 getTopFrecentSites: () => [], 303 executePlacesQuery: async (sql, options) => ({ sql, options }), 304 }, 305 shortHostname() {}, 306 }, 307 OS: { 308 File: { 309 writeAtomic() {}, 310 makeDir() {}, 311 stat() {}, 312 Error: {}, 313 read() {}, 314 exists() {}, 315 remove() {}, 316 removeEmptyDir() {}, 317 }, 318 Path: { 319 join() { 320 return "/"; 321 }, 322 }, 323 Constants: { 324 Path: { 325 localProfileDir: "/", 326 }, 327 }, 328 }, 329 PathUtils: { 330 join(...parts) { 331 return parts[parts.length - 1]; 332 }, 333 joinRelative(...parts) { 334 return parts[parts.length - 1]; 335 }, 336 getProfileDir() { 337 return Promise.resolve("/"); 338 }, 339 getLocalProfileDir() { 340 return Promise.resolve("/"); 341 }, 342 }, 343 PlacesUtils: { 344 get bookmarks() { 345 return TEST_GLOBAL.Cc["@mozilla.org/browser/nav-bookmarks-service;1"]; 346 }, 347 get history() { 348 return TEST_GLOBAL.Cc["@mozilla.org/browser/nav-history-service;1"]; 349 }, 350 observers: { 351 addListener() {}, 352 removeListener() {}, 353 }, 354 }, 355 Preferences: FakePrefs, 356 PrivateBrowsingUtils: { 357 isBrowserPrivate: () => false, 358 isWindowPrivate: () => false, 359 permanentPrivateBrowsing: false, 360 }, 361 DownloadsViewUI: { 362 getDisplayName: () => "filename.ext", 363 getSizeWithUnits: () => "1.5 MB", 364 }, 365 FileUtils: { 366 // eslint-disable-next-line object-shorthand 367 File: function () {}, // NB: This is a function/constructor 368 }, 369 Region: { 370 home: "US", 371 REGION_TOPIC: "browser-region-updated", 372 }, 373 Services: { 374 dirsvc: { 375 get: () => ({ parent: { parent: { path: "appPath" } } }), 376 }, 377 env: { 378 set: () => undefined, 379 }, 380 locale: { 381 get appLocaleAsBCP47() { 382 return "en-US"; 383 }, 384 negotiateLanguages() {}, 385 }, 386 urlFormatter: { formatURL: str => str, formatURLPref: str => str }, 387 mm: { 388 addMessageListener: (_msg, _cb) => this.receiveMessage(), 389 removeMessageListener() {}, 390 }, 391 obs: { 392 addObserver() {}, 393 removeObserver() {}, 394 notifyObservers() {}, 395 }, 396 uuid: { 397 generateUUID() { 398 return "{foo-123-foo}"; 399 }, 400 }, 401 console: { logStringMessage: () => {} }, 402 prefs: new FakensIPrefService(), 403 tm: { 404 dispatchToMainThread: cb => cb(), 405 idleDispatchToMainThread: cb => cb(), 406 }, 407 eTLD: { 408 getBaseDomain({ spec }) { 409 return spec.match(/\/([^/]+)/)[1]; 410 }, 411 getBaseDomainFromHost(host) { 412 return host.match(/.*?(\w+\.\w+)$/)[1]; 413 }, 414 getPublicSuffix() {}, 415 }, 416 io: { 417 newURI: spec => ({ 418 mutate: () => ({ 419 setRef: ref => ({ 420 finalize: () => ({ 421 ref, 422 spec, 423 schemeIs: scheme => spec.startsWith(scheme), 424 }), 425 }), 426 }), 427 spec, 428 }), 429 }, 430 search: { 431 init() { 432 return Promise.resolve(); 433 }, 434 getVisibleEngines: () => 435 Promise.resolve([{ identifier: "google" }, { identifier: "bing" }]), 436 defaultEngine: { 437 identifier: "google", 438 aliases: ["@google"], 439 }, 440 defaultPrivateEngine: { 441 identifier: "bing", 442 aliases: ["@bing"], 443 }, 444 getEngineByAlias: async () => null, 445 }, 446 scriptSecurityManager: { 447 createNullPrincipal() {}, 448 getSystemPrincipal() {}, 449 }, 450 vc: { 451 compare(a, b) { 452 // Rather than re-write Services.vc.compare completely, do 453 // a simple comparison of the major version. 454 // This means this function will give the wrong output for differences 455 // in minor versions, but should be sufficient for unit tests. 456 let majorA = parseInt(a, 10); 457 let majorB = parseInt(b, 10); 458 if (majorA === majorB) { 459 return 0; 460 } 461 return majorA > majorB ? 1 : -1; 462 }, 463 }, 464 wm: { 465 getMostRecentWindow: () => window, 466 getMostRecentBrowserWindow: () => window, 467 getEnumerator: () => [], 468 }, 469 ww: { registerNotification() {}, unregisterNotification() {} }, 470 appinfo: { appBuildID: "20180710100040", version: "69.0a1" }, 471 scriptloader: { loadSubScript: () => {} }, 472 startup: { 473 getStartupInfo() { 474 return { 475 process: { 476 getTime() { 477 return 1588010448000; 478 }, 479 }, 480 }; 481 }, 482 }, 483 }, 484 XPCOMUtils: { 485 defineLazyGlobalGetters: updateGlobalOrObject, 486 defineLazyServiceGetter: updateGlobalOrObject, 487 defineLazyServiceGetters: updateGlobalOrObject, 488 defineLazyPreferenceGetter(object, name) { 489 updateGlobalOrObject(object)[name] = ""; 490 }, 491 generateQI() { 492 return {}; 493 }, 494 }, 495 EventEmitter, 496 ShellService: { 497 doesAppNeedPin: () => false, 498 isDefaultBrowser: () => true, 499 }, 500 FilterExpressions: { 501 eval() { 502 return Promise.resolve(false); 503 }, 504 }, 505 RemoteSettings, 506 Localization: class { 507 async formatMessages(stringsIds) { 508 return Promise.resolve( 509 stringsIds.map(({ id, args }) => ({ value: { string_id: id, args } })) 510 ); 511 } 512 async formatValue(stringId) { 513 return Promise.resolve(stringId); 514 } 515 }, 516 FxAccountsConfig: { 517 promiseConnectAccountURI(id) { 518 return Promise.resolve(id); 519 }, 520 }, 521 FX_MONITOR_OAUTH_CLIENT_ID: "fake_client_id", 522 ExperimentAPI: {}, 523 NimbusFeatures: FakeNimbusFeatures([ 524 "glean", 525 "newtab", 526 "newtabTrainhop", 527 "pocketNewtab", 528 "newtabSmartShortcuts", 529 "newtabInferredPersonalization", 530 "newtabWidgets", 531 "newtabOhttpImages", 532 "cookieBannerHandling", 533 ]), 534 TelemetryEnvironment: { 535 setExperimentActive() {}, 536 currentEnvironment: { 537 profile: { 538 creationDate: 16587, 539 }, 540 settings: {}, 541 }, 542 }, 543 Sampling: { 544 ratioSample(_seed, _ratios) { 545 return Promise.resolve(0); 546 }, 547 }, 548 BrowserHandler: { 549 get kiosk() { 550 return false; 551 }, 552 }, 553 TelemetrySession: { 554 getMetadata(reason) { 555 return { 556 reason, 557 sessionId: "fake_session_id", 558 }; 559 }, 560 }, 561 PageThumbs: { 562 addExpirationFilter() {}, 563 removeExpirationFilter() {}, 564 }, 565 Logger: FakeLogger, 566 LinksCache: class {}, 567 FaviconFeed: class {}, 568 569 getFxAccountsSingleton() {}, 570 AboutNewTab: {}, 571 Glean: { 572 activityStream: { 573 eventClick: { 574 record() {}, 575 }, 576 endSession: { 577 record() {}, 578 }, 579 }, 580 newtab: { 581 opened: { 582 record() {}, 583 }, 584 closed: { 585 record() {}, 586 }, 587 locale: { 588 set() {}, 589 }, 590 newtabCategory: { 591 set() {}, 592 }, 593 homepageCategory: { 594 set() {}, 595 }, 596 blockedSponsors: { 597 set() {}, 598 }, 599 sovAllocation: { 600 set() {}, 601 }, 602 }, 603 newtabSearch: { 604 enabled: { 605 set() {}, 606 }, 607 }, 608 newtabHandoffPreference: { 609 enabled: { 610 set() {}, 611 }, 612 }, 613 pocket: { 614 enabled: { 615 set() {}, 616 }, 617 impression: { 618 record() {}, 619 }, 620 isSignedIn: { 621 set() {}, 622 }, 623 sponsoredStoriesEnabled: { 624 set() {}, 625 }, 626 click: { 627 record() {}, 628 }, 629 save: { 630 record() {}, 631 }, 632 topicClick: { 633 record() {}, 634 }, 635 shim: { 636 set() {}, 637 }, 638 }, 639 topsites: { 640 enabled: { 641 set() {}, 642 }, 643 sponsoredEnabled: { 644 set() {}, 645 }, 646 impression: { 647 record() {}, 648 }, 649 click: { 650 record() {}, 651 }, 652 rows: { 653 set() {}, 654 }, 655 showPrivacyClick: { 656 record() {}, 657 }, 658 dismiss: { 659 record() {}, 660 }, 661 prefChanged: { 662 record() {}, 663 }, 664 sponsoredTilesConfigured: { 665 set() {}, 666 }, 667 sponsoredTilesReceived: { 668 set() {}, 669 }, 670 }, 671 topSites: { 672 pingType: { 673 set() {}, 674 }, 675 position: { 676 set() {}, 677 }, 678 source: { 679 set() {}, 680 }, 681 tileId: { 682 set() {}, 683 }, 684 reportingUrl: { 685 set() {}, 686 }, 687 advertiser: { 688 set() {}, 689 }, 690 contextId: { 691 set() {}, 692 }, 693 }, 694 }, 695 GleanPings: { 696 newtab: { 697 submit() {}, 698 }, 699 topSites: { 700 submit() {}, 701 }, 702 spoc: { 703 submit() {}, 704 }, 705 }, 706 userAgent: "", 707 Utils: { 708 SERVER_URL: "bogus://foo", 709 }, 710 NewTabContentPing, 711 ProxyService: { 712 registerChannelFilter() {}, 713 unregisterChannelFilter() {}, 714 }, 715 }; 716 overrider.set(TEST_GLOBAL); 717 718 describe("activity-stream", () => { 719 after(() => overrider.restore()); 720 files.forEach(file => req(file)); 721 });