diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m new file mode 100644 index 00000000000000..0011b0524cf41f --- /dev/null +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m @@ -0,0 +1,109 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import "RCTAssert.h" +#import "RCTLog.h" +#import "RCTBridge.h" + +@interface RCTLoggingTests : XCTestCase + +@end + +@implementation RCTLoggingTests +{ + RCTBridge *_bridge; + + dispatch_semaphore_t _logSem; + RCTLogLevel _lastLogLevel; + RCTLogSource _lastLogSource; + NSString *_lastLogMessage; +} + +- (void)setUp +{ +#if RUNNING_ON_CI + NSURL *scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"]; + RCTAssert(scriptURL != nil, @"Could not locate main.jsBundle"); +#else + NSString *app = @"Examples/UIExplorer/UIExplorerIntegrationTests/js/IntegrationTestsApp"; + NSURL *scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]]; +#endif + + _bridge = [[RCTBridge alloc] initWithBundleURL:scriptURL moduleProvider:NULL launchOptions:nil]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5]; + while (date.timeIntervalSinceNow > 0 && _bridge.loading) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + } + XCTAssertFalse(_bridge.loading); + + _logSem = dispatch_semaphore_create(0); + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (source == RCTLogSourceJavaScript) { + _lastLogLevel = level; + _lastLogSource = source; + _lastLogMessage = message; + dispatch_semaphore_signal(_logSem); + } + }); +} + +- (void)tearDown +{ + [_bridge invalidate]; + _bridge = nil; + + RCTSetLogFunction(RCTDefaultLogFunction); +} + +- (void)testLogging +{ + [_bridge enqueueJSCall:@"LoggingTestModule.logToConsole" args:@[@"Invoking console.log"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelInfo); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Invoking console.log"); + + [_bridge enqueueJSCall:@"LoggingTestModule.warning" args:@[@"Generating warning"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelWarning); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Warning: Generating warning"); + + [_bridge enqueueJSCall:@"LoggingTestModule.invariant" args:@[@"Invariant failed"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Invariant Violation: Invariant failed"); + + [_bridge enqueueJSCall:@"LoggingTestModule.logErrorToConsole" args:@[@"Invoking console.error"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Invoking console.error"); + + [_bridge enqueueJSCall:@"LoggingTestModule.throwError" args:@[@"Throwing an error"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Throwing an error"); +} + +@end diff --git a/IntegrationTests/IntegrationTestsApp.js b/IntegrationTests/IntegrationTestsApp.js index 95c2697c17ab13..ce136f7fd6fafc 100644 --- a/IntegrationTests/IntegrationTestsApp.js +++ b/IntegrationTests/IntegrationTestsApp.js @@ -36,6 +36,9 @@ TESTS.forEach( (test) => AppRegistry.registerComponent(test.displayName, () => test) ); +// Modules required for integration tests +require('LoggingTestModule'); + var IntegrationTestsApp = React.createClass({ getInitialState: function() { return { diff --git a/IntegrationTests/LoggingTestModule.js b/IntegrationTests/LoggingTestModule.js new file mode 100644 index 00000000000000..96e59298d8bebf --- /dev/null +++ b/IntegrationTests/LoggingTestModule.js @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule LoggingTestModule + */ +'use strict'; + +var warning = require('warning'); +var invariant = require('invariant'); + +module.exports = { + logToConsole: function(str) { + console.log(str); + }, + warning: function(str) { + warning(false, str); + }, + invariant: function(str) { + invariant(false, str); + }, + logErrorToConsole: function(str) { + console.error(str); + }, + throwError: function(str) { + throw new Error(str); + } +}; diff --git a/packager/react-packager/src/Resolver/polyfills/console.js b/packager/react-packager/src/Resolver/polyfills/console.js index e08e8ef7ce7198..5461602b0ed43e 100644 --- a/packager/react-packager/src/Resolver/polyfills/console.js +++ b/packager/react-packager/src/Resolver/polyfills/console.js @@ -366,7 +366,6 @@ }; function setupConsole(global) { - var originalConsole = global.console; if (!global.nativeLoggingHook) { @@ -375,16 +374,23 @@ function getNativeLogFunction(level) { return function() { - var str = Array.prototype.map.call(arguments, function(arg) { - return inspect(arg, {depth: 10}); - }).join(', '); - if (str.slice(0, 10) === "'Warning: " && level >= LOG_LEVELS.error) { + var str; + if (arguments.length === 1 && typeof arguments[0] === 'string') { + str = arguments[0]; + } else { + str = Array.prototype.map.call(arguments, function(arg) { + return inspect(arg, {depth: 10}); + }).join(', '); + } + + var logLevel = level; + if (str.slice(0, 9) === 'Warning: ' && logLevel >= LOG_LEVELS.error) { // React warnings use console.error so that a stack trace is shown, // but we don't (currently) want these to show a redbox // (Note: Logic duplicated in ExceptionsManager.js.) - level = LOG_LEVELS.warn; + logLevel = LOG_LEVELS.warn; } - global.nativeLoggingHook(str, level); + global.nativeLoggingHook(str, logLevel); }; }