/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

import TestTools
import XCTest

final class ServerConfigurationTests: XCTestCase {

  var configuration = Fixtures.defaultConfiguration

  typealias Fixtures = ServerConfigurationFixtures

  let exampleURL = SampleURLs.valid

  override func setUp() {
    super.setUp()

    configuration = Fixtures.defaultConfiguration
  }

  func testUsingDefaults() {
    XCTAssertTrue(configuration.isDefaults, "Should assume defaults are being used unless otherwise expressed")

    configuration = Fixtures.configuration(withDictionary: ["defaults": false])
    XCTAssertFalse(configuration.isDefaults, "Should store whether or not defaults are being used")
  }

  func testCreatingWithEmptyAppID() {
    configuration = Fixtures.configuration(withDictionary: ["appID": ""])
    XCTAssertEqual(
      configuration.appID,
      "",
      "Should use the given app identifier regardless of value"
    )
  }

  func testCreatingWithDefaultAdvertisingIdEnabled() {
    XCTAssertFalse(
      configuration.isAdvertisingIDEnabled,
      "Advertising identifier enabled should default to false"
    )
  }

  func testCreatingWithKnownAdvertisingIdEnabled() {
    configuration = Fixtures.configuration(withDictionary: ["advertisingIDEnabled": true])
    XCTAssertTrue(
      configuration.isAdvertisingIDEnabled,
      "Advertising identifier enabled should be settable"
    )
  }

  func testCreatingWithDefaultImplicitPurchaseLoggingEnabled() {
    XCTAssertFalse(
      configuration.isImplicitPurchaseLoggingSupported,
      "Implicit purchase logging enabled should default to false"
    )
  }

  func testCreatingWithKnownImplicitPurchaseLoggingEnabled() {
    configuration = Fixtures.configuration(withDictionary: ["implicitPurchaseLoggingEnabled": true])
    XCTAssertTrue(
      configuration.isImplicitPurchaseLoggingSupported,
      "Implicit purchase logging enabled should be settable"
    )
  }

  func testCreatingWithDefaultImplicitLoggingEnabled() {
    XCTAssertFalse(
      configuration.isImplicitLoggingSupported,
      "Implicit logging enabled should default to false"
    )
  }

  func testCreatingWithKnownImplicitLoggingEnabled() {
    configuration = Fixtures.configuration(withDictionary: ["implicitLoggingEnabled": true])
    XCTAssertTrue(
      configuration.isImplicitLoggingSupported,
      "Implicit logging enabled should be settable"
    )
  }

  func testCreatingWithDefaultCodelessEventsEnabled() {
    XCTAssertFalse(
      configuration.isCodelessEventsEnabled,
      "Codeless events enabled should default to false"
    )
  }

  func testCreatingWithKnownCodelessEventsEnabled() {
    configuration = Fixtures.configuration(withDictionary: ["codelessEventsEnabled": true])
    XCTAssertTrue(
      configuration.isCodelessEventsEnabled,
      "Codeless events enabled should be settable"
    )
  }

  func testCreatingWithDefaultUninstallTrackingEnabled() {
    XCTAssertFalse(
      configuration.isUninstallTrackingEnabled,
      "Uninstall tracking enabled should default to false"
    )
  }

  func testCreatingWithKnownUninstallTrackingEnabled() {
    configuration = Fixtures.configuration(withDictionary: ["uninstallTrackingEnabled": true])
    XCTAssertTrue(
      configuration.isUninstallTrackingEnabled,
      "Uninstall tracking enabled should be settable"
    )
  }

  func testCreatingWithoutAppName() {
    XCTAssertNil(configuration.appName, "Should not use a default value for app name")
  }

  func testCreatingWithEmptyAppName() {
    configuration = Fixtures.configuration(withDictionary: ["appName": ""])
    XCTAssertEqual(
      configuration.appName,
      "",
      "Should use the given app name regardless of value"
    )
  }

  func testCreatingWithKnownAppName() {
    configuration = Fixtures.configuration(withDictionary: ["appName": "foo"])
    XCTAssertEqual(
      configuration.appName,
      "foo",
      "App name should be settable"
    )
  }

  func testCreatingWithoutDefaultShareMode() {
    XCTAssertNil(
      configuration.defaultShareMode,
      "Should not provide a default for the default share mode"
    )
  }

  func testCreatingWithKnownDefaultShareMode() {
    configuration = Fixtures.configuration(withDictionary: ["defaultShareMode": "native"])
    XCTAssertEqual(
      configuration.defaultShareMode,
      "native",
      "Default share mode should be settable"
    )
  }

  func testCreatingWithEmptyDefaultShareMode() {
    configuration = Fixtures.configuration(withDictionary: ["defaultShareMode": ""])
    XCTAssertEqual(
      configuration.defaultShareMode,
      "",
      "Should use the given share mode regardless of value"
    )
  }

  func testCreatingWithDefaultLoginTooltipEnabled() {
    XCTAssertFalse(
      configuration.isLoginTooltipEnabled,
      "Login tooltip enabled should default to false"
    )
  }

  func testCreatingWithKnownLoginTooltipEnabled() {
    configuration = Fixtures.configuration(withDictionary: ["loginTooltipEnabled": true])
    XCTAssertTrue(
      configuration.isLoginTooltipEnabled,
      "Login tooltip enabled should be settable"
    )
  }

  func testCreatingWithoutLoginTooltipText() {
    XCTAssertNil(configuration.loginTooltipText, "Should not use a default value for the login tooltip text")
  }

  func testCreatingWithEmptyLoginTooltipText() {
    configuration = Fixtures.configuration(withDictionary: ["loginTooltipText": ""])
    XCTAssertEqual(
      configuration.loginTooltipText,
      "",
      "Should use the given login tooltip text regardless of value"
    )
  }

  func testCreatingWithKnownLoginTooltipText() {
    configuration = Fixtures.configuration(withDictionary: ["loginTooltipText": "foo"])
    XCTAssertEqual(
      configuration.loginTooltipText,
      "foo",
      "Login tooltip text should be settable"
    )
  }

  func testCreatingWithoutTimestamp() {
    XCTAssertNil(configuration.timestamp, "Should not have a timestamp by default")
  }

  func testCreatingWithTimestamp() {
    let date = Date()
    configuration = Fixtures.configuration(withDictionary: ["timestamp": date])
    XCTAssertEqual(
      configuration.timestamp,
      date,
      "Should use the timestamp given during creation"
    )
  }

  func testCreatingWithDefaultSessionTimeoutInterval() {
    XCTAssertEqual(
      configuration.sessionTimeoutInterval,
      60,
      "Should set the correct default timeout interval"
    )
  }

  func testCreatingWithSessionTimeoutInterval() {
    configuration = Fixtures.configuration(withDictionary: ["sessionTimeoutInterval": 200])
    XCTAssertEqual(
      configuration.sessionTimeoutInterval,
      200,
      "Should set the session timeout interval from the remote"
    )
  }

  func testCreatingWithoutLoggingToken() {
    XCTAssertNil(
      configuration.loggingToken,
      "Should not provide a default for the logging token"
    )
  }

  func testCreatingWithEmptyLoggingToken() {
    configuration = Fixtures.configuration(withDictionary: ["loggingToken": ""])
    XCTAssertEqual(
      configuration.loggingToken,
      "",
      "Should use the logging token given during creation"
    )
  }

  func testCreatingWithKnownLoggingToken() {
    configuration = Fixtures.configuration(withDictionary: ["loggingToken": "foo"])
    XCTAssertEqual(
      configuration.loggingToken,
      "foo",
      "Should use the logging token given during creation"
    )
  }

  func testCreatingWithoutSmartLoginBookmarkUrl() {
    XCTAssertNil(
      configuration.smartLoginBookmarkIconURL,
      "Should not provide a default url for the smart login bookmark icon"
    )
  }

  func testCreatingWithInvalidSmartLoginBookmarkUrl() {
    configuration = Fixtures.configuration(withDictionary: ["smartLoginBookmarkIconURL": exampleURL])
    XCTAssertEqual(
      configuration.smartLoginBookmarkIconURL,
      exampleURL,
      "Should use the url given during creation"
    )
  }

  func testCreatingWithValidSmartBookmarkUrl() {
    configuration = Fixtures.configuration(withDictionary: ["smartLoginBookmarkIconURL": exampleURL])
    XCTAssertEqual(
      configuration.smartLoginBookmarkIconURL,
      exampleURL,
      "Should use the url given during creation"
    )
  }

  func testCreatingWithSmartLoginOptionsDefault() {
    XCTAssertEqual(
      configuration.smartLoginOptions,
      [],
      "Should default smart login options to unknown"
    )
  }

  func testCreatingWithSmartLoginOptionsEnabled() {
    configuration = Fixtures.configuration(
      withDictionary: ["smartLoginOptions": FBSDKServerConfigurationSmartLoginOptions.enabled.rawValue]
    )
    XCTAssertEqual(
      configuration.smartLoginOptions,
      FBSDKServerConfigurationSmartLoginOptions.enabled,
      "Should use the smartLoginOptions given during creation"
    )
  }

  func testCreatingWithoutErrorConfiguration() {
    XCTAssertNil(configuration.errorConfiguration, "Should not have an error configuration by default")
  }

  func testCreatingWithErrorConfiguration() {
    let errorConfiguration = _ErrorConfiguration(dictionary: nil)
    configuration = Fixtures.configuration(withDictionary: ["errorConfiguration": errorConfiguration])

    XCTAssertEqual(
      configuration.errorConfiguration,
      errorConfiguration,
      "Error configuration should be settable"
    )
  }

  func testCreatingWithoutSmartLoginMenuUrl() {
    XCTAssertNil(
      configuration.smartLoginMenuIconURL,
      "Should not provide a default url for the smart login menu icon"
    )
  }

  func testCreatingWithInvalidSmartLoginMenuUrl() {
    configuration = Fixtures.configuration(withDictionary: ["smartLoginMenuIconURL": exampleURL])
    XCTAssertEqual(
      configuration.smartLoginMenuIconURL,
      exampleURL,
      "Should use the url given during creation"
    )
  }

  func testCreatingWithValidSmartLoginMenuUrl() {
    configuration = Fixtures.configuration(withDictionary: ["smartLoginMenuIconURL": exampleURL])
    XCTAssertEqual(
      configuration.smartLoginMenuIconURL,
      exampleURL,
      "Should use the url given during creation"
    )
  }

  func testCreatingWithoutUpdateMessage() {
    XCTAssertNil(
      configuration.updateMessage,
      "Should not provide a default for the update message"
    )
  }

  func testCreatingWithEmptyUpdateMessage() {
    configuration = Fixtures.configuration(withDictionary: ["updateMessage": ""])
    XCTAssertEqual(
      configuration.updateMessage,
      "",
      "Should use the update message given during creation"
    )
  }

  func testCreatingWithKnownUpdateMessage() {
    configuration = Fixtures.configuration(withDictionary: ["updateMessage": "foo"])
    XCTAssertEqual(
      configuration.updateMessage,
      "foo",
      "Should use the update message given during creation"
    )
  }

  func testCreatingWithoutEventBindings() {
    XCTAssertNil(
      configuration.eventBindings,
      "Should not provide default event bindings"
    )
  }

  func testCreatingWithEmptyEventBindings() {
    configuration = Fixtures.configuration(withDictionary: ["eventBindings": []])
    XCTAssertNotNil(configuration.eventBindings, "Should use the empty list of event bindings it was created with")
    XCTAssertEqual(
      configuration.eventBindings?.isEmpty,
      true,
      "Should use the empty list of event bindings it was created with"
    )
  }

  func testCreatingWithEventBindings() {
    let bindings = [["a": "b"]]
    configuration = Fixtures.configuration(withDictionary: ["eventBindings": bindings])

    XCTAssertEqual(
      configuration.eventBindings as? [[String: String]],
      bindings,
      "Event binding should be settable"
    )
  }

  func testCreatingWithoutDialogConfigurations() {
    XCTAssertNil(
      configuration.dialogConfigurations(),
      "Should not have dialog configurations by default"
    )
  }

  func testCreatingWithDialogConfigurations() {
    let dialogConfigurations = [
      "dialog": "Hello",
      "dialog2": "World",
    ]

    configuration = Fixtures.configuration(withDictionary: ["dialogConfigurations": dialogConfigurations])
    XCTAssertEqual(
      configuration.dialogConfigurations() as? [String: String],
      dialogConfigurations,
      "Should set the exact dialog configurations it was created with"
    )
  }

  func testCreatingWithoutDialogFlows() {
    // Need to recreate with a new appID to invalidate cache of default configuration
    configuration = _ServerConfiguration.defaultServerConfiguration(forAppID: name)

    let expectedDefaultDialogFlows = [
      FBSDKDialogConfigurationNameDefault: [
        FBSDKDialogConfigurationFeatureUseNativeFlow: false,
        FBSDKDialogConfigurationFeatureUseSafariViewController: true,
      ],
      DialogConfigurationName.message: [
        FBSDKDialogConfigurationFeatureUseNativeFlow: true,
      ],
    ]

    XCTAssertEqual(
      configuration.dialogFlows() as? [String: [String: Bool]],
      expectedDefaultDialogFlows,
      "Should use the expected default dialog flow"
    )
  }

  func testCreatingWithDialogFlows() {
    let dialogFlows = [
      "foo": [
        FBSDKDialogConfigurationFeatureUseNativeFlow: true,
        FBSDKDialogConfigurationFeatureUseSafariViewController: true,
      ],
      "bar": [
        FBSDKDialogConfigurationFeatureUseNativeFlow: false,
      ],
    ]

    configuration = Fixtures.configuration(withDictionary: ["dialogFlows": dialogFlows])

    XCTAssertEqual(
      configuration.dialogFlows() as? [String: [String: Bool]],
      dialogFlows,
      "Should set the exact dialog flows it was created with"
    )
  }

  func testCreatingWithoutAAMRules() {
    XCTAssertNil(
      configuration.aamRules,
      "Should not have aam rules by default"
    )
  }

  func testCreatingWithAAMRules() {
    let rules = ["foo": "bar"]

    configuration = Fixtures.configuration(withDictionary: ["aamRules": rules])

    XCTAssertEqual(
      configuration.aamRules as? [String: String],
      rules,
      "Should set the exact aam rules it was created with"
    )
  }

  func testCreatingWithoutRestrictiveParams() {
    XCTAssertNil(
      configuration.restrictiveParams,
      "Should not have restrictive params by default"
    )
  }

  func testCreatingWithRestrictiveParams() {
    let params = ["foo": "bar"]

    configuration = Fixtures.configuration(withDictionary: ["restrictiveParams": params])

    XCTAssertEqual(
      configuration.restrictiveParams as? [String: String],
      params,
      "Should set the exact restrictive params it was created with"
    )
  }

  func testCreatingWithoutSuggestedEventSetting() {
    XCTAssertNil(
      configuration.suggestedEventsSetting,
      "Should not have a suggested events setting by default"
    )
  }

  func testCreatingWithSuggestedEventSetting() {
    let setting = ["foo": "bar"]
    configuration = Fixtures.configuration(withDictionary: ["suggestedEventsSetting": setting])

    XCTAssertEqual(
      configuration.suggestedEventsSetting as? [String: String],
      setting,
      "Should set the exact suggested events setting it was created with"
    )
  }

  func testEncodingAndDecoding() throws {
    let errorConfiguration = _ErrorConfiguration(dictionary: nil)

    configuration = Fixtures.configuration(withDictionary: [
      "appID": "appID",
      "appName": "appName",
      "loginTooltipEnabled": true,
      "loginTooltipText": "loginTooltipText",
      "defaultShareMode": "defaultShareMode",
      "advertisingIDEnabled": true,
      "implicitLoggingEnabled": true,
      "implicitPurchaseLoggingEnabled": true,
      "codelessEventsEnabled": true,
      "uninstallTrackingEnabled": true,
      "dialogFlows": ["foo": "Bar"],
      "timestamp": Date(),
      "errorConfiguration": errorConfiguration,
      "sessionTimeoutInterval": 100,
      "defaults": true,
      "loggingToken": "loggingToken",
      "smartLoginOptions": FBSDKServerConfigurationSmartLoginOptions.enabled.rawValue,
      "smartLoginBookmarkIconURL": exampleURL,
      "smartLoginMenuIconURL": exampleURL,
      "updateMessage": "updateMessage",
      "eventBindings": [["foo": "bar"]],
      "restrictiveParams": ["restrictiveParams": "foo"],
      "AAMRules": ["AAMRules": "foo"],
      "suggestedEventsSetting": ["suggestedEventsSetting": "foo"],
    ])

    // Encode and Decode
    let object = configuration
    let decodedObject = try CodabilityTesting.encodeAndDecode(object)

    // Test Objects
    // XCTAssertEqual(decodedObject, object, .isCodable) // Doesn't work since isEqual not implemented
    XCTAssertNotIdentical(decodedObject, object, .isCodable)

    // Test Codable Properties
    XCTAssertEqual(decodedObject.isAdvertisingIDEnabled, object.isAdvertisingIDEnabled, .isCodable)
    XCTAssertEqual(decodedObject.appID, object.appID, .isCodable)
    XCTAssertEqual(decodedObject.appName, object.appName, .isCodable)
    XCTAssertEqual(decodedObject.defaultShareMode, object.defaultShareMode, .isCodable)
    XCTAssertEqualDicts(decodedObject.dialogFlows(), object.dialogFlows(), .isCodable)
    XCTAssertEqualDicts(decodedObject.dialogConfigurations(), object.dialogConfigurations(), .isCodable)
    // FBSDKErrorConfiguration does not implement isEqual: so just test for not nil
    XCTAssertNotNil(decodedObject.errorConfiguration)
    XCTAssertEqual(decodedObject.isImplicitLoggingSupported, object.isImplicitLoggingSupported, .isCodable)
    XCTAssertEqual(decodedObject.isImplicitPurchaseLoggingSupported, object.isImplicitPurchaseLoggingSupported)
    XCTAssertEqual(decodedObject.isCodelessEventsEnabled, object.isCodelessEventsEnabled, .isCodable)
    XCTAssertEqual(decodedObject.isLoginTooltipEnabled, object.isLoginTooltipEnabled, .isCodable)
    XCTAssertEqual(decodedObject.isUninstallTrackingEnabled, object.isUninstallTrackingEnabled, .isCodable)
    XCTAssertEqual(decodedObject.loginTooltipText, object.loginTooltipText, .isCodable)
    XCTAssertEqual(decodedObject.timestamp, object.timestamp, .isCodable)
    XCTAssertEqual(decodedObject.sessionTimeoutInterval, object.sessionTimeoutInterval, .isCodable)
    XCTAssertEqual(decodedObject.loggingToken, object.loggingToken, .isCodable)
    XCTAssertEqual(decodedObject.smartLoginOptions, object.smartLoginOptions, .isCodable)
    XCTAssertEqual(decodedObject.smartLoginBookmarkIconURL, object.smartLoginBookmarkIconURL, .isCodable)
    XCTAssertEqual(decodedObject.smartLoginMenuIconURL, object.smartLoginMenuIconURL, .isCodable)
    XCTAssertEqual(decodedObject.updateMessage, object.updateMessage, .isCodable)
    XCTAssertEqualDicts(decodedObject.restrictiveParams, object.restrictiveParams, .isCodable)
    XCTAssertEqualDicts(decodedObject.aamRules, object.aamRules, .isCodable)
    XCTAssertEqualDicts(decodedObject.suggestedEventsSetting, object.suggestedEventsSetting, .isCodable)
    XCTAssertEqual(decodedObject.version, object.version, .isCodable)

    // Event bindings is [[String: String]], i.e. an array of dicts
    let objectEventBindings = try XCTUnwrap(object.eventBindings)
    let decodedObjectEventBindings = try XCTUnwrap(decodedObject.eventBindings)
    for (decodedDict, objectDict) in zip(objectEventBindings, decodedObjectEventBindings) {
      XCTAssertEqualDicts(decodedDict, objectDict, .isCodable)
    }

    // isDefaults is not codable
    XCTAssertFalse(
      decodedObject.isDefaults, // was set to true
      "Should not encode whether default values were used to create server configuration"
    )
  }

  func testRetrievingInvalidDialogConfigurationForDialogName() {
    configuration = Fixtures.configuration(withDictionary: [
      "dialogConfigurations": [
        "foo": "bar",
      ],
    ])

    XCTAssertEqual(
      configuration.dialogConfigurations()?["foo"] as? String,
      "bar",
      "Should be able to retrieve an invalid dialog configuration by name"
    )
  }

  func testRetrievingValidDialogConfigurationForDialogName() {
    let fooConfiguration = _DialogConfiguration(
      name: "foo",
      url: exampleURL,
      appVersions: ["1", "2"]
    )

    configuration = Fixtures.configuration(withDictionary: [
      "dialogConfigurations": [
        "foo": fooConfiguration,
      ],
    ])

    XCTAssertEqual(
      configuration.dialogConfiguration(forDialogName: "foo"),
      fooConfiguration,
      "Should be able to retrieve a valid dialog configuration by name"
    )
  }

  // MARK: - Native Dialog Checks

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / nil           / nil            / false
  func testNativeDialogForMissingLoginMissingSharingMissingDefaultValue() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / nil           / true           / true
  func testNativeDialogForMissingLoginMissingSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / nil           / false          / false
  func testNativeDialogForMissingLoginMissingSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / true          / nil            / false
  func testNativeDialogForMissingLoginTrueSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / true          / true           / true
  func testNativeDialogForMissingLoginTrueSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / true          / false          / false
  func testNativeDialogForMissingLoginTrueSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / false         / nil            / false
  func testNativeDialogForMissingLoginFalseSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / false         / true           / true
  func testNativeDialogForMissingLoginFalseSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / false         / false          / false
  func testNativeDialogForMissingLoginFalseSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / nil           / nil            / true
  func testNativeDialogForTrueLoginMissingSharingMissingDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / nil           / true           / true
  func testNativeDialogForTrueLoginMissingSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / nil           / false          / true
  func testNativeDialogForTrueLoginMissingSharingFalseDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / true          / nil            / true
  func testNativeDialogForTrueLoginTrueSharingMissingDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / true          / true           / true
  func testNativeDialogForTrueLoginTrueSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / true          / false          / true
  func testNativeDialogForTrueLoginTrueSharingFalseDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / false         / nil            / true
  func testNativeDialogForTrueLoginFalseSharingMissingDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / false         / true           / true
  func testNativeDialogForTrueLoginFalseSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / false         / false          / true
  func testNativeDialogForTrueLoginFalseSharingFalseDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / nil           / nil            / false
  func testNativeDialogForFalseLoginMissingSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / nil           / true           / false
  func testNativeDialogForFalseLoginMissingSharingTrueDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / nil           / false          / false
  func testNativeDialogForFalseLoginMissingSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / true          / nil            / false
  func testNativeDialogForFalseLoginTrueSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / true          / true           / false
  func testNativeDialogForFalseLoginTrueSharingTrueDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / true          / false          / false
  func testNativeDialogForFalseLoginTrueSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / false         / nil            / false
  func testNativeDialogForFalseLoginFalseSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / false         / true           / false
  func testNativeDialogForFalseLoginFalseSharingTrueDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / false         / false          / false
  func testNativeDialogForFalseLoginFalseSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / nil           / nil            / false
  func testNativeDialogForMissingDialogMissingSharingMissingDefaultValue() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / nil           / true           / true
  func testNativeDialogForMissingDialogMissingSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / nil           / false          / false
  func testNativeDialogForMissingDialogMissingSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / true          / nil            / false
  func testNativeDialogForMissingDialogTrueSharingMissingDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / true          / true           / true
  func testNativeDialogForMissingDialogTrueSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / true          / false          / true
  func testNativeDialogForMissingDialogTrueSharingFalseDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / false         / nil            / false
  func testNativeDialogForMissingDialogFalseSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / false         / true           / false
  func testNativeDialogForMissingDialogFalseSharingTrueDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / false         / false          / false
  func testNativeDialogForMissingDialogFalseSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / nil           / nil            / true
  func testNativeDialogForTrueDialogMissingSharingMissingDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / nil           / true           / true
  func testNativeDialogForTrueDialogMissingSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / nil           / false          / true
  func testNativeDialogForTrueDialogMissingSharingFalseDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / true          / nil            / true
  func testNativeDialogForTrueDialogTrueSharingMissingDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / true          / true           / true
  func testNativeDialogForTrueDialogTrueSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / true          / false          / true
  func testNativeDialogForTrueDialogTrueSharingFalseDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / false         / nil            / true
  func testNativeDialogForTrueDialogFalseSharingMissingDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / false         / true           / true
  func testNativeDialogForTrueDialogFalseSharingTrueDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / false         / false          / true
  func testNativeDialogForTrueDialogFalseSharingFalseDefault() {
    assertNativeDialogIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / nil           / nil            / false
  func testNativeDialogForFalseDialogMissingSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / nil           / true           / false
  func testNativeDialogForFalseDialogMissingSharingTrueDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / nil           / false          / false
  func testNativeDialogForFalseDialogMissingSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / true          / nil            / false
  func testNativeDialogForFalseDialogTrueSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / true          / true           / false
  func testNativeDialogForFalseDialogTrueSharingTrueDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / true          / false          / false
  func testNativeDialogForFalseDialogTrueSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / false         / nil            / false
  func testNativeDialogForFalseDialogFalseSharingMissingDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / false         / true           / false
  func testNativeDialogForFalseDialogFalseSharingTrueDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / false         / false          / false
  func testNativeDialogForFalseDialogFalseSharingFalseDefault() {
    assertNativeDialogIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: false
    )
  }

  // MARK: - SafariVC Checks

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / nil           / nil            / false
  func testSafariVCForMissingLoginMissingSharingMissingDefaultValue() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / nil           / true           / true
  func testSafariVCForMissingLoginMissingSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / nil           / false          / false
  func testSafariVCForMissingLoginMissingSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / true          / nil            / false
  func testSafariVCForMissingLoginTrueSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / true          / true           / true
  func testSafariVCForMissingLoginTrueSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / true          / false          / false
  func testSafariVCForMissingLoginTrueSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / false         / nil            / false
  func testSafariVCForMissingLoginFalseSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / false         / true           / true
  func testSafariVCForMissingLoginFalseSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / nil   / false         / false          / false
  func testSafariVCForMissingLoginFalseSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / nil           / nil            / true
  func testSafariVCForTrueLoginMissingSharingMissingDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / nil           / true           / true
  func testSafariVCForTrueLoginMissingSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / nil           / false          / true
  func testSafariVCForTrueLoginMissingSharingFalseDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / true          / nil            / true
  func testSafariVCForTrueLoginTrueSharingMissingDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / true          / true           / true
  func testSafariVCForTrueLoginTrueSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / true          / false          / true
  func testSafariVCForTrueLoginTrueSharingFalseDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / false         / nil            / true
  func testSafariVCForTrueLoginFalseSharingMissingDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / false         / true           / true
  func testSafariVCForTrueLoginFalseSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / true  / false         / false          / true
  func testSafariVCForTrueLoginFalseSharingFalseDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "login",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / nil           / nil            / false
  func testSafariVCForFalseLoginMissingSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / nil           / true           / false
  func testSafariVCForFalseLoginMissingSharingTrueDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / nil           / false          / false
  func testSafariVCForFalseLoginMissingSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / true          / nil            / false
  func testSafariVCForFalseLoginTrueSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / true          / true           / false
  func testSafariVCForFalseLoginTrueSharingTrueDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / true          / false          / false
  func testSafariVCForFalseLoginTrueSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / false         / nil            / false
  func testSafariVCForFalseLoginFalseSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / false         / true           / false
  func testSafariVCForFalseLoginFalseSharingTrueDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'login'  / false / false         / false          / false
  func testSafariVCForFalseLoginFalseSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "login",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / nil           / nil            / false
  func testSafariVCForMissingDialogMissingSharingMissingDefaultValue() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / nil           / true           / true
  func testSafariVCForMissingDialogMissingSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / nil           / false          / false
  func testSafariVCForMissingDialogMissingSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / true          / nil            / false
  func testSafariVCForMissingDialogTrueSharingMissingDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / true          / true           / true
  func testSafariVCForMissingDialogTrueSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / true          / false          / true
  func testSafariVCForMissingDialogTrueSharingFalseDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / false         / nil            / false
  func testSafariVCForMissingDialogFalseSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / false         / true           / false
  func testSafariVCForMissingDialogFalseSharingTrueDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / nil   / false         / false          / false
  func testSafariVCForMissingDialogFalseSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: nil,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / nil           / nil            / true
  func testSafariVCForTrueDialogMissingSharingMissingDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / nil           / true           / true
  func testSafariVCForTrueDialogMissingSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / nil           / false          / true
  func testSafariVCForTrueDialogMissingSharingFalseDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / true          / nil            / true
  func testSafariVCForTrueDialogTrueSharingMissingDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / true          / true           / true
  func testSafariVCForTrueDialogTrueSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / true          / false          / true
  func testSafariVCForTrueDialogTrueSharingFalseDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / false         / nil            / true
  func testSafariVCForTrueDialogFalseSharingMissingDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / false         / true           / true
  func testSafariVCForTrueDialogFalseSharingTrueDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / true  / false         / false          / true
  func testSafariVCForTrueDialogFalseSharingFalseDefault() {
    assertSafariVcIs(
      expected: true,
      forFeatureName: "foo",
      withFeatureValue: true,
      sharingValue: false,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / nil           / nil            / false
  func testSafariVCForFalseDialogMissingSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / nil           / true           / false
  func testSafariVCForFalseDialogMissingSharingTrueDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / nil           / false          / false
  func testSafariVCForFalseDialogMissingSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: nil,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / true          / nil            / false
  func testSafariVCForFalseDialogTrueSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / true          / true           / false
  func testSafariVCForFalseDialogTrueSharingTrueDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / true          / false          / false
  func testSafariVCForFalseDialogTrueSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: true,
      defaultValue: false
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / false         / nil            / false
  func testSafariVCForFalseDialogFalseSharingMissingDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: nil
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / false         / true           / false
  func testSafariVCForFalseDialogFalseSharingTrueDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: true
    )
  }

  // querying / value / sharing value / default value  / expected
  // 'foo'    / false / false         / false          / false
  func testSafariVCForFalseDialogFalseSharingFalseDefault() {
    assertSafariVcIs(
      expected: false,
      forFeatureName: "foo",
      withFeatureValue: false,
      sharingValue: false,
      defaultValue: false
    )
  }

  // MARK: - Helpers

  func setupConfigWithDialogFlowKey(
    flowKey: String,
    forFeatureName name: String,
    withFeatureValue featureValue: Bool?, // swiftlint:disable:this discouraged_optional_boolean
    sharingValue: Bool?, // swiftlint:disable:this discouraged_optional_boolean
    defaultValue: Bool? // swiftlint:disable:this discouraged_optional_boolean
  ) {
    var dialogFlows: [String: Any] = [:]
    if featureValue != nil {
      dialogFlows[name] = [
        flowKey: featureValue,
      ]
    }
    if sharingValue != nil {
      dialogFlows["sharing"] = [
        flowKey: sharingValue,
      ]
    }
    if defaultValue != nil {
      dialogFlows["default"] = [
        flowKey: defaultValue,
      ]
    }

    configuration = Fixtures.configuration(withDictionary: ["dialogFlows": dialogFlows])
  }

  func assertSafariVcIs(
    expected: Bool,
    forFeatureName name: String,
    withFeatureValue featureValue: Bool?, // swiftlint:disable:this discouraged_optional_boolean
    sharingValue: Bool?, // swiftlint:disable:this discouraged_optional_boolean
    defaultValue: Bool? // swiftlint:disable:this discouraged_optional_boolean
  ) {
    setupConfigWithDialogFlowKey(
      flowKey: "use_safari_vc",
      forFeatureName: name,
      withFeatureValue: featureValue,
      sharingValue: sharingValue,
      defaultValue: defaultValue
    )

    XCTAssertEqual(
      configuration.useSafariViewController(forDialogName: name),
      expected,
      """
      Use safari view controller for dialog name: \(name),
        should return true when the feature value is: \(String(describing: featureValue)),
        the sharing value is: \(String(describing: sharingValue)),
        and the default value is: \(String(describing: defaultValue))
      """
    )
  }

  func assertNativeDialogIs(
    expected: Bool,
    forFeatureName name: String,
    withFeatureValue featureValue: Bool?, // swiftlint:disable:this discouraged_optional_boolean
    sharingValue: Bool?, // swiftlint:disable:this discouraged_optional_boolean
    defaultValue: Bool? // swiftlint:disable:this discouraged_optional_boolean
  ) {
    setupConfigWithDialogFlowKey(
      flowKey: "use_native_flow",
      forFeatureName: name,
      withFeatureValue: featureValue,
      sharingValue: sharingValue,
      defaultValue: defaultValue
    )

    XCTAssertEqual(
      configuration.useNativeDialog(forDialogName: name),
      expected,
      """
      Use native dialog for dialog name: \(name),
        should return true when the feature value is: \(String(describing: featureValue)),
        the sharing value is: \(String(describing: sharingValue)),
        and the default value is: \(String(describing: defaultValue))
      """
    )
  }

  // This helper is to work around: Protocol 'Any' as a type cannot conform to 'Equatable'
  func XCTAssertEqualDicts(
    _ lhs: [String: Any]?,
    _ rhs: [String: Any]?,
    _ message: @autoclosure () -> String = "",
    file: StaticString = #filePath,
    line: UInt = #line
  ) {
    if let lhs = lhs,
       let rhs = rhs {
      let dict1 = NSDictionary(dictionary: lhs)
      let dict2 = NSDictionary(dictionary: rhs)
      XCTAssertEqual(dict1, dict2, message(), file: file, line: line)
    } else if lhs == nil && rhs != nil {
      XCTFail("LHS Dict is nil", file: file, line: line)
    } else if lhs != nil && rhs == nil {
      XCTFail("RHS Dict is nil", file: file, line: line)
    }
  }
}

// MARK: - Assumptions

// swiftformat:disable:next extensionaccesscontrol
fileprivate extension String {
  static let isCodable = "FBSDKServerConfiguration should be encodable and decodable"
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

import XCTest

final class DrawableTests: XCTestCase {
  let size = CGSize(width: 100, height: 100)
  let placeholderImageColor = UIColor(
    red: 157.0 / 255.0,
    green: 177.0 / 255.0,
    blue: 204.0 / 255.0,
    alpha: 1.0
  )

  func testBaseClassPathWithSize() {
    XCTAssertNil(FBIcon().path(with: size))
  }

  func testDefaultScale() {
    XCTAssertEqual(
      _HumanSilhouetteIcon().image(size: size)?.scale,
      UIScreen.main.scale,
      "Icons should default their scale to the scale of the main screen"
    )
  }

  func testDefaultScaleWithColor() {
    XCTAssertEqual(
      _HumanSilhouetteIcon().image(size: size, color: .red)?.scale,
      UIScreen.main.scale,
      "Scale should not be affected by the color"
    )
  }

  func testCustomScale() {
    XCTAssertEqual(
      _HumanSilhouetteIcon().image(
        size: size,
        scale: 2.0
      )?.scale,
      2.0,
      "Icons should accept a custom scale"
    )
  }

  func testSystemColor() throws {
    try XCTSkipIf(
      true,
      "This test is being skipped due to changes in Xcode 14.1 that produce new and different PNG data"
    )

    let potentialImage = _HumanSilhouetteIcon().image(
      size: size,
      scale: 2.0,
      color: .red
    )

    guard let image = potentialImage else {
      return XCTFail("Should be able to create an image with a valid size")
    }

    let redIcon = UIImage(
      named: "redSilhouette.png",
      in: Bundle(for: Self.self),
      compatibleWith: nil
    )

    XCTAssertEqual(
      image.pngData(),
      redIcon?.pngData(),
      "Should create the expected image for the size and color"
    )
  }

  // MARK: Human Silhouette Icon

  func testImageWithInvalidSize() {
    XCTAssertNil(_HumanSilhouetteIcon().image(size: .zero), "An image must have a non-zero size")
  }

  func testPlaceholderImageColor() throws {
    try XCTSkipIf(
      true,
      "This test is being skipped due to changes in Xcode 14.1 that produce new and different PNG data"
    )

    let potentialImage = _HumanSilhouetteIcon().image(
      size: size,
      scale: 2.0,
      color: placeholderImageColor
    )
    let customIcon = UIImage(
      named: "customColorSilhouette.png",
      in: Bundle(for: Self.self),
      compatibleWith: nil
    )

    guard let image = potentialImage else {
      return XCTFail("Should be able to create an image with a valid size")
    }

    XCTAssertEqual(
      image.pngData(),
      customIcon?.pngData(),
      "Should create the expected image for the size and color"
    )
  }

  // MARK: Logo Icon

  func testLogo() throws {
    try XCTSkipIf(
      true,
      "This test is being skipped due to changes in Xcode 14.1 that produce new and different PNG data"
    )

    let potentialImage = _FBLogo().image(
      size: size,
      scale: 2.0,
      color: .red
    )
    let storedImage = UIImage(
      named: "redLogo.png",
      in: Bundle(for: Self.self),
      compatibleWith: nil
    )

    guard let image = potentialImage else {
      return XCTFail("Should be able to create an image with a valid size")
    }

    XCTAssertEqual(
      image.pngData(),
      storedImage?.pngData(),
      "Should create the expected image"
    )
  }

  // MARK: Close Icon

  func testCloseIcon() throws {
    try XCTSkipIf(
      true,
      "This test is being skipped due to changes in Xcode 14.1 that produce new and different PNG data"
    )

    guard let image = _FBCloseIcon().image(
      size: size,
      primaryColor: .red,
      secondaryColor: .green,
      scale: 2.0
    ) else {
      return XCTFail("Should be able to create an image with a valid size")
    }

    let storedImage = UIImage(
      named: "closeIcon.png",
      in: Bundle(for: Self.self),
      compatibleWith: nil
    )

    XCTAssertEqual(
      image.pngData(),
      storedImage?.pngData(),
      "Should create the expected image"
    )
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

import TestTools
import XCTest

final class ImpressionLoggerFactoryTests: XCTestCase {

  // swiftlint:disable implicitly_unwrapped_optional
  var factory: _ImpressionLoggerFactory!
  var graphRequestFactory: TestGraphRequestFactory!
  var eventLogger: TestEventLogger!
  var notificationCenter: TestNotificationCenter!
  var accessTokenWallet: TestAccessTokenWallet.Type!
  // swiftlint:enable implicitly_unwrapped_optional

  override func setUp() {
    super.setUp()

    graphRequestFactory = TestGraphRequestFactory()
    eventLogger = TestEventLogger()
    notificationCenter = TestNotificationCenter()
    accessTokenWallet = TestAccessTokenWallet.self

    factory = _ImpressionLoggerFactory(
      graphRequestFactory: graphRequestFactory,
      eventLogger: eventLogger,
      notificationCenter: notificationCenter,
      accessTokenWallet: accessTokenWallet
    )
  }

  override func tearDown() {
    factory = nil
    graphRequestFactory = nil
    eventLogger = nil
    notificationCenter = nil
    accessTokenWallet = nil

    super.tearDown()
  }

  func testInitialization() {
    XCTAssertTrue(
      factory.graphRequestFactory === graphRequestFactory,
      "Should use the provided graph request factory"
    )
    XCTAssertTrue(
      factory.eventLogger === eventLogger,
      "Should use the provided event logger"
    )
    XCTAssertTrue(
      factory.notificationCenter === notificationCenter,
      "Should use the provided notification center"
    )
    XCTAssertTrue(
      factory.accessTokenWallet === accessTokenWallet,
      "Should use the provided access token wallet"
    )
  }

  func testCreatingImpressionLogger() {
    let logger = factory.makeImpressionLogger(withEventName: .adClick)

    XCTAssertTrue(
      logger is _ViewImpressionLogger,
      "Should make the correct type of impression logger"
    )
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

import TestTools
import XCTest

final class ImpressionLoggingButtonTests: XCTestCase {

  // swiftlint:disable:next implicitly_unwrapped_optional
  var impressionLoggerFactory: TestImpressionLoggerFactory!

  override func setUp() {
    super.setUp()

    impressionLoggerFactory = TestImpressionLoggerFactory()

    ImpressionLoggingButton.resetClassDependencies()
  }

  override func tearDown() {
    ImpressionLoggingButton.resetClassDependencies()

    impressionLoggerFactory = nil

    super.tearDown()
  }

  func testDefaultClassDependencies() {
    XCTAssertNil(
      ImpressionLoggingButton.impressionLoggerFactory,
      "Should not have an impression logger factory by default"
    )
  }

  func testConfiguringClassDependencies() {
    ImpressionLoggingButton.configure(impressionLoggerFactory: impressionLoggerFactory)

    XCTAssertTrue(
      ImpressionLoggingButton.impressionLoggerFactory === impressionLoggerFactory,
      "Should be able to configure with an impression logger factory"
    )
  }

  func testLayoutSubviews() {
    ImpressionLoggingButton.configure(impressionLoggerFactory: impressionLoggerFactory)

    let button = TestImpressionButton(frame: .zero)

    button.layoutSubviews()

    XCTAssertEqual(
      impressionLoggerFactory.capturedEventName,
      button.impressionTrackingEventName,
      "Should make an event logger with the expected event name"
    )
    XCTAssertEqual(
      impressionLoggerFactory.impressionLogger.capturedIdentifier,
      button.impressionTrackingIdentifier,
      "Should log an event with the expected identifier"
    )
    XCTAssertEqual(
      impressionLoggerFactory.impressionLogger.capturedParameters as? [AppEvents.ParameterName: String],
      button.analyticsParameters as? [AppEvents.ParameterName: String],
      "Should log an event with the expected parameters"
    )
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

import TestTools
import XCTest

final class ViewImpressionLoggerTests: XCTestCase {

  // swiftlint:disable implicitly_unwrapped_optional
  var graphRequestFactory: TestGraphRequestFactory!
  var logger: TestEventLogger!
  var notificationCenter: TestNotificationCenter!
  // swiftlint:enable implicitly_unwrapped_optional
  let sharedTrackerName = AppEvents.Name("shared")
  lazy var tracker = createImpressionLogger(named: sharedTrackerName)
  let impressionIdentifier = "foo"
  let parameters: [AppEvents.ParameterName: String] = [.init("bar"): "baz"]

  override func setUp() {
    super.setUp()

    graphRequestFactory = TestGraphRequestFactory()
    logger = TestEventLogger()
    notificationCenter = TestNotificationCenter()

    TestAccessTokenWallet.current = SampleAccessTokens.validToken
    _ViewImpressionLogger.setDependencies(
      .init(
        graphRequestFactory: graphRequestFactory,
        eventLogger: logger,
        notificationDeliverer: notificationCenter,
        tokenWallet: TestAccessTokenWallet.self
      )
    )
    tracker = createImpressionLogger(named: sharedTrackerName)
  }

  override func tearDown() {
    graphRequestFactory = nil
    logger = nil
    notificationCenter = nil
    TestAccessTokenWallet.current = nil
    _ViewImpressionLogger.resetDependencies()
    super.tearDown()
  }

  // MARK: - Dependencies

  func testDefaultTypeDependencies() throws {
    _ViewImpressionLogger.resetDependencies()
    let dependencies = try _ViewImpressionLogger.getDependencies()

    XCTAssertTrue(
      dependencies.graphRequestFactory is GraphRequestFactory,
      .defaultDependency("GraphRequestFactory", for: "graph request factory")
    )

    XCTAssertIdentical(
      dependencies.eventLogger as AnyObject,
      AppEvents.shared,
      .customDependency(for: "event logging")
    )

    XCTAssertIdentical(
      dependencies.notificationDeliverer as AnyObject,
      NotificationCenter.default,
      .customDependency(for: "notification delivery")
    )

    XCTAssertIdentical(
      dependencies.tokenWallet as AnyObject,
      AccessToken.self,
      .customDependency(for: "access token")
    )
  }

  func testCustomTypeDependencies() throws {
    let dependencies = try _ViewImpressionLogger.getDependencies()

    XCTAssertIdentical(
      dependencies.graphRequestFactory as AnyObject,
      graphRequestFactory,
      .customDependency(for: "graph request factory")
    )

    XCTAssertIdentical(
      dependencies.eventLogger as AnyObject,
      logger,
      .customDependency(for: "event logging")
    )

    XCTAssertIdentical(
      dependencies.notificationDeliverer as AnyObject,
      notificationCenter,
      .customDependency(for: "notification delivery")
    )

    XCTAssertIdentical(
      dependencies.tokenWallet as AnyObject,
      TestAccessTokenWallet.self,
      .customDependency(for: "access token")
    )
  }

  // MARK: - Notifications

  func testCreatingSetsUpNotificationObserving() {
    XCTAssertTrue(
      notificationCenter.capturedAddObserverInvocations.contains(
        TestNotificationCenter.ObserverEvidence(
          observer: tracker as Any,
          name: UIApplication.didEnterBackgroundNotification,
          selector: #selector(_ViewImpressionLogger.applicationDidEnterBackground),
          object: nil
        )
      ),
      "Should start observing application backgrounding upon initialization"
    )
  }

  func testBackgroundingNotificationAction() {
    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )
    XCTAssertFalse(tracker.trackedImpressions.isEmpty)

    tracker.applicationDidEnterBackground(Notification(name: .init("foo")))

    XCTAssertTrue(
      tracker.trackedImpressions.isEmpty,
      "Backgrounding the app should clear any tracked impressions"
    )
  }

  // MARK: - Impression Logging

  func testLoggingSingleImpression() {
    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )

    XCTAssertEqual(
      logger.capturedEventName,
      sharedTrackerName,
      "Should log an impression with the event name"
    )
    XCTAssertEqual(
      logger.capturedParameters as? [AppEvents.ParameterName: String],
      parameters,
      "Should log an impression with the expected parameters"
    )
    XCTAssertTrue(
      logger.capturedIsImplicitlyLogged,
      "Impressions should be implicitly logged"
    )
    XCTAssertEqual(
      logger.capturedAccessToken,
      SampleAccessTokens.validToken,
      "Should log an impression with the expected access token"
    )
  }

  func testLoggingIdenticalImpressions() {
    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )

    logger.capturedEventName = nil

    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )

    XCTAssertNil(
      logger.capturedEventName,
      "Should not log the same impression more than once per tracker instance"
    )
  }

  func testLoggingIdenticalImpressionsFromDifferentTrackers() {
    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )

    logger.capturedEventName = nil
    tracker = createImpressionLogger(named: AppEvents.Name(name))

    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )

    XCTAssertEqual(
      logger.capturedEventName,
      AppEvents.Name(name),
      "Should log an impression from a new tracker even if the impression is not valid"
    )
    XCTAssertNil(
      logger.capturedParameters?[.init("__view_impression_identifier__")],
      "Should not log the same impression twice even if the trackers are different"
    )
  }

  func testLoggingDifferentImpressionsFromSameTracker() {
    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )

    logger.capturedEventName = nil

    tracker.logImpression(
      withIdentifier: name,
      parameters: parameters
    )

    XCTAssertEqual(
      logger.capturedEventName,
      sharedTrackerName,
      "Should log different impressions using the same tracker"
    )
  }

  func testLoggingDifferentImpressionsFromDifferentTrackers() {
    tracker.logImpression(
      withIdentifier: impressionIdentifier,
      parameters: parameters
    )

    logger.capturedEventName = nil
    tracker = createImpressionLogger(named: AppEvents.Name(name))

    tracker.logImpression(
      withIdentifier: name,
      parameters: parameters
    )

    XCTAssertEqual(
      logger.capturedEventName,
      AppEvents.Name(name),
      "Should be able to log different impressions using different trackers"
    )
  }

  // MARK: - Helpers

  func createImpressionLogger(named name: AppEvents.Name) -> _ViewImpressionLogger {
    _ViewImpressionLogger(eventName: name)
  }
}

// swiftformat:disable extensionaccesscontrol

// MARK: - Assumptions

fileprivate extension String {
  static func defaultDependency(_ dependency: String, for type: String) -> String {
    "The _ViewImpressionLogger type uses \(dependency) as its \(type) dependency by default"
  }

  static func customDependency(for type: String) -> String {
    "The _ViewImpressionLogger type uses a custom \(type) dependency when provided"
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

import TestTools
import XCTest

// swiftlint:disable implicitly_unwrapped_optional

final class WebDialogTests: XCTestCase, WebDialogDelegate {

  var windowFinder: _WindowFinding!
  var dialogView: FBWebDialogView!
  var errorFactory: ErrorCreating!
  var webDialogDidCancelWasCalled = false
  var webDialogDidFailWasCalled = false
  var capturedDidCompleteResults: [String: String]?
  var capturedError: Error?
  var parameters = ["foo": "bar"]

  override func setUp() {
    errorFactory = TestErrorFactory()
    windowFinder = TestWindowFinder(window: UIWindow())
    dialogView = FBWebDialogView()

    _WebDialog.setDependencies(.init(errorFactory: errorFactory, windowFinder: windowFinder))
    super.setUp()
  }

  override func tearDown() {
    windowFinder = nil
    dialogView = nil
    errorFactory = nil

    _WebDialog.resetDependencies()

    super.tearDown()
  }

  func testDefaultTypeDependencies() throws {
    _WebDialog.resetDependencies()
    let dependencies = try _WebDialog.getDependencies()

    XCTAssertTrue(
      dependencies.errorFactory is _ErrorFactory,
      .defaultDependency("the error factory", for: "creating errors")
    )

    XCTAssertIdentical(
      dependencies.windowFinder as AnyObject,
      InternalUtility.shared,
      .defaultDependency("the shared InternalUtility", for: "window finding")
    )
  }

  func testCustomTypeDependencies() throws {
    let dependencies = try _WebDialog.getDependencies()

    XCTAssertIdentical(
      dependencies.errorFactory as AnyObject,
      errorFactory,
      .customDependency(for: "error factoring")
    )

    XCTAssertIdentical(
      dependencies.windowFinder as AnyObject,
      windowFinder,
      .customDependency(for: "window finding")
    )
  }

  func testShowWithInvalidURLFromParameters() {
    let dialog = createAndShowDialog(name: name)

    XCTAssertEqual(
      dialog.name,
      name,
      "Should create a dialog with the provided name"
    )
    XCTAssertEqual(
      dialog.parameters,
      parameters,
      "Should create a dialog with the provided parameters"
    )
    XCTAssertTrue(
      dialog.delegate === self,
      "Should create a dialog with the provided delegate"
    )
    XCTAssertTrue(
      webDialogDidFailWasCalled,
      "Should fail to show if it cannot parse a url from the parameters"
    )
  }

  func testShowWithValidURLFromParametersWithoutWindow() {
    _WebDialog.resetDependencies()
    createAndShowDialog()

    XCTAssertTrue(
      webDialogDidFailWasCalled,
      "Should fail to show if it cannot locate a window to present in"
    )
  }

  // MARK: - Delegate Methods

  func testDidCompleteWithResults() {
    let dialog = createAndShowDialog()
    let results = ["foo": name]

    dialog.webDialogView(dialogView, didCompleteWithResults: results)

    XCTAssertEqual(
      capturedDidCompleteResults,
      results,
      "Should call the web dialog delegate methods when the web dialog view delegate methods are called"
    )
  }

  func testDidFailWithError() {
    let dialog = createAndShowDialog()

    dialog.webDialogView(dialogView, didFailWithError: SampleError())

    XCTAssertTrue(
      capturedError is SampleError,
      "Should call the web dialog delegate methods when the web dialog view delegate methods are called"
    )
  }

  func testDidCancel() {
    let dialog = createAndShowDialog()

    dialog.webDialogViewDidCancel(dialogView)

    XCTAssertTrue(
      webDialogDidCancelWasCalled,
      "Should call the web dialog delegate methods when the web dialog view delegate methods are called"
    )
  }

  // MARK: - Helpers

  @discardableResult
  func createAndShowDialog(
    name: String = "example",
    parameters: [String: String]? = nil,
    delegate: WebDialogDelegate? = nil
  ) -> _WebDialog {
    let webDialog = _WebDialog(name: name, parameters: parameters ?? self.parameters)
    webDialog.delegate = delegate ?? self
    webDialog.show()
    return webDialog
  }

  func webDialog(_ webDialog: _WebDialog, didCompleteWithResults results: [String: Any]) {
    capturedDidCompleteResults = results as? [String: String]
  }

  func webDialog(_ webDialog: _WebDialog, didFailWithError error: Error) {
    capturedError = error
    webDialogDidFailWasCalled = true
  }

  func webDialogDidCancel(_ webDialog: _WebDialog) {
    webDialogDidCancelWasCalled = true
  }
}

// swiftformat:disable extensionaccesscontrol

// MARK: - Assumptions

fileprivate extension String {
  static func defaultDependency(_ dependency: String, for type: String) -> String {
    "The _WebDialog type uses \(dependency) as its \(type) dependency by default"
  }

  static func customDependency(for type: String) -> String {
    "The _WebDialog type uses a custom \(type) dependency when provided"
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

import TestTools
import XCTest

final class WebDialogViewTests: XCTestCase, WebDialogViewDelegate {

  // swiftlint:disable implicitly_unwrapped_optional
  var webView: TestWebView!
  var dialog: FBWebDialogView!
  var capturedDelegateDidFailError: Error?
  var webViewFactory: TestWebViewFactory!
  var urlOpener: TestInternalURLOpener!
  var errorFactory: TestErrorFactory!
  // swiftlint:enable implicitly_unwrapped_optional

  let frame = CGRect(origin: .zero, size: CGSize(width: 10, height: 10))
  var delegateDidFailWithErrorWasCalled = false
  var capturedDidCompleteResults: [String: String]?
  var webDialogViewDidCancelWasCalled = false
  var webDialogViewDidFinishLoadWasCalled = false

  override func setUp() {
    super.setUp()

    webViewFactory = TestWebViewFactory()
    urlOpener = TestInternalURLOpener()
    errorFactory = TestErrorFactory()

    webView = webViewFactory.webView
    FBWebDialogView.configure(
      webViewProvider: webViewFactory,
      urlOpener: urlOpener,
      errorFactory: errorFactory
    )

    dialog = FBWebDialogView(frame: frame)
    dialog.delegate = self
  }

  override func tearDown() {
    webView = nil
    capturedDelegateDidFailError = nil
    webViewFactory = nil
    urlOpener = nil
    errorFactory = nil
    dialog = nil

    super.tearDown()
  }

  func testCreatingWithDefaults() {
    FBWebDialogView.resetClassDependencies()
    let dialog = FBWebDialogView(frame: .zero)

    XCTAssertNil(
      dialog.webView,
      "Should not have a webview by default"
    )
    XCTAssertNil(
      FBWebDialogView.webViewProvider,
      "Should not have a web view provider by default"
    )
    XCTAssertNil(
      FBWebDialogView.urlOpener,
      "Should not have a url opener by default"
    )
    XCTAssertNil(
      FBWebDialogView.errorFactory,
      "Should not have an error factory by default"
    )
  }

  func testCreatingWithClassDependencies() {
    XCTAssertIdentical(
      FBWebDialogView.webViewProvider,
      webViewFactory,
      "Should be able to configure a web view provider"
    )
    XCTAssertIdentical(
      FBWebDialogView.urlOpener,
      urlOpener,
      "Should be able to configure an internal URL opener"
    )
    XCTAssertIdentical(
      FBWebDialogView.errorFactory,
      errorFactory,
      "Should be able to configure an error factory"
    )
  }

  func testRequestsWebView() {
    XCTAssertEqual(
      webViewFactory.capturedFrame,
      .zero,
      "Should request a webview with a frame of zero"
    )
  }

  func testSetsWebViewAsSubview() {
    XCTAssertNotNil(
      dialog.subviews.first as? TestWebView,
      "Should add the webview from the factory as a subview"
    )
  }

  func testSetsSelfAsNavigationDelegate() {
    guard let delegate = webView.navigationDelegate
    else {
      return XCTFail("Should add a child webview on creation")
    }
    XCTAssertEqual(
      ObjectIdentifier(delegate),
      ObjectIdentifier(dialog),
      "Should set the dialog as the webview's navigation delegate"
    )
  }

  func testActivityIndicatorDefaultState() {
    XCTAssertTrue(
      activityIndicatorView.hidesWhenStopped,
      "The activity indicator view should hide when stopped"
    )
    XCTAssertFalse(
      activityIndicatorView.isAnimating,
      "The activity indicator view should not animate by default"
    )
  }

  func testLoadingURL() {
    dialog.load(SampleURLs.valid)

    XCTAssertEqual(
      webView.capturedRequest,
      URLRequest(url: SampleURLs.valid),
      "Should attempt to load the request in the webview"
    )
    XCTAssertTrue(
      activityIndicatorView.isAnimating,
      "Should start animating the activity indicator during loading"
    )
  }

  func testStopLoadingURL() {
    dialog.load(SampleURLs.valid)
    dialog.stopLoading()

    XCTAssertEqual(
      webView.stopLoadingCallCount,
      1,
      "Stopping the dialog view should stop loading the request in the webview"
    )
    XCTAssertFalse(
      activityIndicatorView.isAnimating,
      "Should stop animating the activity indicator when loading stops"
    )
  }

  func testLayingOutSubviewsWithoutEnoughSpace() {
    dialog.bounds = CGRect(origin: .zero, size: .zero)
    dialog.draw(.zero)
    dialog.layoutSubviews()

    XCTAssertEqual(
      dialog.frame,
      .init(x: 5, y: 5, width: 0, height: 0),
      "Should set the expected frame when layout out subviews"
    )
  }

  func testLayingOutSubviewsWithEnoughSpace() {
    dialog.bounds = CGRect(origin: .zero, size: CGSize(width: 50, height: 50))
    dialog.draw(.zero)
    dialog.layoutSubviews()

    XCTAssertEqual(
      dialog.frame,
      .init(x: -20, y: -20, width: 50, height: 50),
      "Should set the expected frame when layout out subviews"
    )
  }

  // MARK: - Delegate Methods

  func testFailingNavigationWithRecognizedError() {
    dialog.webView(WKWebView(), didFail: nil, withError: SampleError())

    XCTAssertFalse(
      activityIndicatorView.isAnimating,
      "Should stop animating the activity indicator when the navigation fails"
    )
    XCTAssertTrue(
      delegateDidFailWithErrorWasCalled,
      "Should invoke the delegate for an unrecognized error"
    )
    XCTAssertTrue(
      capturedDelegateDidFailError is SampleError,
      "Should propage the error from the failure"
    )
  }

  func testFailingNavigationWithCancellationError() {
    let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled, userInfo: nil)
    dialog.webView(WKWebView(), didFail: nil, withError: error)

    XCTAssertFalse(
      delegateDidFailWithErrorWasCalled,
      "Should not invoke the delegate for a cancellation error"
    )
  }

  func testFailingNavigationWithWebKitError() {
    let error = NSError(domain: "WebKitErrorDomain", code: 102, userInfo: nil)
    dialog.webView(WKWebView(), didFail: nil, withError: error)

    XCTAssertFalse(
      delegateDidFailWithErrorWasCalled,
      "Should not invoke the delegate for a webkit error"
    )
  }

  func testDecideNavigationPolicyWithConnectScheme() {
    var policy: WKNavigationActionPolicy?
    dialog.webView(
      WKWebView(),
      decidePolicyFor: TestWebKitNavigationAction(
        stubbedRequest: URLRequest(url: URLs.connectURL)
      )
    ) {
      policy = $0
    }

    XCTAssertEqual(policy, .cancel)
    XCTAssertEqual(capturedDidCompleteResults, [:])
  }

  func testDecideNavigationPolicyWithQueryAndFragment() {
    [
      (URLs.connectURLWithQuery, ["bar": "baz"]),
      (URLs.connectURLWithFragment, ["fragment": ""]),
      (URLs.connectURLWithQueryAndFragment, ["bar": "baz", "fragment": ""]),
    ].forEach {
      let (url, expectedResults) = $0
      capturedDidCompleteResults = nil

      var policy: WKNavigationActionPolicy?
      dialog.webView(
        WKWebView(),
        decidePolicyFor: TestWebKitNavigationAction(
          stubbedRequest: URLRequest(url: url)
        )
      ) {
        policy = $0
      }

      XCTAssertEqual(policy, .cancel)
      XCTAssertEqual(capturedDidCompleteResults, expectedResults)
    }
  }

  func testDecideNavigationPolicyWithCancelledUrl() {
    var policy: WKNavigationActionPolicy?
    dialog.webView(
      WKWebView(),
      decidePolicyFor: TestWebKitNavigationAction(
        stubbedRequest: URLRequest(url: URLs.cancelURLWithoutError)
      )
    ) {
      policy = $0
    }

    XCTAssertEqual(policy, .cancel)
    XCTAssertNil(
      capturedDidCompleteResults,
      "Should not pass parameters on deciding the navigation policy of a cancelled url"
    )
    XCTAssertTrue(
      webDialogViewDidCancelWasCalled,
      "Should inform the delegate when a url cancels the navigation"
    )
  }

  func testDecideNavigationPolicyWithCancelledUrlWithError() throws {
    var policy: WKNavigationActionPolicy?
    dialog.webView(
      WKWebView(),
      decidePolicyFor: TestWebKitNavigationAction(
        stubbedRequest: URLRequest(url: URLs.cancelURLWithError)
      )
    ) {
      policy = $0
    }

    XCTAssertEqual(policy, .cancel)
    XCTAssertNil(
      capturedDidCompleteResults,
      "Should not pass parameters on deciding the navigation policy of a cancelled url"
    )
    XCTAssertTrue(
      delegateDidFailWithErrorWasCalled,
      "Should invoke the delegate with failure when the url is cancelled and contains an error"
    )

    let errorTestMessage = "Should create an error from the URL and call the delegate with it"
    let error = try XCTUnwrap(capturedDelegateDidFailError, errorTestMessage)
    let testError = try XCTUnwrap(error as? TestSDKError, errorTestMessage)

    XCTAssertEqual(
      testError.code,
      999,
      errorTestMessage
    )
    XCTAssertEqual(
      testError.message,
      "anErrorOhNO",
      errorTestMessage
    )
  }

  func testDecideNavigationPolicyWithActivatedNavigationType() {
    var policy: WKNavigationActionPolicy?
    dialog.webView(
      WKWebView(),
      decidePolicyFor: TestWebKitNavigationAction(
        stubbedRequest: URLRequest(url: SampleURLs.valid),
        navigationType: .linkActivated
      )
    ) {
      policy = $0
    }

    XCTAssertEqual(urlOpener.capturedOpenURL, SampleURLs.valid)
    urlOpener.capturedOpenURLCompletion?(true)
    XCTAssertEqual(
      policy,
      .cancel,
      "Completing with a successful url opening will should set the policy to cancelled"
    )

    policy = nil

    urlOpener.capturedOpenURLCompletion?(false)
    XCTAssertEqual(
      policy,
      .cancel,
      "Completing with a failed url opening will should set the policy to cancelled"
    )
  }

  func testDecideNavigationPolicyWithoutConnectUrlWithoutActivatedNavigationType() {
    var policy: WKNavigationActionPolicy?
    dialog.webView(
      WKWebView(),
      decidePolicyFor: TestWebKitNavigationAction(
        stubbedRequest: URLRequest(url: SampleURLs.valid),
        navigationType: .other
      )
    ) {
      policy = $0
    }

    XCTAssertNil(urlOpener.capturedOpenURL)
    XCTAssertEqual(
      policy,
      .allow,
      "The default navigation policy should be allowed"
    )
  }

  func testDidFinishNavigation() {
    dialog.webView(
      WKWebView(),
      didFinish: nil
    )
    XCTAssertFalse(
      activityIndicatorView.isAnimating,
      "Finishing loading should stop the activity indicator"
    )
    XCTAssertTrue(
      webDialogViewDidFinishLoadWasCalled,
      "Should call the delegate method when loading is finished"
    )
  }

  // MARK: - Helpers

  enum URLs {
    // swiftlint:disable force_unwrapping
    static let connectURL = URL(string: "fbconnect://foo")!
    static let connectURLWithQuery = URL(string: "fbconnect://foo?bar=baz")!
    static let connectURLWithFragment = URL(string: "fbconnect://foo#fragment")!
    static let connectURLWithQueryAndFragment = URL(string: "fbconnect://foo?bar=baz#fragment")!
    static let cancelURLWithoutError = URL(string: "fbconnect://cancel")!
    static let cancelURLWithError = URL(string: "fbconnect://cancel?error_code=999&error_msg=anErrorOhNO")!
    // swiftlint:enable force_unwrapping
  }

  var activityIndicatorView: UIActivityIndicatorView {
    guard let loadingIndicator = webView.subviews.first as? UIActivityIndicatorView else {
      fatalError("Should provide an activity indicator view during setup")
    }
    return loadingIndicator
  }

  func webDialogView(
    _ webDialogView: FBWebDialogView,
    didCompleteWithResults results: [String: Any]
  ) {
    capturedDidCompleteResults = results as? [String: String]
  }

  func webDialogView(
    _ webDialogView: FBWebDialogView,
    didFailWithError error: Error
  ) {
    delegateDidFailWithErrorWasCalled = true
    capturedDelegateDidFailError = error
  }

  func webDialogViewDidCancel(_ webDialogView: FBWebDialogView) {
    webDialogViewDidCancelWasCalled = true
  }

  func webDialogViewDidFinishLoad(_ webDialogView: FBWebDialogView) {
    webDialogViewDidFinishLoadWasCalled = true
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBSDKCoreKit

final class WebViewFactoryTests: XCTestCase {

  // swiftlint:disable implicitly_unwrapped_optional
  var factory: _WebViewFactory!
  // swiftlint:enable implicitly_unwrapped_optional
  let frame = CGRect(origin: .zero, size: .init(width: 5, height: 5))

  override func setUp() {
    super.setUp()
    factory = _WebViewFactory()
  }

  override func tearDown() {
    factory = nil

    super.tearDown()
  }

  func testCreatingWebView() {
    guard let webView = factory.createWebView(frame: frame) as? WKWebView else {
      return XCTFail("Should create a webview of the expected concrete type")
    }

    XCTAssertEqual(
      webView.frame,
      frame,
      "Should create a webview with the given frame"
    )
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import FBSDKCoreKit_Basics
import XCTest

final class BundleTests: XCTestCase {

  // swiftlint:disable:next implicitly_unwrapped_optional
  var bundle: Bundle!

  override func setUp() {
    super.setUp()
    bundle = Bundle(for: BundleTests.self)
  }

  override func tearDown() {
    bundle = nil
    super.tearDown()
  }

  func testInfoDictionary() throws {
    let expected = try XCTUnwrap(bundle.infoDictionary)
    let actual = try XCTUnwrap(bundle.fb_infoDictionary, .infoDictionary)

    XCTAssertEqual(
      Set(actual.keys),
      Set(expected.keys),
      .infoDictionary
    )
  }

  func testBundleIdentifier() throws {
    let bundleIdentifier = try XCTUnwrap(bundle.fb_bundleIdentifier, .bundleIdentifier)
    XCTAssertEqual(bundleIdentifier, bundle.bundleIdentifier, .bundleIdentifier)
  }

  func testIndexingInfoDictionary() throws {
    let expected = try XCTUnwrap(bundle.infoDictionary?[kCFBundleNameKey as String] as? String)

    XCTAssertEqual(
      bundle.fb_object(forInfoDictionaryKey: kCFBundleNameKey as String) as? String,
      expected,
      .infoDictionaryIndexing
    )
  }
}

// swiftformat:disable extensionaccesscontrol

// MARK: - Assumptions

fileprivate extension String {
  static let infoDictionary = "An info dictionary is provided through an internal abstraction"
  static let bundleIdentifier = "A bundle identifier is provided through an internal abstraction"
  static let infoDictionaryIndexing = "An info dictionary is indexed through an internal abstraction"
}