diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.m b/Libraries/Text/TextInput/Multiline/RCTUITextView.m index 97e4b5a2460b19..36b84c86f288b7 100644 --- a/Libraries/Text/TextInput/Multiline/RCTUITextView.m +++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.m @@ -218,13 +218,13 @@ - (void)setTextAlignment:(NSTextAlignment)textAlignment - (void)setAttributedText:(NSAttributedString *)attributedText { -#if !TARGET_OS_OSX // TODO(macOS ISS#2323203) // Using `setAttributedString:` while user is typing breaks some internal mechanics // when entering complex input languages such as Chinese, Korean or Japanese. // see: https://github.com/facebook/react-native/issues/19339 // We try to avoid calling this method as much as we can. // If the text has changed, there is nothing we can do. +#if !TARGET_OS_OSX // TODO(macOS ISS#2323203) if (![super.attributedText.string isEqualToString:attributedText.string]) { [super setAttributedText:attributedText]; } else { @@ -233,11 +233,22 @@ - (void)setAttributedText:(NSAttributedString *)attributedText [self copyTextAttributesFrom:attributedText]; } } - - [self textDidChange]; #else // [TODO(macOS ISS#2323203) - [self.textStorage setAttributedString:attributedText]; + if (![self.textStorage isEqualTo:attributedText.string]) { + if (attributedText != nil) { + [self.textStorage setAttributedString:attributedText]; + } else { + // Avoid Exception thrown while executing UI block: *** -[NSBigMutableString replaceCharactersInRange:withString:]: nil argument + [self.textStorage setAttributedString:[NSAttributedString new]]; + } + } else { + // But if the text is preserved, we just copying the attributes from the source string. + if (![self.textStorage isEqualToAttributedString:attributedText]) { + [self copyTextAttributesFrom:attributedText]; + } + } #endif // ]TODO(macOS ISS#2323203) + [self textDidChange]; } #pragma mark - Overrides diff --git a/RNTester/Podfile.lock b/RNTester/Podfile.lock index 487ff013c0b78a..6c8a6ac5b2b853 100644 --- a/RNTester/Podfile.lock +++ b/RNTester/Podfile.lock @@ -348,33 +348,33 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost-for-react-native: a110407d9db2642fd2e1bcd7c5a51c81f2521dc9 DoubleConversion: a1bc12a74baa397a2609e0f10e19b8062d864053 - FBLazyVector: f0e0df84578f80a66fd746f051e447b860b47bf3 - FBReactNativeSpec: d3e3e305a38696069a21420e6c2dd79d067c73a9 + FBLazyVector: 8da30c49d2692165d80699445ff23dff19e114e1 + FBReactNativeSpec: c1ff48ca5f424c6b4dc326852220b70f0583f99b Folly: feff29ba9d0b7c2e4f793a94942831d6cc5bbad7 glog: b3f6d74f3e2d33396addc0ee724d2b2b79fc3e00 - RCTRequired: a573cb908cfc02fdaaf6e63e7e1c55e8153fc977 - RCTTypeSafety: d58e22c29cb25122c11172a3121484a517b48ca5 - React: 2b0d09dac43fd5a442dd412b2a07dcba5038b223 - React-ART: 8b1fba341468349688d8c055d7305ee2b9fb0ac9 - React-Core: 118f5e89b085aa6be453da4c31e30fd38c7bd7e3 - React-CoreModules: 1eccf9a46ef23e9a31fc445a01225ee0a2a05936 - React-cxxreact: 4c23c5df9b8d304a7daf572152bd0155ba38ca00 - React-jsi: 54f8e7208d44c3f4cacbdee580c8bff7dc9a6655 - React-jsiexecutor: 42cb111d20bee4f18a994f6c33a9973f3157da65 - React-jsinspector: 60e026ea8263b4dcea8df64b12fac06642f6fdda - React-RCTActionSheet: 99c8723f606fd1b55cd5a3b794fd86d8b14677a5 - React-RCTAnimation: bd0ee82d577e61e2c485903bf387d36294d954ac - React-RCTBlob: 20e60087c155b0f3bd0b2401ea43ffdd4a625648 - React-RCTImage: 9afd4553833185d6099e82c990ff604ebc0a3804 - React-RCTLinking: 9c03f63acb0da803efc3218509743e63d22256c0 - React-RCTNetwork: 0893c8dff3c0cbf2bdb61444e9aae842f7824399 - React-RCTPushNotification: 416b0e93705ca217c63e1ecd6d6b494281bea375 - React-RCTSettings: 1778d7ecf07703bbfc7f565eac820447355513c0 - React-RCTTest: 53a3563d3bf54d4342dd599518748f62cd0def14 - React-RCTText: 8d87a5dcd9a50bfef8ae61d4a6da95e39dc9d2b3 - React-RCTVibration: b7b18d0cf7cffa5ba48a006b13e5cac337b61701 - ReactCommon: a309e51dec398e5095e9e07410fa25d9ef0fd4c2 - Yoga: 135abc3b97d777f821297e5dff54e5cec85568f8 + RCTRequired: f97536ed637e3f45ad5908168b0d74c446ccac01 + RCTTypeSafety: ede90b8a90e0dd629d19984788d434ae23828a64 + React: b8f3da30d525b068a54ac964abe933c8ca617dce + React-ART: d45ff8a3eb41b11513259371573fe77100d64b79 + React-Core: bd1d31a06447d4fad4e657c6c2d99e006e6ae92e + React-CoreModules: 39adfbf96f09b6fee8430cae929911add8e1921e + React-cxxreact: c190cf3bfc4cf67f5578bd7d1875b1e027d218c7 + React-jsi: 1e2cdb8a09f8dac172b58882e11229af86d4f708 + React-jsiexecutor: 3d3137d20b4b1df8d2f23fc51315922fd46d13d3 + React-jsinspector: 42d71e60149806237cfc4789a8968c89e91c17ce + React-RCTActionSheet: e6f9797fda2c1a40301bf090233d37ede984690f + React-RCTAnimation: 83e00862029ab643fea67cd371184da4ec371993 + React-RCTBlob: cd4dd353c0219e32c7f053056d4cc756ea029915 + React-RCTImage: c91c9ef999f4408096ee26d630499bb832264889 + React-RCTLinking: fad8541eab16a923fcbb5ac40b07e847aacbe662 + React-RCTNetwork: 5010fd890d40126287016a6940af04bf12f6286e + React-RCTPushNotification: 89628bdae834b098e4beb2a94d5e95f49446c066 + React-RCTSettings: 96f66fd85f85a92e92df41e611a88f0fb2a8512c + React-RCTTest: 58d069361c8efb625aadf65ee05ba0653ebb8a11 + React-RCTText: 961b9d9a3e4c97e6fcdfba4677a15b2bc9cd7102 + React-RCTVibration: ce6aa29f6c4a4f3dd5313f29a3e13fe9c8262887 + ReactCommon: fb3b55f28bfde51fe4806e7b12f581c401ea3312 + Yoga: e476e70ffbcbfce593ced8395f5d89f4d9503884 PODFILE CHECKSUM: 2bf56de39dc7e153b4f1637e0f4874f2591fc55a diff --git a/RNTester/RNTesterIntegrationTests/RNTesterLoadAllPages.m b/RNTester/RNTesterIntegrationTests/RNTesterLoadAllPages.m new file mode 100644 index 00000000000000..5a7a232e924f15 --- /dev/null +++ b/RNTester/RNTesterIntegrationTests/RNTesterLoadAllPages.m @@ -0,0 +1,97 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +// TODO(OSS Candidate ISS#2710739) + +#import +#import + +#import +#import + +#import + +@interface RNTesterLoadAllPages : XCTestCase +{ + RCTTestRunner *_runner; + NSString *_testName; +} + +@end + +@implementation RNTesterLoadAllPages + +- (void)setUp +{ + _runner = RCTInitRunnerForApp(@"RNTester/js/RNTesterApp", nil, nil); +} + +- (id)initWithName:(NSString *)testName { + // This method for dynamically adding tests borrowed from: + // https://github.com/google/google-toolbox-for-mac/blob/master/UnitTesting/GTMGoogleTestRunner.mm + // Xcode 6.1 started taking the testName from the selector instead of calling -name. + // So we will add selectors to this XCTestCase. + Class cls = [self class]; + NSString *selectorTestName = testName; + SEL selector = sel_registerName([selectorTestName UTF8String]); + Method method = class_getInstanceMethod(cls, @selector(internalTestRunner)); + IMP implementation = method_getImplementation(method); + const char *encoding = method_getTypeEncoding(method); + // We may be called more than once for the same testName. Check before adding new method to avoid + // failure from adding multiple methods with the same name. + if (!class_getInstanceMethod(cls, selector) && + !class_addMethod(cls, selector, implementation, encoding)) { + // If we can't add a method, we should blow up here. + [NSException raise:NSInternalInconsistencyException + format:@"Unable to add %@ to %@.", testName, cls]; + } + if ((self = [super initWithSelector:selector])) { + _testName = testName; + } + return self; +} + +- (NSString *)name { + return _testName; +} + ++ (XCTestSuite*)defaultTestSuite { + RCTTestRunner *runner = RCTInitRunnerForApp(@"RNTester/js/RNTesterApp", nil, nil); + + __block NSMutableArray *testNames = [NSMutableArray new]; + + RCTLogFunction defaultLogFunction = RCTGetLogFunction(); + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + defaultLogFunction(level, source, fileName, lineNumber, message); + if (level == RCTLogLevelTrace) { + [testNames addObject:message]; + } + }); + + [runner runTest:_cmd module:@"EnumerateExamplePages"]; + + RCTSetLogFunction(defaultLogFunction); + + XCTestSuite *suite = [XCTestSuite testSuiteForTestCaseClass:self]; + + for (NSString *testName in testNames) { + XCTestCase *testCase = [[self alloc] initWithName:testName]; + [suite addTest:testCase]; + } + + return suite; +} + +- (void)internalTestRunner +{ + // this performs the actual test + NSString *testName = [@"LoadPageTest_" stringByAppendingString:_testName]; + [_runner runTest:_cmd module:testName]; +} + +@end diff --git a/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m b/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m index 21b28f90f153ef..dd8c882c0d2711 100644 --- a/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m +++ b/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m @@ -43,7 +43,9 @@ - (void)test##name \ RCT_TEST(LayoutExample) RCT_TEST(ARTExample) RCT_TEST(ScrollViewExample) +#if !TARGET_OS_OSX // Reason: Intermittent failure: crash deallocating NSTextStorage of a TextView: tracked by https://github.com/microsoft/react-native-macos/issues/357 RCT_TEST(TextExample) +#endif #if !TARGET_OS_TV // No switch or slider available on tvOS RCT_TEST(SwitchExample) diff --git a/RNTester/RNTesterPods.xcodeproj/project.pbxproj b/RNTester/RNTesterPods.xcodeproj/project.pbxproj index 64f3063dd01dba..07d8e51aa1d9a8 100644 --- a/RNTester/RNTesterPods.xcodeproj/project.pbxproj +++ b/RNTester/RNTesterPods.xcodeproj/project.pbxproj @@ -54,6 +54,8 @@ 38B2630C2444F628006AB4D5 /* FlexibleSizeExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */; }; 38BB7CD524455EDD00803CDE /* OCMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38A93816244532460025DABB /* OCMock.framework */; }; 38BB7CFD244560CC00803CDE /* RNTesterUnitTests_macOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F153471233AB2C7006DFE44 /* RNTesterUnitTests_macOS.m */; }; + 38C8AC1F246611E500BA7FAA /* RNTesterLoadAllPages.m in Sources */ = {isa = PBXBuildFile; fileRef = 38C8AC1E246611E500BA7FAA /* RNTesterLoadAllPages.m */; }; + 38C8AC20246611E500BA7FAA /* RNTesterLoadAllPages.m in Sources */ = {isa = PBXBuildFile; fileRef = 38C8AC1E246611E500BA7FAA /* RNTesterLoadAllPages.m */; }; 38CB64352445042D009035CC /* RNTesterTurboModuleProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CB07C99226467E60039471C /* RNTesterTurboModuleProvider.mm */; }; 3D2AFAF51D646CF80089D1A3 /* legacy_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D2AFAF41D646CF80089D1A3 /* legacy_image@2x.png */; }; 413F654822EE16AED0DF2540 /* libPods-RNTester.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F94084777080D7B374FB4783 /* libPods-RNTester.a */; }; @@ -192,6 +194,7 @@ 38A93816244532460025DABB /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCMock.framework; sourceTree = ""; }; 38A9381B244532460025DABB /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = ""; }; 38BB7CB524455C4100803CDE /* libPods-RNTester-macOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libPods-RNTester-macOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 38C8AC1E246611E500BA7FAA /* RNTesterLoadAllPages.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNTesterLoadAllPages.m; sourceTree = ""; }; 3CCA44B8467CB06ACED8E0E9 /* Pods-RNTester-macOSIntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTester-macOSIntegrationTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RNTester-macOSIntegrationTests/Pods-RNTester-macOSIntegrationTests.debug.xcconfig"; sourceTree = ""; }; 3D2AFAF41D646CF80089D1A3 /* legacy_image@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "legacy_image@2x.png"; path = "RNTester/legacy_image@2x.png"; sourceTree = ""; }; 406E6935B300A29BA57EE1B0 /* Pods-RNTester-macOSUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTester-macOSUnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RNTester-macOSUnitTests/Pods-RNTester-macOSUnitTests.debug.xcconfig"; sourceTree = ""; }; @@ -631,6 +634,7 @@ isa = PBXGroup; children = ( E7C1241922BEC44B00DA25C0 /* RNTesterIntegrationTests.m */, + 38C8AC1E246611E500BA7FAA /* RNTesterLoadAllPages.m */, E7DB215E22B2F3EC005AC45F /* RCTLoggingTests.m */, E7DB216122B2F3EC005AC45F /* RCTRootViewIntegrationTests.m */, E7DB215F22B2F3EC005AC45F /* RCTUIManagerScenarioTests.m */, @@ -1300,6 +1304,7 @@ 383838E0244BC5A0005FAC75 /* RCTUIManagerScenarioTests.m in Sources */, 383838E2244BC5A8005FAC75 /* RNTesterTestModule.m in Sources */, 383838E1244BC5A4005FAC75 /* RNTesterSnapshotTests.m in Sources */, + 38C8AC20246611E500BA7FAA /* RNTesterLoadAllPages.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1347,6 +1352,7 @@ E7DB216622B2F3EC005AC45F /* RCTRootViewIntegrationTests.m in Sources */, E7DB216422B2F3EC005AC45F /* RCTUIManagerScenarioTests.m in Sources */, E7DB216522B2F3EC005AC45F /* RNTesterSnapshotTests.m in Sources */, + 38C8AC1F246611E500BA7FAA /* RNTesterLoadAllPages.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RNTester/js/RNTesterApp.ios.js b/RNTester/js/RNTesterApp.ios.js index be1f4c5e2755b2..42c8cf26cf2b8f 100644 --- a/RNTester/js/RNTesterApp.ios.js +++ b/RNTester/js/RNTesterApp.ios.js @@ -25,6 +25,8 @@ const { BackHandler, Button, Linking, + NativeModules, // TODO(OSS Candidate ISS#2710739) + Platform, // TODO(OSS Candidate ISS#2710739) SafeAreaView, StyleSheet, Text, @@ -32,6 +34,9 @@ const { YellowBox, } = require('react-native'); +const {TestModule} = NativeModules; // TODO(OSS Candidate ISS#2710739) +const requestAnimationFrame = require('fbjs/lib/requestAnimationFrame'); // TODO(OSS Candidate ISS#2710739) + import type {RNTesterExample} from './types/RNTesterTypes'; import type {RNTesterAction} from './utils/RNTesterActions'; import type {RNTesterNavigationState} from './utils/RNTesterNavigationReducer'; @@ -143,7 +148,16 @@ const styles = StyleSheet.create({ headerContainer: { borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: {semantic: 'separatorColor'}, // TODO(OSS Candidate ISS#2710739) - backgroundColor: {semantic: 'tertiarySystemBackgroundColor'}, // TODO(OSS Candidate ISS#2710739) + ...Platform.select({ + // [TODO(macOS ISS#2323203) + ios: { + backgroundColor: {semantic: 'tertiarySystemBackgroundColor'}, + }, + macos: { + backgroundColor: {semantic: 'windowBackgroundColor'}, + }, + }), + // ]TODO(macOS ISS#2323203) }, header: { height: 40, @@ -197,7 +211,54 @@ RNTesterList.ComponentExamples.concat(RNTesterList.APIExamples).forEach( () => Snapshotter, ); } + + // [TODO(OSS Candidate ISS#2710739) + class LoadPageTest extends React.Component<{}> { + componentDidMount() { + requestAnimationFrame(() => { + TestModule.markTestCompleted(); + }); + } + + render() { + return ; + } + } + + AppRegistry.registerComponent( + 'LoadPageTest_' + Example.key, + () => LoadPageTest, + ); + // ]TODO(OSS Candidate ISS#2710739) }, ); +// [TODO(OSS Candidate ISS#2710739) +class EnumerateExamplePages extends React.Component<{}> { + render() { + RNTesterList.ComponentExamples.concat(RNTesterList.APIExamples).forEach( + (Example: RNTesterExample) => { + let skipTest = false; + if ('skipTest' in Example) { + const platforms = Example.skipTest; + skipTest = + platforms !== undefined && + (Platform.OS in platforms || 'default' in platforms); + } + if (!skipTest) { + console.trace(Example.key); + } + }, + ); + TestModule.markTestCompleted(); + return ; + } +} + +AppRegistry.registerComponent( + 'EnumerateExamplePages', + () => EnumerateExamplePages, +); +// ]TODO(OSS Candidate ISS#2710739) + module.exports = RNTesterApp; diff --git a/RNTester/js/RNTesterApp.macos.js b/RNTester/js/RNTesterApp.macos.js index d2a78b85ea9f3d..3a748b8aa9f5b8 100644 --- a/RNTester/js/RNTesterApp.macos.js +++ b/RNTester/js/RNTesterApp.macos.js @@ -8,199 +8,9 @@ * @flow */ -// TODO(macOS ISS#2323203) Copied from RNTesterApp.ios.js +// TODO(macOS ISS#2323203) -'use strict'; - -const RNTesterActions = require('./utils/RNTesterActions'); -const RNTesterExampleContainer = require('./components/RNTesterExampleContainer'); -const RNTesterExampleList = require('./components/RNTesterExampleList'); -const RNTesterList = require('./utils/RNTesterList.macos'); -const RNTesterNavigationReducer = require('./utils/RNTesterNavigationReducer'); -const React = require('react'); /* $FlowFixMe allow macOS to share iOS file */ -const SnapshotViewIOS = require('./examples/Snapshot/SnapshotViewIOS.ios'); -const URIActionMap = require('./utils/URIActionMap'); - -const { - AppRegistry, - AsyncStorage, - BackHandler, - Button, - Linking, - SafeAreaView, - StyleSheet, - Text, - View, - YellowBox, -} = require('react-native'); - -import type {RNTesterExample} from './types/RNTesterTypes'; -import type {RNTesterAction} from './utils/RNTesterActions'; -import type {RNTesterNavigationState} from './utils/RNTesterNavigationReducer'; - -type Props = { - exampleFromAppetizeParams?: ?string, -}; - -YellowBox.ignoreWarnings([ - 'Module RCTImagePickerManager requires main queue setup', -]); - -const APP_STATE_KEY = 'RNTesterAppState.v2'; - -const Header = ({onBack, title}: {onBack?: () => mixed, title: string}) => ( - - - - {title} - - {onBack && ( - -