Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ReactAndroid/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- needed for screenshot tests -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- needed for image onError tests -->
<uses-permission android:name="android.permission.INTERNET" />

<application
android:hardwareAccelerated="false">
Expand Down
2 changes: 2 additions & 0 deletions ReactAndroid/src/androidTest/buck-runner/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- needed for screenshot tests -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- needed for image onError tests -->
<uses-permission android:name="android.permission.INTERNET" />

<application
android:hardwareAccelerated="false">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* 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.
*/

package com.facebook.react.tests;

import android.view.View;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.StringRecordingModule;

/**
* Simple test case to check that onError does not get called with undefined
*/
public class ImageErrorTestCase extends ReactAppInstrumentationTestCase {

private StringRecordingModule mStringRecordingModule;

@Override
protected String getReactApplicationKeyUnderTest() {
return "ImageErrorTestApp";
}

public void testErrorHasCause() throws Exception {
assertNotNull(getViewByTestId("image-1"));
assertNotNull(getViewByTestId("image-2"));
assertNotNull(getViewByTestId("image-3"));

Thread.sleep(3000);

assertEquals(3, mStringRecordingModule.getCalls().size());
assertEquals("Got error: Unsupported uri scheme! Uri is: ", mStringRecordingModule.getCalls().get(0));
assertEquals("Got error: /does/not/exist: open failed: ENOENT (No such file or directory)", mStringRecordingModule.getCalls().get(1));
assertEquals("Got error: Unexpected HTTP code Response{protocol=http/1.1, code=404, message=Not Found, url=https://typo_error_facebook.github.io/react/logo-og.png}", mStringRecordingModule.getCalls().get(2));
}

@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
mStringRecordingModule = new StringRecordingModule();
return super.createReactInstanceSpecForTest()
.addNativeModule(mStringRecordingModule);
}
}
58 changes: 58 additions & 0 deletions ReactAndroid/src/androidTest/js/ImageErrorTestApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* 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.
*
* @format
*/

'use strict';

const React = require('React');
const Image = require('Image');
const StyleSheet = require('StyleSheet');
const View = require('View');

const RecordingModule = require('NativeModules').Recording;

class ImageErrorTestApp extends React.Component {
onError = e => {
RecordingModule.record('Got error: ' + e.nativeEvent.error);
};

render() {
// For some reason image-2 needs explicit height. Without it onError is not triggered.
return (
<View>
<Image
testID="image-1"
source={{uri: '/does/not/exist'}}
onError={this.onError}
/>
<Image
testID="image-2"
source={{uri: 'file:///does/not/exist'}}
style={styles.image}
onError={this.onError}
/>
<Image
testID="image-3"
source={{
uri: 'https://TYPO_ERROR_facebook.github.io/react/logo-og.png',
}}
onError={this.onError}
/>
</View>
);
}
}

const styles = StyleSheet.create({
image: {
height: 50,
width: 50,
},
});

module.exports = ImageErrorTestApp;
4 changes: 4 additions & 0 deletions ReactAndroid/src/androidTest/js/TestBundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ const apps = [
appKey: 'ImageOverlayColorTestApp',
component: () => require('ImageOverlayColorTestApp'),
},
{
appKey: 'ImageErrorTestApp',
component: () => require('ImageErrorTestApp'),
},
{
appKey: 'InitialPropsTestApp',
component: () => require('InitialPropsTestApp'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,18 @@ public class ImageLoadEvent extends Event<ImageLoadEvent> {
private final @Nullable String mImageUri;
private final int mWidth;
private final int mHeight;
private final @Nullable String mImageError;

public ImageLoadEvent(int viewId, @ImageEventType int eventType) {
this(viewId, eventType, null);
}

public ImageLoadEvent(int viewId, @ImageEventType int eventType, boolean error, String message) {
this(viewId, eventType, null, 0, 0, message);
}

public ImageLoadEvent(int viewId, @ImageEventType int eventType, String imageUri) {
this(viewId, eventType, imageUri, 0, 0);
this(viewId, eventType, imageUri, 0, 0, null);
}

public ImageLoadEvent(
Expand All @@ -48,11 +53,22 @@ public ImageLoadEvent(
@Nullable String imageUri,
int width,
int height) {
this(viewId, eventType, imageUri, width, height, null);
}

public ImageLoadEvent(
int viewId,
@ImageEventType int eventType,
@Nullable String imageUri,
int width,
int height,
@Nullable String message) {
super(viewId);
mEventType = eventType;
mImageUri = imageUri;
mWidth = width;
mHeight = height;
mImageError = message;
}

public static String eventNameForType(@ImageEventType int eventType) {
Expand Down Expand Up @@ -88,7 +104,7 @@ public short getCoalescingKey() {
public void dispatch(RCTEventEmitter rctEventEmitter) {
WritableMap eventData = null;

if (mImageUri != null || mEventType == ON_LOAD) {
if (mImageUri != null || (mEventType == ON_LOAD || mEventType == ON_ERROR)) {
eventData = Arguments.createMap();

if (mImageUri != null) {
Expand All @@ -103,6 +119,8 @@ public void dispatch(RCTEventEmitter rctEventEmitter) {
source.putString("url", mImageUri);
}
eventData.putMap("source", source);
} else if (mEventType == ON_ERROR) {
eventData.putString("error", mImageError);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,8 @@ public void onFinalImageSet(
@Override
public void onFailure(String id, Throwable throwable) {
mEventDispatcher.dispatchEvent(
new ImageLoadEvent(getId(), ImageLoadEvent.ON_ERROR));
mEventDispatcher.dispatchEvent(
new ImageLoadEvent(getId(), ImageLoadEvent.ON_LOAD_END));
new ImageLoadEvent(getId(), ImageLoadEvent.ON_ERROR,
true, throwable.getMessage()));
}
};
}
Expand Down