Revert "Refactor iOS integration_test API to support Swift, dynamically add native tests (#88013)" (#88377)
This reverts commit 9e3de9a3289fbcdd88ff73c808333a58146be87d.
This commit is contained in:
parent
3572a7e8b7
commit
463f9739f7
@ -281,27 +281,14 @@ To build `integration_test/foo_test.dart` from the command line, run:
|
||||
flutter build ios --config-only integration_test/foo_test.dart
|
||||
```
|
||||
|
||||
In Xcode, add a test file called `RunnerTests.m` or `RunnerTests.swift` (or any name of your choice) to the new target and
|
||||
In Xcode, add a test file called `RunnerTests.m` (or any name of your choice) to the new target and
|
||||
replace the file:
|
||||
|
||||
```objective-c
|
||||
@import XCTest;
|
||||
@import integration_test;
|
||||
|
||||
@interface RunnerTests : FLTIntegrationTestCase
|
||||
@end
|
||||
|
||||
@implementation RunnerTests
|
||||
@end
|
||||
```
|
||||
or in Swift:
|
||||
````swift
|
||||
import integration_test
|
||||
import XCTest
|
||||
|
||||
class RunnerSwiftTests: FLTIntegrationTestCase {
|
||||
}
|
||||
|
||||
INTEGRATION_TEST_IOS_RUNNER(RunnerTests)
|
||||
```
|
||||
|
||||
Run `Product > Test` to run the integration tests on your selected device.
|
||||
|
@ -25,24 +25,6 @@ void main() {
|
||||
// Build our app.
|
||||
app.main();
|
||||
|
||||
// Pump a frame.
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify that platform version is retrieved.
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(Widget widget) =>
|
||||
widget is Text &&
|
||||
widget.data!.startsWith('Platform: ${Platform.operatingSystem}'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('verify screenshot', (WidgetTester tester) async {
|
||||
// Build our app.
|
||||
app.main();
|
||||
|
||||
// On Android, this is required prior to taking the screenshot.
|
||||
await binding.convertFlutterSurfaceToImage();
|
||||
|
||||
@ -57,5 +39,15 @@ void main() {
|
||||
expect(secondPng.isNotEmpty, isTrue);
|
||||
|
||||
expect(listEquals(firstPng, secondPng), isTrue);
|
||||
|
||||
// Verify that platform version is retrieved.
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(Widget widget) =>
|
||||
widget is Text &&
|
||||
widget.data!.startsWith('Platform: ${Platform.operatingSystem}'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -10,14 +10,13 @@
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
4DB404AC7CF2C89658A01173 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81BF64028CE7AE2E6196250D /* libPods-RunnerTests.a */; };
|
||||
769541CB23A0351900E5C350 /* RunnerObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 769541CA23A0351900E5C350 /* RunnerObjCTests.m */; };
|
||||
769541CB23A0351900E5C350 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 769541CA23A0351900E5C350 /* RunnerTests.m */; };
|
||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
|
||||
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
C2A5EDF11F4FDBF3ABFD7006 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A5A90428602E25C0DE2F6 /* libPods-Runner.a */; };
|
||||
F77B951926C3504400F785B3 /* RunnerSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77B951826C3504400F785B3 /* RunnerSwiftTests.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -53,7 +52,7 @@
|
||||
750225973AAB5D7832AFA60C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
769541BF23A0337200E5C350 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||
769541C823A0351900E5C350 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
769541CA23A0351900E5C350 /* RunnerObjCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerObjCTests.m; sourceTree = "<group>"; };
|
||||
769541CA23A0351900E5C350 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = "<group>"; };
|
||||
769541CC23A0351900E5C350 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
@ -69,7 +68,6 @@
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D69CCAD5F82E76E2E22BFA96 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E23EF4D45DAE46B9DDB9B445 /* 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 = "<group>"; };
|
||||
F77B951826C3504400F785B3 /* RunnerSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerSwiftTests.swift; sourceTree = "<group>"; };
|
||||
FCE3953801588FC13ED9E898 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -106,8 +104,7 @@
|
||||
769541C923A0351900E5C350 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
769541CA23A0351900E5C350 /* RunnerObjCTests.m */,
|
||||
F77B951826C3504400F785B3 /* RunnerSwiftTests.swift */,
|
||||
769541CA23A0351900E5C350 /* RunnerTests.m */,
|
||||
769541CC23A0351900E5C350 /* Info.plist */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
@ -236,7 +233,6 @@
|
||||
TargetAttributes = {
|
||||
769541C723A0351900E5C350 = {
|
||||
CreatedOnToolsVersion = 11.0;
|
||||
LastSwiftMigration = 1300;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = 97C146ED1CF9000F007C117D;
|
||||
};
|
||||
@ -365,8 +361,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
769541CB23A0351900E5C350 /* RunnerObjCTests.m in Sources */,
|
||||
F77B951926C3504400F785B3 /* RunnerSwiftTests.swift in Sources */,
|
||||
769541CB23A0351900E5C350 /* RunnerTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -480,14 +475,11 @@
|
||||
baseConfigurationReference = 09505407E99803EF7AA92DE7 /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = RunnerTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.instrumentationAdapterExample.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
|
||||
};
|
||||
name = Debug;
|
||||
@ -497,13 +489,11 @@
|
||||
baseConfigurationReference = FCE3953801588FC13ED9E898 /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = RunnerTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.instrumentationAdapterExample.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
|
||||
};
|
||||
name = Release;
|
||||
@ -513,13 +503,11 @@
|
||||
baseConfigurationReference = 750225973AAB5D7832AFA60C /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = RunnerTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.instrumentationAdapterExample.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
|
||||
};
|
||||
name = Profile;
|
||||
|
@ -20,20 +20,6 @@
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "769541C723A0351900E5C350"
|
||||
BuildableName = "RunnerTests.xctest"
|
||||
BlueprintName = "RunnerTests"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
|
@ -1,49 +0,0 @@
|
||||
// Copyright 2014 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 integration_test;
|
||||
@import XCTest;
|
||||
|
||||
// Test without macro.
|
||||
@interface RunnerObjCTests : FLTIntegrationTestCase
|
||||
@end
|
||||
|
||||
@implementation RunnerObjCTests
|
||||
|
||||
+ (NSArray<NSInvocation *> *)testInvocations {
|
||||
// Add a test to verify the Flutter dart tests have been dynamically added to this test case.
|
||||
SEL selector = @selector(testDynamicTestMethods);
|
||||
NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
invocation.selector = selector;
|
||||
|
||||
return [super.testInvocations arrayByAddingObject:invocation];
|
||||
}
|
||||
|
||||
- (void)testDynamicTestMethods {
|
||||
XCTAssertTrue([self respondsToSelector:NSSelectorFromString(@"testVerifyScreenshot")]);
|
||||
XCTAssertTrue([self respondsToSelector:NSSelectorFromString(@"testVerifyText")]);
|
||||
XCTAssertTrue([self respondsToSelector:NSSelectorFromString(@"screenshotPlaceholder")]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Test deprecated macro. Do not use.
|
||||
INTEGRATION_TEST_IOS_RUNNER(RunnerObjCMacroTests)
|
||||
|
||||
@interface DeprecatedIntegrationTestIosTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation DeprecatedIntegrationTestIosTests
|
||||
|
||||
- (void)testIntegrationTest {
|
||||
NSString *testResult;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
BOOL testPass = [[IntegrationTestIosTest new] testIntegrationTest:&testResult];
|
||||
#pragma clang diagnostic pop
|
||||
XCTAssertTrue(testPass, @"%@", testResult);
|
||||
}
|
||||
|
||||
@end
|
@ -2,8 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import integration_test
|
||||
import XCTest
|
||||
@import XCTest;
|
||||
@import integration_test;
|
||||
|
||||
class RunnerSwiftTests: FLTIntegrationTestCase {
|
||||
}
|
||||
INTEGRATION_TEST_IOS_RUNNER(RunnerTests)
|
@ -1,27 +0,0 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// XCTest is weakly linked.
|
||||
#if __has_include(<XCTest/XCTest.h>)
|
||||
|
||||
@import XCTest;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FLTIntegrationTestCase : XCTestCase
|
||||
@end
|
||||
|
||||
/*!
|
||||
Deprecated. Prefer directly inheriting from @c FLTIntegrationTestCase
|
||||
*/
|
||||
#define INTEGRATION_TEST_IOS_RUNNER(__test_class) \
|
||||
@interface __test_class : FLTIntegrationTestCase \
|
||||
@end \
|
||||
\
|
||||
@implementation __test_class \
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif
|
@ -1,75 +0,0 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// XCTest is weakly linked.
|
||||
#if __has_include(<XCTest/XCTest.h>)
|
||||
|
||||
#import "FLTIntegrationTestCase.h"
|
||||
|
||||
#import "FLTIntegrationTestRunner.h"
|
||||
#import "IntegrationTestPlugin.h"
|
||||
|
||||
@import ObjectiveC.runtime;
|
||||
@import XCTest;
|
||||
|
||||
@implementation FLTIntegrationTestCase
|
||||
|
||||
+ (NSArray<NSInvocation *> *)testInvocations {
|
||||
if (self == [FLTIntegrationTestCase class]) {
|
||||
// Do not add any tests for this base class.
|
||||
return @[];
|
||||
}
|
||||
FLTIntegrationTestRunner *integrationTestRunner = [FLTIntegrationTestRunner new];
|
||||
NSMutableArray<NSInvocation *> *testInvocations = [NSMutableArray new];
|
||||
[integrationTestRunner testIntegrationTestWithResults:^(NSString *testName, BOOL success, NSString *failureMessage) {
|
||||
// For every Flutter dart test, dynamically generate an Objective-C method mirroring the test results
|
||||
// so it is reported as a native XCTest run result.
|
||||
IMP assertImplementation = imp_implementationWithBlock(^(id _self) {
|
||||
XCTAssertTrue(success, @"%@", failureMessage);
|
||||
});
|
||||
|
||||
// Create an appropriate XCTest method name based on the dart test name.
|
||||
// Example: dart test "verify widget" becomes "testVerifyWidget"
|
||||
NSString *upperCamelTestName = [testName.localizedCapitalizedString stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
NSString *testSelectorName = [NSString stringWithFormat:@"test%@", upperCamelTestName];
|
||||
SEL testSelector = NSSelectorFromString(testSelectorName);
|
||||
class_addMethod(self, testSelector, assertImplementation, "v@:");
|
||||
|
||||
// Add the new class method as a test invocation to the XCTestCase.
|
||||
NSMethodSignature *signature = [self instanceMethodSignatureForSelector:testSelector];
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
invocation.selector = testSelector;
|
||||
|
||||
[testInvocations addObject:invocation];
|
||||
}];
|
||||
|
||||
NSDictionary<NSString *, UIImage *> *capturedScreenshotsByName = integrationTestRunner.capturedScreenshotsByName;
|
||||
if (capturedScreenshotsByName.count > 0) {
|
||||
// If the Flutter dart tests have captured screenshots, add them to the XCTest bundle.
|
||||
IMP screenshotImplementation = imp_implementationWithBlock(^(id _self) {
|
||||
[capturedScreenshotsByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, UIImage *screenshot, BOOL *stop) {
|
||||
XCTAttachment *attachment = [XCTAttachment attachmentWithImage:screenshot];
|
||||
attachment.lifetime = XCTAttachmentLifetimeKeepAlways;
|
||||
if (name != nil) {
|
||||
attachment.name = name;
|
||||
}
|
||||
[_self addAttachment:attachment];
|
||||
}];
|
||||
});
|
||||
|
||||
SEL attachmentSelector = NSSelectorFromString(@"screenshotPlaceholder");
|
||||
class_addMethod(self, attachmentSelector, screenshotImplementation, "v@:");
|
||||
|
||||
NSMethodSignature *attachmentSignature = [self instanceMethodSignatureForSelector:attachmentSelector];
|
||||
NSInvocation *attachmentInvocation = [NSInvocation invocationWithMethodSignature:attachmentSignature];
|
||||
attachmentInvocation.selector = attachmentSelector;
|
||||
|
||||
[testInvocations addObject:attachmentInvocation];
|
||||
}
|
||||
return testInvocations;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
@ -1,43 +0,0 @@
|
||||
// Copyright 2014 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;
|
||||
|
||||
@class UIImage;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^FLTIntegrationTestResults)(NSString *testName, BOOL success, NSString *_Nullable failureMessage);
|
||||
|
||||
|
||||
@interface FLTIntegrationTestRunner : NSObject
|
||||
|
||||
/**
|
||||
* Any screenshots captured by the plugin.
|
||||
*/
|
||||
@property (copy, readonly) NSDictionary<NSString *, UIImage *> *capturedScreenshotsByName;
|
||||
|
||||
/*!
|
||||
Start dart tests and wait for results.
|
||||
|
||||
@param testResult Will be called once per every completed dart test.
|
||||
*/
|
||||
- (void)testIntegrationTestWithResults:(NS_NOESCAPE FLTIntegrationTestResults)testResult;
|
||||
|
||||
@end
|
||||
|
||||
DEPRECATED_MSG_ATTRIBUTE("Use FLTIntegrationTestRunner instead.")
|
||||
@interface IntegrationTestIosTest : NSObject
|
||||
|
||||
/*!
|
||||
Initate dart tests and wait for results.
|
||||
|
||||
@param testResult Will be set to a string describing the results.
|
||||
@returns @c YES if all tests succeeded.
|
||||
*/
|
||||
- (BOOL)testIntegrationTest:(NSString *_Nullable *_Nullable)testResult;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -1,86 +0,0 @@
|
||||
// Copyright 2014 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 "FLTIntegrationTestRunner.h"
|
||||
|
||||
#import "IntegrationTestPlugin.h"
|
||||
|
||||
@import UIKit;
|
||||
|
||||
@interface FLTIntegrationTestRunner ()
|
||||
|
||||
@property IntegrationTestPlugin *integrationTestPlugin;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLTIntegrationTestRunner
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_integrationTestPlugin = [IntegrationTestPlugin instance];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)testIntegrationTestWithResults:(NS_NOESCAPE FLTIntegrationTestResults)testResult {
|
||||
IntegrationTestPlugin *integrationTestPlugin = self.integrationTestPlugin;
|
||||
UIViewController *rootViewController = UIApplication.sharedApplication.delegate.window.rootViewController;
|
||||
if (![rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
testResult(@"setup", NO, @"rootViewController was not expected FlutterViewController");
|
||||
}
|
||||
FlutterViewController *flutterViewController = (FlutterViewController *)rootViewController;
|
||||
[integrationTestPlugin setupChannels:flutterViewController.engine.binaryMessenger];
|
||||
|
||||
// Spin the runloop.
|
||||
while (!integrationTestPlugin.testResults) {
|
||||
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
|
||||
}
|
||||
|
||||
[integrationTestPlugin.testResults enumerateKeysAndObjectsUsingBlock:^(NSString *test, NSString *result, BOOL *stop) {
|
||||
if ([result isEqualToString:@"success"]) {
|
||||
testResult(test, YES, nil);
|
||||
} else {
|
||||
testResult(test, NO, result);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *,UIImage *> *)capturedScreenshotsByName {
|
||||
return self.integrationTestPlugin.capturedScreenshotsByName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Deprecated
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
|
||||
|
||||
@implementation IntegrationTestIosTest
|
||||
|
||||
- (BOOL)testIntegrationTest:(NSString **)testResult {
|
||||
NSLog(@"==================== Test Results =====================");
|
||||
NSMutableArray<NSString *> *failedTests = [NSMutableArray array];
|
||||
NSMutableArray<NSString *> *testNames = [NSMutableArray array];
|
||||
[[FLTIntegrationTestRunner new] testIntegrationTestWithResults:^(NSString *testName, BOOL success, NSString *message) {
|
||||
[testNames addObject:testName];
|
||||
if (success) {
|
||||
NSLog(@"%@ passed.", testName);
|
||||
} else {
|
||||
NSLog(@"%@ failed: %@", testName, message);
|
||||
[failedTests addObject:testName];
|
||||
}
|
||||
}];
|
||||
NSLog(@"================== Test Results End ====================");
|
||||
BOOL testPass = failedTests.count == 0;
|
||||
if (!testPass && testResult != NULL) {
|
||||
*testResult =
|
||||
[NSString stringWithFormat:@"Detected failed integration test(s) %@ among %@",
|
||||
failedTests.description, testNames.description];
|
||||
}
|
||||
return testPass;
|
||||
}
|
||||
|
||||
@end
|
||||
#pragma clang diagnostic pop
|
@ -0,0 +1,48 @@
|
||||
// Copyright 2014 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/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol FLTIntegrationTestScreenshotDelegate;
|
||||
|
||||
@interface IntegrationTestIosTest : NSObject
|
||||
|
||||
- (instancetype)initWithScreenshotDelegate:(nullable id<FLTIntegrationTestScreenshotDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Initate dart tests and wait for results. @c testResult will be set to a string describing the results.
|
||||
*
|
||||
* @return @c YES if all tests succeeded.
|
||||
*/
|
||||
- (BOOL)testIntegrationTest:(NSString *_Nullable *_Nullable)testResult;
|
||||
|
||||
@end
|
||||
|
||||
#define INTEGRATION_TEST_IOS_RUNNER(__test_class) \
|
||||
@interface __test_class : XCTestCase<FLTIntegrationTestScreenshotDelegate> \
|
||||
@end \
|
||||
\
|
||||
@implementation __test_class \
|
||||
\
|
||||
- (void)testIntegrationTest { \
|
||||
NSString *testResult; \
|
||||
IntegrationTestIosTest *integrationTestIosTest = integrationTestIosTest = [[IntegrationTestIosTest alloc] initWithScreenshotDelegate:self]; \
|
||||
BOOL testPass = [integrationTestIosTest testIntegrationTest:&testResult]; \
|
||||
XCTAssertTrue(testPass, @"%@", testResult); \
|
||||
} \
|
||||
\
|
||||
- (void)didTakeScreenshot:(UIImage *)screenshot attachmentName:(NSString *)name { \
|
||||
XCTAttachment *attachment = [XCTAttachment attachmentWithImage:screenshot]; \
|
||||
attachment.lifetime = XCTAttachmentLifetimeKeepAlways; \
|
||||
if (name != nil) { \
|
||||
attachment.name = name; \
|
||||
} \
|
||||
[self addAttachment:attachment]; \
|
||||
} \
|
||||
\
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,63 @@
|
||||
// Copyright 2014 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 "IntegrationTestIosTest.h"
|
||||
#import "IntegrationTestPlugin.h"
|
||||
|
||||
@interface IntegrationTestIosTest()
|
||||
@property (nonatomic) IntegrationTestPlugin *integrationTestPlugin;
|
||||
@end
|
||||
|
||||
@implementation IntegrationTestIosTest
|
||||
|
||||
- (instancetype)initWithScreenshotDelegate:(id<FLTIntegrationTestScreenshotDelegate>)delegate {
|
||||
self = [super init];
|
||||
_integrationTestPlugin = [IntegrationTestPlugin instance];
|
||||
_integrationTestPlugin.screenshotDelegate = delegate;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithScreenshotDelegate:nil];
|
||||
}
|
||||
|
||||
- (BOOL)testIntegrationTest:(NSString **)testResult {
|
||||
IntegrationTestPlugin *integrationTestPlugin = self.integrationTestPlugin;
|
||||
|
||||
UIViewController *rootViewController =
|
||||
[[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
||||
if (![rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
NSLog(@"expected FlutterViewController as rootViewController.");
|
||||
return NO;
|
||||
}
|
||||
FlutterViewController *flutterViewController = (FlutterViewController *)rootViewController;
|
||||
[integrationTestPlugin setupChannels:flutterViewController.engine.binaryMessenger];
|
||||
while (!integrationTestPlugin.testResults) {
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.f, NO);
|
||||
}
|
||||
NSDictionary<NSString *, NSString *> *testResults = integrationTestPlugin.testResults;
|
||||
NSMutableArray<NSString *> *passedTests = [NSMutableArray array];
|
||||
NSMutableArray<NSString *> *failedTests = [NSMutableArray array];
|
||||
NSLog(@"==================== Test Results =====================");
|
||||
for (NSString *test in testResults.allKeys) {
|
||||
NSString *result = testResults[test];
|
||||
if ([result isEqualToString:@"success"]) {
|
||||
NSLog(@"%@ passed.", test);
|
||||
[passedTests addObject:test];
|
||||
} else {
|
||||
NSLog(@"%@ failed: %@", test, result);
|
||||
[failedTests addObject:test];
|
||||
}
|
||||
}
|
||||
NSLog(@"================== Test Results End ====================");
|
||||
BOOL testPass = failedTests.count == 0;
|
||||
if (!testPass && testResult) {
|
||||
*testResult =
|
||||
[NSString stringWithFormat:@"Detected failed integration test(s) %@ among %@",
|
||||
failedTests.description, testResults.allKeys.description];
|
||||
}
|
||||
return testPass;
|
||||
}
|
||||
|
||||
@end
|
@ -6,6 +6,13 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol FLTIntegrationTestScreenshotDelegate
|
||||
|
||||
/** This will be called when a dart integration test triggers a window screenshot with @c takeScreenshot. */
|
||||
- (void)didTakeScreenshot:(UIImage *)screenshot attachmentName:(nullable NSString *)name;
|
||||
|
||||
@end
|
||||
|
||||
/** A Flutter plugin that's responsible for communicating the test results back
|
||||
* to iOS XCTest. */
|
||||
@interface IntegrationTestPlugin : NSObject <FlutterPlugin>
|
||||
@ -16,11 +23,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *testResults;
|
||||
|
||||
/**
|
||||
* Mapping of screenshot images by suggested names, captured by the dart tests.
|
||||
*/
|
||||
@property (copy, readonly) NSDictionary<NSString *, UIImage *> *capturedScreenshotsByName;
|
||||
|
||||
/** Fetches the singleton instance of the plugin. */
|
||||
+ (IntegrationTestPlugin *)instance;
|
||||
|
||||
@ -28,6 +30,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@property(weak, nonatomic) id<FLTIntegrationTestScreenshotDelegate> screenshotDelegate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -2,10 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "IntegrationTestPlugin.h"
|
||||
|
||||
@import UIKit;
|
||||
|
||||
#import "IntegrationTestPlugin.h"
|
||||
|
||||
static NSString *const kIntegrationTestPluginChannel = @"plugins.flutter.io/integration_test";
|
||||
static NSString *const kMethodTestFinished = @"allTestsFinished";
|
||||
static NSString *const kMethodScreenshot = @"captureScreenshot";
|
||||
@ -16,13 +16,10 @@ static NSString *const kMethodRevertImage = @"revertFlutterImage";
|
||||
|
||||
@property(nonatomic, readwrite) NSDictionary<NSString *, NSString *> *testResults;
|
||||
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
@implementation IntegrationTestPlugin {
|
||||
NSDictionary<NSString *, NSString *> *_testResults;
|
||||
NSMutableDictionary<NSString *, UIImage *> *_capturedScreenshotsByName;
|
||||
}
|
||||
|
||||
+ (IntegrationTestPlugin *)instance {
|
||||
@ -35,13 +32,7 @@ static NSString *const kMethodRevertImage = @"revertFlutterImage";
|
||||
}
|
||||
|
||||
- (instancetype)initForRegistration {
|
||||
return [self init];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_capturedScreenshotsByName = [NSMutableDictionary new];
|
||||
return self;
|
||||
return [super init];
|
||||
}
|
||||
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
|
||||
@ -68,7 +59,7 @@ static NSString *const kMethodRevertImage = @"revertFlutterImage";
|
||||
// If running as a native Xcode test, attach to test.
|
||||
UIImage *screenshot = [self capturePngScreenshot];
|
||||
NSString *name = call.arguments[@"name"];
|
||||
_capturedScreenshotsByName[name] = screenshot;
|
||||
[self.screenshotDelegate didTakeScreenshot:screenshot attachmentName:name];
|
||||
|
||||
// Also pass back along the channel for the driver to handle.
|
||||
NSData *pngData = UIImagePNGRepresentation(screenshot);
|
||||
|
@ -19,14 +19,7 @@ LICENSE
|
||||
s.public_header_files = 'Classes/**/*.h'
|
||||
s.dependency 'Flutter'
|
||||
s.ios.framework = 'UIKit'
|
||||
# Weakly link for parts of API that need to be run in XCTest targets.
|
||||
s.ios.weak_framework = 'XCTest'
|
||||
|
||||
s.platform = :ios, '8.0'
|
||||
s.pod_target_xcconfig = {
|
||||
'DEFINES_MODULE' => 'YES',
|
||||
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
|
||||
# Find XCTest framework.
|
||||
'FRAMEWORK_SEARCH_PATHS' => '$(PLATFORM_DIR)/Developer/Library/Frameworks',
|
||||
}
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user