126 lines
5.7 KiB
Objective-C
126 lines
5.7 KiB
Objective-C
// Hardware-true input injection for the native-sdk canvas shim, driven by
|
|
// verify_input.sh: a minimal XCUITest bundle (no .xcodeproj — compiled with
|
|
// clang, hosted by the stock XCTRunner.app, run via `xcodebuild
|
|
// test-without-building` and a generated .xctestrun). XCUITest synthesizes
|
|
// real UITouch / keyboard events through the system input path, so these
|
|
// tests prove the shim's touch forwarding and first-responder keyboard
|
|
// wiring — model-state ground truth comes from the automation snapshot the
|
|
// script reads between test invocations.
|
|
//
|
|
// Target coordinates arrive in points via TestingEnvironmentVariables
|
|
// ("x,y" pairs parsed from the automation snapshot's widget bounds):
|
|
// NATIVE_SDK_TARGET_BUNDLE_ID app under test
|
|
// NATIVE_SDK_TAP_POINT "Add task" button center
|
|
// NATIVE_SDK_TEXTBOX_POINT draft text field center
|
|
// NATIVE_SDK_BLUR_POINT non-focusable point (header text)
|
|
// NATIVE_SDK_TYPE_TEXT text typed on the system keyboard
|
|
// NATIVE_SDK_PRESCROLL_TAPS taps on NATIVE_SDK_TAP_POINT to grow the list before scrolling
|
|
// NATIVE_SDK_SCROLL_FROM / NATIVE_SDK_SCROLL_TO drag gesture endpoints
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
static NSString *NativeSdkEnv(NSString *name) {
|
|
return NSProcessInfo.processInfo.environment[name] ?: @"";
|
|
}
|
|
|
|
static CGPoint NativeSdkPointFromEnv(NSString *name) {
|
|
NSArray<NSString *> *parts = [NativeSdkEnv(name) componentsSeparatedByString:@","];
|
|
if (parts.count != 2) return CGPointZero;
|
|
return CGPointMake(parts[0].doubleValue, parts[1].doubleValue);
|
|
}
|
|
|
|
@interface NativeSdkInputUITests : XCTestCase
|
|
@end
|
|
|
|
@implementation NativeSdkInputUITests
|
|
|
|
- (XCUIApplication *)foregroundApp {
|
|
NSString *bundleId = NativeSdkEnv(@"NATIVE_SDK_TARGET_BUNDLE_ID");
|
|
XCTAssertTrue(bundleId.length > 0, @"NATIVE_SDK_TARGET_BUNDLE_ID must be set");
|
|
XCUIApplication *app = [[XCUIApplication alloc] initWithBundleIdentifier:bundleId];
|
|
// Activate (not launch): the script launched the app with its
|
|
// automation environment; model state persists across test steps.
|
|
[app activate];
|
|
XCTAssertTrue([app waitForState:XCUIApplicationStateRunningForeground timeout:30],
|
|
@"app under test did not reach the foreground");
|
|
return app;
|
|
}
|
|
|
|
- (XCUICoordinate *)coordinateIn:(XCUIApplication *)app point:(CGPoint)point {
|
|
XCTAssertFalse(CGPointEqualToPoint(point, CGPointZero), @"missing injection coordinate");
|
|
XCUICoordinate *origin = [app coordinateWithNormalizedOffset:CGVectorMake(0, 0)];
|
|
return [origin coordinateWithOffset:CGVectorMake(point.x, point.y)];
|
|
}
|
|
|
|
// (a) A real tap on the "Add task" button center; the script asserts the
|
|
// open count grew in the automation snapshot.
|
|
- (void)testTapAddTask {
|
|
XCUIApplication *app = [self foregroundApp];
|
|
[[self coordinateIn:app point:NativeSdkPointFromEnv(@"NATIVE_SDK_TAP_POINT")] tap];
|
|
// Give the host-pumped frame loop a beat to publish the snapshot.
|
|
[NSThread sleepForTimeInterval:1.0];
|
|
}
|
|
|
|
// (b) Tapping the draft textbox raises the system keyboard (asserted here,
|
|
// against the real UI), typed text flows through the shim's text path (the
|
|
// script asserts the draft value in the snapshot), and tapping a
|
|
// non-focusable point dismisses the keyboard.
|
|
- (void)testFocusTypeAndDismissKeyboard {
|
|
XCUIApplication *app = [self foregroundApp];
|
|
XCUIElement *keyboard = app.keyboards.firstMatch;
|
|
|
|
[[self coordinateIn:app point:NativeSdkPointFromEnv(@"NATIVE_SDK_TEXTBOX_POINT")] tap];
|
|
XCTAssertTrue([keyboard waitForExistenceWithTimeout:10],
|
|
@"system keyboard should appear when the textbox takes focus");
|
|
|
|
NSString *text = NativeSdkEnv(@"NATIVE_SDK_TYPE_TEXT");
|
|
XCTAssertTrue(text.length > 0, @"NATIVE_SDK_TYPE_TEXT must be set");
|
|
[app typeText:text];
|
|
|
|
[[self coordinateIn:app point:NativeSdkPointFromEnv(@"NATIVE_SDK_BLUR_POINT")] tap];
|
|
BOOL hidden = NO;
|
|
for (int attempt = 0; attempt < 40; attempt++) {
|
|
if (!keyboard.exists) {
|
|
hidden = YES;
|
|
break;
|
|
}
|
|
[NSThread sleepForTimeInterval:0.25];
|
|
}
|
|
XCTAssertTrue(hidden, @"system keyboard should hide when textbox focus leaves");
|
|
[NSThread sleepForTimeInterval:1.0];
|
|
}
|
|
|
|
// (M4) Rotate the simulator to landscape / back to portrait through the
|
|
// system orientation path (there is no simctl rotation command); the
|
|
// layout-verification script asserts the relayout against the automation
|
|
// snapshot between calls. NATIVE_SDK_ORIENTATION selects the target orientation.
|
|
- (void)testRotate {
|
|
XCUIApplication *app = [self foregroundApp];
|
|
NSString *orientation = NativeSdkEnv(@"NATIVE_SDK_ORIENTATION");
|
|
XCUIDevice.sharedDevice.orientation = [orientation isEqualToString:@"landscape"]
|
|
? UIDeviceOrientationLandscapeLeft
|
|
: UIDeviceOrientationPortrait;
|
|
// Let the rotation animation, safe-area propagation, and the app's
|
|
// viewport push + relayout settle before the script reads the snapshot.
|
|
[NSThread sleepForTimeInterval:2.0];
|
|
XCTAssertTrue(app.exists);
|
|
}
|
|
|
|
// (c) Grow the list past the viewport with real taps, then drag-scroll it;
|
|
// the script asserts the scroll offset moved in the snapshot.
|
|
- (void)testDragScroll {
|
|
XCUIApplication *app = [self foregroundApp];
|
|
NSInteger taps = NativeSdkEnv(@"NATIVE_SDK_PRESCROLL_TAPS").integerValue;
|
|
XCUICoordinate *add = [self coordinateIn:app point:NativeSdkPointFromEnv(@"NATIVE_SDK_TAP_POINT")];
|
|
for (NSInteger index = 0; index < taps; index++) {
|
|
[add tap];
|
|
}
|
|
|
|
XCUICoordinate *from = [self coordinateIn:app point:NativeSdkPointFromEnv(@"NATIVE_SDK_SCROLL_FROM")];
|
|
XCUICoordinate *to = [self coordinateIn:app point:NativeSdkPointFromEnv(@"NATIVE_SDK_SCROLL_TO")];
|
|
[from pressForDuration:0.1 thenDragToCoordinate:to];
|
|
[NSThread sleepForTimeInterval:1.0];
|
|
}
|
|
|
|
@end
|