/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

const path = require("path");
const webpack = require("webpack");
const { ResourceUriPlugin } = require("../../tools/resourceUriPlugin");
const { MozSrcUriPlugin } = require("../../tools/mozsrcUriPlugin");

const PATHS = {
  // Where is the entry point for the unit tests?
  testEntryFile: path.resolve(__dirname, "test/unit/unit-entry.js"),

  // A glob-style pattern matching all unit tests
  testFilesPattern: "test/unit/**/*.js",

  // The base directory of all source files (used for path resolution in webpack importing)
  moduleResolveDirectory: __dirname,

  // a RegEx matching all Cu.import statements of local files
  resourcePathRegEx: /^resource:\/\/activity-stream\//,

  coverageReportingPath: "logs/coverage/",
};

// When tweaking here, be sure to review the docs about the execution ordering
// semantics of the preprocessors array, as they are somewhat odd.
const preprocessors = {};
preprocessors[PATHS.testFilesPattern] = [
  "webpack", // require("karma-webpack")
  "sourcemap", // require("karma-sourcemap-loader")
];

module.exports = function (config) {
  const isTDD = config.tdd;
  const browsers = isTDD ? ["Firefox"] : ["FirefoxHeadless"]; // require("karma-firefox-launcher")
  config.set({
    singleRun: !isTDD,
    browsers,
    customLaunchers: {
      FirefoxHeadless: {
        base: "Firefox",
        flags: ["--headless"],
      },
    },
    frameworks: [
      "chai", // require("chai") require("karma-chai")
      "mocha", // require("mocha") require("karma-mocha")
      "sinon", // require("sinon") require("karma-sinon")
    ],
    reporters: [
      "coverage-istanbul", // require("karma-coverage")
      "mocha", // require("karma-mocha-reporter")

      // for bin/try-runner.js to parse the output easily
      "json", // require("karma-json-reporter")
    ],
    jsonReporter: {
      // So this doesn't get interleaved with other karma output
      stdout: false,
      outputFile: path.join("logs", "karma-run-results.json"),
    },
    coverageIstanbulReporter: {
      reports: ["lcov", "text-summary"], // for some reason "lcov" reallys means "lcov" and "html"
      "report-config": {
        // so the full m-c path gets printed; needed for Searchfox integration
        lcov: {
          projectRoot: "../../..",
        },
      },
      dir: PATHS.coverageReportingPath,
      // This will make karma fail if coverage reporting is less than the minimums here
      thresholds: !isTDD && {
        each: {
          statements: 100,
          lines: 100,
          functions: 100,
          branches: 66,
          overrides: {
            "lib/AboutPreferences.sys.mjs": {
              statements: 98,
              lines: 98,
              functions: 94,
              branches: 66,
            },
            /**
             * TelemetryFeed.sys.mjs is tested via an xpcshell test
             */
            "lib/TelemetryFeed.sys.mjs": {
              statements: 10,
              lines: 10,
              functions: 9,
              branches: 0,
            },
            "content-src/lib/init-store.js": {
              statements: 98,
              lines: 98,
              functions: 100,
              branches: 100,
            },
            "lib/DownloadsManager.sys.mjs": {
              statements: 100,
              lines: 100,
              functions: 100,
              branches: 78,
            },
            /**
             * PlacesFeed.sys.mjs is tested via an xpcshell test
             */
            "lib/PlacesFeed.sys.mjs": {
              statements: 7,
              lines: 7,
              functions: 8,
              branches: 0,
            },
            "lib/UTEventReporting.sys.mjs": {
              statements: 100,
              lines: 100,
              functions: 100,
              branches: 75,
            },
            "lib/Screenshots.sys.mjs": {
              statements: 94,
              lines: 94,
              functions: 75,
              branches: 84,
            },
            /**
             * Store.sys.mjs is tested via an xpcshell test
             */
            "lib/Store.sys.mjs": {
              statements: 8,
              lines: 8,
              functions: 0,
              branches: 0,
            },
            /**
             * TopSitesFeed.sys.mjs is tested via an xpcshell test
             */
            "lib/TopSitesFeed.sys.mjs": {
              statements: 9,
              lines: 9,
              functions: 5,
              branches: 0,
            },
            /**
             * TopStoresFeed.sys.mjs is not tested in automation and is slated
             * for eventual removal.
             */
            "lib/TopStoriesFeed.sys.mjs": {
              statements: 0,
              lines: 0,
              functions: 0,
              branches: 0,
            },
            /**
             * WallpaperFeed.sys.mjs is tested via an xpcshell test
             */
            "lib/Wallpapers/WallpaperFeed.sys.mjs": {
              statements: 0,
              lines: 0,
              functions: 0,
              branches: 0,
            },
            "content-src/components/DiscoveryStreamComponents/PersonalizedCard/PersonalizedCard.jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            "content-src/components/Base/Base.jsx": {
              statements: 0,
              lines: 0,
              functions: 0,
              branches: 0,
            },
            "content-src/components/DiscoveryStreamComponents/FeatureHighlight/FollowSectionButtonHighlight.jsx":
              {
                statements: 88,
                lines: 88,
                functions: 80,
              },
            "content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx":
              {
                statements: 88,
                lines: 88,
                functions: 80,
              },
            // for all other components in FeatureHighlight that dont have bespoke functionality
            "content-src/components/DiscoveryStreamComponents/FeatureHighlight/!(FeatureHighlight).jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            "content-src/components/DiscoveryStreamComponents/ReportContent/ReportContent.jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            /**
             * TopicSelection.jsx is tested via an xpcshell test
             */
            "content-src/components/DiscoveryStreamComponents/TopicSelection/*.jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            "content-src/components/DiscoveryStreamComponents/InterestPicker/*.jsx":
              {
                statements: 40,
                lines: 39,
                functions: 37,
                branches: 25,
              },
            "content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx":
              {
                statements: 94.94,
                lines: 94.84,
                functions: 9.91,
                branches: 70.72,
              },
            "content-src/components/DiscoveryStreamComponents/CardSections/CardSections.jsx":
              {
                statements: 83.11,
                lines: 82.52,
                functions: 79.31,
                branches: 52.8,
              },
            "content-src/components/DiscoveryStreamComponents/SectionContextMenu/SectionContextMenu.jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            "content-src/components/DiscoveryStreamComponents/AdBanner/AdBanner.jsx":
              {
                statements: 94.12,
                lines: 94.12,
                functions: 75,
                branches: 60,
              },
            "content-src/components/DiscoveryStreamComponents/AdBannerContextMenu/AdBannerContextMenu.jsx":
              {
                statements: 84,
                lines: 84,
                functions: 83,
              },
            "content-src/components/DiscoveryStreamComponents/BriefingCard/BriefingCard.jsx":
              {
                statements: 92.31,
                branches: 66.67,
                functions: 90,
                lines: 94.59,
              },
            /**
             * PromoCard currently has no functionality and is a placeholder component
             */
            "content-src/components/DiscoveryStreamComponents/PromoCard/PromoCard.jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            "content-src/components/DiscoveryStreamComponents/**/*.jsx": {
              statements: 80.95,
              lines: 80.95,
              functions: 71.43,
              branches: 70.9,
            },
            /**
             * WallpaperCategories.jsx is tested via an xpcshell test
             */
            "content-src/components/WallpaperCategories/WallpaperCategories.jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            /**
             * Notifications.jsx is tested via an xpcshell test
             */
            "content-src/components/Notifications/**/*.jsx": {
              statements: 0,
              lines: 0,
              functions: 0,
              branches: 0,
            },
            "content-src/components/Weather/Weather.jsx": {
              statements: 48,
              lines: 49,
              functions: 31.2,
              branches: 31.2,
            },
            "content-src/components/Widgets/WeatherForecast/WeatherForecast.jsx":
              {
                statements: 0,
                lines: 0,
                functions: 0,
                branches: 0,
              },
            "content-src/components/Weather/LocationSearch.jsx": {
              statements: 0,
              lines: 0,
              functions: 0,
              branches: 0,
            },
            "content-src/components/DiscoveryStreamAdmin/*.jsx": {
              statements: 0,
              lines: 0,
              functions: 0,
              branches: 0,
            },
            "content-src/components/CustomizeMenu/SectionsMgmtPanel/*.jsx": {
              statements: 86,
              lines: 76,
              functions: 86,
              branches: 76,
            },
            "content-src/components/CustomizeMenu/ContentSection/*.jsx": {
              statements: 82,
              lines: 82,
              functions: 100,
              branches: 67,
            },
            "content-src/components/CustomizeMenu/**/*.jsx": {
              statements: 68,
              lines: 66,
              functions: 80,
              branches: 16,
            },
            "content-src/components/CustomizeMenu/*.jsx": {
              statements: 98,
              lines: 98,
              functions: 98,
              branches: 98,
            },
            "content-src/lib/link-menu-options.js": {
              statements: 96,
              lines: 96,
              functions: 96,
              branches: 70,
            },
            "content-src/lib/utils.jsx": {
              branches: 60,
              statements: 95.56,
              lines: 95.4,
              functions: 92.86,
            },
            "content-src/components/MessageWrapper/MessageWrapper.jsx": {
              lines: 50,
              statements: 51.1,
              functions: 31.2,
              branches: 31.2,
            },
            "content-src/components/Search/Search.jsx": {
              statements: 38,
              lines: 39,
              functions: 28,
              branches: 25,
            },
            "content-src/components/**/*.jsx": {
              statements: 51.1,
              lines: 52.38,
              functions: 31.2,
              branches: 31.2,
            },
          },
        },
      },
    },
    files: [
      // Load React 16 from toolkit vendor for enzyme compatibility, instead of React19
      "../../../toolkit/content/vendor/react/react.js",
      "../../../toolkit/content/vendor/react/react-dom.js",
      "../../../toolkit/content/vendor/react/react-dom-server.js",
      "test/vendor/react-dom-test-utils.js",
      "../../../toolkit/content/vendor/react/prop-types.js",
      "../../../toolkit/content/vendor/react/react-redux.js",
      "../../../toolkit/content/vendor/react/redux.js",
      PATHS.testEntryFile,
    ],
    preprocessors,
    webpack: {
      mode: "none",
      devtool: "inline-source-map",
      // This resolve config allows us to import with paths relative to the root directory, e.g. "lib/ActivityStream.sys.mjs"
      resolve: {
        extensions: [".js", ".jsx", ".mjs"],
        modules: [PATHS.moduleResolveDirectory, "node_modules"],
      },
      plugins: [
        // The ResourceUriPlugin handles translating resource URIs in import
        // statements in .mjs files to paths on the filesystem.
        new ResourceUriPlugin({
          resourcePathRegExes: [
            [new RegExp("^resource://newtab/"), path.join(__dirname, "./")],
            [
              new RegExp("^resource:///modules/asrouter/"),
              path.join(__dirname, "../../components/asrouter/modules/"),
            ],
            [
              new RegExp("^resource:///modules/topsites/"),
              path.join(__dirname, "../../components/topsites/"),
            ],
            [
              new RegExp(
                "^moz-src:///toolkit/components/search/SearchShortcuts.sys.mjs"
              ),
              path.join(
                __dirname,
                "../../../toolkit/components/search/SearchShortcuts.sys.mjs"
              ),
            ],
            [
              new RegExp("^resource:///modules/Dedupe.sys.mjs"),
              path.join(__dirname, "../../modules/Dedupe.sys.mjs"),
            ],
          ],
        }),
        new MozSrcUriPlugin({
          baseDir: path.join(__dirname, "..", "..", ".."),
        }),

        new webpack.DefinePlugin({
          "process.env.NODE_ENV": JSON.stringify("development"),
        }),

        // Replace react-redux imports with React 16-compatible version from toolkit
        new webpack.NormalModuleReplacementPlugin(
          /^react-redux$/,
          path.resolve(
            __dirname,
            "../../../toolkit/content/vendor/react/react-redux.js"
          )
        ),
      ],
      externals: [
        // Use React 16 from toolkit vendor files for enzyme compatibility
        {
          react: "React",
          "react-dom": "ReactDOM",
          "react-dom/client": "ReactDOM",
          "react-dom/server": "ReactDOMServer",
          "react-dom/server.browser": "ReactDOMServer",
          "react-dom/test-utils": "ReactTestUtils",
          "prop-types": "PropTypes",
          "react-redux": "ReactRedux",
          redux: "Redux",
          // enzyme needs these for backwards compatibility with 0.13.
          // see https://github.com/airbnb/enzyme/blob/master/docs/guides/webpack.md#using-enzyme-with-webpack
          "react/addons": true,
          "react/lib/ReactContext": true,
          "react/lib/ExecutionEnvironment": true,
        },
        // Exclude use-sync-external-store completely
        // eslint-disable-next-line consistent-return
        function ({ request }, callback) {
          if (/^use-sync-external-store/.test(request)) {
            return callback(null, "var {}");
          }
          callback();
        },
      ],
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: [/node_modules\/(?!@fluent\/).*/, /test/],
            loader: "babel-loader",
          },
          {
            test: /\.jsx$/,
            exclude: /node_modules/,
            loader: "babel-loader",
            options: {
              presets: ["@babel/preset-react"],
            },
          },
          {
            test: /\.md$/,
            use: "raw-loader",
          },
          {
            enforce: "post",
            test: /\.js[x]?$/,
            loader: "@jsdevtools/coverage-istanbul-loader",
            options: { esModules: true },
            include: [
              path.resolve("content-src"),
              path.resolve("lib"),
              path.resolve("common"),
            ],
            exclude: [path.resolve("test"), path.resolve("vendor")],
          },
        ],
      },
    },
    // Silences some overly-verbose logging of individual module builds
    webpackMiddleware: { noInfo: true },
  });
};
