unit-entry.js (16117B)
1 import { 2 FakePrefs, 3 FakensIPrefService, 4 GlobalOverrider, 5 FakeConsoleAPI, 6 FakeLogger, 7 FakeNimbusFeatures, 8 } from "tests/unit/utils"; 9 import Adapter from "enzyme-adapter-react-16"; 10 import chaiJsonSchema from "chai-json-schema"; 11 import enzyme from "enzyme"; 12 import FxMSCommonSchema from "../../content-src/schemas/FxMSCommon.schema.json"; 13 import { 14 MESSAGE_TYPE_LIST, 15 MESSAGE_TYPE_HASH, 16 } from "modules/ActorConstants.mjs"; 17 import { MESSAGING_EXPERIMENTS_DEFAULT_FEATURES } from "modules/MessagingExperimentConstants.sys.mjs"; 18 19 enzyme.configure({ adapter: new Adapter() }); 20 21 // Cause React warnings to make tests that trigger them fail 22 const origConsoleError = console.error; 23 console.error = function (msg, ...args) { 24 origConsoleError.apply(console, [msg, ...args]); 25 26 if ( 27 /(Invalid prop|Failed prop type|Check the render method|React Intl)/.test( 28 msg 29 ) 30 ) { 31 throw new Error(msg); 32 } 33 }; 34 35 const req = require.context(".", true, /\.test\.jsx?$/); 36 const files = req.keys(); 37 38 // This exposes sinon assertions to chai.assert 39 sinon.assert.expose(assert, { prefix: "" }); 40 41 chai.use(chaiJsonSchema); 42 chai.tv4.addSchema("file:///FxMSCommon.schema.json", FxMSCommonSchema); 43 44 const overrider = new GlobalOverrider(); 45 46 const RemoteSettings = name => ({ 47 get: () => { 48 if (name === "attachment") { 49 return Promise.resolve([{ attachment: {} }]); 50 } 51 return Promise.resolve([]); 52 }, 53 on: () => {}, 54 off: () => {}, 55 }); 56 RemoteSettings.pollChanges = () => {}; 57 58 class JSWindowActorParent { 59 sendAsyncMessage(name, data) { 60 return { name, data }; 61 } 62 } 63 64 class JSWindowActorChild { 65 sendAsyncMessage(name, data) { 66 return { name, data }; 67 } 68 69 sendQuery(name, data) { 70 return Promise.resolve({ name, data }); 71 } 72 73 get contentWindow() { 74 return { 75 Promise, 76 }; 77 } 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 ASRouterPreferences: { 127 console: new FakeConsoleAPI({ 128 maxLogLevel: "off", // set this to "debug" or "all" to get more ASRouter logging in tests 129 prefix: "ASRouter", 130 }), 131 }, 132 AWScreenUtils: { 133 evaluateTargetingAndRemoveScreens() { 134 return true; 135 }, 136 async removeScreens() { 137 return true; 138 }, 139 evaluateScreenTargeting() { 140 return true; 141 }, 142 }, 143 BrowserUtils: { 144 sendToDeviceEmailsSupported() { 145 return true; 146 }, 147 }, 148 UpdateUtils: { getUpdateChannel() {} }, 149 BasePromiseWorker: class { 150 constructor() { 151 this.ExceptionHandlers = []; 152 } 153 post() {} 154 }, 155 browserSearchRegion: "US", 156 BrowserWindowTracker: { getTopWindow() {} }, 157 ChromeUtils: { 158 defineLazyGetter(object, name, f) { 159 updateGlobalOrObject(object)[name] = f(); 160 }, 161 defineESModuleGetters: updateGlobalOrObject, 162 generateQI() { 163 return {}; 164 }, 165 importESModule() { 166 return global; 167 }, 168 }, 169 ClientEnvironment: { 170 get userId() { 171 return "foo123"; 172 }, 173 }, 174 Components: { 175 Constructor(classId) { 176 switch (classId) { 177 case "@mozilla.org/referrer-info;1": 178 return function (referrerPolicy, sendReferrer, originalReferrer) { 179 this.referrerPolicy = referrerPolicy; 180 this.sendReferrer = sendReferrer; 181 this.originalReferrer = originalReferrer; 182 }; 183 } 184 return function () {}; 185 }, 186 isSuccessCode: () => true, 187 }, 188 ConsoleAPI: FakeConsoleAPI, 189 // NB: These are functions/constructors 190 // eslint-disable-next-line object-shorthand 191 ContentSearchUIController: function () {}, 192 // eslint-disable-next-line object-shorthand 193 ContentSearchHandoffUIController: function () {}, 194 Cc: { 195 "@mozilla.org/browser/nav-bookmarks-service;1": { 196 addObserver() {}, 197 getService() { 198 return this; 199 }, 200 removeObserver() {}, 201 SOURCES: {}, 202 TYPE_BOOKMARK: {}, 203 }, 204 "@mozilla.org/browser/nav-history-service;1": { 205 addObserver() {}, 206 executeQuery() {}, 207 getNewQuery() {}, 208 getNewQueryOptions() {}, 209 getService() { 210 return this; 211 }, 212 insert() {}, 213 markPageAsTyped() {}, 214 removeObserver() {}, 215 pageFrecencyThreshold() {}, 216 }, 217 "@mozilla.org/io/string-input-stream;1": { 218 createInstance() { 219 return {}; 220 }, 221 }, 222 "@mozilla.org/updates/update-checker;1": { createInstance() {} }, 223 "@mozilla.org/widget/useridleservice;1": { 224 getService() { 225 return { 226 idleTime: 0, 227 addIdleObserver() {}, 228 removeIdleObserver() {}, 229 }; 230 }, 231 }, 232 "@mozilla.org/streamConverters;1": { 233 getService() { 234 return this; 235 }, 236 }, 237 "@mozilla.org/network/stream-loader;1": { 238 createInstance() { 239 return {}; 240 }, 241 }, 242 }, 243 Ci: { 244 nsICryptoHash: {}, 245 nsIReferrerInfo: { UNSAFE_URL: 5 }, 246 nsITimer: { TYPE_ONE_SHOT: 1 }, 247 nsIWebProgressListener: { LOCATION_CHANGE_SAME_DOCUMENT: 1 }, 248 nsIDOMWindow: Object, 249 nsITrackingDBService: { 250 TRACKERS_ID: 1, 251 TRACKING_COOKIES_ID: 2, 252 CRYPTOMINERS_ID: 3, 253 FINGERPRINTERS_ID: 4, 254 SOCIAL_ID: 5, 255 }, 256 nsICookieBannerService: { 257 MODE_DISABLED: 0, 258 MODE_REJECT: 1, 259 MODE_REJECT_OR_ACCEPT: 2, 260 MODE_UNSET: 3, 261 }, 262 }, 263 Cu: { 264 importGlobalProperties() {}, 265 now: () => window.performance.now(), 266 cloneInto: o => JSON.parse(JSON.stringify(o)), 267 isInAutomation: true, 268 }, 269 console: { 270 ...console, 271 error() {}, 272 }, 273 dump() {}, 274 EveryWindow: { 275 registerCallback: (_id, _init, _uninit) => {}, 276 unregisterCallback: _id => {}, 277 }, 278 setTimeout: window.setTimeout.bind(window), 279 clearTimeout: window.clearTimeout.bind(window), 280 fetch() {}, 281 // eslint-disable-next-line object-shorthand 282 Image: function () {}, // NB: This is a function/constructor 283 IOUtils: { 284 writeJSON() { 285 return Promise.resolve(0); 286 }, 287 readJSON() { 288 return Promise.resolve({}); 289 }, 290 read() { 291 return Promise.resolve(new Uint8Array()); 292 }, 293 makeDirectory() { 294 return Promise.resolve(0); 295 }, 296 write() { 297 return Promise.resolve(0); 298 }, 299 exists() { 300 return Promise.resolve(0); 301 }, 302 remove() { 303 return Promise.resolve(0); 304 }, 305 stat() { 306 return Promise.resolve(0); 307 }, 308 }, 309 NewTabUtils: { 310 activityStreamProvider: { 311 getTopFrecentSites: () => [], 312 executePlacesQuery: async (sql, options) => ({ sql, options }), 313 }, 314 }, 315 OS: { 316 File: { 317 writeAtomic() {}, 318 makeDir() {}, 319 stat() {}, 320 Error: {}, 321 read() {}, 322 exists() {}, 323 remove() {}, 324 removeEmptyDir() {}, 325 }, 326 Path: { 327 join() { 328 return "/"; 329 }, 330 }, 331 Constants: { 332 Path: { 333 localProfileDir: "/", 334 }, 335 }, 336 }, 337 PathUtils: { 338 join(...parts) { 339 return parts[parts.length - 1]; 340 }, 341 joinRelative(...parts) { 342 return parts[parts.length - 1]; 343 }, 344 getProfileDir() { 345 return Promise.resolve("/"); 346 }, 347 getLocalProfileDir() { 348 return Promise.resolve("/"); 349 }, 350 toFileURI(path) { 351 return `file://${path}`; 352 }, 353 }, 354 PlacesUtils: { 355 get bookmarks() { 356 return TEST_GLOBAL.Cc["@mozilla.org/browser/nav-bookmarks-service;1"]; 357 }, 358 get history() { 359 return TEST_GLOBAL.Cc["@mozilla.org/browser/nav-history-service;1"]; 360 }, 361 observers: { 362 addListener() {}, 363 removeListener() {}, 364 }, 365 }, 366 Preferences: FakePrefs, 367 PrivateBrowsingUtils: { 368 isBrowserPrivate: () => false, 369 isWindowPrivate: () => false, 370 permanentPrivateBrowsing: false, 371 }, 372 DownloadsViewUI: { 373 getDisplayName: () => "filename.ext", 374 getSizeWithUnits: () => "1.5 MB", 375 }, 376 FileUtils: { 377 // eslint-disable-next-line object-shorthand 378 File: function () {}, // NB: This is a function/constructor 379 }, 380 Region: { 381 home: "US", 382 REGION_TOPIC: "browser-region-updated", 383 }, 384 Services: { 385 sysinfo: { 386 getProperty() { 387 return false; 388 }, 389 }, 390 dirsvc: { 391 get: () => ({ parent: { parent: { path: "appPath" } } }), 392 }, 393 env: { 394 get: () => undefined, 395 set: () => undefined, 396 exists: () => false, 397 }, 398 locale: { 399 get appLocaleAsBCP47() { 400 return "en-US"; 401 }, 402 negotiateLanguages() {}, 403 }, 404 urlFormatter: { formatURL: str => str, formatURLPref: str => str }, 405 mm: { 406 addMessageListener: (_msg, _cb) => this.receiveMessage(), 407 removeMessageListener() {}, 408 }, 409 obs: { 410 addObserver() {}, 411 removeObserver() {}, 412 notifyObservers() {}, 413 }, 414 uuid: { 415 generateUUID() { 416 return "{foo-123-foo}"; 417 }, 418 }, 419 console: { logStringMessage: () => {} }, 420 prefs: new FakensIPrefService(), 421 tm: { 422 dispatchToMainThread: cb => cb(), 423 idleDispatchToMainThread: cb => cb(), 424 }, 425 eTLD: { 426 getBaseDomain({ spec }) { 427 return spec.match(/\/([^/]+)/)[1]; 428 }, 429 getBaseDomainFromHost(host) { 430 return host.match(/.*?(\w+\.\w+)$/)[1]; 431 }, 432 getPublicSuffix() {}, 433 }, 434 io: { 435 newURI: spec => ({ 436 mutate: () => ({ 437 setRef: ref => ({ 438 finalize: () => ({ 439 ref, 440 spec, 441 }), 442 }), 443 }), 444 spec, 445 }), 446 }, 447 search: { 448 init() { 449 return Promise.resolve(); 450 }, 451 getVisibleEngines: () => 452 Promise.resolve([{ identifier: "google" }, { identifier: "bing" }]), 453 defaultEngine: { 454 identifier: "google", 455 aliases: ["@google"], 456 }, 457 defaultPrivateEngine: { 458 identifier: "bing", 459 aliases: ["@bing"], 460 }, 461 getEngineByAlias: async () => null, 462 }, 463 scriptSecurityManager: { 464 createNullPrincipal() {}, 465 getSystemPrincipal() {}, 466 }, 467 wm: { 468 getMostRecentWindow: () => window, 469 getMostRecentBrowserWindow: () => window, 470 getEnumerator: () => [], 471 }, 472 ww: { registerNotification() {}, unregisterNotification() {} }, 473 appinfo: { appBuildID: "20180710100040", version: "69.0a1" }, 474 scriptloader: { loadSubScript: () => {} }, 475 startup: { 476 getStartupInfo() { 477 return { 478 process: { 479 getTime() { 480 return 1588010448000; 481 }, 482 }, 483 }; 484 }, 485 }, 486 }, 487 XPCOMUtils: { 488 declareLazy: declaration => { 489 Object.entries(declaration).forEach(([key, value]) => { 490 if (typeof value === "function") { 491 updateGlobalOrObject(global)[key] = value(); 492 } else if (typeof value === "object" && value.pref) { 493 updateGlobalOrObject(global)[key] = value.default; 494 } 495 }); 496 return global; 497 }, 498 defineLazyGlobalGetters: updateGlobalOrObject, 499 defineLazyServiceGetter: updateGlobalOrObject, 500 defineLazyServiceGetters: updateGlobalOrObject, 501 defineLazyPreferenceGetter(object, name, _pref, defaultValue = "") { 502 updateGlobalOrObject(object)[name] = defaultValue; 503 }, 504 generateQI() { 505 return {}; 506 }, 507 }, 508 ShellService: { 509 doesAppNeedPin: () => false, 510 isDefaultBrowser: () => true, 511 }, 512 FilterExpressions: { 513 eval() { 514 return Promise.resolve(false); 515 }, 516 }, 517 RemoteSettings, 518 Localization: class { 519 async formatMessages(stringsIds) { 520 return Promise.resolve( 521 stringsIds.map(({ id, args }) => ({ value: { string_id: id, args } })) 522 ); 523 } 524 async formatValue(stringId) { 525 return Promise.resolve(stringId); 526 } 527 }, 528 FxAccountsConfig: { 529 promiseConnectAccountURI(id) { 530 return Promise.resolve(id); 531 }, 532 }, 533 FX_MONITOR_OAUTH_CLIENT_ID: "fake_client_id", 534 ExperimentAPI: {}, 535 FeatureCalloutBroker: { 536 showFeatureCallout() {}, 537 }, 538 NimbusFeatures: FakeNimbusFeatures([ 539 ...MESSAGING_EXPERIMENTS_DEFAULT_FEATURES, 540 "glean", 541 "newtab", 542 "pocketNewtab", 543 "cookieBannerHandling", 544 ]), 545 TelemetryEnvironment: { 546 setExperimentActive() {}, 547 currentEnvironment: { 548 profile: { 549 creationDate: 16587, 550 }, 551 settings: {}, 552 }, 553 }, 554 Sampling: { 555 ratioSample(_seed, _ratios) { 556 return Promise.resolve(0); 557 }, 558 }, 559 BrowserHandler: { 560 get kiosk() { 561 return false; 562 }, 563 }, 564 TelemetrySession: { 565 getMetadata(reason) { 566 return { 567 reason, 568 sessionId: "fake_session_id", 569 }; 570 }, 571 }, 572 PageThumbs: { 573 addExpirationFilter() {}, 574 removeExpirationFilter() {}, 575 }, 576 Logger: FakeLogger, 577 getFxAccountsSingleton() {}, 578 AboutNewTab: {}, 579 Glean: { 580 messagingExperiments: { 581 reachCfr: { 582 record() {}, 583 }, 584 reachFxmsMessage15: { 585 record() {}, 586 }, 587 }, 588 messagingSystem: { 589 messageRequestTime: { 590 start() {}, 591 stopAndAccumulate() {}, 592 }, 593 }, 594 newtab: { 595 opened: { 596 record() {}, 597 }, 598 closed: { 599 record() {}, 600 }, 601 locale: { 602 set() {}, 603 }, 604 newtabCategory: { 605 set() {}, 606 }, 607 homepageCategory: { 608 set() {}, 609 }, 610 blockedSponsors: { 611 set() {}, 612 }, 613 sovAllocation: { 614 set() {}, 615 }, 616 }, 617 newtabSearch: { 618 enabled: { 619 set() {}, 620 }, 621 }, 622 pocket: { 623 enabled: { 624 set() {}, 625 }, 626 impression: { 627 record() {}, 628 }, 629 isSignedIn: { 630 set() {}, 631 }, 632 sponsoredStoriesEnabled: { 633 set() {}, 634 }, 635 click: { 636 record() {}, 637 }, 638 save: { 639 record() {}, 640 }, 641 topicClick: { 642 record() {}, 643 }, 644 }, 645 topsites: { 646 enabled: { 647 set() {}, 648 }, 649 sponsoredEnabled: { 650 set() {}, 651 }, 652 impression: { 653 record() {}, 654 }, 655 click: { 656 record() {}, 657 }, 658 rows: { 659 set() {}, 660 }, 661 showPrivacyClick: { 662 record() {}, 663 }, 664 dismiss: { 665 record() {}, 666 }, 667 prefChanged: { 668 record() {}, 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 }, 703 Utils: { 704 SERVER_URL: "bogus://foo", 705 }, 706 707 MESSAGE_TYPE_LIST, 708 MESSAGE_TYPE_HASH, 709 }; 710 overrider.set(TEST_GLOBAL); 711 712 describe("asrouter", () => { 713 after(() => overrider.restore()); 714 console.log( 715 "Loading files from unit-entry.js (msg should show once - bug 1967579)" 716 ); 717 files.forEach(file => req(file)); 718 });