blob: 918dd51fc8a5a8f15e77e0fff267eaaa295947b7 [file] [log] [blame]
// 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 LocalAuthentication;
@import XCTest;
@import local_auth_ios;
#import <OCMock/OCMock.h>
// Set a long timeout to avoid flake due to slow CI.
static const NSTimeInterval kTimeout = 30.0;
/**
* A context factory that returns preset contexts.
*/
@interface StubAuthContextFactory : NSObject <FLAAuthContextFactory>
@property(copy, nonatomic) NSMutableArray *contexts;
- (instancetype)initWithContexts:(NSArray *)contexts;
@end
@implementation StubAuthContextFactory
- (instancetype)initWithContexts:(NSArray *)contexts {
self = [super init];
if (self) {
_contexts = [contexts mutableCopy];
}
return self;
}
- (LAContext *)createAuthContext {
NSAssert(self.contexts.count > 0, @"Insufficient test contexts provided");
LAContext *context = [self.contexts firstObject];
[self.contexts removeObjectAtIndex:0];
return context;
}
@end
#pragma mark -
@interface FLTLocalAuthPluginTests : XCTestCase
@end
@implementation FLTLocalAuthPluginTests
- (void)setUp {
self.continueAfterFailure = NO;
}
- (void)testSuccessfullAuthWithBiometrics {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
FLAAuthStrings *strings = [self createAuthStrings];
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(YES, nil);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:YES
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(resultDetails.result, FLAAuthResultSuccess);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testSuccessfullAuthWithoutBiometrics {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
FLAAuthStrings *strings = [self createAuthStrings];
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(YES, nil);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(resultDetails.result, FLAAuthResultSuccess);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testFailedAuthWithBiometrics {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
FLAAuthStrings *strings = [self createAuthStrings];
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(NO, [NSError errorWithDomain:@"error" code:LAErrorAuthenticationFailed userInfo:nil]);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:YES
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
// TODO(stuartmorgan): Fix this; this was the pre-Pigeon-migration
// behavior, so is preserved as part of the migration, but a failed
// authentication should return failure, not an error that results in a
// PlatformException.
XCTAssertEqual(resultDetails.result, FLAAuthResultErrorNotAvailable);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testFailedWithUnknownErrorCode {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
FLAAuthStrings *strings = [self createAuthStrings];
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(resultDetails.result, FLAAuthResultErrorNotAvailable);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testSystemCancelledWithoutStickyAuth {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
FLAAuthStrings *strings = [self createAuthStrings];
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(NO, [NSError errorWithDomain:@"error" code:LAErrorSystemCancel userInfo:nil]);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(resultDetails.result, FLAAuthResultFailure);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testFailedAuthWithoutBiometrics {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
FLAAuthStrings *strings = [self createAuthStrings];
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(NO, [NSError errorWithDomain:@"error" code:LAErrorAuthenticationFailed userInfo:nil]);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
// TODO(stuartmorgan): Fix this; this was the pre-Pigeon-migration
// behavior, so is preserved as part of the migration, but a failed
// authentication should return failure, not an error that results in a
// PlatformException.
XCTAssertEqual(resultDetails.result, FLAAuthResultErrorNotAvailable);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testLocalizedFallbackTitle {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
FLAAuthStrings *strings = [self createAuthStrings];
strings.localizedFallbackTitle = @"a title";
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(YES, nil);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
OCMVerify([mockAuthContext
setLocalizedFallbackTitle:strings.localizedFallbackTitle]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testSkippedLocalizedFallbackTitle {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
FLAAuthStrings *strings = [self createAuthStrings];
strings.localizedFallbackTitle = nil;
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
// a background thread.
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
void (^reply)(BOOL, NSError *);
[invocation getArgument:&reply atIndex:4];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
reply(YES, nil);
});
};
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:strings.reason reply:[OCMArg any]])
.andDo(backgroundThreadReplyCaller);
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
[plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO
sticky:NO
useErrorDialogs:NO]
strings:strings
completion:^(FLAAuthResultDetails *_Nullable resultDetails,
FlutterError *_Nullable error) {
OCMVerify([mockAuthContext setLocalizedFallbackTitle:nil]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testDeviceSupportsBiometrics_withEnrolledHardware {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
FlutterError *error;
NSNumber *result = [plugin deviceCanSupportBiometricsWithError:&error];
XCTAssertTrue([result boolValue]);
XCTAssertNil(error);
}
- (void)testDeviceSupportsBiometrics_withNonEnrolledHardware {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) {
// Write error
NSError *__autoreleasing *authError;
[invocation getArgument:&authError atIndex:3];
*authError = [NSError errorWithDomain:@"error" code:LAErrorBiometryNotEnrolled userInfo:nil];
// Write return value
BOOL returnValue = NO;
NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)];
[invocation setReturnValue:&nsReturnValue];
};
OCMStub([mockAuthContext canEvaluatePolicy:policy
error:(NSError * __autoreleasing *)[OCMArg anyPointer]])
.andDo(canEvaluatePolicyHandler);
FlutterError *error;
NSNumber *result = [plugin deviceCanSupportBiometricsWithError:&error];
XCTAssertTrue([result boolValue]);
XCTAssertNil(error);
}
- (void)testDeviceSupportsBiometrics_withNoBiometricHardware {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) {
// Write error
NSError *__autoreleasing *authError;
[invocation getArgument:&authError atIndex:3];
*authError = [NSError errorWithDomain:@"error" code:0 userInfo:nil];
// Write return value
BOOL returnValue = NO;
NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)];
[invocation setReturnValue:&nsReturnValue];
};
OCMStub([mockAuthContext canEvaluatePolicy:policy
error:(NSError * __autoreleasing *)[OCMArg anyPointer]])
.andDo(canEvaluatePolicyHandler);
FlutterError *error;
NSNumber *result = [plugin deviceCanSupportBiometricsWithError:&error];
XCTAssertFalse([result boolValue]);
XCTAssertNil(error);
}
- (void)testGetEnrolledBiometricsWithFaceID {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
OCMStub([mockAuthContext biometryType]).andReturn(LABiometryTypeFaceID);
FlutterError *error;
NSArray<FLAAuthBiometricWrapper *> *result = [plugin getEnrolledBiometricsWithError:&error];
XCTAssertEqual([result count], 1);
XCTAssertEqual(result[0].value, FLAAuthBiometricFace);
XCTAssertNil(error);
}
- (void)testGetEnrolledBiometricsWithTouchID {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
OCMStub([mockAuthContext biometryType]).andReturn(LABiometryTypeTouchID);
FlutterError *error;
NSArray<FLAAuthBiometricWrapper *> *result = [plugin getEnrolledBiometricsWithError:&error];
XCTAssertEqual([result count], 1);
XCTAssertEqual(result[0].value, FLAAuthBiometricFingerprint);
XCTAssertNil(error);
}
- (void)testGetEnrolledBiometricsWithoutEnrolledHardware {
id mockAuthContext = OCMClassMock([LAContext class]);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) {
// Write error
NSError *__autoreleasing *authError;
[invocation getArgument:&authError atIndex:3];
*authError = [NSError errorWithDomain:@"error" code:LAErrorBiometryNotEnrolled userInfo:nil];
// Write return value
BOOL returnValue = NO;
NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)];
[invocation setReturnValue:&nsReturnValue];
};
OCMStub([mockAuthContext canEvaluatePolicy:policy
error:(NSError * __autoreleasing *)[OCMArg anyPointer]])
.andDo(canEvaluatePolicyHandler);
FlutterError *error;
NSArray<FLAAuthBiometricWrapper *> *result = [plugin getEnrolledBiometricsWithError:&error];
XCTAssertEqual([result count], 0);
XCTAssertNil(error);
}
- (void)testIsDeviceSupportedHandlesSupported {
id mockAuthContext = OCMClassMock([LAContext class]);
OCMStub([mockAuthContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication
error:[OCMArg setTo:nil]])
.andReturn(YES);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
FlutterError *error;
NSNumber *result = [plugin isDeviceSupportedWithError:&error];
XCTAssertTrue([result boolValue]);
XCTAssertNil(error);
}
- (void)testIsDeviceSupportedHandlesUnsupported {
id mockAuthContext = OCMClassMock([LAContext class]);
OCMStub([mockAuthContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication
error:[OCMArg setTo:nil]])
.andReturn(NO);
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc]
initWithContextFactory:[[StubAuthContextFactory alloc]
initWithContexts:@[ mockAuthContext ]]];
FlutterError *error;
NSNumber *result = [plugin isDeviceSupportedWithError:&error];
XCTAssertFalse([result boolValue]);
XCTAssertNil(error);
}
// Creates an FLAAuthStrings with placeholder values.
- (FLAAuthStrings *)createAuthStrings {
return [FLAAuthStrings makeWithReason:@"a reason"
lockOut:@"locked out"
goToSettingsButton:@"Go To Settings"
goToSettingsDescription:@"Settings"
cancelButton:@"Cancel"
localizedFallbackTitle:nil];
}
@end