Signal-iOS/Signal/test/ViewControllers/BadgeExpirationSheetStateTest.swift
Evan Hahn 370ff654e7
Change license to AGPL
Change license to AGPL

This commit:

- Updates the `LICENSE` file

- Start every file with something like:

      // Copyright YEAR_FIRST_PUBLISHED Signal Messenger, LLC
      // SPDX-License-Identifier: AGPL-3.0-only

---

First, I removed existing license headers with this Ruby 3.1.2 script:

    require 'set'

    EXTENSIONS_TO_CHECK = Set['.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift']

    same = 0
    different = 0

    all_files = `git ls-files`.lines.map { |line| line.strip }
    all_files.each do |relative_path|
      if relative_path == 'Pods'
        next
      end

      unless EXTENSIONS_TO_CHECK.include? File.extname(relative_path)
        next
      end

      path = File.expand_path(relative_path)

      contents = File.read(path)
      new_contents = contents.sub(/\/\/\n\/\/  Copyright .*\n\/\/\n\n/, '')

      if contents == new_contents
        same += 1
      else
        different += 1
      end

      File.write(path, new_contents)
    end

    puts "updated #{different} file(s), left #{same} untouched"

I'm sure this script could be improved, but it worked well enough.

Then, I created `Scripts/lint/lint-license-headers` and ran it to auto-
fix a lot of files. This changed the mode of some files, but I think
that's actually desirable. For example,
`SignalServiceKit/src/Util/AppContext.m` previously had a mode of
`0755/-rwxr-xr-x`, and it's now `0644/-rw-r--r--`.

Then I fixed some stragglers and updated the precommit script.

See [a similar change in the Desktop app][0].

[0]: 8bfaf598af
2022-10-13 08:25:37 -05:00

274 lines
15 KiB
Swift

//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import XCTest
import Foundation
import Signal
class BadgeExpirationSheetStateTest: XCTestCase {
typealias State = BadgeExpirationSheetState
private func getBoostBadge(populateAssets: Bool = true) -> ProfileBadge {
let result = try! ProfileBadge(jsonDictionary: [
"id": "BOOST",
"category": "donor",
"name": "A Boost",
"description": "A boost badge!",
"sprites6": ["ldpi.png", "mdpi.png", "hdpi.png", "xhdpi.png", "xxhdpi.png", "xxxhdpi.png"]
])
if populateAssets {
result._testingOnly_populateAssets()
}
return result
}
private func getSubscriptionBadge(populateAssets: Bool = true) -> ProfileBadge {
let result = try! ProfileBadge(jsonDictionary: [
"id": "R_MED",
"category": "donor",
"name": "Subscriber X",
"description": "A subscriber badge!",
"sprites6": ["ldpi.png", "mdpi.png", "hdpi.png", "xhdpi.png", "xxhdpi.png", "xxxhdpi.png"]
])
if populateAssets {
result._testingOnly_populateAssets()
}
return result
}
private func getGiftBadge(populateAssets: Bool = true) -> ProfileBadge {
let result = try! ProfileBadge(jsonDictionary: [
"id": "GIFT",
"category": "donor",
"name": "A Gift",
"description": "A gift badge!",
"sprites6": ["ldpi.png", "mdpi.png", "hdpi.png", "xhdpi.png", "xxhdpi.png", "xxxhdpi.png"]
])
if populateAssets {
result._testingOnly_populateAssets()
}
return result
}
func testBadge() throws {
let badge = getSubscriptionBadge()
let state = State(badge: badge, mode: .subscriptionExpiredBecauseNotRenewed)
XCTAssertIdentical(state.badge, badge)
}
func testTitleText() throws {
let testCases: [(State, String)] = [
(
State(badge: getSubscriptionBadge(),
mode: .subscriptionExpiredBecauseOfChargeFailure(chargeFailure: Subscription.ChargeFailure(code: "insufficient_funds"))),
NSLocalizedString("BADGE_EXPIRED_SUBSCRIPTION_TITLE",
comment: "Title for subscription on the badge expiration sheet.")
),
(
State(badge: getSubscriptionBadge(), mode: .subscriptionExpiredBecauseNotRenewed),
NSLocalizedString("BADGE_EXPIRED_SUBSCRIPTION_TITLE",
comment: "Title for subscription on the badge expiration sheet.")
),
(
State(badge: getSubscriptionBadge(), mode: .boostExpired(hasCurrentSubscription: true)),
NSLocalizedString("BADGE_EXPIRED_BOOST_TITLE",
comment: "Title for boost on the badge expiration sheet.")
),
(
State(badge: getGiftBadge(), mode: .giftBadgeExpired(hasCurrentSubscription: true)),
NSLocalizedString("BADGE_EXPIRED_GIFT_TITLE",
value: "Your Gift Badge Has Expired",
comment: "Title for gift on the badge expiration sheet.")
),
(
State(badge: getGiftBadge(), mode: .giftNotRedeemed(fullName: "")),
NSLocalizedString("GIFT_NOT_REDEEMED_TITLE",
value: "Your Gift Has Expired",
comment: "Title when trying to redeem a gift that's already expired.")
)
]
for (state, expected) in testCases {
XCTAssertEqual(state.titleText, expected)
}
}
func testBody() throws {
let subscriptionBadge = getSubscriptionBadge()
let chargeFailureTestCases: [[String?]: String] = [
["authentication_required"]: NSLocalizedString("DONATION_PAYMENT_ERROR_AUTHENTICATION_REQUIRED",
comment: "Donation payment error for decline failures where authentication is required."),
["approve_with_id"]: NSLocalizedString("DONATION_PAYMENT_ERROR_PAYMENT_CANNOT_BE_AUTHORIZED",
comment: "Donation payment error for decline failures where the payment cannot be authorized."),
["call_issuer"]: NSLocalizedString("DONATION_PAYMENT_ERROR_CALL_ISSUER",
comment: "Donation payment error for decline failures where the user may need to contact their card or bank."),
["card_not_supported"]: NSLocalizedString("DONATION_PAYMENT_ERROR_CARD_NOT_SUPPORTED",
comment: "Donation payment error for decline failures where the card is not supported."),
[
"card_velocity_exceeded", "currency_not_supported", "do_not_honor",
"do_not_try_again", "fraudulent", "generic_decline", "invalid_account",
"invalid_amount", "lost_card", "merchant_blacklist",
"new_account_information_available", "no_action_taken", "not_permitted",
"restricted_card", "revocation_of_all_authorizations",
"revocation_of_authorization", "security_violation", "service_not_allowed",
"stolen_card", "stop_payment_order", "testmode_decline", "transaction_not_allowed",
"try_again_later", "withdrawal_count_limit_exceeded",
"GARBAGE", nil
]: NSLocalizedString("DONATION_PAYMENT_ERROR_OTHER",
comment: "Donation payment error for unspecified decline failures."),
["expired_card"]: NSLocalizedString("DONATION_PAYMENT_ERROR_EXPIRED_CARD",
comment: "Donation payment error for decline failures where the card has expired."),
["incorrect_number"]: NSLocalizedString("DONATION_PAYMENT_ERROR_INCORRECT_CARD_NUMBER",
comment: "Donation payment error for decline failures where the card number is incorrect."),
["incorrect_cvc", "invalid_cvc"]: NSLocalizedString("DONATION_PAYMENT_ERROR_INCORRECT_CARD_VERIFICATION_CODE",
comment: "Donation payment error for decline failures where the card verification code (often called CVV or CVC) is incorrect."),
["insufficient_funds"]: NSLocalizedString("DONATION_PAYMENT_ERROR_INSUFFICIENT_FUNDS",
comment: "Donation payment error for decline failures where the card has insufficient funds."),
["invalid_expiry_month"]: NSLocalizedString("DONATION_PAYMENT_ERROR_INVALID_EXPIRY_MONTH",
comment: "Donation payment error for decline failures where the expiration month on the payment method is incorrect."),
["invalid_expiry_year"]: NSLocalizedString("DONATION_PAYMENT_ERROR_INVALID_EXPIRY_YEAR",
comment: "Donation payment error for decline failures where the expiration year on the payment method is incorrect."),
["invalid_number"]: NSLocalizedString("DONATION_PAYMENT_ERROR_INVALID_NUMBER",
comment: "Donation payment error for decline failures where the card number is incorrect."),
[
"issuer_not_available", "processing_error", "reenter_transaction"
]: NSLocalizedString("DONATION_PAYMENT_ERROR_ISSUER_NOT_AVAILABLE",
comment: "Donation payment error for \"issuer not available\" decline failures. The user should try again or contact their card/bank.")
]
for (codes, expectedSubstring) in chargeFailureTestCases {
for code in codes {
let chargeFailure: Subscription.ChargeFailure
if let code = code {
chargeFailure = Subscription.ChargeFailure(code: code)
} else {
chargeFailure = Subscription.ChargeFailure()
}
let state = State(badge: subscriptionBadge,
mode: .subscriptionExpiredBecauseOfChargeFailure(chargeFailure: chargeFailure))
let body = state.body
XCTAssert(body.text.contains(expectedSubstring))
XCTAssert(body.text.contains(subscriptionBadge.localizedName))
XCTAssertTrue(body.hasLearnMoreLink)
}
}
let otherTestCases: [(State, String, Bool)] = [
(
State(badge: getSubscriptionBadge(), mode: .subscriptionExpiredBecauseNotRenewed),
NSLocalizedString("BADGE_SUBSCRIPTION_EXPIRED_BECAUSE_OF_INACTIVITY_BODY_FORMAT",
comment: "Body of the sheet shown when your subscription is canceled due to inactivity"),
true
),
(
State(badge: getSubscriptionBadge(), mode: .boostExpired(hasCurrentSubscription: false)),
NSLocalizedString("BADGE_EXPIRED_BOOST_BODY",
comment: "String explaining to the user that their boost badge has expired on the badge expiry sheet.")
+ "\n\n"
+ NSLocalizedString("BADGE_EXPIRED_MONTHLY_CALL_TO_ACTION",
comment: "Shown when a non-monthly badge expires to suggest starting a recurring donation."),
false
),
(
State(badge: getSubscriptionBadge(), mode: .boostExpired(hasCurrentSubscription: true)),
NSLocalizedString("BADGE_EXPIRED_BOOST_CURRENT_SUSTAINER_BODY",
comment: "String explaining to the user that their boost badge has expired while they are a current subscription sustainer on the badge expiry sheet."),
false
),
(
State(badge: getGiftBadge(), mode: .giftBadgeExpired(hasCurrentSubscription: false)),
NSLocalizedString(
"BADGE_EXPIRED_GIFT_BODY",
value: "Your gift badge has expired and is no longer available to be displayed on your profile.",
comment: "String explaining to the user that their gift badge has expired. Shown on the badge expiration sheet."
)
+ "\n\n"
+ NSLocalizedString(
"BADGE_EXPIRED_MONTHLY_CALL_TO_ACTION",
comment: "Shown when a non-monthly badge expires to suggest starting a recurring donation."
),
false
),
(
State(badge: getGiftBadge(), mode: .giftBadgeExpired(hasCurrentSubscription: true)),
NSLocalizedString(
"BADGE_EXPIRED_GIFT_BODY",
value: "Your gift badge has expired and is no longer available to be displayed on your profile.",
comment: "String explaining to the user that their gift badge has expired. Shown on the badge expiration sheet."
),
false
),
(
State(badge: getGiftBadge(), mode: .giftNotRedeemed(fullName: "John Doe")),
NSLocalizedString(
"GIFT_NOT_REDEEMED_BODY_FORMAT",
value: "Your gift from %@ has expired and can no longer be redeemed.",
comment: "Shown when trying to redeem a gift that's already expired. Embeds {{contact name}}."
).replacingOccurrences(of: "%@", with: "John Doe"),
false
)
]
for (state, expectedFormat, expectedHasLearnMore) in otherTestCases {
let body = state.body
XCTAssertEqual(body.text, String(format: expectedFormat, state.badge.localizedName))
XCTAssertEqual(body.hasLearnMoreLink, expectedHasLearnMore)
}
}
func testActionButton() throws {
let testCases: [(State, State.ActionButton)] = [
(
State(badge: getSubscriptionBadge(),
mode: .subscriptionExpiredBecauseOfChargeFailure(chargeFailure: Subscription.ChargeFailure(code: "insufficient_funds"))),
State.ActionButton(action: .dismiss, text: CommonStrings.okayButton, hasNotNow: false)
),
(
State(badge: getSubscriptionBadge(), mode: .subscriptionExpiredBecauseNotRenewed),
State.ActionButton(action: .openSubscriptionsView,
text: NSLocalizedString("BADGE_EXPIRED_SUBSCRIPTION_RENEWAL_BUTTON",
comment: "Button text when a badge expires, asking you to renew your subscription"),
hasNotNow: true)
),
(
State(badge: getSubscriptionBadge(), mode: .boostExpired(hasCurrentSubscription: false)),
State.ActionButton(action: .openSubscriptionsView,
text: NSLocalizedString("BADGE_EXPIRED_BOOST_RENEWAL_BUTTON",
comment: "Button title for boost on the badge expiration sheet, used if the user is not already a sustainer."),
hasNotNow: true)
),
(
State(badge: getSubscriptionBadge(), mode: .boostExpired(hasCurrentSubscription: true)),
State.ActionButton(action: .openBoostView,
text: NSLocalizedString("BADGE_EXPIRED_BOOST_RENEWAL_BUTTON_SUSTAINER",
comment: "Button title for boost on the badge expiration sheet, used if the user is already a sustainer."),
hasNotNow: true)
),
(
State(badge: getGiftBadge(), mode: .giftBadgeExpired(hasCurrentSubscription: false)),
State.ActionButton(action: .openSubscriptionsView,
text: NSLocalizedString("BADGE_EXPIRED_RENEWAL_MONTHLY",
value: "Make a Monthly Donation",
comment: "Button title to donate monthly on the badge expiration sheet."),
hasNotNow: true)
),
(
State(badge: getGiftBadge(), mode: .giftBadgeExpired(hasCurrentSubscription: true)),
State.ActionButton(action: .dismiss, text: CommonStrings.okButton, hasNotNow: false)
),
(
State(badge: getGiftBadge(), mode: .giftNotRedeemed(fullName: "")),
State.ActionButton(action: .dismiss, text: CommonStrings.okButton, hasNotNow: false)
)
]
for (state, expectedActionButton) in testCases {
XCTAssertEqual(state.actionButton.action, expectedActionButton.action)
XCTAssertEqual(state.actionButton.text, expectedActionButton.text)
XCTAssertEqual(state.actionButton.hasNotNow, expectedActionButton.hasNotNow)
}
}
}