From 0cc45abbff4d6953ed72d320f3954bd9ab0ee57c Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Wed, 31 Jul 2024 11:12:51 -0700 Subject: [PATCH] [in_app_purchase_storekit] convert TranslatorTests to swift (#7232) Part of https://github.com/flutter/flutter/issues/151624 --- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../darwin/Classes/FIAObjectTranslator.m | 14 +- .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../example/ios/RunnerTests/TranslatorTests.m | 1 - .../ios/RunnerTests/TranslatorTests.swift | 1 + .../macos/Runner.xcodeproj/project.pbxproj | 8 +- .../macos/RunnerTests/TranslatorTests.m | 1 - .../macos/RunnerTests/TranslatorTests.swift | 1 + .../InAppPurchasePluginTests.swift | 2 +- .../shared/RunnerTests/TranslatorTests.m | 518 ---------------- .../shared/RunnerTests/TranslatorTests.swift | 551 ++++++++++++++++++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- 12 files changed, 573 insertions(+), 538 deletions(-) delete mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m create mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.swift delete mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.m create mode 120000 packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.swift delete mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.swift diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 982f192746..af76d756ce 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.17+3 + +* Converts TranslatorTests to swift. + ## 0.3.17+2 * Converts FIAPPaymentQueueDeleteTests to swift. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index b9509eb4be..bf05976534 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -118,11 +118,9 @@ return nil; } NSMutableDictionary *map = [[NSMutableDictionary alloc] init]; - [map setObject:[locale objectForKey:NSLocaleCurrencySymbol] ?: [NSNull null] - forKey:@"currencySymbol"]; - [map setObject:[locale objectForKey:NSLocaleCurrencyCode] ?: [NSNull null] - forKey:@"currencyCode"]; - [map setObject:[locale objectForKey:NSLocaleCountryCode] ?: [NSNull null] forKey:@"countryCode"]; + [map setObject:locale.currencySymbol ?: [NSNull null] forKey:@"currencySymbol"]; + [map setObject:locale.currencyCode ?: [NSNull null] forKey:@"currencyCode"]; + [map setObject:locale.countryCode ?: [NSNull null] forKey:@"countryCode"]; return map; } @@ -509,9 +507,9 @@ [pigeonProducts addObject:[self convertProductToPigeon:product]]; }; - SKProductsResponseMessage *msg = - [SKProductsResponseMessage makeWithProducts:pigeonProducts - invalidProductIdentifiers:productsResponse.invalidProductIdentifiers]; + SKProductsResponseMessage *msg = [SKProductsResponseMessage + makeWithProducts:pigeonProducts + invalidProductIdentifiers:productsResponse.invalidProductIdentifiers ?: @[]]; return msg; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index 3c0fb772c8..c51ca47549 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -23,7 +23,7 @@ F27694112C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694102C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift */; }; F27694172C49DBCA00277144 /* FIATransactionCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */; }; F295AD3A2C1256DD0067C78A /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = F295AD392C1256DD0067C78A /* Stubs.m */; }; - F295AD462C1256F50067C78A /* TranslatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F295AD402C1256F50067C78A /* TranslatorTests.m */; }; + F2D5272A2C583C4A00C137C7 /* TranslatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527292C583C4A00C137C7 /* TranslatorTests.swift */; }; F2D5271A2C50627500C137C7 /* PaymentQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527192C50627500C137C7 /* PaymentQueueTests.swift */; }; /* End PBXBuildFile section */ @@ -81,7 +81,7 @@ F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FIATransactionCacheTests.swift; path = ../../shared/RunnerTests/FIATransactionCacheTests.swift; sourceTree = ""; }; F295AD362C1251300067C78A /* Stubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stubs.h; path = ../../shared/RunnerTests/Stubs.h; sourceTree = ""; }; F295AD392C1256DD0067C78A /* Stubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Stubs.m; path = ../../shared/RunnerTests/Stubs.m; sourceTree = ""; }; - F295AD402C1256F50067C78A /* TranslatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TranslatorTests.m; path = ../../shared/RunnerTests/TranslatorTests.m; sourceTree = ""; }; + F2D527292C583C4A00C137C7 /* TranslatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TranslatorTests.swift; path = ../../shared/RunnerTests/TranslatorTests.swift; sourceTree = ""; }; F2D527192C50627500C137C7 /* PaymentQueueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PaymentQueueTests.swift; path = ../../shared/RunnerTests/PaymentQueueTests.swift; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; /* End PBXFileReference section */ @@ -186,12 +186,12 @@ A59001A521E69658004A3E5E /* RunnerTests */ = { isa = PBXGroup; children = ( + F2D527292C583C4A00C137C7 /* TranslatorTests.swift */, F2D527192C50627500C137C7 /* PaymentQueueTests.swift */, F27694162C49DBCA00277144 /* FIATransactionCacheTests.swift */, F27694102C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift */, F24C45E12C409D41000C6C72 /* InAppPurchasePluginTests.swift */, F276940A2C47268700277144 /* ProductRequestHandlerTests.swift */, - F295AD402C1256F50067C78A /* TranslatorTests.m */, F295AD392C1256DD0067C78A /* Stubs.m */, F295AD362C1251300067C78A /* Stubs.h */, F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */, @@ -437,8 +437,8 @@ F24C45E22C409D42000C6C72 /* InAppPurchasePluginTests.swift in Sources */, F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */, F276940B2C47268700277144 /* ProductRequestHandlerTests.swift in Sources */, - F295AD462C1256F50067C78A /* TranslatorTests.m in Sources */, F295AD3A2C1256DD0067C78A /* Stubs.m in Sources */, + F2D5272A2C583C4A00C137C7 /* TranslatorTests.swift in Sources */, F27694172C49DBCA00277144 /* FIATransactionCacheTests.swift in Sources */, F27694112C49BF6F00277144 /* FIAPPaymentQueueDeleteTests.swift in Sources */, ); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m deleted file mode 120000 index ac58ed9697..0000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/TranslatorTests.m \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.swift new file mode 120000 index 0000000000..743854417c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.swift @@ -0,0 +1 @@ +../../shared/RunnerTests/TranslatorTests.swift \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index 8e29bf1bb9..3407adfdc9 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -32,9 +32,9 @@ F27694132C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694122C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift */; }; F27694192C49DBE800277144 /* FIATransactionCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F27694182C49DBE800277144 /* FIATransactionCacheTests.swift */; }; F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C3A7402BD9D33D000D35F2 /* Stubs.swift */; }; + F2D527262C583C1C00C137C7 /* TranslatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527252C583C1C00C137C7 /* TranslatorTests.swift */; }; F2D5271E2C50645600C137C7 /* PaymentQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D5271D2C50645600C137C7 /* PaymentQueueTests.swift */; }; F79BDC1C2905FC3200E3999D /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC1B2905FC3200E3999D /* Stubs.m */; }; - F79BDC1E2905FC3900E3999D /* TranslatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC1D2905FC3900E3999D /* TranslatorTests.m */; }; F8270DE1AEF80A8CF2C45688 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 045C0F7D19875EDA98DF0B7F /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ @@ -97,11 +97,11 @@ F27694182C49DBE800277144 /* FIATransactionCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FIATransactionCacheTests.swift; path = ../../shared/RunnerTests/FIATransactionCacheTests.swift; sourceTree = ""; }; F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; F2C3A7402BD9D33D000D35F2 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; + F2D527252C583C1C00C137C7 /* TranslatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TranslatorTests.swift; path = ../../shared/RunnerTests/TranslatorTests.swift; sourceTree = ""; }; F2D5271D2C50645600C137C7 /* PaymentQueueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PaymentQueueTests.swift; path = ../../shared/RunnerTests/PaymentQueueTests.swift; sourceTree = ""; }; F700DD0228E652A10004836B /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F79BDC152905FC0500E3999D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../../shared/RunnerTests/Info.plist; sourceTree = ""; }; F79BDC1B2905FC3200E3999D /* Stubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Stubs.m; path = ../../shared/RunnerTests/Stubs.m; sourceTree = ""; }; - F79BDC1D2905FC3900E3999D /* TranslatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TranslatorTests.m; path = ../../shared/RunnerTests/TranslatorTests.m; sourceTree = ""; }; F79BDC1F2906023C00E3999D /* Stubs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Stubs.h; path = ../../shared/RunnerTests/Stubs.h; sourceTree = ""; }; F8E47120B1A50B8A1C480FDB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -217,6 +217,7 @@ F700DD0328E652A10004836B /* RunnerTests */ = { isa = PBXGroup; children = ( + F2D527252C583C1C00C137C7 /* TranslatorTests.swift */, F2D5271D2C50645600C137C7 /* PaymentQueueTests.swift */, F27694182C49DBE800277144 /* FIATransactionCacheTests.swift */, F27694122C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift */, @@ -225,7 +226,6 @@ F79BDC1F2906023C00E3999D /* Stubs.h */, F79BDC152905FC0500E3999D /* Info.plist */, F79BDC1B2905FC3200E3999D /* Stubs.m */, - F79BDC1D2905FC3900E3999D /* TranslatorTests.m */, F2C3A7402BD9D33D000D35F2 /* Stubs.swift */, F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */, ); @@ -465,10 +465,10 @@ files = ( F2D5271E2C50645600C137C7 /* PaymentQueueTests.swift in Sources */, F24C45E42C409D87000C6C72 /* InAppPurchasePluginTests.swift in Sources */, - F79BDC1E2905FC3900E3999D /* TranslatorTests.m in Sources */, F79BDC1C2905FC3200E3999D /* Stubs.m in Sources */, F27694092C4724B200277144 /* ProductRequestHandlerTests.swift in Sources */, F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */, + F2D527262C583C1C00C137C7 /* TranslatorTests.swift in Sources */, F27694192C49DBE800277144 /* FIATransactionCacheTests.swift in Sources */, F27694132C49BF7B00277144 /* FIAPPaymentQueueDeleteTests.swift in Sources */, ); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.m deleted file mode 120000 index ac58ed9697..0000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.m +++ /dev/null @@ -1 +0,0 @@ -../../shared/RunnerTests/TranslatorTests.m \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.swift new file mode 120000 index 0000000000..743854417c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/RunnerTests/TranslatorTests.swift @@ -0,0 +1 @@ +../../shared/RunnerTests/TranslatorTests.swift \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift index d8cce01387..a074f13316 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift @@ -82,7 +82,7 @@ final class InAppPurchasePluginTests: XCTestCase { } XCTAssertEqual(unwrappedProducts.count, 1) - XCTAssertEqual(response.invalidProductIdentifiers, nil) + XCTAssertEqual(response.invalidProductIdentifiers, []) XCTAssertEqual(unwrappedProducts[0].productIdentifier, "123") expectation.fulfill() } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m deleted file mode 100644 index 7ffe4c6ac7..0000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import "Stubs.h" - -@import in_app_purchase_storekit; - -@interface TranslatorTest : XCTestCase - -@property(strong, nonatomic) NSDictionary *periodMap; -@property(strong, nonatomic) NSMutableDictionary *discountMap; -@property(strong, nonatomic) NSMutableDictionary *discountMissingIdentifierMap; -@property(strong, nonatomic) NSMutableDictionary *productMap; -@property(strong, nonatomic) NSDictionary *productResponseMap; -@property(strong, nonatomic) NSDictionary *paymentMap; -@property(copy, nonatomic) NSDictionary *paymentDiscountMap; -@property(strong, nonatomic) NSDictionary *transactionMap; -@property(strong, nonatomic) NSDictionary *errorMap; -@property(strong, nonatomic) NSDictionary *localeMap; -@property(strong, nonatomic) NSDictionary *storefrontMap; -@property(strong, nonatomic) NSDictionary *storefrontAndPaymentTransactionMap; -@end - -@implementation TranslatorTest -- (void)setUp { - self.periodMap = @{@"numberOfUnits" : @(0), @"unit" : @(0)}; - - self.discountMap = [[NSMutableDictionary alloc] initWithDictionary:@{ - @"price" : @"1", - @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], - @"numberOfPeriods" : @1, - @"subscriptionPeriod" : self.periodMap, - @"paymentMode" : @1, - }]; - if (@available(iOS 12.2, *)) { - self.discountMap[@"identifier"] = @"test offer id"; - self.discountMap[@"type"] = @(SKProductDiscountTypeIntroductory); - } - self.discountMissingIdentifierMap = [[NSMutableDictionary alloc] initWithDictionary:@{ - @"price" : @"1", - @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], - @"numberOfPeriods" : @1, - @"subscriptionPeriod" : self.periodMap, - @"paymentMode" : @1, - @"identifier" : [NSNull null], - @"type" : @0, - }]; - - self.productMap = [[NSMutableDictionary alloc] initWithDictionary:@{ - @"price" : @"1", - @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], - @"productIdentifier" : @"123", - @"localizedTitle" : @"title", - @"localizedDescription" : @"des", - }]; - self.productMap[@"subscriptionPeriod"] = self.periodMap; - self.productMap[@"introductoryPrice"] = self.discountMap; - if (@available(iOS 12.2, *)) { - self.productMap[@"discounts"] = @[ self.discountMap ]; - } - self.productMap[@"subscriptionGroupIdentifier"] = @"com.group"; - - self.productResponseMap = - @{@"products" : @[ self.productMap ], @"invalidProductIdentifiers" : @[]}; - self.paymentMap = @{ - @"productIdentifier" : @"123", - @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", - @"quantity" : @(2), - @"applicationUsername" : @"app user name", - @"simulatesAskToBuyInSandbox" : @(NO) - }; - self.paymentDiscountMap = @{ - @"identifier" : @"payment_discount_identifier", - @"keyIdentifier" : @"payment_discount_key_identifier", - @"nonce" : @"d18981e0-9003-4365-98a2-4b90e3b62c52", - @"signature" : @"this is a encrypted signature", - @"timestamp" : @([NSDate date].timeIntervalSince1970), - }; - NSDictionary *originalTransactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - @"originalTransaction" : [NSNull null], - }; - self.transactionMap = @{ - @"transactionIdentifier" : @"567", - @"transactionState" : @(SKPaymentTransactionStatePurchasing), - @"payment" : [NSNull null], - @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" - code:123 - userInfo:@{}]], - @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), - @"originalTransaction" : originalTransactionMap, - }; - self.errorMap = @{ - @"code" : @(123), - @"domain" : @"test_domain", - @"userInfo" : @{ - @"key" : @"value", - } - }; - self.storefrontMap = @{ - @"countryCode" : @"USA", - @"identifier" : @"unique_identifier", - }; - - self.storefrontAndPaymentTransactionMap = @{ - @"storefront" : self.storefrontMap, - @"transaction" : self.transactionMap, - }; -} - -- (void)testSKProductSubscriptionPeriodStubToMap { - SKProductSubscriptionPeriodStub *period = - [[SKProductSubscriptionPeriodStub alloc] initWithMap:self.periodMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKProductSubscriptionPeriod:period]; - XCTAssertEqualObjects(map, self.periodMap); -} - -- (void)testSKProductDiscountStubToMap { - SKProductDiscountStub *discount = [[SKProductDiscountStub alloc] initWithMap:self.discountMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKProductDiscount:discount]; - XCTAssertEqualObjects(map, self.discountMap); -} - -- (void)testProductToMap { - SKProductStub *product = [[SKProductStub alloc] initWithMap:self.productMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKProduct:product]; - XCTAssertEqualObjects(map, self.productMap); -} - -- (void)testProductResponseToMap { - SKProductsResponseStub *response = - [[SKProductsResponseStub alloc] initWithMap:self.productResponseMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKProductsResponse:response]; - XCTAssertEqualObjects(map, self.productResponseMap); -} - -- (void)testPaymentToMap { - SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:self.paymentMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKPayment:payment]; - XCTAssertEqualObjects(map, self.paymentMap); -} - -- (void)testPaymentTransactionToMap { - // payment is not KVC, cannot test payment field. - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:self.transactionMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]; - XCTAssertEqualObjects(map, self.transactionMap); -} - -- (void)testError { - NSErrorStub *error = [[NSErrorStub alloc] initWithMap:self.errorMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromNSError:error]; - XCTAssertEqualObjects(map, self.errorMap); -} - -- (void)testErrorWithNSNumberAsUserInfo { - NSError *error = [NSError errorWithDomain:SKErrorDomain code:3 userInfo:@{@"key" : @42}]; - NSDictionary *expectedMap = - @{@"domain" : SKErrorDomain, @"code" : @3, @"userInfo" : @{@"key" : @42}}; - NSDictionary *map = [FIAObjectTranslator getMapFromNSError:error]; - XCTAssertEqualObjects(expectedMap, map); -} - -- (void)testErrorWithMultipleUnderlyingErrors { - NSError *underlyingErrorOne = [NSError errorWithDomain:SKErrorDomain code:2 userInfo:nil]; - NSError *underlyingErrorTwo = [NSError errorWithDomain:SKErrorDomain code:1 userInfo:nil]; - NSError *mainError = [NSError - errorWithDomain:SKErrorDomain - code:3 - userInfo:@{@"underlyingErrors" : @[ underlyingErrorOne, underlyingErrorTwo ]}]; - NSDictionary *expectedMap = @{ - @"domain" : SKErrorDomain, - @"code" : @3, - @"userInfo" : @{ - @"underlyingErrors" : @[ - @{@"domain" : SKErrorDomain, @"code" : @2, @"userInfo" : @{}}, - @{@"domain" : SKErrorDomain, @"code" : @1, @"userInfo" : @{}} - ] - } - }; - NSDictionary *map = [FIAObjectTranslator getMapFromNSError:mainError]; - XCTAssertEqualObjects(expectedMap, map); -} - -- (void)testErrorWithNestedUnderlyingError { - NSError *underlyingError = [NSError errorWithDomain:SKErrorDomain code:2 userInfo:nil]; - NSError *mainError = - [NSError errorWithDomain:SKErrorDomain - code:3 - userInfo:@{@"nesting" : @{@"underlyingError" : underlyingError}}]; - NSDictionary *expectedMap = @{ - @"domain" : SKErrorDomain, - @"code" : @3, - @"userInfo" : @{ - @"nesting" : @{ - @"underlyingError" : @{@"domain" : SKErrorDomain, @"code" : @2, @"userInfo" : @{}}, - - } - } - }; - NSDictionary *map = [FIAObjectTranslator getMapFromNSError:mainError]; - XCTAssertEqualObjects(expectedMap, map); -} - -- (void)testErrorWithUnsupportedUserInfo { - NSError *error = [NSError errorWithDomain:SKErrorDomain - code:3 - userInfo:@{@"user_info" : [[NSObject alloc] init]}]; - NSDictionary *expectedMap = @{ - @"domain" : SKErrorDomain, - @"code" : @3, - @"userInfo" : @{ - @"user_info" : [NSString - stringWithFormat: - @"Unable to encode native userInfo object of type %@ to map. Please submit an " - @"issue at https://github.com/flutter/flutter/issues/new with the title " - @"\"[in_app_purchase_storekit] Unable to encode userInfo of type %@\" and add " - @"reproduction steps and the error details in the description field.", - [NSObject class], [NSObject class]] - } - }; - NSDictionary *map = [FIAObjectTranslator getMapFromNSError:error]; - XCTAssertEqualObjects(expectedMap, map); -} - -- (void)testLocaleToMap { - NSLocale *system = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; - NSDictionary *map = [FIAObjectTranslator getMapFromNSLocale:system]; - XCTAssertEqualObjects(map[@"currencySymbol"], system.currencySymbol); - XCTAssertEqualObjects(map[@"countryCode"], system.countryCode); -} - -- (void)testSKStorefrontToMap { - if (@available(iOS 13.0, *)) { - SKStorefront *storefront = [[SKStorefrontStub alloc] initWithMap:self.storefrontMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKStorefront:storefront]; - XCTAssertEqualObjects(map, self.storefrontMap); - } -} - -- (void)testSKStorefrontAndSKPaymentTransactionToMap { - if (@available(iOS 13.0, *)) { - SKStorefront *storefront = [[SKStorefrontStub alloc] initWithMap:self.storefrontMap]; - SKPaymentTransaction *transaction = - [[SKPaymentTransactionStub alloc] initWithMap:self.transactionMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKStorefront:storefront - andSKPaymentTransaction:transaction]; - XCTAssertEqualObjects(map, self.storefrontAndPaymentTransactionMap); - } -} - -- (void)testSKPaymentDiscountFromMap { - if (@available(iOS 12.2, *)) { - NSString *error = nil; - SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:self.paymentDiscountMap withError:&error]; - - XCTAssertEqual(paymentDiscount.identifier, self.paymentDiscountMap[@"identifier"]); - XCTAssertEqual(paymentDiscount.keyIdentifier, self.paymentDiscountMap[@"keyIdentifier"]); - XCTAssertEqualObjects(paymentDiscount.nonce, - [[NSUUID alloc] initWithUUIDString:self.paymentDiscountMap[@"nonce"]]); - XCTAssertEqual(paymentDiscount.signature, self.paymentDiscountMap[@"signature"]); - XCTAssertEqual(paymentDiscount.timestamp, self.paymentDiscountMap[@"timestamp"]); - } -} - -- (void)testSKPaymentDiscountFromMapMissingIdentifier { - if (@available(iOS 12.2, *)) { - NSArray *invalidValues = @[ [NSNull null], @(1), @"" ]; - - for (id value in invalidValues) { - NSDictionary *discountMap = @{ - @"identifier" : value, - @"keyIdentifier" : @"payment_discount_key_identifier", - @"nonce" : @"d18981e0-9003-4365-98a2-4b90e3b62c52", - @"signature" : @"this is a encrypted signature", - @"timestamp" : @([NSDate date].timeIntervalSince1970), - }; - - NSString *error = nil; - [FIAObjectTranslator getSKPaymentDiscountFromMap:discountMap withError:&error]; - - XCTAssertNotNil(error); - XCTAssertEqualObjects( - error, @"When specifying a payment discount the 'identifier' field is mandatory."); - } - } -} - -- (void)testGetMapFromSKProductDiscountMissingIdentifier { - if (@available(iOS 12.2, *)) { - SKProductDiscountStub *discount = - [[SKProductDiscountStub alloc] initWithMap:self.discountMissingIdentifierMap]; - NSDictionary *map = [FIAObjectTranslator getMapFromSKProductDiscount:discount]; - XCTAssertEqualObjects(map, self.discountMissingIdentifierMap); - } -} - -- (void)testSKPaymentDiscountFromMapMissingKeyIdentifier { - if (@available(iOS 12.2, *)) { - NSArray *invalidValues = @[ [NSNull null], @(1), @"" ]; - - for (id value in invalidValues) { - NSDictionary *discountMap = @{ - @"identifier" : @"payment_discount_identifier", - @"keyIdentifier" : value, - @"nonce" : @"d18981e0-9003-4365-98a2-4b90e3b62c52", - @"signature" : @"this is a encrypted signature", - @"timestamp" : @([NSDate date].timeIntervalSince1970), - }; - - NSString *error = nil; - [FIAObjectTranslator getSKPaymentDiscountFromMap:discountMap withError:&error]; - - XCTAssertNotNil(error); - XCTAssertEqualObjects( - error, @"When specifying a payment discount the 'keyIdentifier' field is mandatory."); - } - } -} - -- (void)testSKPaymentDiscountFromMapMissingNonce { - if (@available(iOS 12.2, *)) { - NSArray *invalidValues = @[ [NSNull null], @(1), @"" ]; - - for (id value in invalidValues) { - NSDictionary *discountMap = @{ - @"identifier" : @"payment_discount_identifier", - @"keyIdentifier" : @"payment_discount_key_identifier", - @"nonce" : value, - @"signature" : @"this is a encrypted signature", - @"timestamp" : @([NSDate date].timeIntervalSince1970), - }; - - NSString *error = nil; - [FIAObjectTranslator getSKPaymentDiscountFromMap:discountMap withError:&error]; - - XCTAssertNotNil(error); - XCTAssertEqualObjects(error, - @"When specifying a payment discount the 'nonce' field is mandatory."); - } - } -} - -- (void)testSKPaymentDiscountFromMapMissingSignature { - if (@available(iOS 12.2, *)) { - NSArray *invalidValues = @[ [NSNull null], @(1), @"" ]; - - for (id value in invalidValues) { - NSDictionary *discountMap = @{ - @"identifier" : @"payment_discount_identifier", - @"keyIdentifier" : @"payment_discount_key_identifier", - @"nonce" : @"d18981e0-9003-4365-98a2-4b90e3b62c52", - @"signature" : value, - @"timestamp" : @([NSDate date].timeIntervalSince1970), - }; - - NSString *error = nil; - [FIAObjectTranslator getSKPaymentDiscountFromMap:discountMap withError:&error]; - - XCTAssertNotNil(error); - XCTAssertEqualObjects( - error, @"When specifying a payment discount the 'signature' field is mandatory."); - } - } -} - -- (void)testSKPaymentDiscountFromMapMissingTimestamp { - if (@available(iOS 12.2, *)) { - NSArray *invalidValues = @[ [NSNull null], @"", @(-1) ]; - - for (id value in invalidValues) { - NSDictionary *discountMap = @{ - @"identifier" : @"payment_discount_identifier", - @"keyIdentifier" : @"payment_discount_key_identifier", - @"nonce" : @"d18981e0-9003-4365-98a2-4b90e3b62c52", - @"signature" : @"this is a encrypted signature", - @"timestamp" : value, - }; - - NSString *error = nil; - [FIAObjectTranslator getSKPaymentDiscountFromMap:discountMap withError:&error]; - - XCTAssertNotNil(error); - XCTAssertEqualObjects( - error, @"When specifying a payment discount the 'timestamp' field is mandatory."); - } - } -} - -- (void)testSKPaymentDiscountFromMapOverflowingTimestamp { - if (@available(iOS 12.2, *)) { - NSDictionary *discountMap = @{ - @"identifier" : @"payment_discount_identifier", - @"keyIdentifier" : @"payment_discount_key_identifier", - @"nonce" : @"d18981e0-9003-4365-98a2-4b90e3b62c52", - @"signature" : @"this is a encrypted signature", - @"timestamp" : @1665044583595, // timestamp 2022 Oct - }; - NSString *error = nil; - SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:discountMap withError:&error]; - XCTAssertNil(error); - XCTAssertNotNil(paymentDiscount); - XCTAssertEqual(paymentDiscount.identifier, discountMap[@"identifier"]); - XCTAssertEqual(paymentDiscount.keyIdentifier, discountMap[@"keyIdentifier"]); - XCTAssertEqualObjects(paymentDiscount.nonce, - [[NSUUID alloc] initWithUUIDString:discountMap[@"nonce"]]); - XCTAssertEqual(paymentDiscount.signature, discountMap[@"signature"]); - XCTAssertEqual(paymentDiscount.timestamp, discountMap[@"timestamp"]); - } -} - -- (void)testSKPaymentDiscountConvertToPigeon { - if (@available(iOS 12.2, *)) { - NSString *error = nil; - SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:self.paymentDiscountMap withError:&error]; - SKPaymentDiscountMessage *paymentDiscountPigeon = - [FIAObjectTranslator convertPaymentDiscountToPigeon:paymentDiscount]; - - XCTAssertNotNil(paymentDiscountPigeon); - XCTAssertEqual(paymentDiscount.identifier, paymentDiscountPigeon.identifier); - XCTAssertEqual(paymentDiscount.keyIdentifier, paymentDiscount.keyIdentifier); - XCTAssertEqualObjects(paymentDiscount.nonce, - [[NSUUID alloc] initWithUUIDString:paymentDiscountPigeon.nonce]); - XCTAssertEqual(paymentDiscount.signature, paymentDiscountPigeon.signature); - XCTAssertEqual([paymentDiscount.timestamp intValue], paymentDiscountPigeon.timestamp); - } -} - -- (void)testSKErrorConvertToPigeon { - NSError *error = [NSError errorWithDomain:SKErrorDomain code:3 userInfo:@{@"key" : @42}]; - SKErrorMessage *msg = [SKErrorMessage makeWithCode:3 - domain:SKErrorDomain - userInfo:@{@"key" : @42}]; - - SKErrorMessage *skerror = [FIAObjectTranslator convertSKErrorToPigeon:error]; - XCTAssertEqual(skerror.domain, msg.domain); - XCTAssertEqual(skerror.code, msg.code); - XCTAssertEqualObjects(skerror.userInfo, msg.userInfo); -} - -- (void)testSKPaymentConvertToPigeon { - if (@available(iOS 12.2, *)) { - SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:self.paymentMap]; - SKPaymentMessage *msg = [FIAObjectTranslator convertPaymentToPigeon:payment]; - - XCTAssertEqual(payment.productIdentifier, msg.productIdentifier); - XCTAssertEqualObjects(payment.requestData, - [msg.requestData dataUsingEncoding:NSUTF8StringEncoding]); - XCTAssertEqual(payment.quantity, msg.quantity); - XCTAssertEqual(payment.applicationUsername, msg.applicationUsername); - XCTAssertEqual(payment.simulatesAskToBuyInSandbox, msg.simulatesAskToBuyInSandbox); - } -} - -- (void)testSKPaymentTransactionConvertToPigeon { - SKPaymentTransactionStub *paymentTransaction = - [[SKPaymentTransactionStub alloc] initWithMap:self.transactionMap]; - - SKPaymentTransactionMessage *msg = - [FIAObjectTranslator convertTransactionToPigeon:paymentTransaction]; - - XCTAssertEqual(msg.payment, NULL); - XCTAssertEqual(msg.transactionState, SKPaymentTransactionStateMessagePurchasing); - XCTAssertEqual(paymentTransaction.transactionDate, - [NSDate dateWithTimeIntervalSince1970:[msg.transactionTimeStamp doubleValue]]); - XCTAssertEqual(paymentTransaction.transactionIdentifier, msg.transactionIdentifier); -} - -- (void)testSKProductResponseCovertToPigeon { - SKProductsResponseStub *response = - [[SKProductsResponseStub alloc] initWithMap:self.productResponseMap]; - SKProductsResponseMessage *responseMsg = - [FIAObjectTranslator convertProductsResponseToPigeon:response]; - - XCTAssertEqual(responseMsg.products.count, 1); - XCTAssertEqual(responseMsg.invalidProductIdentifiers.count, 0); - - SKProductMessage *productMsg = responseMsg.products[0]; - - // These values are being set in productResponseMap in setUp() - XCTAssertEqualObjects(productMsg.price, @"1"); - XCTAssertEqualObjects(productMsg.productIdentifier, @"123"); - XCTAssertEqualObjects(productMsg.localizedTitle, @"title"); - XCTAssertEqualObjects(productMsg.localizedDescription, @"des"); - XCTAssertEqualObjects(productMsg.subscriptionGroupIdentifier, @"com.group"); - - SKPriceLocaleMessage *localeMsg = productMsg.priceLocale; - SKProductSubscriptionPeriodMessage *subPeriod = productMsg.subscriptionPeriod; - SKProductDiscountMessage *introDiscount = productMsg.introductoryPrice; - NSArray *discounts = productMsg.discounts; - - XCTAssertEqualObjects(localeMsg.countryCode, nil); - XCTAssertEqualObjects(localeMsg.currencyCode, nil); - XCTAssertEqualObjects(localeMsg.currencySymbol, @"\u00a4"); - - XCTAssertEqual(subPeriod.unit, SKSubscriptionPeriodUnitMessageDay); - XCTAssertEqual(subPeriod.numberOfUnits, 0); - - XCTAssertEqualObjects(introDiscount.price, @"1"); - XCTAssertEqual(introDiscount.numberOfPeriods, 1); - XCTAssertEqual(introDiscount.paymentMode, SKProductDiscountPaymentModeMessagePayUpFront); - - XCTAssertEqual(discounts.count, 1); -} - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.swift new file mode 100644 index 0000000000..204b2f2dbf --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.swift @@ -0,0 +1,551 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit +import XCTest + +@testable import in_app_purchase_storekit + +final class ObjectTranslatorTest: XCTestCase { + private var periodMap: [String: Any] { + ["numberOfUnits": 0, "unit": 0] + } + private var discountMap: [String: Any] { + var map: [String: Any] = [ + "price": "1", + "priceLocale": FIAObjectTranslator.getMapFrom(NSLocale.system), + "numberOfPeriods": 1, + "subscriptionPeriod": periodMap, + "paymentMode": 1, + ] + if #available(iOS 12.2, *) { + map["identifier"] = "test offer id" + // Type is being instantiated like this because of Swift naming weirdness + let type: SKProductDiscount.`Type` = .introductory + map["type"] = type.rawValue + } + return map + } + private var discountMissingIdentifierMap: [String: Any] { + [ + "price": "1", + "priceLocale": FIAObjectTranslator.getMapFrom(NSLocale.system), + "numberOfPeriods": 1, + "subscriptionPeriod": periodMap, + "paymentMode": 1, + "identifier": NSNull(), + "type": 0, + ] + } + private var productMap: [String: Any] { + var map: [String: Any] = [ + "price": "1", + "priceLocale": FIAObjectTranslator.getMapFrom(NSLocale.system), + "productIdentifier": "123", + "localizedTitle": "title", + "localizedDescription": "des", + "subscriptionPeriod": periodMap, + "introductoryPrice": discountMap, + "subscriptionGroupIdentifier": "com.group", + ] + if #available(iOS 12.2, *) { + map["discounts"] = [discountMap] + } + return map + } + private var productResponseMap: [String: Any] { + ["products": [productMap], "invalidProductIdentifiers": []] + } + private var paymentMap: [String: Any] { + [ + "productIdentifier": "123", + "requestData": "abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", + "quantity": 2, + "applicationUsername": "app user name", + "simulatesAskToBuyInSandbox": false, + ] + } + private var paymentDiscountMap: [String: Any] { + [ + "identifier": "payment_discount_identifier", + "keyIdentifier": "payment_discount_key_identifier", + "nonce": "d18981e0-9003-4365-98a2-4b90e3b62c52", + "signature": "this is an encrypted signature", + "timestamp": Int(Date().timeIntervalSince1970), + ] + } + private var transactionMap: [String: Any] { + [ + "transactionIdentifier": "567", + "transactionState": SKPaymentTransactionState.purchasing.rawValue, + "payment": NSNull(), + "error": FIAObjectTranslator.getMapFrom( + NSError(domain: "test_stub", code: 123, userInfo: [:])), + "transactionTimeStamp": Int(Date().timeIntervalSince1970), + "originalTransaction": originalTransactionMap, + ] + } + private var originalTransactionMap: [String: Any] { + [ + "transactionIdentifier": "567", + "transactionState": SKPaymentTransactionState.purchasing.rawValue, + "payment": NSNull(), + "error": FIAObjectTranslator.getMapFrom( + NSError(domain: "test_stub", code: 123, userInfo: [:])), + "transactionTimeStamp": Int(Date().timeIntervalSince1970), + "originalTransaction": NSNull(), + ] + } + private var errorMap: [String: Any] { + [ + "code": 123, + "domain": "test_domain", + "userInfo": ["key": "value"], + ] + } + private var storefrontMap: [String: Any] { + [ + "countryCode": "USA", + "identifier": "unique_identifier", + ] + } + private var storefrontAndPaymentTransactionMap: [String: Any] { + [ + "storefront": storefrontMap, + "transaction": transactionMap, + ] + } + + func testSKProductSubscriptionPeriodStubToMap() { + let period = SKProductSubscriptionPeriodStub(map: periodMap) + let map = FIAObjectTranslator.getMapFrom(period) + + XCTAssertEqual(map as NSDictionary, periodMap as NSDictionary) + } + + func testSKProductDiscountStubToMap() { + let discount = SKProductDiscountStub(map: discountMap) + let map = FIAObjectTranslator.getMapFrom(discount) + + XCTAssertEqual(map as NSDictionary, discountMap as NSDictionary) + } + + func testProductToMap() { + let product = SKProductStub(map: productMap) + let map = FIAObjectTranslator.getMapFrom(product) + + XCTAssertEqual(map as NSDictionary, productMap as NSDictionary) + } + + func testProductResponseToMap() { + let response = SKProductsResponseStub(map: productResponseMap) + let map = FIAObjectTranslator.getMapFrom(response) + + XCTAssertEqual(map as NSDictionary, productResponseMap as NSDictionary) + } + + func testPaymentToMap() { + let payment = FIAObjectTranslator.getSKMutablePayment(fromMap: paymentMap) + let map = FIAObjectTranslator.getMapFrom(payment) + + XCTAssertEqual(map as NSDictionary, paymentMap as NSDictionary) + } + + func testPaymentTransactionToMap() { + let paymentTransaction = SKPaymentTransactionStub(map: transactionMap) + let map = FIAObjectTranslator.getMapFrom(paymentTransaction) + + XCTAssertEqual(map as NSDictionary, transactionMap as NSDictionary) + } + + func testError() { + let error = NSErrorStub(map: errorMap) + let map = FIAObjectTranslator.getMapFrom(error) + + XCTAssertEqual(map as NSDictionary, errorMap as NSDictionary) + } + + func testErrorWithNSNumberAsUserInfo() { + let error = NSError(domain: SKErrorDomain, code: 3, userInfo: ["key": 42]) + let expectedMap: [String: Any] = [ + "domain": SKErrorDomain, + "code": 3, + "userInfo": ["key": 42], + ] + let map = FIAObjectTranslator.getMapFrom(error) + + XCTAssertEqual(map as NSDictionary, expectedMap as NSDictionary) + } + + func testErrorWithMultipleUnderlyingErrors() { + let underlyingErrorOne = NSError(domain: SKErrorDomain, code: 2, userInfo: nil) + let underlyingErrorTwo = NSError(domain: SKErrorDomain, code: 1, userInfo: nil) + let mainError = NSError( + domain: SKErrorDomain, + code: 3, + userInfo: ["underlyingErrors": [underlyingErrorOne, underlyingErrorTwo]] + ) + let expectedMap: [String: Any] = [ + "domain": SKErrorDomain, + "code": 3, + "userInfo": [ + "underlyingErrors": [ + ["domain": SKErrorDomain, "code": 2, "userInfo": [:]], + ["domain": SKErrorDomain, "code": 1, "userInfo": [:]], + ] + ], + ] + let map = FIAObjectTranslator.getMapFrom(mainError) + + XCTAssertEqual(map as NSDictionary, expectedMap as NSDictionary) + } + + func testErrorWithNestedUnderlyingError() { + let underlyingError = NSError(domain: SKErrorDomain, code: 2, userInfo: nil) + let mainError = NSError( + domain: SKErrorDomain, + code: 3, + userInfo: ["nesting": ["underlyingError": underlyingError]] + ) + let expectedMap: [String: Any] = [ + "domain": SKErrorDomain, + "code": 3, + "userInfo": [ + "nesting": [ + "underlyingError": ["domain": SKErrorDomain, "code": 2, "userInfo": [:]] + ] + ], + ] + let map = FIAObjectTranslator.getMapFrom(mainError) + + XCTAssertEqual(map as NSDictionary, expectedMap as NSDictionary) + } + + func testErrorWithUnsupportedUserInfo() { + let error = NSError( + domain: SKErrorDomain, + code: 3, + userInfo: ["user_info": NSObject()] + ) + let expectedMap: [String: Any] = [ + "domain": SKErrorDomain, + "code": 3, + "userInfo": [ + "user_info": String( + format: """ + Unable to encode native userInfo object of type %@ to map. \ + Please submit an issue at https://github.com/flutter/flutter/issues/new \ + with the title "[in_app_purchase_storekit] Unable to encode userInfo of type %@\" \ + and add reproduction steps and the error details in the description field. + """, + NSStringFromClass(NSObject.self), NSStringFromClass(NSObject.self) + ) + ], + ] + let map = FIAObjectTranslator.getMapFrom(error) + + XCTAssertEqual(map as NSDictionary, expectedMap as NSDictionary) + } + + func testLocaleToMap() { + let system = Locale(identifier: "en_US") + let map = FIAObjectTranslator.getMapFrom(system) + + XCTAssertEqual(map["currencySymbol"] as? String, system.currencySymbol) + XCTAssertEqual(map["countryCode"] as? String, system.regionCode) + } + + func testSKStorefrontToMap() { + if #available(iOS 13.0, *) { + let storefront = SKStorefrontStub(map: storefrontMap) + let map = FIAObjectTranslator.getMapFrom(storefront) + + XCTAssertEqual(map as NSDictionary, storefrontMap as NSDictionary) + } + } + + func testSKStorefrontAndSKPaymentTransactionToMap() { + if #available(iOS 13.0, *) { + let storefront = SKStorefrontStub(map: storefrontMap) + let transaction = SKPaymentTransactionStub(map: transactionMap) + let map = FIAObjectTranslator.getMapFrom(storefront, andSKPaymentTransaction: transaction) + + XCTAssertEqual(map as NSDictionary, storefrontAndPaymentTransactionMap as NSDictionary) + } + } + + func testSKPaymentDiscountFromMap() throws { + if #available(iOS 12.2, *) { + var error: NSString? + let paymentDiscount = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: paymentDiscountMap, withError: &error) + + XCTAssertNil(error) + + let unwrappedDiscount = try XCTUnwrap(paymentDiscount) + let unwrappedNonce = try XCTUnwrap(paymentDiscountMap["nonce"] as? String) + + XCTAssertEqual(unwrappedDiscount.identifier, paymentDiscountMap["identifier"] as? String) + XCTAssertEqual( + unwrappedDiscount.keyIdentifier, paymentDiscountMap["keyIdentifier"] as? String) + XCTAssertEqual( + unwrappedDiscount.nonce, UUID(uuidString: unwrappedNonce)) + XCTAssertEqual(unwrappedDiscount.signature, paymentDiscountMap["signature"] as? String) + XCTAssertEqual(unwrappedDiscount.timestamp as? Int, paymentDiscountMap["timestamp"] as? Int) + } + } + + func testSKPaymentDiscountFromMapMissingIdentifier() { + if #available(iOS 12.2, *) { + let invalidValues: [Any?] = [NSNull(), 1, ""] + for value in invalidValues { + let discountMap: [String: Any?] = [ + "identifier": value, + "keyIdentifier": "payment_discount_key_identifier", + "nonce": "d18981e0-9003-4365-98a2-4b90e3b62c52", + "signature": "this is an encrypted signature", + "timestamp": Int(Date().timeIntervalSince1970), + ] + var error: NSString? + let _ = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: discountMap as [String: Any], withError: &error) + + XCTAssertNotNil(error) + XCTAssertEqual( + error, "When specifying a payment discount the 'identifier' field is mandatory.") + } + } + } + + func testGetMapFromSKProductDiscountMissingIdentifier() { + if #available(iOS 12.2, *) { + let discount = SKProductDiscountStub(map: discountMissingIdentifierMap) + let map = FIAObjectTranslator.getMapFrom(discount) + + XCTAssertEqual(map as NSDictionary, discountMissingIdentifierMap as NSDictionary) + } + } + + func testSKPaymentDiscountFromMapMissingKeyIdentifier() { + if #available(iOS 12.2, *) { + let invalidValues: [Any?] = [NSNull(), 1, ""] + for value in invalidValues { + let discountMap: [String: Any?] = [ + "identifier": "payment_discount_identifier", + "keyIdentifier": value, + "nonce": "d18981e0-9003-4365-98a2-4b90e3b62c52", + "signature": "this is an encrypted signature", + "timestamp": Int(Date().timeIntervalSince1970), + ] + var error: NSString? + let _ = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: discountMap as [String: Any], withError: &error) + + XCTAssertNotNil(error) + XCTAssertEqual( + error, "When specifying a payment discount the 'keyIdentifier' field is mandatory.") + } + } + } + + func testSKPaymentDiscountFromMapMissingNonce() { + if #available(iOS 12.2, *) { + let invalidValues: [Any?] = [NSNull(), 1, ""] + for value in invalidValues { + let discountMap: [String: Any?] = [ + "identifier": "payment_discount_identifier", + "keyIdentifier": "payment_discount_key_identifier", + "nonce": value, + "signature": "this is an encrypted signature", + "timestamp": Int(Date().timeIntervalSince1970), + ] + var error: NSString? + let _ = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: discountMap as [String: Any], withError: &error) + + XCTAssertNotNil(error) + XCTAssertEqual(error, "When specifying a payment discount the 'nonce' field is mandatory.") + } + } + } + + func testSKPaymentDiscountFromMapMissingSignature() { + if #available(iOS 12.2, *) { + let invalidValues: [Any?] = [NSNull(), 1, ""] + for value in invalidValues { + let discountMap: [String: Any?] = [ + "identifier": "payment_discount_identifier", + "keyIdentifier": "payment_discount_key_identifier", + "nonce": "d18981e0-9003-4365-98a2-4b90e3b62c52", + "signature": value, + "timestamp": Int(Date().timeIntervalSince1970), + ] + var error: NSString? + let _ = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: discountMap as [String: Any], withError: &error) + + XCTAssertNotNil(error) + XCTAssertEqual( + error, "When specifying a payment discount the 'signature' field is mandatory.") + } + } + } + + func testSKPaymentDiscountFromMapMissingTimestamp() { + if #available(iOS 12.2, *) { + let invalidValues: [Any?] = [NSNull(), "", -1] + for value in invalidValues { + let discountMap: [String: Any?] = [ + "identifier": "payment_discount_identifier", + "keyIdentifier": "payment_discount_key_identifier", + "nonce": "d18981e0-9003-4365-98a2-4b90e3b62c52", + "signature": "this is an encrypted signature", + "timestamp": value, + ] + var error: NSString? + let _ = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: discountMap as [String: Any], withError: &error) + + XCTAssertNotNil(error) + XCTAssertEqual( + error, "When specifying a payment discount the 'timestamp' field is mandatory.") + } + } + } + + func testSKPaymentDiscountFromMapOverflowingTimestamp() throws { + if #available(iOS 12.2, *) { + let discountMap: [String: Any] = [ + "identifier": "payment_discount_identifier", + "keyIdentifier": "payment_discount_key_identifier", + "nonce": "d18981e0-9003-4365-98a2-4b90e3b62c52", + "signature": "this is an encrypted signature", + "timestamp": 1_665_044_583_595, // timestamp 2022 Oct + ] + var error: NSString? + let paymentDiscount = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: discountMap, withError: &error) + XCTAssertNil(error) + + let unwrappedPaymentDiscount = try XCTUnwrap(paymentDiscount) + let identifier = try XCTUnwrap(discountMap["identifier"] as? String) + XCTAssertEqual(unwrappedPaymentDiscount.identifier, identifier) + + let keyIdentifier = try XCTUnwrap(discountMap["keyIdentifier"] as? String) + XCTAssertEqual(unwrappedPaymentDiscount.keyIdentifier, keyIdentifier) + + let nonceString = try XCTUnwrap(discountMap["nonce"] as? String) + let nonce = try XCTUnwrap(UUID(uuidString: nonceString)) + XCTAssertEqual(unwrappedPaymentDiscount.nonce, nonce) + + let signature = try XCTUnwrap(discountMap["signature"] as? String) + XCTAssertEqual(unwrappedPaymentDiscount.signature, signature) + + let timestamp = try XCTUnwrap(discountMap["timestamp"] as? Int) + XCTAssertEqual(unwrappedPaymentDiscount.timestamp as? Int, timestamp) + } + } + + func testSKPaymentDiscountConvertToPigeon() throws { + if #available(iOS 12.2, *) { + var error: NSString? + let paymentDiscount = try XCTUnwrap( + FIAObjectTranslator.getSKPaymentDiscount( + fromMap: paymentDiscountMap, withError: &error)) + let paymentDiscountPigeon = try XCTUnwrap( + FIAObjectTranslator.convertPaymentDiscount( + toPigeon: paymentDiscount)) + + XCTAssertNotNil(paymentDiscountPigeon) + XCTAssertEqual(paymentDiscount.identifier, paymentDiscountPigeon.identifier) + XCTAssertEqual(paymentDiscount.keyIdentifier, paymentDiscount.keyIdentifier) + XCTAssertEqual(paymentDiscount.nonce, UUID(uuidString: paymentDiscountPigeon.nonce)) + XCTAssertEqual(paymentDiscount.signature, paymentDiscountPigeon.signature) + + let paymentDiscountTimestamp = paymentDiscount.timestamp as? Int + let paymentDiscountPigeonTimestamp = paymentDiscountPigeon.timestamp + + XCTAssertEqual(paymentDiscountTimestamp, paymentDiscountPigeonTimestamp) + } + } + + func testSKErrorConvertToPigeon() throws { + let error = NSError(domain: SKErrorDomain, code: 3, userInfo: ["key": 42]) + let msg = SKErrorMessage.make( + withCode: 3, domain: SKErrorDomain, userInfo: ["key": 42] as [String: Any]) + let skerror = try XCTUnwrap(FIAObjectTranslator.convertSKError(toPigeon: error)) + + XCTAssertEqual(skerror.domain, msg.domain) + XCTAssertEqual(skerror.code, msg.code) + + let skerrorUserInfo = skerror.userInfo + let msgUserInfo = try XCTUnwrap(msg.userInfo) + + XCTAssertEqual(skerrorUserInfo as NSDictionary?, msgUserInfo as NSDictionary) + } + + func testSKPaymentConvertToPigeon() throws { + if #available(iOS 12.2, *) { + let payment = FIAObjectTranslator.getSKMutablePayment(fromMap: paymentMap) + let msg = try XCTUnwrap(FIAObjectTranslator.convertPayment(toPigeon: payment)) + let msgRequestData = try XCTUnwrap(msg.requestData) + + XCTAssertEqual(payment.productIdentifier, msg.productIdentifier) + XCTAssertEqual(payment.requestData, msgRequestData.data(using: .utf8)) + XCTAssertEqual(payment.quantity, msg.quantity) + XCTAssertEqual(payment.applicationUsername, msg.applicationUsername) + XCTAssertEqual(payment.simulatesAskToBuyInSandbox, msg.simulatesAskToBuyInSandbox) + } + } + + func testSKPaymentTransactionConvertToPigeon() throws { + let paymentTransaction = SKPaymentTransactionStub(map: transactionMap) + let msg = FIAObjectTranslator.convertTransaction(toPigeon: paymentTransaction) + + let unwrappedMsg = try XCTUnwrap(msg) + let unwrappedTimeStamp = try XCTUnwrap(unwrappedMsg.transactionTimeStamp) + + XCTAssertEqual(unwrappedMsg.transactionState, SKPaymentTransactionStateMessage.purchasing) + XCTAssertEqual( + paymentTransaction.transactionDate, + Date(timeIntervalSince1970: TimeInterval(truncating: unwrappedTimeStamp))) + XCTAssertEqual(paymentTransaction.transactionIdentifier, unwrappedMsg.transactionIdentifier) + } + + func testSKProductResponseCovertToPigeon() throws { + let response = SKProductsResponseStub(map: productResponseMap) + let responseMsg = FIAObjectTranslator.convertProductsResponse(toPigeon: response) + let unwrappedMsg = try XCTUnwrap(responseMsg) + + let products = try XCTUnwrap(unwrappedMsg.products) + XCTAssertEqual(products.count, 1) + + let invalidProductIdentifiers = try XCTUnwrap(unwrappedMsg.invalidProductIdentifiers) + XCTAssertTrue(invalidProductIdentifiers.isEmpty) + + let productMsg = try XCTUnwrap(unwrappedMsg.products?.first) + XCTAssertEqual(productMsg.productIdentifier, "123") + XCTAssertEqual(productMsg.localizedTitle, "title") + XCTAssertEqual(productMsg.localizedDescription, "des") + XCTAssertEqual(productMsg.subscriptionGroupIdentifier, "com.group") + + let localeMsg = try XCTUnwrap(productMsg.priceLocale) + XCTAssertEqual(localeMsg.countryCode, "") + XCTAssertEqual(localeMsg.currencyCode, "") + XCTAssertEqual(localeMsg.currencySymbol, "\u{00a4}") + + let subPeriod = try XCTUnwrap(productMsg.subscriptionPeriod) + XCTAssertEqual(subPeriod.unit, SKSubscriptionPeriodUnitMessage.day) + XCTAssertEqual(subPeriod.numberOfUnits, 0) + + let introDiscount = try XCTUnwrap(productMsg.introductoryPrice) + XCTAssertEqual(introDiscount.price, "1") + XCTAssertEqual(introDiscount.numberOfPeriods, 1) + XCTAssertEqual(introDiscount.paymentMode, SKProductDiscountPaymentModeMessage.payUpFront) + + let discounts = try XCTUnwrap(productMsg.discounts) + XCTAssertEqual(discounts.count, 1) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index d9f7056227..d9e8486660 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.17+2 +version: 0.3.17+3 environment: sdk: ^3.2.3