mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e07bbbe24d | ||
|
|
579eb3f9fe | ||
|
|
a59e19240a | ||
|
|
204207f4c9 | ||
|
|
8bdef5d2c0 | ||
|
|
71f56fc2e3 | ||
|
|
d642e60291 |
+2
-1
@@ -1,12 +1,13 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
target = android-26
|
||||
|
||||
[download]
|
||||
max_number_of_retries = 3
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
google = https://maven.google.com
|
||||
|
||||
[alias]
|
||||
rntester = //RNTester/android/app:app
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* @generated by scripts/bump-oss-version.js
|
||||
*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @generated by scripts/bump-oss-version.js
|
||||
* @flow
|
||||
*/
|
||||
|
||||
exports.version = {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
minor: 56,
|
||||
patch: 0,
|
||||
prerelease: null,
|
||||
prerelease: 'rc.1',
|
||||
};
|
||||
|
||||
@@ -127,4 +127,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
if (!global.__RCTProfileIsProfiling) {
|
||||
const YellowBox = require('YellowBox');
|
||||
YellowBox.install();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppContainer;
|
||||
|
||||
@@ -1,554 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('EventEmitter');
|
||||
const Platform = require('Platform');
|
||||
const React = require('React');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const RCTLog = require('RCTLog');
|
||||
|
||||
const infoLog = require('infoLog');
|
||||
const openFileInEditor = require('openFileInEditor');
|
||||
const parseErrorStack = require('parseErrorStack');
|
||||
const stringifySafe = require('stringifySafe');
|
||||
const symbolicateStackTrace = require('symbolicateStackTrace');
|
||||
|
||||
import type EmitterSubscription from 'EmitterSubscription';
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
type WarningInfo = {
|
||||
count: number,
|
||||
stacktrace: Array<StackFrame>,
|
||||
symbolicated: boolean,
|
||||
};
|
||||
|
||||
const _warningEmitter = new EventEmitter();
|
||||
const _warningMap: Map<string, WarningInfo> = new Map();
|
||||
const IGNORED_WARNINGS: Array<string> = [];
|
||||
|
||||
/**
|
||||
* YellowBox renders warnings at the bottom of the app being developed.
|
||||
*
|
||||
* Warnings help guard against subtle yet significant issues that can impact the
|
||||
* quality of the app. This "in your face" style of warning allows developers to
|
||||
* notice and correct these issues as quickly as possible.
|
||||
*
|
||||
* By default, the warning box is enabled in `__DEV__`. Set the following flag
|
||||
* to disable it (and call `console.warn` to update any rendered <YellowBox>):
|
||||
*
|
||||
* console.disableYellowBox = true;
|
||||
* console.warn('YellowBox is disabled.');
|
||||
*
|
||||
* Ignore specific warnings by calling:
|
||||
*
|
||||
* YellowBox.ignoreWarnings(['Warning: ...']);
|
||||
*
|
||||
* (DEPRECATED) Warnings can be ignored programmatically by setting the array:
|
||||
*
|
||||
* console.ignoredYellowBox = ['Warning: ...'];
|
||||
*
|
||||
* Strings in `console.ignoredYellowBox` can be a prefix of the warning that
|
||||
* should be ignored.
|
||||
*/
|
||||
|
||||
if (__DEV__) {
|
||||
const {error, warn} = console;
|
||||
|
||||
(console: any).error = function() {
|
||||
error.apply(console, arguments);
|
||||
// Show yellow box for the `warning` module.
|
||||
if (
|
||||
typeof arguments[0] === 'string' &&
|
||||
arguments[0].startsWith('Warning: ')
|
||||
) {
|
||||
updateWarningMap.apply(null, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
(console: any).warn = function() {
|
||||
warn.apply(console, arguments);
|
||||
updateWarningMap.apply(null, arguments);
|
||||
};
|
||||
|
||||
if (Platform.isTesting) {
|
||||
(console: any).disableYellowBox = true;
|
||||
}
|
||||
|
||||
RCTLog.setWarningHandler((...args) => {
|
||||
updateWarningMap.apply(null, args);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple function for formatting strings.
|
||||
*
|
||||
* Replaces placeholders with values passed as extra arguments
|
||||
*
|
||||
* @param {string} format the base string
|
||||
* @param ...args the values to insert
|
||||
* @return {string} the replaced string
|
||||
*/
|
||||
function sprintf(format, ...args) {
|
||||
let index = 0;
|
||||
return format.replace(/%s/g, match => args[index++]);
|
||||
}
|
||||
|
||||
function updateWarningMap(...args): void {
|
||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an error
|
||||
* found when Flow v0.68 was deployed. To see the error delete this comment
|
||||
* and run Flow. */
|
||||
if (console.disableYellowBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
let warning;
|
||||
if (typeof args[0] === 'string') {
|
||||
const [format, ...formatArgs] = args;
|
||||
const argCount = (format.match(/%s/g) || []).length;
|
||||
warning = [
|
||||
sprintf(format, ...formatArgs.slice(0, argCount).map(stringifySafe)),
|
||||
...formatArgs.slice(argCount).map(stringifySafe),
|
||||
].join(' ');
|
||||
} else {
|
||||
warning = args.map(stringifySafe).join(' ');
|
||||
}
|
||||
|
||||
if (warning.startsWith('(ADVICE)')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const warningInfo = _warningMap.get(warning);
|
||||
if (warningInfo) {
|
||||
warningInfo.count += 1;
|
||||
} else {
|
||||
const error: any = new Error();
|
||||
error.framesToPop = 2;
|
||||
|
||||
_warningMap.set(warning, {
|
||||
count: 1,
|
||||
stacktrace: parseErrorStack(error),
|
||||
symbolicated: false,
|
||||
});
|
||||
}
|
||||
|
||||
_warningEmitter.emit('warning', _warningMap);
|
||||
}
|
||||
|
||||
function ensureSymbolicatedWarning(warning: string): void {
|
||||
const prevWarningInfo = _warningMap.get(warning);
|
||||
if (!prevWarningInfo || prevWarningInfo.symbolicated) {
|
||||
return;
|
||||
}
|
||||
prevWarningInfo.symbolicated = true;
|
||||
|
||||
symbolicateStackTrace(prevWarningInfo.stacktrace).then(
|
||||
stack => {
|
||||
const nextWarningInfo = _warningMap.get(warning);
|
||||
if (nextWarningInfo) {
|
||||
nextWarningInfo.stacktrace = stack;
|
||||
_warningEmitter.emit('warning', _warningMap);
|
||||
}
|
||||
},
|
||||
error => {
|
||||
const nextWarningInfo = _warningMap.get(warning);
|
||||
if (nextWarningInfo) {
|
||||
infoLog('Failed to symbolicate warning, "%s":', warning, error);
|
||||
_warningEmitter.emit('warning', _warningMap);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function isWarningIgnored(warning: string): boolean {
|
||||
const isIgnored = IGNORED_WARNINGS.some((ignoredWarning: string) =>
|
||||
warning.includes(ignoredWarning),
|
||||
);
|
||||
|
||||
if (isIgnored) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
return (
|
||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.68 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
Array.isArray(console.ignoredYellowBox) &&
|
||||
console.ignoredYellowBox.some(ignorePrefix =>
|
||||
warning.startsWith(String(ignorePrefix)),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const WarningRow = ({count, warning, onPress}) => {
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
const View = require('View');
|
||||
|
||||
const countText =
|
||||
count > 1 ? (
|
||||
<Text style={styles.listRowCount}>{'(' + count + ') '}</Text>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<View style={styles.listRow}>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
onPress={onPress}
|
||||
style={styles.listRowContent}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.listRowText} numberOfLines={2}>
|
||||
{countText}
|
||||
{warning}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
type StackRowProps = {frame: StackFrame};
|
||||
const StackRow = ({frame}: StackRowProps) => {
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
const {file, lineNumber} = frame;
|
||||
let fileName;
|
||||
if (file) {
|
||||
const fileParts = file.split('/');
|
||||
fileName = fileParts[fileParts.length - 1];
|
||||
} else {
|
||||
fileName = '<unknown file>';
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
style={styles.openInEditorButton}
|
||||
underlayColor="transparent"
|
||||
onPress={openFileInEditor.bind(null, file, lineNumber)}>
|
||||
<Text style={styles.inspectorCountText}>
|
||||
{fileName}:{lineNumber}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
};
|
||||
|
||||
const WarningInspector = ({
|
||||
warningInfo,
|
||||
warning,
|
||||
stacktraceVisible,
|
||||
onDismiss,
|
||||
onMinimize,
|
||||
toggleStacktrace,
|
||||
}) => {
|
||||
const ScrollView = require('ScrollView');
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
const View = require('View');
|
||||
const {count, stacktrace} = warningInfo || {};
|
||||
|
||||
const countSentence =
|
||||
'Warning encountered ' + count + ' time' + (count - 1 ? 's' : '') + '.';
|
||||
|
||||
let stacktraceList;
|
||||
if (stacktraceVisible && stacktrace) {
|
||||
stacktraceList = (
|
||||
<View style={styles.stacktraceList}>
|
||||
{stacktrace.map((frame, ii) => <StackRow frame={frame} key={ii} />)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.inspector}>
|
||||
<SafeAreaView style={styles.safeArea}>
|
||||
<View style={styles.inspectorCount}>
|
||||
<Text style={styles.inspectorCountText}>{countSentence}</Text>
|
||||
<TouchableHighlight
|
||||
onPress={toggleStacktrace}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.inspectorButtonText}>
|
||||
{stacktraceVisible ? '\u{25BC}' : '\u{25B6}'} Stacktrace
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
<ScrollView style={styles.inspectorWarning}>
|
||||
{stacktraceList}
|
||||
<Text style={styles.inspectorWarningText}>{warning}</Text>
|
||||
</ScrollView>
|
||||
<View style={styles.inspectorButtons}>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
onPress={onMinimize}
|
||||
style={styles.inspectorButton}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.inspectorButtonText}>Minimize</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
activeOpacity={0.5}
|
||||
onPress={onDismiss}
|
||||
style={styles.inspectorButton}
|
||||
underlayColor="transparent">
|
||||
<Text style={styles.inspectorButtonText}>Dismiss</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
class YellowBox extends React.Component<
|
||||
mixed,
|
||||
{
|
||||
stacktraceVisible: boolean,
|
||||
inspecting: ?string,
|
||||
warningMap: Map<any, any>,
|
||||
},
|
||||
> {
|
||||
_listener: ?EmitterSubscription;
|
||||
dismissWarning: (warning: ?string) => void;
|
||||
|
||||
constructor(props: mixed, context: mixed) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
inspecting: null,
|
||||
stacktraceVisible: false,
|
||||
warningMap: _warningMap,
|
||||
};
|
||||
this.dismissWarning = warning => {
|
||||
const {inspecting, warningMap} = this.state;
|
||||
if (warning) {
|
||||
warningMap.delete(warning);
|
||||
} else {
|
||||
warningMap.clear();
|
||||
}
|
||||
this.setState({
|
||||
inspecting: warning && inspecting !== warning ? inspecting : null,
|
||||
warningMap,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
static ignoreWarnings(warnings: Array<string>): void {
|
||||
warnings.forEach((warning: string) => {
|
||||
if (IGNORED_WARNINGS.indexOf(warning) === -1) {
|
||||
IGNORED_WARNINGS.push(warning);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let scheduled = null;
|
||||
this._listener = _warningEmitter.addListener('warning', warningMap => {
|
||||
// Use `setImmediate` because warnings often happen during render, but
|
||||
// state cannot be set while rendering.
|
||||
scheduled =
|
||||
scheduled ||
|
||||
setImmediate(() => {
|
||||
scheduled = null;
|
||||
this.setState({
|
||||
warningMap,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const {inspecting} = this.state;
|
||||
if (inspecting != null) {
|
||||
ensureSymbolicatedWarning(inspecting);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._listener) {
|
||||
this._listener.remove();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.68 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
if (console.disableYellowBox || this.state.warningMap.size === 0) {
|
||||
return null;
|
||||
}
|
||||
const ScrollView = require('ScrollView');
|
||||
const View = require('View');
|
||||
const Text = require('Text');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
|
||||
const {inspecting, stacktraceVisible} = this.state;
|
||||
const inspector =
|
||||
inspecting !== null ? (
|
||||
<WarningInspector
|
||||
warningInfo={this.state.warningMap.get(inspecting)}
|
||||
warning={inspecting}
|
||||
stacktraceVisible={stacktraceVisible}
|
||||
onDismiss={() => this.dismissWarning(inspecting)}
|
||||
onMinimize={() => this.setState({inspecting: null})}
|
||||
toggleStacktrace={() =>
|
||||
this.setState({stacktraceVisible: !stacktraceVisible})
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const rows = [];
|
||||
this.state.warningMap.forEach((warningInfo, warning) => {
|
||||
if (!isWarningIgnored(warning)) {
|
||||
rows.push(
|
||||
<WarningRow
|
||||
key={warning}
|
||||
count={warningInfo.count}
|
||||
warning={warning}
|
||||
onPress={() => this.setState({inspecting: warning})}
|
||||
onDismiss={() => this.dismissWarning(warning)}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const listStyle = [
|
||||
styles.list,
|
||||
// Additional `0.4` so the 5th row can peek into view.
|
||||
{height: Math.min(rows.length, 4.4) * (rowGutter + rowHeight)},
|
||||
];
|
||||
return (
|
||||
<View style={inspector ? styles.fullScreen : listStyle}>
|
||||
{!inspector &&
|
||||
rows.length > 0 && (
|
||||
<TouchableHighlight
|
||||
style={styles.dismissAllContainer}
|
||||
onPress={() => this.dismissWarning(null)}>
|
||||
<Text style={styles.dismissAll}>Dismiss All</Text>
|
||||
</TouchableHighlight>
|
||||
)}
|
||||
<ScrollView style={listStyle} scrollsToTop={false}>
|
||||
{rows}
|
||||
</ScrollView>
|
||||
{inspector}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const backgroundColor = opacity => 'rgba(250, 186, 48, ' + opacity + ')';
|
||||
const textColor = 'white';
|
||||
const rowGutter = 1;
|
||||
const rowHeight = 46;
|
||||
|
||||
// For unknown reasons, setting elevation: Number.MAX_VALUE causes remote debugging to
|
||||
// hang on iOS (some sort of overflow maybe). Setting it to Number.MAX_SAFE_INTEGER fixes the iOS issue, but since
|
||||
// elevation is an android-only style property we might as well remove it altogether for iOS.
|
||||
// See: https://github.com/facebook/react-native/issues/12223
|
||||
const elevation =
|
||||
Platform.OS === 'android' ? Number.MAX_SAFE_INTEGER : undefined;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullScreen: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
elevation: elevation,
|
||||
position: 'absolute',
|
||||
},
|
||||
inspector: {
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
height: '100%',
|
||||
paddingTop: 5,
|
||||
elevation: elevation,
|
||||
},
|
||||
inspectorButtons: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
inspectorButton: {
|
||||
flex: 1,
|
||||
paddingVertical: 22,
|
||||
backgroundColor: backgroundColor(1),
|
||||
},
|
||||
safeArea: {
|
||||
flex: 1,
|
||||
},
|
||||
stacktraceList: {
|
||||
paddingBottom: 5,
|
||||
},
|
||||
inspectorButtonText: {
|
||||
color: textColor,
|
||||
fontSize: 14,
|
||||
opacity: 0.8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
openInEditorButton: {
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5,
|
||||
},
|
||||
inspectorCount: {
|
||||
padding: 15,
|
||||
paddingBottom: 0,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
inspectorCountText: {
|
||||
color: textColor,
|
||||
fontSize: 14,
|
||||
},
|
||||
inspectorWarning: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 15,
|
||||
},
|
||||
inspectorWarningText: {
|
||||
color: textColor,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
list: {
|
||||
backgroundColor: 'transparent',
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
elevation: elevation,
|
||||
},
|
||||
listRow: {
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
height: rowHeight,
|
||||
marginTop: rowGutter,
|
||||
},
|
||||
listRowContent: {
|
||||
flex: 1,
|
||||
},
|
||||
listRowCount: {
|
||||
color: 'rgba(255, 255, 255, 0.5)',
|
||||
},
|
||||
listRowText: {
|
||||
color: textColor,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: Platform.OS === 'android' ? 5 : 7,
|
||||
marginLeft: 15,
|
||||
marginRight: 15,
|
||||
},
|
||||
dismissAllContainer: {
|
||||
height: 20,
|
||||
justifyContent: 'center',
|
||||
marginTop: -30,
|
||||
marginRight: 5,
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
alignSelf: 'flex-end',
|
||||
paddingHorizontal: 10,
|
||||
borderRadius: 10,
|
||||
},
|
||||
dismissAll: {
|
||||
color: 'white',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBox;
|
||||
@@ -92,10 +92,10 @@ export type ____LayoutStyle_Internal = $ReadOnly<{|
|
||||
export type ____TransformStyle_Internal = $ReadOnly<{|
|
||||
transform?: $ReadOnlyArray<
|
||||
| {|+perspective: number | AnimatedNode|}
|
||||
| {|+rotate: string|}
|
||||
| {|+rotateX: string|}
|
||||
| {|+rotateY: string|}
|
||||
| {|+rotateZ: string|}
|
||||
| {|+rotate: string | AnimatedNode|}
|
||||
| {|+rotateX: string | AnimatedNode|}
|
||||
| {|+rotateY: string | AnimatedNode|}
|
||||
| {|+rotateZ: string | AnimatedNode|}
|
||||
| {|+scale: number | AnimatedNode|}
|
||||
| {|+scaleX: number | AnimatedNode|}
|
||||
| {|+scaleY: number | AnimatedNode|}
|
||||
|
||||
@@ -20,6 +20,7 @@ const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationI
|
||||
* - Less chance of typos.
|
||||
*/
|
||||
const UTFSequence = deepFreezeAndThrowOnMutationInDev({
|
||||
BOM: '\ufeff', // byte order mark
|
||||
BULLET: '\u2022', // bullet: •
|
||||
BULLET_SP: '\u00A0\u2022\u00A0', // •
|
||||
MIDDOT: '\u00B7', // normal middle dot: ·
|
||||
@@ -31,6 +32,8 @@ const UTFSequence = deepFreezeAndThrowOnMutationInDev({
|
||||
NDASH_SP: '\u00A0\u2013\u00A0', // –
|
||||
NBSP: '\u00A0', // non-breaking space:
|
||||
PIZZA: '\uD83C\uDF55',
|
||||
TRIANGLE_LEFT: '\u25c0', // black left-pointing triangle
|
||||
TRIANGLE_RIGHT: '\u25b6', // black right-pointing triangle
|
||||
});
|
||||
|
||||
module.exports = UTFSequence;
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const Text = require('Text');
|
||||
const UTFSequence = require('UTFSequence');
|
||||
|
||||
const stringifySafe = require('stringifySafe');
|
||||
|
||||
import type {TextStyleProp} from 'StyleSheet';
|
||||
|
||||
export type Category = string;
|
||||
export type Message = $ReadOnly<{|
|
||||
content: string,
|
||||
substitutions: $ReadOnlyArray<
|
||||
$ReadOnly<{|
|
||||
length: number,
|
||||
offset: number,
|
||||
|}>,
|
||||
>,
|
||||
|}>;
|
||||
|
||||
const SUBSTITUTION = UTFSequence.BOM + '%s';
|
||||
|
||||
const YellowBoxCategory = {
|
||||
parse(
|
||||
args: $ReadOnlyArray<mixed>,
|
||||
): $ReadOnly<{|
|
||||
category: Category,
|
||||
message: Message,
|
||||
|}> {
|
||||
const categoryParts = [];
|
||||
const contentParts = [];
|
||||
const substitutionOffsets = [];
|
||||
|
||||
const remaining = [...args];
|
||||
|
||||
if (typeof remaining[0] === 'string') {
|
||||
const formatString = String(remaining.shift());
|
||||
const formatStringParts = formatString.split('%s');
|
||||
const substitutionCount = formatStringParts.length - 1;
|
||||
const substitutions = remaining.splice(0, substitutionCount);
|
||||
|
||||
let categoryString = '';
|
||||
let contentString = '';
|
||||
|
||||
let substitutionIndex = 0;
|
||||
for (const formatStringPart of formatStringParts) {
|
||||
categoryString += formatStringPart;
|
||||
contentString += formatStringPart;
|
||||
|
||||
if (substitutionIndex < substitutionCount) {
|
||||
if (substitutionIndex < substitutions.length) {
|
||||
const substitution = stringifySafe(
|
||||
substitutions[substitutionIndex],
|
||||
);
|
||||
substitutionOffsets.push({
|
||||
length: substitution.length,
|
||||
offset: contentString.length,
|
||||
});
|
||||
|
||||
categoryString += SUBSTITUTION;
|
||||
contentString += substitution;
|
||||
} else {
|
||||
substitutionOffsets.push({
|
||||
length: 2,
|
||||
offset: contentString.length,
|
||||
});
|
||||
|
||||
categoryString += '%s';
|
||||
contentString += '%s';
|
||||
}
|
||||
|
||||
substitutionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
categoryParts.push(categoryString);
|
||||
contentParts.push(contentString);
|
||||
}
|
||||
|
||||
const remainingArgs = remaining.map(stringifySafe);
|
||||
categoryParts.push(...remainingArgs);
|
||||
contentParts.push(...remainingArgs);
|
||||
|
||||
return {
|
||||
category: categoryParts.join(' '),
|
||||
message: {
|
||||
content: contentParts.join(' '),
|
||||
substitutions: substitutionOffsets,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
render(
|
||||
{content, substitutions}: Message,
|
||||
substitutionStyle: TextStyleProp,
|
||||
): React.Node {
|
||||
const elements = [];
|
||||
|
||||
const lastOffset = substitutions.reduce(
|
||||
(prevOffset, substitution, index) => {
|
||||
const key = String(index);
|
||||
|
||||
if (substitution.offset > prevOffset) {
|
||||
const prevPart = content.substr(
|
||||
prevOffset,
|
||||
substitution.offset - prevOffset,
|
||||
);
|
||||
elements.push(<Text key={key}>{prevPart}</Text>);
|
||||
}
|
||||
|
||||
const substititionPart = content.substr(
|
||||
substitution.offset,
|
||||
substitution.length,
|
||||
);
|
||||
elements.push(
|
||||
<Text key={key + '.5'} style={substitutionStyle}>
|
||||
{substititionPart}
|
||||
</Text>,
|
||||
);
|
||||
|
||||
return substitution.offset + substitution.length;
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
if (lastOffset < content.length - 1) {
|
||||
const lastPart = content.substr(lastOffset);
|
||||
elements.push(<Text key="-1">{lastPart}</Text>);
|
||||
}
|
||||
|
||||
return elements;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = YellowBoxCategory;
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxWarning = require('YellowBoxWarning');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
|
||||
export type Registry = Map<Category, $ReadOnlyArray<YellowBoxWarning>>;
|
||||
|
||||
export type Observer = (registry: Registry) => void;
|
||||
|
||||
export type Subscription = $ReadOnly<{|
|
||||
unsubscribe: () => void,
|
||||
|}>;
|
||||
|
||||
const observers: Set<{observer: Observer}> = new Set();
|
||||
const ignorePatterns: Set<string> = new Set();
|
||||
const registry: Registry = new Map();
|
||||
|
||||
let disabled = false;
|
||||
let projection = new Map();
|
||||
let updateTimeout = null;
|
||||
|
||||
function isWarningIgnored(warning: YellowBoxWarning): boolean {
|
||||
for (const pattern of ignorePatterns) {
|
||||
if (warning.message.content.includes(pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleUpdate(): void {
|
||||
projection = new Map();
|
||||
if (!disabled) {
|
||||
for (const [category, warnings] of registry) {
|
||||
const filtered = warnings.filter(warning => !isWarningIgnored(warning));
|
||||
if (filtered.length > 0) {
|
||||
projection.set(category, filtered);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updateTimeout == null) {
|
||||
updateTimeout = setImmediate(() => {
|
||||
updateTimeout = null;
|
||||
for (const {observer} of observers) {
|
||||
observer(projection);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const YellowBoxRegistry = {
|
||||
add({
|
||||
args,
|
||||
framesToPop,
|
||||
}: $ReadOnly<{|
|
||||
args: $ReadOnlyArray<mixed>,
|
||||
framesToPop: number,
|
||||
|}>): void {
|
||||
if (typeof args[0] === 'string' && args[0].startsWith('(ADVICE)')) {
|
||||
return;
|
||||
}
|
||||
const {category, message, stack} = YellowBoxWarning.parse({
|
||||
args,
|
||||
framesToPop: framesToPop + 1,
|
||||
});
|
||||
|
||||
let warnings = registry.get(category);
|
||||
if (warnings == null) {
|
||||
warnings = [];
|
||||
}
|
||||
warnings = [...warnings, new YellowBoxWarning(message, stack)];
|
||||
|
||||
registry.delete(category);
|
||||
registry.set(category, warnings);
|
||||
|
||||
handleUpdate();
|
||||
},
|
||||
|
||||
delete(category: Category): void {
|
||||
if (registry.has(category)) {
|
||||
registry.delete(category);
|
||||
handleUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
clear(): void {
|
||||
if (registry.size > 0) {
|
||||
registry.clear();
|
||||
handleUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
addIgnorePatterns(patterns: $ReadOnlyArray<string>): void {
|
||||
const newPatterns = patterns.filter(
|
||||
pattern => !ignorePatterns.has(pattern),
|
||||
);
|
||||
if (newPatterns.length === 0) {
|
||||
return;
|
||||
}
|
||||
for (const pattern of newPatterns) {
|
||||
ignorePatterns.add(pattern);
|
||||
}
|
||||
handleUpdate();
|
||||
},
|
||||
|
||||
setDisabled(value: boolean): void {
|
||||
if (value === disabled) {
|
||||
return;
|
||||
}
|
||||
disabled = value;
|
||||
handleUpdate();
|
||||
},
|
||||
|
||||
isDisabled(): boolean {
|
||||
return disabled;
|
||||
},
|
||||
|
||||
observe(observer: Observer): Subscription {
|
||||
const subscription = {observer};
|
||||
observers.add(subscription);
|
||||
observer(projection);
|
||||
return {
|
||||
unsubscribe(): void {
|
||||
observers.delete(subscription);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = YellowBoxRegistry;
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const symbolicateStackTrace = require('symbolicateStackTrace');
|
||||
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
type CacheKey = string;
|
||||
|
||||
export type Stack = Array<StackFrame>;
|
||||
|
||||
const cache: Map<CacheKey, Promise<Stack>> = new Map();
|
||||
|
||||
const YellowBoxSymbolication = {
|
||||
symbolicate(stack: Stack): Promise<Stack> {
|
||||
const key = getCacheKey(stack);
|
||||
|
||||
let promise = cache.get(key);
|
||||
if (promise == null) {
|
||||
promise = symbolicateStackTrace(stack).then(sanitize);
|
||||
cache.set(key, promise);
|
||||
}
|
||||
|
||||
return promise;
|
||||
},
|
||||
};
|
||||
|
||||
const getCacheKey = (stack: Stack): CacheKey => {
|
||||
return JSON.stringify(stack);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sanitize because sometimes, `symbolicateStackTrace` gives us invalid values.
|
||||
*/
|
||||
const sanitize = (maybeStack: mixed): Stack => {
|
||||
if (!Array.isArray(maybeStack)) {
|
||||
throw new Error('Expected stack to be an array.');
|
||||
}
|
||||
const stack = [];
|
||||
for (const maybeFrame of maybeStack) {
|
||||
if (typeof maybeFrame !== 'object' || maybeFrame == null) {
|
||||
throw new Error('Expected each stack frame to be an object.');
|
||||
}
|
||||
if (typeof maybeFrame.column !== 'number' && maybeFrame.column != null) {
|
||||
throw new Error('Expected stack frame `column` to be a nullable number.');
|
||||
}
|
||||
if (typeof maybeFrame.file !== 'string') {
|
||||
throw new Error('Expected stack frame `file` to be a string.');
|
||||
}
|
||||
if (typeof maybeFrame.lineNumber !== 'number') {
|
||||
throw new Error('Expected stack frame `lineNumber` to be a number.');
|
||||
}
|
||||
if (typeof maybeFrame.methodName !== 'string') {
|
||||
throw new Error('Expected stack frame `methodName` to be a string.');
|
||||
}
|
||||
stack.push({
|
||||
column: maybeFrame.column,
|
||||
file: maybeFrame.file,
|
||||
lineNumber: maybeFrame.lineNumber,
|
||||
methodName: maybeFrame.methodName,
|
||||
});
|
||||
}
|
||||
return stack;
|
||||
};
|
||||
|
||||
module.exports = YellowBoxSymbolication;
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxSymbolication = require('YellowBoxSymbolication');
|
||||
|
||||
const parseErrorStack = require('parseErrorStack');
|
||||
|
||||
import type {Category, Message} from 'YellowBoxCategory';
|
||||
import type {Stack} from 'YellowBoxSymbolication';
|
||||
|
||||
export type SymbolicationRequest = $ReadOnly<{|
|
||||
abort: () => void,
|
||||
|}>;
|
||||
|
||||
class YellowBoxWarning {
|
||||
static parse({
|
||||
args,
|
||||
framesToPop,
|
||||
}: $ReadOnly<{|
|
||||
args: $ReadOnlyArray<mixed>,
|
||||
framesToPop: number,
|
||||
|}>): {|
|
||||
category: Category,
|
||||
message: Message,
|
||||
stack: Stack,
|
||||
|} {
|
||||
return {
|
||||
...YellowBoxCategory.parse(args),
|
||||
stack: createStack({framesToPop: framesToPop + 1}),
|
||||
};
|
||||
}
|
||||
|
||||
message: Message;
|
||||
stack: Stack;
|
||||
symbolicated:
|
||||
| $ReadOnly<{|error: null, stack: null, status: 'NONE'|}>
|
||||
| $ReadOnly<{|error: null, stack: null, status: 'PENDING'|}>
|
||||
| $ReadOnly<{|error: null, stack: Stack, status: 'COMPLETE'|}>
|
||||
| $ReadOnly<{|error: Error, stack: null, status: 'FAILED'|}> = {
|
||||
error: null,
|
||||
stack: null,
|
||||
status: 'NONE',
|
||||
};
|
||||
|
||||
constructor(message: Message, stack: Stack) {
|
||||
this.message = message;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
getAvailableStack(): Stack {
|
||||
return this.symbolicated.status === 'COMPLETE'
|
||||
? this.symbolicated.stack
|
||||
: this.stack;
|
||||
}
|
||||
|
||||
symbolicate(callback: () => void): SymbolicationRequest {
|
||||
let aborted = false;
|
||||
|
||||
if (this.symbolicated.status !== 'COMPLETE') {
|
||||
const updateStatus = (error: ?Error, stack: ?Stack): void => {
|
||||
if (error != null) {
|
||||
this.symbolicated = {error, stack: null, status: 'FAILED'};
|
||||
} else if (stack != null) {
|
||||
this.symbolicated = {error: null, stack, status: 'COMPLETE'};
|
||||
} else {
|
||||
this.symbolicated = {error: null, stack: null, status: 'PENDING'};
|
||||
}
|
||||
if (!aborted) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
updateStatus(null, null);
|
||||
YellowBoxSymbolication.symbolicate(this.stack).then(
|
||||
stack => {
|
||||
updateStatus(null, stack);
|
||||
},
|
||||
error => {
|
||||
updateStatus(error, null);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
abort(): void {
|
||||
aborted = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function createStack({framesToPop}: $ReadOnly<{|framesToPop: number|}>): Stack {
|
||||
const error: any = new Error();
|
||||
error.framesToPop = framesToPop + 1;
|
||||
return parseErrorStack(error);
|
||||
}
|
||||
|
||||
module.exports = YellowBoxWarning;
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
|
||||
describe('YellowBoxCategory', () => {
|
||||
it('parses strings', () => {
|
||||
expect(YellowBoxCategory.parse(['A'])).toEqual({
|
||||
category: 'A',
|
||||
message: {
|
||||
content: 'A',
|
||||
substitutions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses strings with arguments', () => {
|
||||
expect(YellowBoxCategory.parse(['A', 'B', 'C'])).toEqual({
|
||||
category: 'A "B" "C"',
|
||||
message: {
|
||||
content: 'A "B" "C"',
|
||||
substitutions: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses formatted strings', () => {
|
||||
expect(YellowBoxCategory.parse(['%s', 'A'])).toEqual({
|
||||
category: '\ufeff%s',
|
||||
message: {
|
||||
content: '"A"',
|
||||
substitutions: [
|
||||
{
|
||||
length: 3,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses formatted strings with insufficient arguments', () => {
|
||||
expect(YellowBoxCategory.parse(['%s %s', 'A'])).toEqual({
|
||||
category: '\ufeff%s %s',
|
||||
message: {
|
||||
content: '"A" %s',
|
||||
substitutions: [
|
||||
{
|
||||
length: 3,
|
||||
offset: 0,
|
||||
},
|
||||
{
|
||||
length: 2,
|
||||
offset: 4,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('parses formatted strings with excess arguments', () => {
|
||||
expect(YellowBoxCategory.parse(['%s', 'A', 'B'])).toEqual({
|
||||
category: '\ufeff%s "B"',
|
||||
message: {
|
||||
content: '"A" "B"',
|
||||
substitutions: [
|
||||
{
|
||||
length: 3,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('treats "%s" in arguments as literals', () => {
|
||||
expect(YellowBoxCategory.parse(['%s', '%s', 'A'])).toEqual({
|
||||
category: '\ufeff%s "A"',
|
||||
message: {
|
||||
content: '"%s" "A"',
|
||||
substitutions: [
|
||||
{
|
||||
length: 4,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxRegistry = require('YellowBoxRegistry');
|
||||
|
||||
const registry = () => {
|
||||
const observer = jest.fn();
|
||||
YellowBoxRegistry.observe(observer).unsubscribe();
|
||||
return observer.mock.calls[0][0];
|
||||
};
|
||||
|
||||
const observe = () => {
|
||||
const observer = jest.fn();
|
||||
return {
|
||||
observer,
|
||||
subscription: YellowBoxRegistry.observe(observer),
|
||||
};
|
||||
};
|
||||
|
||||
describe('YellowBoxRegistry', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
it('adds and deletes warnings', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
const {category: categoryA} = YellowBoxCategory.parse(['A']);
|
||||
|
||||
expect(registry().size).toBe(1);
|
||||
expect(registry().get(categoryA)).not.toBe(undefined);
|
||||
|
||||
YellowBoxRegistry.delete(categoryA);
|
||||
expect(registry().size).toBe(0);
|
||||
expect(registry().get(categoryA)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('clears all warnings', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C'], framesToPop: 0});
|
||||
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.clear();
|
||||
expect(registry().size).toBe(0);
|
||||
});
|
||||
|
||||
it('sorts warnings in chronological order', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C'], framesToPop: 0});
|
||||
|
||||
const {category: categoryA} = YellowBoxCategory.parse(['A']);
|
||||
const {category: categoryB} = YellowBoxCategory.parse(['B']);
|
||||
const {category: categoryC} = YellowBoxCategory.parse(['C']);
|
||||
|
||||
expect(Array.from(registry().keys())).toEqual([
|
||||
categoryA,
|
||||
categoryB,
|
||||
categoryC,
|
||||
]);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
|
||||
// Expect `A` to be hoisted to the end of the registry.
|
||||
expect(Array.from(registry().keys())).toEqual([
|
||||
categoryB,
|
||||
categoryC,
|
||||
categoryA,
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignores warnings matching patterns', () => {
|
||||
YellowBoxRegistry.add({args: ['A!'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B?'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C!'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['!']);
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['?']);
|
||||
expect(registry().size).toBe(0);
|
||||
});
|
||||
|
||||
it('ignores all warnings when disabled', () => {
|
||||
YellowBoxRegistry.add({args: ['A!'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B?'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['C!'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
expect(registry().size).toBe(0);
|
||||
|
||||
YellowBoxRegistry.setDisabled(false);
|
||||
expect(registry().size).toBe(3);
|
||||
});
|
||||
|
||||
it('groups warnings by simple categories', () => {
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(2);
|
||||
});
|
||||
|
||||
it('groups warnings by format string categories', () => {
|
||||
YellowBoxRegistry.add({args: ['%s', 'A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(2);
|
||||
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
});
|
||||
|
||||
it('groups warnings with consideration for arguments', () => {
|
||||
YellowBoxRegistry.add({args: ['A', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A', 'C'], framesToPop: 0});
|
||||
expect(registry().size).toBe(2);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'A', 'A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'B', 'A'], framesToPop: 0});
|
||||
expect(registry().size).toBe(3);
|
||||
|
||||
YellowBoxRegistry.add({args: ['%s', 'B', 'B'], framesToPop: 0});
|
||||
expect(registry().size).toBe(4);
|
||||
});
|
||||
|
||||
it('ignores warnings starting with "(ADVICE)"', () => {
|
||||
YellowBoxRegistry.add({args: ['(ADVICE) ...'], framesToPop: 0});
|
||||
expect(registry().size).toBe(0);
|
||||
});
|
||||
|
||||
it('does not ignore warnings formatted to start with "(ADVICE)"', () => {
|
||||
YellowBoxRegistry.add({args: ['%s ...', '(ADVICE)'], framesToPop: 0});
|
||||
expect(registry().size).toBe(1);
|
||||
});
|
||||
|
||||
it('immediately updates new observers', () => {
|
||||
const {observer} = observe();
|
||||
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
expect(observer.mock.calls[0][0]).toBe(registry());
|
||||
});
|
||||
|
||||
it('sends batched updates asynchoronously', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
YellowBoxRegistry.add({args: ['B'], framesToPop: 0});
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('stops sending updates to unsubscribed observers', () => {
|
||||
const {observer, subscription} = observe();
|
||||
subscription.unsubscribe();
|
||||
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
expect(observer.mock.calls[0][0]).toBe(registry());
|
||||
});
|
||||
|
||||
it('updates observers when a warning is added or deleted', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
const {category: categoryA} = YellowBoxCategory.parse(['A']);
|
||||
YellowBoxRegistry.delete(categoryA);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing when category does not exist.
|
||||
YellowBoxRegistry.delete(categoryA);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
|
||||
it('updates observers when cleared', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.add({args: ['A'], framesToPop: 0});
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
YellowBoxRegistry.clear();
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing when already empty.
|
||||
YellowBoxRegistry.clear();
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
|
||||
it('updates observers when an ignore pattern is added', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['?']);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
YellowBoxRegistry.addIgnorePatterns(['!']);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing for an existing ignore pattern.
|
||||
YellowBoxRegistry.addIgnorePatterns(['!']);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
|
||||
it('updates observers when disabled or enabled', () => {
|
||||
const {observer} = observe();
|
||||
expect(observer.mock.calls.length).toBe(1);
|
||||
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
// Does nothing when already disabled.
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(2);
|
||||
|
||||
YellowBoxRegistry.setDisabled(false);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
|
||||
// Does nothing when already enabled.
|
||||
YellowBoxRegistry.setDisabled(false);
|
||||
jest.runAllImmediates();
|
||||
expect(observer.mock.calls.length).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
jest.mock('symbolicateStackTrace');
|
||||
|
||||
const YellowBoxSymbolication = require('YellowBoxSymbolication');
|
||||
|
||||
const symbolicateStackTrace: JestMockFn<
|
||||
$ReadOnlyArray<Array<StackFrame>>,
|
||||
Promise<Array<StackFrame>>,
|
||||
> = (require('symbolicateStackTrace'): any);
|
||||
|
||||
const createStack = methodNames =>
|
||||
methodNames.map(methodName => ({
|
||||
column: null,
|
||||
file: 'file://path/to/file.js',
|
||||
lineNumber: 1,
|
||||
methodName,
|
||||
}));
|
||||
|
||||
describe('YellowBoxSymbolication', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
symbolicateStackTrace.mockImplementation(async stack => stack);
|
||||
});
|
||||
|
||||
it('symbolicates different stacks', () => {
|
||||
YellowBoxSymbolication.symbolicate(createStack(['A', 'B', 'C']));
|
||||
YellowBoxSymbolication.symbolicate(createStack(['D', 'E', 'F']));
|
||||
|
||||
expect(symbolicateStackTrace.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('batch symbolicates equivalent stacks', () => {
|
||||
YellowBoxSymbolication.symbolicate(createStack(['A', 'B', 'C']));
|
||||
YellowBoxSymbolication.symbolicate(createStack(['A', 'B', 'C']));
|
||||
|
||||
expect(symbolicateStackTrace.mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
jest.mock('YellowBoxSymbolication');
|
||||
|
||||
const YellowBoxSymbolication: {|
|
||||
symbolicate: JestMockFn<
|
||||
$ReadOnlyArray<Array<StackFrame>>,
|
||||
Promise<Array<StackFrame>>,
|
||||
>,
|
||||
|} = (require('YellowBoxSymbolication'): any);
|
||||
const YellowBoxWarning = require('YellowBoxWarning');
|
||||
|
||||
const createStack = methodNames =>
|
||||
methodNames.map(methodName => ({
|
||||
column: null,
|
||||
file: 'file://path/to/file.js',
|
||||
lineNumber: 1,
|
||||
methodName,
|
||||
}));
|
||||
|
||||
describe('YellowBoxWarning', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
YellowBoxSymbolication.symbolicate.mockImplementation(async stack =>
|
||||
createStack(stack.map(frame => `S(${frame.methodName})`)),
|
||||
);
|
||||
});
|
||||
|
||||
it('starts without a symbolicated stack', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error: null,
|
||||
stack: null,
|
||||
status: 'NONE',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates when symbolication is in progress', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
warning.symbolicate(callback);
|
||||
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error: null,
|
||||
stack: null,
|
||||
status: 'PENDING',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates when symbolication finishes', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
warning.symbolicate(callback);
|
||||
|
||||
jest.runAllTicks();
|
||||
|
||||
expect(callback.mock.calls.length).toBe(2);
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error: null,
|
||||
stack: createStack(['S(A)', 'S(B)', 'S(C)']),
|
||||
status: 'COMPLETE',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates when symbolication fails', () => {
|
||||
const error = new Error('...');
|
||||
YellowBoxSymbolication.symbolicate.mockImplementation(async stack => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
warning.symbolicate(callback);
|
||||
|
||||
jest.runAllTicks();
|
||||
|
||||
expect(callback.mock.calls.length).toBe(2);
|
||||
expect(warning.symbolicated).toEqual({
|
||||
error,
|
||||
stack: null,
|
||||
status: 'FAILED',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not update aborted requests', () => {
|
||||
const warning = new YellowBoxWarning(
|
||||
{content: '...', substitutions: []},
|
||||
createStack(['A', 'B', 'C']),
|
||||
);
|
||||
const callback = jest.fn();
|
||||
const request = warning.symbolicate(callback);
|
||||
request.abort();
|
||||
|
||||
jest.runAllTicks();
|
||||
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
hitSlop?: ?EdgeInsetsProp,
|
||||
label: string,
|
||||
onPress: () => void,
|
||||
|}>;
|
||||
|
||||
const YellowBoxButton = (props: Props): React.Node => (
|
||||
<YellowBoxPressable
|
||||
hitSlop={props.hitSlop}
|
||||
onPress={props.onPress}
|
||||
style={styles.root}>
|
||||
<Text numberOfLines={1} style={styles.label}>
|
||||
{props.label}
|
||||
</Text>
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
borderRadius: 14,
|
||||
height: 28,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
label: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 12,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 16,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxButton;
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const PixelRatio = require('PixelRatio');
|
||||
|
||||
const scale = PixelRatio.get();
|
||||
|
||||
/**
|
||||
* We use inline images for YellowBox in order to avoid display latency due to
|
||||
* resource contention with symbolicating stack traces.
|
||||
*
|
||||
* The following steps were used to create these:
|
||||
*
|
||||
* 1. Download SVG files from: https://feathericons.com
|
||||
* 2. Rasterize SVG files to PNG files at 16dp, 36dp, and 48dp.
|
||||
* 3. Convert to Base64: https://www.google.com/search?q=base64+image+encoder
|
||||
*
|
||||
* @see https://github.com/feathericons/feather
|
||||
* @copyright 2013-2017 Cole Bemis
|
||||
* @license MIT
|
||||
*/
|
||||
const YellowBoxImageSource = {
|
||||
alertTriangle:
|
||||
scale > 2
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAB60lEQVRoge2Z3W3DIBSFj9oFPAIjZARGyAiMkBHuJh4hI2QEj5AR3Me+tQ91JALHmD8bKvmTkCr5Auc6/kzUACcnRXzuvL4GoAB8Afjeea9qXADcAfw4475c65orgBl++NeYl5ouUQiHt5tQTRJuwB6b5zLY49QVGn7I0bo+kuv60IQbuHf5CWCIqOkCgX93maia1MkRAUMo+OI+AvUPp7a50EzcUCBF6psJrUkYiZgnZJ7eId8mMeIyhpW5hyLw72LKCXsl86VqwgAKceKapW5e/nZpJnSsuHaTM7muyDq7C63JprJS69YxhNTpSlkpKeLGNHCo0EJChcSNaQA4SGiFtBMXJFSI3YVOPXFB6kMoUl9NaE0Wl4h5KQ0AOwqde+KmNrCL0EKCxJ64qQ0AlYVWSBfXZusgW6Oa0Dni2hiEv0qsoci+yUJrsoikLlKAkP11ygK54taiSOgb/O5b/DMqS+gBZeLWJlnoEX7XwQkBDPIktlEkz7hWrEmxZG4M5L9GXYTk0qxwcopKxa3VABN6cosM/C5LxTUof4ReMKHf1nRlaSnuGsGM7kfU4w8RF5Bz4aNlokLe/HQ/ngl9/Qih4L9k3h4hA1+S3odxu3Q77Hl4r1Hg75n6D01M2Difbp02Mi3ZTk5OLH4BUyEtOlDYuK0AAAAASUVORK5CYII='
|
||||
: scale > 1
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABVklEQVRYheWX4U3DMBBGH4gBMoJHyAgeoSNkAxjBG5QNOkJHCGzQDcoGZQP4gY3Oqe1cEscS4pNOqs9Jvqvv6ZrCf9fDhnutD4A3H810Br4mcW5l7hLmIdze5mZi+OJD5syeBYzC6CjyR5Ef9zI/CJMb0Im9zufC/qG2eQdchcGQuGYQ+9dJgZvl0B2xbJGrZW6IIevFXp9YVwcyB540syJfFcgSeJb0cVcDcg68XAFQCUhH+ShLBcBGIA158LQFqIB8zBRwEp9fgctcxQld/L2pZxZVAk/KkucjaDGQmoknrz35KEE2sABIRxm8tVIBaZgHb61UQOYmXk7aFgQVJ6QWPCnLAriYAVILnpTxD7yh/9EZiIEE4m+y29uMkGy1nQ6i9wYFRB5PwKdYP/v1msmnUe89gn695bG0iqjdXeMiRu9599csvGKZ0jlu0Ac/7d2rxX9Q37HW6QfX/ZguAAAAAElFTkSuQmCC'
|
||||
: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAvUlEQVQ4jbWT4Q3CIBCFP40DdANxg24gIzhKuwEjuIFxAkcwTtARGicoG+iPXlMCB8UfvoQc4e7ePV4A/ogWuMlqc0W7AsEo0QMNcPplugMmwMia5KwKWkNIuIkHq3wLXGQ/Sq4IC3wkLpOfmZyKeEpIEKsDYB8VN0Afkfpg30uNiycbdKcNqXEOxdBEWoEAoqta8uZ0iqqkxwGDUrSFAXAHZpOWd/+ubD5Kz335Cx1wZna4Bh54AddauVl8ARfCLO9Xq7xGAAAAAElFTkSuQmCC',
|
||||
check:
|
||||
scale > 2
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAqElEQVRoge3YTQ7CIBRF4bPUu/8JS6gTSaqilh95vuR+CaO2cGgYNAUzMzOzFgHlPhRaMkDAcRoltKaTeIxPtQHxGn+Q5AgJx8cQjo8hHB9DOP76Yiu/RcTmN18WLiQCjs3zBkYXVGOeLWd+xcIr5pgyEzDz7FIjISPP/FRPUM+9W4nvYVfuCSXeB3669ldEOzRFfCUSx1cicXwlEsdXIvEPKDMzM7PMbtugw3XTpNA2AAAAAElFTkSuQmCC'
|
||||
: scale > 1
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAf0lEQVRYhe2UvQ2AIBQGL3EBR3AESkv3bxxFN8DmWUgwvkI+En1X0cBd+IMg+DuDyDMCs413kfMiX4EMbD3l8oCaPIU85B4mYLEF5XJscrYFPRGvb/sZ4IlocubJGdH0wj1FSG77XYT0qdUi5O+8jOjyyZQRUnkZ0UUeBMF3OQC/0VsyGlxligAAAABJRU5ErkJggg=='
|
||||
: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAASElEQVQ4jWNgGJHAgIGBIYESze8ZGBjWU6L5PAMDgwBNNCdAFZJt83qoQmRDSHK2AFQhzBCy/IxsCNkBJsDAwLAfiknWPBIBAETPFeuA4fr6AAAAAElFTkSuQmCC',
|
||||
loader:
|
||||
scale > 2
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABXElEQVRoge2a3W3DMAyEr+0CHkGjaISOcKN4k6zQETpCR+gGzgbpQ10kcamIpKQ6avQBBPxg3pHwL2UDg/8LASxrcNdKnCwATmssrUyeWgnju/DmXs8tRP+Sh2kgAJga1rFlWj2rcMD5YqQh77QJLbzIORjyRIJQCJW5ngYo5AVlrsgkCGqbsDbAhFfxqZsSZibP0oDXQ43HQPsg82i7sBoR+VcJq2YxKcPo0IoJLRZXmYGC6ezQmQUdVqhPBVH/CNBTSMkLVlzjA8Bbocb7GoPBoADi+umZilYzbrG/JrnljOvy734iu4To/BQaDB6Rl4LciPPF9Lmjhgvi+s7w6tCIGw3WKS0P8fvWNjt0ZkGHFeq7CQXTbkZKGg2JOxrqPUZ3s6ziNdju38IjS/dLi0EQpDLX2gDQYHEX6Hx5/YcA+6H0NgAYPnCMj3x7Mxq4wTGx3Q1E578aDDR8AX0mOGD6BEN/AAAAAElFTkSuQmCC'
|
||||
: scale > 1
|
||||
? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABN0lEQVRYhe2WzU3EMBCFP34KyJEjJaQDXAIlJJ24BSow2wEdhHSwJSwd7JHbcmC0mOxMnDiWDIInWbHkN29exo4n8IvRAEFGU8OAA04yulyR60Jm7msbyIZloAMGwBfI4UWrWxM08LW/weC4iOMNTog4g0awKjBG827GxBwC3996NHizAifsSrTRmlsZm23CT9adktyXSq6ZUPdxgiXnZzW8CLcLuC3lvqA/gCt5NtjlPQL7TP0Wu1HtRRu4PO3T4TKTz2kG+AG9IN6CR/Su9iojBw69egfghWgL/pGCp+JFVPUqTjWjlsuqeAo1o6rt2C8QcNiV0UxoHPMieojmz0CfMKyhl1hN84xbI3gnz5Ftp7kH3iT5LsFdDUf6pzSJ6r2glIFDbuDNhqRH4I7Pvv4EvG/QqocP2Jh/xzzX/zUAAAAASUVORK5CYII='
|
||||
: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAsklEQVQ4jaWTYRHCMAyFP7gJmIQ6oChgEpBQKXMwC3MADpAAEiphDuBHC4QuDRu8u9ylyWtem7Rgw2X7GT1wsghb4beAVzhtsfYyJgs44AoEQzBkjrMId1HkKPwyZ6oMSnxYsnk1NqT7yMo34Fzhd9meGJvs7Hh3NhqCLXDI/rT0lKsR+KOJgc9RdaRRarkZvELogYsi8HqxjUhGYE+aQg1jzketwFTZXHbbEpjB8eU7PwAbLiJz46707gAAAABJRU5ErkJggg==',
|
||||
};
|
||||
|
||||
module.exports = YellowBoxImageSource;
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const React = require('React');
|
||||
const ScrollView = require('ScrollView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const View = require('View');
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxInspectorFooter = require('YellowBoxInspectorFooter');
|
||||
const YellowBoxInspectorHeader = require('YellowBoxInspectorHeader');
|
||||
const YellowBoxInspectorSourceMapStatus = require('YellowBoxInspectorSourceMapStatus');
|
||||
const YellowBoxInspectorStackFrame = require('YellowBoxInspectorStackFrame');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
const openFileInEditor = require('openFileInEditor');
|
||||
|
||||
import type YellowBoxWarning from 'YellowBoxWarning';
|
||||
import type {SymbolicationRequest} from 'YellowBoxWarning';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: () => void,
|
||||
onMinimize: () => void,
|
||||
warnings: $ReadOnlyArray<YellowBoxWarning>,
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
selectedIndex: number,
|
||||
|};
|
||||
|
||||
class YellowBoxInspector extends React.Component<Props, State> {
|
||||
_symbolication: ?SymbolicationRequest;
|
||||
|
||||
state = {
|
||||
selectedIndex: 0,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {warnings} = this.props;
|
||||
const {selectedIndex} = this.state;
|
||||
|
||||
const warning = warnings[selectedIndex];
|
||||
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<YellowBoxInspectorHeader
|
||||
onSelectIndex={this._handleSelectIndex}
|
||||
selectedIndex={selectedIndex}
|
||||
warnings={warnings}
|
||||
/>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.bodyContent}
|
||||
key={selectedIndex}
|
||||
style={styles.body}>
|
||||
<View>
|
||||
<View style={styles.bodyHeading}>
|
||||
<Text style={styles.bodyHeadingText}>Warning</Text>
|
||||
</View>
|
||||
<Text style={styles.bodyText}>
|
||||
{YellowBoxCategory.render(
|
||||
warning.message,
|
||||
styles.substitutionText,
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.bodySection}>
|
||||
<View style={styles.bodyHeading}>
|
||||
<Text style={styles.bodyHeadingText}>Stack</Text>
|
||||
<YellowBoxInspectorSourceMapStatus
|
||||
status={warning.symbolicated.status}
|
||||
/>
|
||||
</View>
|
||||
{warning.getAvailableStack().map((frame, index) => (
|
||||
<YellowBoxInspectorStackFrame
|
||||
key={index}
|
||||
frame={frame}
|
||||
onPress={
|
||||
warning.symbolicated.status === 'COMPLETE'
|
||||
? () => {
|
||||
openFileInEditor(frame.file, frame.lineNumber);
|
||||
}
|
||||
: null
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
<YellowBoxInspectorFooter
|
||||
onDismiss={this.props.onDismiss}
|
||||
onMinimize={this.props.onMinimize}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._handleSymbolication();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State): void {
|
||||
if (
|
||||
prevProps.warnings !== this.props.warnings ||
|
||||
prevState.selectedIndex !== this.state.selectedIndex
|
||||
) {
|
||||
this._cancelSymbolication();
|
||||
this._handleSymbolication();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this._cancelSymbolication();
|
||||
}
|
||||
|
||||
_handleSymbolication(): void {
|
||||
const warning = this.props.warnings[this.state.selectedIndex];
|
||||
if (warning.symbolicated.status !== 'COMPLETE') {
|
||||
this._symbolication = warning.symbolicate(() => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_cancelSymbolication(): void {
|
||||
if (this._symbolication != null) {
|
||||
this._symbolication.abort();
|
||||
this._symbolication = null;
|
||||
}
|
||||
}
|
||||
|
||||
_handleSelectIndex = (selectedIndex: number): void => {
|
||||
this.setState({selectedIndex});
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
elevation: Platform.OS === 'android' ? Number.MAX_SAFE_INTEGER : undefined,
|
||||
height: '100%',
|
||||
},
|
||||
body: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
borderBottomColor: YellowBoxStyle.getDividerColor(0.95),
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: YellowBoxStyle.getDividerColor(0.95),
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
flex: 1,
|
||||
},
|
||||
bodyContent: {
|
||||
paddingVertical: 12,
|
||||
},
|
||||
bodyHeading: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
marginBottom: 6,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
bodyHeadingText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
flex: 1,
|
||||
fontSize: 20,
|
||||
fontWeight: '600',
|
||||
includeFontPadding: false,
|
||||
lineHeight: 28,
|
||||
},
|
||||
bodyText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
substitutionText: {
|
||||
color: YellowBoxStyle.getTextColor(0.6),
|
||||
},
|
||||
bodySection: {
|
||||
marginTop: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspector;
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const View = require('View');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: () => void,
|
||||
onMinimize: () => void,
|
||||
|}>;
|
||||
|
||||
const YellowBoxInspectorFooter = (props: Props): React.Node => (
|
||||
<View style={styles.root}>
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={props.onMinimize}
|
||||
style={styles.button}>
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.label}>Minimize</Text>
|
||||
</View>
|
||||
<SafeAreaView />
|
||||
</YellowBoxPressable>
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={props.onDismiss}
|
||||
style={styles.button}>
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.label}>Dismiss</Text>
|
||||
</View>
|
||||
<SafeAreaView />
|
||||
</YellowBoxPressable>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
flexDirection: 'row',
|
||||
},
|
||||
button: {
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
alignItems: 'center',
|
||||
height: 48,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
label: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorFooter;
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const React = require('React');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const UTFSequence = require('UTFSequence');
|
||||
const View = require('View');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type YellowBoxWarning from 'YellowBoxWarning';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onSelectIndex: (selectedIndex: number) => void,
|
||||
selectedIndex: number,
|
||||
warnings: $ReadOnlyArray<YellowBoxWarning>,
|
||||
|}>;
|
||||
|
||||
const YellowBoxInspectorHeader = (props: Props): React.Node => {
|
||||
const prevIndex = props.selectedIndex - 1;
|
||||
const nextIndex = props.selectedIndex + 1;
|
||||
|
||||
const titleText =
|
||||
props.warnings.length === 1
|
||||
? 'Single Occurrence'
|
||||
: `Occurrence ${props.selectedIndex + 1} of ${props.warnings.length}`;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.root}>
|
||||
<View style={styles.header}>
|
||||
<YellowBoxInspectorHeaderButton
|
||||
disabled={props.warnings[prevIndex] == null}
|
||||
label={UTFSequence.TRIANGLE_LEFT}
|
||||
onPress={() => props.onSelectIndex(prevIndex)}
|
||||
/>
|
||||
<View style={styles.headerTitle}>
|
||||
<Text style={styles.headerTitleText}>{titleText}</Text>
|
||||
</View>
|
||||
<YellowBoxInspectorHeaderButton
|
||||
disabled={props.warnings[nextIndex] == null}
|
||||
label={UTFSequence.TRIANGLE_RIGHT}
|
||||
onPress={() => props.onSelectIndex(nextIndex)}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const YellowBoxInspectorHeaderButton = (
|
||||
props: $ReadOnly<{|
|
||||
disabled: boolean,
|
||||
label: React.Node,
|
||||
onPress?: ?() => void,
|
||||
|}>,
|
||||
): React.Node => (
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={props.disabled ? null : props.onPress}
|
||||
style={styles.headerButton}>
|
||||
{props.disabled ? null : (
|
||||
<Text style={styles.headerButtonText}>{props.label}</Text>
|
||||
)}
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
height: Platform.select({
|
||||
android: 48,
|
||||
ios: 44,
|
||||
}),
|
||||
},
|
||||
headerButton: {
|
||||
alignItems: 'center',
|
||||
aspectRatio: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerButtonText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 16,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 20,
|
||||
},
|
||||
headerTitle: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerTitleText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
includeFontPadding: false,
|
||||
lineHeight: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorHeader;
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Animated = require('Animated');
|
||||
const Easing = require('Easing');
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const View = require('View');
|
||||
const YellowBoxImageSource = require('YellowBoxImageSource');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {CompositeAnimation} from 'AnimatedImplementation';
|
||||
import type AnimatedInterpolation from 'AnimatedInterpolation';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
status: 'COMPLETE' | 'FAILED' | 'NONE' | 'PENDING',
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
animation: ?CompositeAnimation,
|
||||
rotate: ?AnimatedInterpolation,
|
||||
|};
|
||||
|
||||
class YellowBoxInspectorSourceMapStatus extends React.Component<Props, State> {
|
||||
state = {
|
||||
animation: null,
|
||||
rotate: null,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
let image;
|
||||
switch (this.props.status) {
|
||||
case 'COMPLETE':
|
||||
image = YellowBoxImageSource.check;
|
||||
break;
|
||||
case 'FAILED':
|
||||
image = YellowBoxImageSource.alertTriangle;
|
||||
break;
|
||||
case 'PENDING':
|
||||
image = YellowBoxImageSource.loader;
|
||||
break;
|
||||
}
|
||||
|
||||
return image == null ? null : (
|
||||
<View
|
||||
style={StyleSheet.compose(
|
||||
styles.root,
|
||||
this.props.status === 'PENDING' ? styles.pending : null,
|
||||
)}>
|
||||
<Animated.Image
|
||||
source={{height: 16, uri: image, width: 16}}
|
||||
style={StyleSheet.compose(
|
||||
styles.image,
|
||||
this.state.rotate == null
|
||||
? null
|
||||
: {transform: [{rotate: this.state.rotate}]},
|
||||
)}
|
||||
/>
|
||||
<Text style={styles.text}>Source Map</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._updateAnimation();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this._updateAnimation();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this.state.animation != null) {
|
||||
this.state.animation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
_updateAnimation(): void {
|
||||
if (this.props.status === 'PENDING') {
|
||||
if (this.state.animation == null) {
|
||||
const animated = new Animated.Value(0);
|
||||
const animation = Animated.loop(
|
||||
Animated.timing(animated, {
|
||||
duration: 2000,
|
||||
easing: Easing.linear,
|
||||
toValue: 1,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
);
|
||||
this.setState(
|
||||
{
|
||||
animation,
|
||||
rotate: animated.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
}),
|
||||
},
|
||||
() => {
|
||||
animation.start();
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this.state.animation != null) {
|
||||
this.state.animation.stop();
|
||||
this.setState({
|
||||
animation: null,
|
||||
rotate: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: YellowBoxStyle.getTextColor(0.8),
|
||||
borderRadius: 12,
|
||||
flexDirection: 'row',
|
||||
height: 24,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
pending: {
|
||||
backgroundColor: YellowBoxStyle.getTextColor(0.6),
|
||||
},
|
||||
image: {
|
||||
marginEnd: 4,
|
||||
tintColor: YellowBoxStyle.getBackgroundColor(1),
|
||||
},
|
||||
text: {
|
||||
color: YellowBoxStyle.getBackgroundColor(1),
|
||||
fontSize: 12,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 16,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorSourceMapStatus;
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {PressEvent} from 'CoreEventTypes';
|
||||
import type {StackFrame} from 'parseErrorStack';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
frame: StackFrame,
|
||||
onPress?: ?(event: PressEvent) => void,
|
||||
|}>;
|
||||
|
||||
const YellowBoxInspectorStackFrame = (props: Props): React.Node => {
|
||||
const {frame, onPress} = props;
|
||||
|
||||
return (
|
||||
<YellowBoxPressable
|
||||
backgroundColor={{
|
||||
default: YellowBoxStyle.getBackgroundColor(0),
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
}}
|
||||
onPress={onPress}
|
||||
style={styles.frame}>
|
||||
<Text style={styles.frameName}>{frame.methodName}</Text>
|
||||
<Text
|
||||
ellipsizeMode="middle"
|
||||
numberOfLines={1}
|
||||
style={styles.frameLocation}>
|
||||
{`${getFrameLocation(frame.file)}:${frame.lineNumber}${
|
||||
frame.column == null ? '' : ':' + frame.column
|
||||
}`}
|
||||
</Text>
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
};
|
||||
|
||||
const getFrameLocation = (uri: string): string => {
|
||||
const queryIndex = uri.indexOf('?');
|
||||
const query = queryIndex < 0 ? '' : uri.substr(queryIndex);
|
||||
|
||||
const path = queryIndex < 0 ? uri : uri.substr(0, queryIndex);
|
||||
const file = path.substr(path.lastIndexOf('/') + 1);
|
||||
|
||||
return file + query;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
frame: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 4,
|
||||
},
|
||||
frameName: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
frameLocation: {
|
||||
color: YellowBoxStyle.getTextColor(0.7),
|
||||
fontSize: 12,
|
||||
fontWeight: '300',
|
||||
includeFontPadding: false,
|
||||
lineHeight: 16,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxInspectorStackFrame;
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Dimensions = require('Dimensions');
|
||||
const React = require('React');
|
||||
const FlatList = require('FlatList');
|
||||
const SafeAreaView = require('SafeAreaView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const View = require('View');
|
||||
const YellowBoxButton = require('YellowBoxButton');
|
||||
const YellowBoxInspector = require('YellowBoxInspector');
|
||||
const YellowBoxListRow = require('YellowBoxListRow');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
import type {Registry} from 'YellowBoxRegistry';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: (category: Category) => void,
|
||||
onDismissAll: () => void,
|
||||
registry: Registry,
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
selectedCategory: ?Category,
|
||||
|};
|
||||
|
||||
const VIEWPORT_RATIO = 0.5;
|
||||
const MAX_ITEMS = Math.floor(
|
||||
(Dimensions.get('window').height * VIEWPORT_RATIO) /
|
||||
(YellowBoxListRow.GUTTER + YellowBoxListRow.HEIGHT),
|
||||
);
|
||||
|
||||
class YellowBoxList extends React.Component<Props, State> {
|
||||
state = {
|
||||
selectedCategory: null,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const selectedWarnings =
|
||||
this.state.selectedCategory == null
|
||||
? null
|
||||
: this.props.registry.get(this.state.selectedCategory);
|
||||
|
||||
if (selectedWarnings != null) {
|
||||
return (
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<YellowBoxInspector
|
||||
onDismiss={this._handleInspectorDismiss}
|
||||
onMinimize={this._handleInspectorMinimize}
|
||||
warnings={selectedWarnings}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const items = [];
|
||||
for (const [category, warnings] of this.props.registry) {
|
||||
items.unshift({category, warnings});
|
||||
}
|
||||
|
||||
const listStyle = {
|
||||
height:
|
||||
// Additional `0.5` so the (N + 1)th row can peek into view.
|
||||
Math.min(items.length, MAX_ITEMS + 0.5) *
|
||||
(YellowBoxListRow.GUTTER + YellowBoxListRow.HEIGHT),
|
||||
};
|
||||
|
||||
return items.length === 0 ? null : (
|
||||
<View style={styles.list}>
|
||||
<View pointerEvents="box-none" style={styles.dismissAll}>
|
||||
<YellowBoxButton
|
||||
hitSlop={{bottom: 4, left: 4, right: 4, top: 4}}
|
||||
label="Dismiss All"
|
||||
onPress={this.props.onDismissAll}
|
||||
/>
|
||||
</View>
|
||||
<FlatList
|
||||
data={items}
|
||||
keyExtractor={item => item.category}
|
||||
renderItem={({item}) => (
|
||||
<YellowBoxListRow {...item} onPress={this._handleRowPress} />
|
||||
)}
|
||||
scrollEnabled={items.length > MAX_ITEMS}
|
||||
scrollsToTop={false}
|
||||
style={listStyle}
|
||||
/>
|
||||
<SafeAreaView style={styles.safeArea} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleInspectorDismiss = () => {
|
||||
const category = this.state.selectedCategory;
|
||||
if (category == null) {
|
||||
return;
|
||||
}
|
||||
this.setState({selectedCategory: null}, () => {
|
||||
this.props.onDismiss(category);
|
||||
});
|
||||
};
|
||||
|
||||
_handleInspectorMinimize = () => {
|
||||
this.setState({selectedCategory: null});
|
||||
};
|
||||
|
||||
_handleRowPress = (category: Category) => {
|
||||
this.setState({selectedCategory: category});
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
list: {
|
||||
bottom: 0,
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
},
|
||||
dismissAll: {
|
||||
bottom: '100%',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
paddingBottom: 4,
|
||||
paddingEnd: 4,
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
},
|
||||
safeArea: {
|
||||
backgroundColor: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
marginTop: StyleSheet.hairlineWidth,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxList;
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const YellowBoxPressable = require('YellowBoxPressable');
|
||||
const View = require('View');
|
||||
const YellowBoxCategory = require('YellowBoxCategory');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
const YellowBoxWarning = require('YellowBoxWarning');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
category: Category,
|
||||
warnings: $ReadOnlyArray<YellowBoxWarning>,
|
||||
onPress: (category: Category) => void,
|
||||
|}>;
|
||||
|
||||
class YellowBoxListRow extends React.Component<Props> {
|
||||
static GUTTER = StyleSheet.hairlineWidth;
|
||||
static HEIGHT = 48;
|
||||
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
const prevProps = this.props;
|
||||
return (
|
||||
prevProps.category !== nextProps.category ||
|
||||
prevProps.onPress !== nextProps.onPress ||
|
||||
prevProps.warnings.length !== nextProps.warnings.length ||
|
||||
prevProps.warnings.some(
|
||||
(prevWarning, index) => prevWarning !== nextProps[index],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {warnings} = this.props;
|
||||
|
||||
return (
|
||||
<YellowBoxPressable onPress={this._handlePress} style={styles.root}>
|
||||
<View style={styles.content}>
|
||||
{warnings.length < 2 ? null : (
|
||||
<Text style={styles.metaText}>{'(' + warnings.length + ') '}</Text>
|
||||
)}
|
||||
<Text numberOfLines={2} style={styles.bodyText}>
|
||||
{YellowBoxCategory.render(
|
||||
warnings[warnings.length - 1].message,
|
||||
styles.substitutionText,
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
</YellowBoxPressable>
|
||||
);
|
||||
}
|
||||
|
||||
_handlePress = () => {
|
||||
this.props.onPress(this.props.category);
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
height: YellowBoxListRow.HEIGHT,
|
||||
justifyContent: 'center',
|
||||
marginTop: YellowBoxListRow.GUTTER,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
content: {
|
||||
alignItems: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
bodyText: {
|
||||
color: YellowBoxStyle.getTextColor(1),
|
||||
flex: 1,
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
metaText: {
|
||||
color: YellowBoxStyle.getTextColor(0.5),
|
||||
fontSize: 14,
|
||||
includeFontPadding: false,
|
||||
lineHeight: 18,
|
||||
},
|
||||
substitutionText: {
|
||||
color: YellowBoxStyle.getTextColor(0.6),
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YellowBoxListRow;
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
||||
const View = require('View');
|
||||
const YellowBoxStyle = require('YellowBoxStyle');
|
||||
|
||||
import type {PressEvent} from 'CoreEventTypes';
|
||||
import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
|
||||
import type {ViewStyleProp} from 'StyleSheet';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
backgroundColor: $ReadOnly<{|
|
||||
default: string,
|
||||
pressed: string,
|
||||
|}>,
|
||||
children?: React.Node,
|
||||
hitSlop?: ?EdgeInsetsProp,
|
||||
onPress?: ?(event: PressEvent) => void,
|
||||
style?: ViewStyleProp,
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
pressed: boolean,
|
||||
|};
|
||||
|
||||
class YellowBoxPressable extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
backgroundColor: {
|
||||
default: YellowBoxStyle.getBackgroundColor(0.95),
|
||||
pressed: YellowBoxStyle.getHighlightColor(1),
|
||||
},
|
||||
};
|
||||
|
||||
state = {
|
||||
pressed: false,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const content = (
|
||||
<View
|
||||
style={StyleSheet.compose(
|
||||
{
|
||||
backgroundColor: this.state.pressed
|
||||
? this.props.backgroundColor.pressed
|
||||
: this.props.backgroundColor.default,
|
||||
},
|
||||
this.props.style,
|
||||
)}>
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
return this.props.onPress == null ? (
|
||||
content
|
||||
) : (
|
||||
<TouchableWithoutFeedback
|
||||
hitSlop={this.props.hitSlop}
|
||||
onPress={this.props.onPress}
|
||||
onPressIn={this._handlePressIn}
|
||||
onPressOut={this._handlePressOut}>
|
||||
{content}
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
_handlePressIn = () => {
|
||||
this.setState({pressed: true});
|
||||
};
|
||||
|
||||
_handlePressOut = () => {
|
||||
this.setState({pressed: false});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = YellowBoxPressable;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBoxStyle = {
|
||||
getBackgroundColor(opacity: number): string {
|
||||
return `rgba(250, 186, 48, ${opacity})`;
|
||||
},
|
||||
|
||||
getDividerColor(opacity: number): string {
|
||||
return `rgba(255, 255, 255, ${opacity})`;
|
||||
},
|
||||
|
||||
getHighlightColor(opacity: number): string {
|
||||
return `rgba(252, 176, 29, ${opacity})`;
|
||||
},
|
||||
|
||||
getTextColor(opacity: number): string {
|
||||
return `rgba(255, 255, 255, ${opacity})`;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = YellowBoxStyle;
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const RCTLog = require('RCTLog');
|
||||
const React = require('React');
|
||||
const YellowBoxList = require('YellowBoxList');
|
||||
const YellowBoxRegistry = require('YellowBoxRegistry');
|
||||
|
||||
import type {Category} from 'YellowBoxCategory';
|
||||
import type {Registry, Subscription} from 'YellowBoxRegistry';
|
||||
|
||||
type Props = $ReadOnly<{||}>;
|
||||
type State = {|
|
||||
registry: ?Registry,
|
||||
|};
|
||||
|
||||
const {error, warn} = console;
|
||||
|
||||
/**
|
||||
* YellowBox displays warnings at the bottom of the screen.
|
||||
*
|
||||
* Warnings help guard against subtle yet significant issues that can impact the
|
||||
* quality of the app. This "in your face" style of warning allows developers to
|
||||
* notice and correct these issues as quickly as possible.
|
||||
*
|
||||
* YellowBox is only enabled in `__DEV__`. Set the following flag to disable it:
|
||||
*
|
||||
* console.disableYellowBox = true;
|
||||
*
|
||||
* Ignore specific warnings by calling:
|
||||
*
|
||||
* YellowBox.ignoreWarnings(['Warning: ...']);
|
||||
*
|
||||
* Strings supplied to `YellowBox.ignoreWarnings` only need to be a substring of
|
||||
* the ignored warning messages.
|
||||
*/
|
||||
class YellowBox extends React.Component<Props, State> {
|
||||
static ignoreWarnings(patterns: $ReadOnlyArray<string>): void {
|
||||
YellowBoxRegistry.addIgnorePatterns(patterns);
|
||||
}
|
||||
|
||||
static install(): void {
|
||||
(console: any).error = function(...args) {
|
||||
error.call(console, ...args);
|
||||
// Show YellowBox for the `warning` module.
|
||||
if (typeof args[0] === 'string' && args[0].startsWith('Warning: ')) {
|
||||
registerWarning(...args);
|
||||
}
|
||||
};
|
||||
|
||||
(console: any).warn = function(...args) {
|
||||
warn.call(console, ...args);
|
||||
registerWarning(...args);
|
||||
};
|
||||
|
||||
if ((console: any).disableYellowBox === true) {
|
||||
YellowBoxRegistry.setDisabled(true);
|
||||
}
|
||||
(Object.defineProperty: any)(console, 'disableYellowBox', {
|
||||
configurable: true,
|
||||
get: () => YellowBoxRegistry.isDisabled(),
|
||||
set: value => YellowBoxRegistry.setDisabled(value),
|
||||
});
|
||||
|
||||
if (Platform.isTesting) {
|
||||
(console: any).disableYellowBox = true;
|
||||
}
|
||||
|
||||
RCTLog.setWarningHandler((...args) => {
|
||||
registerWarning(...args);
|
||||
});
|
||||
}
|
||||
|
||||
static uninstall(): void {
|
||||
(console: any).error = error;
|
||||
(console: any).warn = error;
|
||||
delete (console: any).disableYellowBox;
|
||||
}
|
||||
|
||||
_subscription: ?Subscription;
|
||||
|
||||
state = {
|
||||
registry: null,
|
||||
};
|
||||
|
||||
render() {
|
||||
// TODO: Ignore warnings that fire when rendering `YellowBox` itself.
|
||||
return this.state.registry == null ? null : (
|
||||
<YellowBoxList
|
||||
onDismiss={this._handleDismiss}
|
||||
onDismissAll={this._handleDismissAll}
|
||||
registry={this.state.registry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._subscription = YellowBoxRegistry.observe(registry => {
|
||||
this.setState({registry});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this._subscription != null) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDismiss = (category: Category): void => {
|
||||
YellowBoxRegistry.delete(category);
|
||||
};
|
||||
|
||||
_handleDismissAll(): void {
|
||||
YellowBoxRegistry.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function registerWarning(...args): void {
|
||||
YellowBoxRegistry.add({args, framesToPop: 2});
|
||||
}
|
||||
|
||||
module.exports = YellowBox;
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const YellowBox = require('YellowBox');
|
||||
const YellowBoxRegistry = require('YellowBoxRegistry');
|
||||
|
||||
describe('YellowBox', () => {
|
||||
const {error, warn} = console;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
(console: any).error = jest.fn();
|
||||
(console: any).warn = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
YellowBox.uninstall();
|
||||
(console: any).error = error;
|
||||
(console: any).warn = warn;
|
||||
});
|
||||
|
||||
it('can set `disableYellowBox` after installing', () => {
|
||||
expect((console: any).disableYellowBox).toBe(undefined);
|
||||
|
||||
YellowBox.install();
|
||||
|
||||
expect((console: any).disableYellowBox).toBe(false);
|
||||
expect(YellowBoxRegistry.isDisabled()).toBe(false);
|
||||
|
||||
(console: any).disableYellowBox = true;
|
||||
|
||||
expect((console: any).disableYellowBox).toBe(true);
|
||||
expect(YellowBoxRegistry.isDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('can set `disableYellowBox` before installing', () => {
|
||||
expect((console: any).disableYellowBox).toBe(undefined);
|
||||
|
||||
(console: any).disableYellowBox = true;
|
||||
YellowBox.install();
|
||||
|
||||
expect((console: any).disableYellowBox).toBe(true);
|
||||
expect(YellowBoxRegistry.isDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('registers warnings', () => {
|
||||
jest.mock('YellowBoxRegistry');
|
||||
|
||||
YellowBox.install();
|
||||
|
||||
expect(YellowBoxRegistry.add).not.toBeCalled();
|
||||
(console: any).warn('...');
|
||||
expect(YellowBoxRegistry.add).toBeCalled();
|
||||
});
|
||||
|
||||
it('registers errors beginning with "Warning: "', () => {
|
||||
jest.mock('YellowBoxRegistry');
|
||||
|
||||
YellowBox.install();
|
||||
|
||||
(console: any).error('...');
|
||||
expect(YellowBoxRegistry.add).not.toBeCalled();
|
||||
|
||||
(console: any).error('Warning: ...');
|
||||
expect(YellowBoxRegistry.add).toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -84,8 +84,8 @@ def enableSeparateBuildPerCPUArchitecture = false
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.2"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.facebook.react.uiapp"
|
||||
@@ -137,7 +137,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
|
||||
// Build React Native from source
|
||||
compile project(':ReactAndroid')
|
||||
|
||||
@@ -21,9 +21,9 @@ static void __makeVersion()
|
||||
{
|
||||
__rnVersion = @{
|
||||
RCTVersionMajor: @(0),
|
||||
RCTVersionMinor: @(0),
|
||||
RCTVersionMinor: @(56),
|
||||
RCTVersionPatch: @(0),
|
||||
RCTVersionPrerelease: [NSNull null],
|
||||
RCTVersionPrerelease: @"rc.1",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -245,8 +245,8 @@ task packageReactNdkLibsForBuck(dependsOn: packageReactNdkLibs, type: Copy) {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.2"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
@@ -295,11 +295,11 @@ android {
|
||||
dependencies {
|
||||
compile fileTree(dir: 'src/main/third-party/java/infer-annotations/', include: ['*.jar'])
|
||||
compile 'javax.inject:javax.inject:1'
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0'
|
||||
compile 'com.facebook.fresco:fresco:1.9.0'
|
||||
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.9.0'
|
||||
compile 'com.facebook.soloader:soloader:0.3.0'
|
||||
compile 'com.facebook.soloader:soloader:0.5.0'
|
||||
compile 'com.google.code.findbugs:jsr305:3.0.0'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.10.0'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0-master
|
||||
VERSION_NAME=0.56.0-rc.1
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -6,8 +6,8 @@ android_prebuilt_aar(
|
||||
|
||||
remote_file(
|
||||
name = "appcompat-binary-aar",
|
||||
sha1 = "7d659f671541394a8bc2b9f909950aa2a5ec87ff",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:23.0.1",
|
||||
sha1 = "da5771a9ac6e8f3a461360eed3c6f921dc7580fd",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:26.1.0",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ import java.util.Map;
|
||||
public class ReactNativeVersion {
|
||||
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
|
||||
"major", 0,
|
||||
"minor", 0,
|
||||
"minor", 56,
|
||||
"patch", 0,
|
||||
"prerelease", null);
|
||||
"prerelease", "rc.1");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ prebuilt_jar(
|
||||
|
||||
remote_file(
|
||||
name = "support-annotations-binary-aar",
|
||||
sha1 = "1fce89a6428c51467090d7f424e4c9c3dbd55f7e",
|
||||
url = "mvn:com.android.support:support-annotations:jar:23.0.1",
|
||||
sha1 = "0814258103cf26a15fcc26ecce35f5b7d24b73f8",
|
||||
url = "mvn:com.android.support:support-annotations:jar:26.1.0",
|
||||
)
|
||||
|
||||
+97
-6
@@ -1,11 +1,102 @@
|
||||
android_prebuilt_aar(
|
||||
android_library(
|
||||
name = "lib-support-v4",
|
||||
aar = ":lib-support-v4-binary-aar",
|
||||
exported_deps = [
|
||||
":lib-support-v4-support-compat",
|
||||
":lib-support-v4-support-core-utils",
|
||||
":lib-support-v4-support-core-ui",
|
||||
":lib-support-v4-support-media-compat",
|
||||
":lib-support-v4-support-fragment",
|
||||
":android-lifecycle-runtime",
|
||||
":android-lifecycle-common.jar",
|
||||
":android-lifecycle-core.jar"
|
||||
],
|
||||
visibility = ["PUBLIC"],
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-binary-aar",
|
||||
sha1 = "9e8da0e4ecf9f63258c7fbd273889252cba2d0c3",
|
||||
url = "mvn:com.android.support:support-v4:aar:23.0.1",
|
||||
prebuilt_jar(
|
||||
name = "android-lifecycle-common.jar",
|
||||
binary_jar = ":android-lifecycle-common-jar"
|
||||
)
|
||||
|
||||
prebuilt_jar(
|
||||
name = "android-lifecycle-core.jar",
|
||||
binary_jar = ":android-lifecycle-core-jar"
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "android-lifecycle-runtime",
|
||||
aar = ":android-lifecycle-runtime-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-compat",
|
||||
aar = ":lib-support-v4-support-compat-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-core-utils",
|
||||
aar = ":lib-support-v4-support-core-utils-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-core-ui",
|
||||
aar = ":lib-support-v4-support-core-ui-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-media-compat",
|
||||
aar = ":lib-support-v4-support-media-compat-aar",
|
||||
)
|
||||
|
||||
android_prebuilt_aar(
|
||||
name = "lib-support-v4-support-fragment",
|
||||
aar = ":lib-support-v4-support-fragment-aar",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "android-lifecycle-runtime-aar",
|
||||
sha1 = "30c60a8a357ee1321ffd0c9f08ef54b24045cd10",
|
||||
url = "mvn:android.arch.lifecycle:runtime:aar:1.0.0"
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "android-lifecycle-common-jar",
|
||||
sha1 = "e414a4cb28434e25c4f6aa71426eb20cf4874ae9",
|
||||
url = "mvn:android.arch.lifecycle:common:jar:1.0.0"
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "android-lifecycle-core-jar",
|
||||
sha1 = "a2d487452376193fc8c103dd2b9bd5f2b1b44563",
|
||||
url = "mvn:android.arch.core:common:jar:1.0.0"
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-compat-aar",
|
||||
sha1 = "1e1c8ffc01d277d8f01dfd11d5d2ce3a2af4b98c",
|
||||
url = "mvn:com.android.support:support-compat:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-core-utils-aar",
|
||||
sha1 = "1bfaae21c4d5c5532c5e732071e9ce234cd58cff",
|
||||
url = "mvn:com.android.support:support-core-utils:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-core-ui-aar",
|
||||
sha1 = "e306308d54052a1ded7bf9c2f5c2fdf5152a1f22",
|
||||
url = "mvn:com.android.support:support-core-ui:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-media-compat-aar",
|
||||
sha1 = "09fb587f27cde19aa8f2e50c5c9ee645d9aec44d",
|
||||
url = "mvn:com.android.support:support-media-compat:aar:26.1.0",
|
||||
)
|
||||
|
||||
remote_file(
|
||||
name = "lib-support-v4-support-fragment-aar",
|
||||
sha1 = "ddacf369bb98dd6558606558de8ddcd53895cf91",
|
||||
url = "mvn:com.android.support:support-fragment:aar:26.1.0",
|
||||
)
|
||||
@@ -56,6 +56,6 @@ python_binary(
|
||||
|
||||
remote_file(
|
||||
name = "appcompat-binary-aar",
|
||||
sha1 = "7d659f671541394a8bc2b9f909950aa2a5ec87ff",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:23.0.1",
|
||||
sha1 = "da5771a9ac6e8f3a461360eed3c6f921dc7580fd",
|
||||
url = "mvn:com.android.support:appcompat-v7:aar:26.1.0",
|
||||
)
|
||||
|
||||
+529
-65
@@ -4,30 +4,124 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* Modified from https://raw.githubusercontent.com/flowtype/flow-typed/e3b0f3034929e0f0fb85c790450a201b380ac2fd/definitions/npm/jest_v17.x.x/flow_v0.33.x-/jest_v17.x.x.js
|
||||
* Modified from https://raw.githubusercontent.com/flowtype/flow-typed/b43dff3e0ed5ccf7033839045f4819e8db40faa9/definitions/npm/jest_v20.x.x/flow_v0.22.x-/jest_v20.x.x.js
|
||||
* Duplicated from www/flow/shared/jest.js
|
||||
* List of modifications:
|
||||
* - added function fail
|
||||
* - added jest.requireActual
|
||||
* - added jest.requireMock
|
||||
* - added JestMockFn.mockName
|
||||
* - added JestMockFn.mockRejectedValue
|
||||
* - added JestMockFn.mockRejectedValueOnce
|
||||
* - added JestMockFn.mockResolvedValue
|
||||
* - added JestMockFn.mockResolvedValueOnce
|
||||
* - added JestMockFn.mock.thrownErrors
|
||||
* - added JestMockFn.mock.returnValues
|
||||
* - added jest.setTimeout
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
/* eslint-disable lint/no-unclear-flowtypes */
|
||||
|
||||
type JestMockFn = {
|
||||
(...args: Array<any>): any,
|
||||
type JestMockFn<TArguments: $ReadOnlyArray<*>, TReturn> = {
|
||||
(...args: TArguments): TReturn,
|
||||
/**
|
||||
* An object for introspecting mock calls
|
||||
*/
|
||||
mock: {
|
||||
calls: Array<Array<any>>,
|
||||
instances: mixed,
|
||||
/**
|
||||
* An array that represents all calls that have been made into this mock
|
||||
* function. Each call is represented by an array of arguments that were
|
||||
* passed during the call.
|
||||
*/
|
||||
calls: Array<TArguments>,
|
||||
/**
|
||||
* An array that contains all the object instances that have been
|
||||
* instantiated from this mock function.
|
||||
*/
|
||||
instances: Array<TReturn>,
|
||||
/**
|
||||
* An array containing the results of a method, and whether they were
|
||||
* returned or thrown.
|
||||
*/
|
||||
results: Array<{isThrown: boolean}>,
|
||||
},
|
||||
mockClear(): Function,
|
||||
mockReset(): Function,
|
||||
mockImplementation(fn: Function): JestMockFn,
|
||||
mockImplementationOnce(fn: Function): JestMockFn,
|
||||
/**
|
||||
* Resets all information stored in the mockFn.mock.calls and
|
||||
* mockFn.mock.instances arrays. Often this is useful when you want to clean
|
||||
* up a mock's usage data between two assertions.
|
||||
*/
|
||||
mockClear(): void,
|
||||
/**
|
||||
* Resets all information stored in the mock. This is useful when you want to
|
||||
* completely restore a mock back to its initial state.
|
||||
*/
|
||||
mockReset(): void,
|
||||
/**
|
||||
* Removes the mock and restores the initial implementation. This is useful
|
||||
* when you want to mock functions in certain test cases and restore the
|
||||
* original implementation in others. Beware that mockFn.mockRestore only
|
||||
* works when mock was created with jest.spyOn. Thus you have to take care of
|
||||
* restoration yourself when manually assigning jest.fn().
|
||||
*/
|
||||
mockRestore(): void,
|
||||
/**
|
||||
* Accepts a function that should be used as the implementation of the mock.
|
||||
* The mock itself will still record all calls that go into and instances
|
||||
* that come from itself -- the only difference is that the implementation
|
||||
* will also be executed when the mock is called.
|
||||
*/
|
||||
mockImplementation(
|
||||
fn: (...args: TArguments) => TReturn,
|
||||
): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Accepts a function that will be used as an implementation of the mock for
|
||||
* one call to the mocked function. Can be chained so that multiple function
|
||||
* calls produce different results.
|
||||
*/
|
||||
mockImplementationOnce(
|
||||
fn: (...args: TArguments) => TReturn,
|
||||
): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Accepts a string to use in test result output in place of "jest.fn()" to
|
||||
* indicate which mock function is being referenced.
|
||||
*/
|
||||
mockName(name: string): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValue(Promise.reject(value));
|
||||
*/
|
||||
mockRejectedValue(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValueOnce(Promise.reject(value));
|
||||
*/
|
||||
mockRejectedValueOnce(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValue(Promise.resolve(value));
|
||||
*/
|
||||
mockResolvedValue(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for: jest.fn().mockReturnValueOnce(Promise.resolve(value));
|
||||
*/
|
||||
mockResolvedValueOnce(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Just a simple sugar function for returning `this`
|
||||
*/
|
||||
mockReturnThis(): void,
|
||||
mockReturnValue(value: any): JestMockFn,
|
||||
mockReturnValueOnce(value: any): JestMockFn,
|
||||
/**
|
||||
* Deprecated: use jest.fn(() => value) instead
|
||||
*/
|
||||
mockReturnValue(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Sugar for only returning a value once inside your mock
|
||||
*/
|
||||
mockReturnValueOnce(value: TReturn): JestMockFn<TArguments, TReturn>,
|
||||
};
|
||||
|
||||
type JestAsymmetricEqualityType = {
|
||||
/**
|
||||
* A custom Jasmine equality tester
|
||||
*/
|
||||
asymmetricMatch(value: mixed): boolean,
|
||||
};
|
||||
|
||||
@@ -44,7 +138,7 @@ type JestCallsType = {
|
||||
type JestClockType = {
|
||||
install(): void,
|
||||
mockDate(date: Date): void,
|
||||
tick(): void,
|
||||
tick(milliseconds?: number): void,
|
||||
uninstall(): void,
|
||||
};
|
||||
|
||||
@@ -68,105 +162,471 @@ type JestPromiseType = {
|
||||
resolves: JestExpectType,
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin: jest-enzyme
|
||||
*/
|
||||
type EnzymeMatchersType = {
|
||||
toBeChecked(): void,
|
||||
toBeDisabled(): void,
|
||||
toBeEmpty(): void,
|
||||
toBePresent(): void,
|
||||
toContainReact(element: React$Element<any>): void,
|
||||
toHaveClassName(className: string): void,
|
||||
toHaveHTML(html: string): void,
|
||||
toHaveProp(propKey: string, propValue?: any): void,
|
||||
toHaveRef(refName: string): void,
|
||||
toHaveState(stateKey: string, stateValue?: any): void,
|
||||
toHaveStyle(styleKey: string, styleValue?: any): void,
|
||||
toHaveTagName(tagName: string): void,
|
||||
toHaveText(text: string): void,
|
||||
toIncludeText(text: string): void,
|
||||
toHaveValue(value: any): void,
|
||||
toMatchElement(element: React$Element<any>): void,
|
||||
toMatchSelector(selector: string): void,
|
||||
};
|
||||
|
||||
type JestExpectType = {
|
||||
not: JestExpectType,
|
||||
not: JestExpectType & EnzymeMatchersType,
|
||||
/**
|
||||
* If you have a mock function, you can use .lastCalledWith to test what
|
||||
* arguments it was last called with.
|
||||
*/
|
||||
lastCalledWith(...args: Array<any>): void,
|
||||
/**
|
||||
* toBe just checks that a value is what you expect. It uses === to check
|
||||
* strict equality.
|
||||
*/
|
||||
toBe(value: any): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalled to ensure that a mock function got called.
|
||||
*/
|
||||
toBeCalled(): void,
|
||||
/**
|
||||
* Use .toBeCalledWith to ensure that a mock function was called with
|
||||
* specific arguments.
|
||||
*/
|
||||
toBeCalledWith(...args: Array<any>): void,
|
||||
/**
|
||||
* Using exact equality with floating point numbers is a bad idea. Rounding
|
||||
* means that intuitive things fail.
|
||||
*/
|
||||
toBeCloseTo(num: number, delta: any): void,
|
||||
/**
|
||||
* Use .toBeDefined to check that a variable is not undefined.
|
||||
*/
|
||||
toBeDefined(): void,
|
||||
/**
|
||||
* Use .toBeFalsy when you don't care what a value is, you just want to
|
||||
* ensure a value is false in a boolean context.
|
||||
*/
|
||||
toBeFalsy(): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeGreaterThan.
|
||||
*/
|
||||
toBeGreaterThan(number: number): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeGreaterThanOrEqual.
|
||||
*/
|
||||
toBeGreaterThanOrEqual(number: number): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeLessThan.
|
||||
*/
|
||||
toBeLessThan(number: number): void,
|
||||
/**
|
||||
* To compare floating point numbers, you can use toBeLessThanOrEqual.
|
||||
*/
|
||||
toBeLessThanOrEqual(number: number): void,
|
||||
/**
|
||||
* Use .toBeInstanceOf(Class) to check that an object is an instance of a
|
||||
* class.
|
||||
*/
|
||||
toBeInstanceOf(cls: Class<*>): void,
|
||||
/**
|
||||
* .toBeNull() is the same as .toBe(null) but the error messages are a bit
|
||||
* nicer.
|
||||
*/
|
||||
toBeNull(): void,
|
||||
/**
|
||||
* Use .toBeTruthy when you don't care what a value is, you just want to
|
||||
* ensure a value is true in a boolean context.
|
||||
*/
|
||||
toBeTruthy(): void,
|
||||
/**
|
||||
* Use .toBeUndefined to check that a variable is undefined.
|
||||
*/
|
||||
toBeUndefined(): void,
|
||||
/**
|
||||
* Use .toContain when you want to check that an item is in a list. For
|
||||
* testing the items in the list, this uses ===, a strict equality check.
|
||||
*/
|
||||
toContain(item: any): void,
|
||||
/**
|
||||
* Use .toContainEqual when you want to check that an item is in a list. For
|
||||
* testing the items in the list, this matcher recursively checks the
|
||||
* equality of all fields, rather than checking for object identity.
|
||||
*/
|
||||
toContainEqual(item: any): void,
|
||||
/**
|
||||
* Use .toEqual when you want to check that two objects have the same value.
|
||||
* This matcher recursively checks the equality of all fields, rather than
|
||||
* checking for object identity.
|
||||
*/
|
||||
toEqual(value: any): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalled to ensure that a mock function got called.
|
||||
*/
|
||||
toHaveBeenCalled(): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalledTimes to ensure that a mock function got called exact
|
||||
* number of times.
|
||||
*/
|
||||
toHaveBeenCalledTimes(number: number): void,
|
||||
/**
|
||||
* Use .toHaveBeenCalledWith to ensure that a mock function was called with
|
||||
* specific arguments.
|
||||
*/
|
||||
toHaveBeenCalledWith(...args: Array<any>): void,
|
||||
toHaveProperty(path: string, value?: any): void,
|
||||
toMatch(regexp: RegExp): void,
|
||||
/**
|
||||
* Use .toHaveBeenLastCalledWith to ensure that a mock function was last called
|
||||
* with specific arguments.
|
||||
*/
|
||||
toHaveBeenLastCalledWith(...args: Array<any>): void,
|
||||
/**
|
||||
* Check that an object has a .length property and it is set to a certain
|
||||
* numeric value.
|
||||
*/
|
||||
toHaveLength(number: number): void,
|
||||
/**
|
||||
*
|
||||
*/
|
||||
toHaveProperty(propPath: string, value?: any): void,
|
||||
/**
|
||||
* Use .toMatch to check that a string matches a regular expression or string.
|
||||
*/
|
||||
toMatch(regexpOrString: RegExp | string): void,
|
||||
/**
|
||||
* Use .toMatchObject to check that a javascript object matches a subset of the properties of an object.
|
||||
*/
|
||||
toMatchObject(object: Object): void,
|
||||
toMatchSnapshot(): void,
|
||||
toThrow(message?: string | Error | Class<Error>): void,
|
||||
toThrowError(message?: string | Error | Class<Error> | RegExp): void,
|
||||
/**
|
||||
* This ensures that a React component matches the most recent snapshot.
|
||||
*/
|
||||
toMatchSnapshot(name?: string): void,
|
||||
/**
|
||||
* Use .toThrow to test that a function throws when it is called.
|
||||
* If you want to test that a specific error gets thrown, you can provide an
|
||||
* argument to toThrow. The argument can be a string for the error message,
|
||||
* a class for the error, or a regex that should match the error.
|
||||
*
|
||||
* Alias: .toThrowError
|
||||
*/
|
||||
toThrow(message?: string | Error | RegExp): void,
|
||||
toThrowError(message?: string | Error | RegExp): void,
|
||||
/**
|
||||
* Use .toThrowErrorMatchingSnapshot to test that a function throws a error
|
||||
* matching the most recent snapshot when it is called.
|
||||
*/
|
||||
toThrowErrorMatchingSnapshot(): void,
|
||||
};
|
||||
|
||||
type JestObjectType = {
|
||||
/**
|
||||
* Disables automatic mocking in the module loader.
|
||||
*
|
||||
* After this method is called, all `require()`s will return the real
|
||||
* versions of each module (rather than a mocked version).
|
||||
*/
|
||||
disableAutomock(): JestObjectType,
|
||||
/**
|
||||
* An un-hoisted version of disableAutomock
|
||||
*/
|
||||
autoMockOff(): JestObjectType,
|
||||
/**
|
||||
* Enables automatic mocking in the module loader.
|
||||
*/
|
||||
enableAutomock(): JestObjectType,
|
||||
/**
|
||||
* An un-hoisted version of enableAutomock
|
||||
*/
|
||||
autoMockOn(): JestObjectType,
|
||||
/**
|
||||
* Clears the mock.calls and mock.instances properties of all mocks.
|
||||
* Equivalent to calling .mockClear() on every mocked function.
|
||||
*/
|
||||
clearAllMocks(): JestObjectType,
|
||||
/**
|
||||
* Resets the state of all mocks. Equivalent to calling .mockReset() on every
|
||||
* mocked function.
|
||||
*/
|
||||
resetAllMocks(): JestObjectType,
|
||||
/**
|
||||
* Restores all mocks back to their original value.
|
||||
*/
|
||||
restoreAllMocks(): JestObjectType,
|
||||
/**
|
||||
* Removes any pending timers from the timer system.
|
||||
*/
|
||||
clearAllTimers(): void,
|
||||
/**
|
||||
* The same as `mock` but not moved to the top of the expectation by
|
||||
* babel-jest.
|
||||
*/
|
||||
doMock(moduleName: string, moduleFactory?: any): JestObjectType,
|
||||
/**
|
||||
* The same as `unmock` but not moved to the top of the expectation by
|
||||
* babel-jest.
|
||||
*/
|
||||
dontMock(moduleName: string): JestObjectType,
|
||||
/**
|
||||
* Returns a new, unused mock function. Optionally takes a mock
|
||||
* implementation.
|
||||
*/
|
||||
fn<TArguments: $ReadOnlyArray<*>, TReturn>(
|
||||
implementation?: (...args: TArguments) => TReturn,
|
||||
): JestMockFn<TArguments, TReturn>,
|
||||
/**
|
||||
* Determines if the given function is a mocked function.
|
||||
*/
|
||||
isMockFunction(fn: Function): boolean,
|
||||
/**
|
||||
* Given the name of a module, use the automatic mocking system to generate a
|
||||
* mocked version of the module for you.
|
||||
*/
|
||||
genMockFromModule(moduleName: string): any,
|
||||
/**
|
||||
* Mocks a module with an auto-mocked version when it is being required.
|
||||
*
|
||||
* The second argument can be used to specify an explicit module factory that
|
||||
* is being run instead of using Jest's automocking feature.
|
||||
*
|
||||
* The third argument can be used to create virtual mocks -- mocks of modules
|
||||
* that don't exist anywhere in the system.
|
||||
*/
|
||||
mock(
|
||||
moduleName: string,
|
||||
moduleFactory?: any,
|
||||
options?: Object,
|
||||
): JestObjectType,
|
||||
/**
|
||||
* Resets the module registry - the cache of all required modules. This is
|
||||
* useful to isolate modules where local state might conflict between tests.
|
||||
*/
|
||||
resetModules(): JestObjectType,
|
||||
/**
|
||||
* Exhausts the micro-task queue (usually interfaced in node via
|
||||
* process.nextTick).
|
||||
*/
|
||||
runAllTicks(): void,
|
||||
/**
|
||||
* Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(),
|
||||
* setInterval(), and setImmediate()).
|
||||
*/
|
||||
runAllTimers(): void,
|
||||
/**
|
||||
* Exhausts all tasks queued by setImmediate().
|
||||
*/
|
||||
runAllImmediates(): void,
|
||||
/**
|
||||
* Executes only the macro task queue (i.e. all tasks queued by setTimeout()
|
||||
* or setInterval() and setImmediate()).
|
||||
*/
|
||||
advanceTimersByTime(msToRun: number): void,
|
||||
/**
|
||||
* Executes only the macro task queue (i.e. all tasks queued by setTimeout()
|
||||
* or setInterval() and setImmediate()).
|
||||
*
|
||||
* Renamed to `advanceTimersByTime`.
|
||||
*/
|
||||
runTimersToTime(msToRun: number): void,
|
||||
/**
|
||||
* Executes only the macro-tasks that are currently pending (i.e., only the
|
||||
* tasks that have been queued by setTimeout() or setInterval() up to this
|
||||
* point)
|
||||
*/
|
||||
runOnlyPendingTimers(): void,
|
||||
/**
|
||||
* Explicitly supplies the mock object that the module system should return
|
||||
* for the specified module. Note: It is recommended to use jest.mock()
|
||||
* instead.
|
||||
*/
|
||||
setMock(moduleName: string, moduleExports: any): JestObjectType,
|
||||
/**
|
||||
* Indicates that the module system should never return a mocked version of
|
||||
* the specified module from require() (e.g. that it should always return the
|
||||
* real module).
|
||||
*/
|
||||
unmock(moduleName: string): JestObjectType,
|
||||
/**
|
||||
* Instructs Jest to use fake versions of the standard timer functions
|
||||
* (setTimeout, setInterval, clearTimeout, clearInterval, nextTick,
|
||||
* setImmediate and clearImmediate).
|
||||
*/
|
||||
useFakeTimers(): JestObjectType,
|
||||
/**
|
||||
* Instructs Jest to use the real versions of the standard timer functions.
|
||||
*/
|
||||
useRealTimers(): JestObjectType,
|
||||
/**
|
||||
* Creates a mock function similar to jest.fn but also tracks calls to
|
||||
* object[methodName].
|
||||
*/
|
||||
spyOn(object: Object, methodName: string): JestMockFn<any, any>,
|
||||
/**
|
||||
* Set the default timeout interval for tests and before/after hooks in milliseconds.
|
||||
* Note: The default timeout interval is 5 seconds if this method is not called.
|
||||
*/
|
||||
setTimeout(timeout: number): JestObjectType,
|
||||
|
||||
// These methods are added separately and not a part of flow-typed OSS
|
||||
// version of this file. They were added in jest@21.0.0.alpha-2 which is not
|
||||
// yet publicly released yet.
|
||||
// TODO T21262347 Add them to OSS version of flow-typed
|
||||
requireActual(module: string): any,
|
||||
requireMock(module: string): any,
|
||||
};
|
||||
|
||||
type JestSpyType = {
|
||||
calls: JestCallsType,
|
||||
};
|
||||
|
||||
declare function afterEach(fn: Function): void;
|
||||
declare function beforeEach(fn: Function): void;
|
||||
declare function afterAll(fn: Function): void;
|
||||
declare function beforeAll(fn: Function): void;
|
||||
declare function describe(name: string, fn: Function): void;
|
||||
declare var it: {
|
||||
(name: string, fn: Function): ?Promise<void>,
|
||||
only(name: string, fn: Function): ?Promise<void>,
|
||||
skip(name: string, fn: Function): ?Promise<void>,
|
||||
/** Runs this function after every test inside this context */
|
||||
declare function afterEach(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** Runs this function before every test inside this context */
|
||||
declare function beforeEach(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** Runs this function after all tests have finished inside this context */
|
||||
declare function afterAll(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** Runs this function before any tests have started inside this context */
|
||||
declare function beforeAll(
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
|
||||
/** A context for grouping tests together */
|
||||
declare var describe: {
|
||||
/**
|
||||
* Creates a block that groups together several related tests in one "test suite"
|
||||
*/
|
||||
(name: string, fn: () => void): void,
|
||||
|
||||
/**
|
||||
* Only run this describe block
|
||||
*/
|
||||
only(name: string, fn: () => void): void,
|
||||
|
||||
/**
|
||||
* Skip running this describe block
|
||||
*/
|
||||
skip(name: string, fn: () => void): void,
|
||||
};
|
||||
declare function fit(name: string, fn: Function): ?Promise<void>;
|
||||
declare function pit(name: string, fn: () => Promise<any>): Promise<void>;
|
||||
|
||||
/** An individual test unit */
|
||||
declare var it: {
|
||||
/**
|
||||
* An individual test unit
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
/**
|
||||
* Only run this test
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
only(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
/**
|
||||
* Skip running this test
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
skip(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
/**
|
||||
* Run the test concurrently
|
||||
*
|
||||
* @param {string} Name of Test
|
||||
* @param {Function} Test
|
||||
* @param {number} Timeout for the test, in milliseconds.
|
||||
*/
|
||||
concurrent(
|
||||
name: string,
|
||||
fn?: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void,
|
||||
};
|
||||
declare function fit(
|
||||
name: string,
|
||||
fn: (done: () => void) => ?Promise<mixed>,
|
||||
timeout?: number,
|
||||
): void;
|
||||
/** An individual test unit */
|
||||
declare var test: typeof it;
|
||||
/** A disabled group of tests */
|
||||
declare var xdescribe: typeof describe;
|
||||
/** A focused group of tests */
|
||||
declare var fdescribe: typeof describe;
|
||||
/** A disabled individual test */
|
||||
declare var xit: typeof it;
|
||||
/** A disabled individual test */
|
||||
declare var xtest: typeof it;
|
||||
|
||||
/** The expect function is used every time you want to test a value */
|
||||
declare var expect: {
|
||||
(value: any): JestExpectType & JestPromiseType,
|
||||
any: any,
|
||||
/** The object that you want to make assertions against */
|
||||
(value: any): JestExpectType & JestPromiseType & EnzymeMatchersType,
|
||||
/** Add additional Jasmine matchers to Jest's roster */
|
||||
extend(matchers: {[name: string]: JestMatcher}): void,
|
||||
objectContaining(any): void,
|
||||
/** Add a module that formats application-specific data structures. */
|
||||
addSnapshotSerializer(serializer: (input: Object) => string): void,
|
||||
assertions(expectedAssertions: number): void,
|
||||
hasAssertions(): void,
|
||||
any(value: mixed): JestAsymmetricEqualityType,
|
||||
anything(): void,
|
||||
arrayContaining(value: Array<mixed>): void,
|
||||
objectContaining(value: Object): void,
|
||||
/** Matches any received string that contains the exact expected string. */
|
||||
stringContaining(value: string): void,
|
||||
stringMatching(value: string | RegExp): void,
|
||||
};
|
||||
|
||||
declare function fail(message?: string): void;
|
||||
|
||||
// TODO handle return type
|
||||
// http://jasmine.github.io/2.4/introduction.html#section-Spies
|
||||
declare function spyOn(value: mixed, method: string): Object;
|
||||
|
||||
type Jest = {
|
||||
autoMockOff(): Jest,
|
||||
autoMockOn(): Jest,
|
||||
resetAllMocks(): Jest,
|
||||
clearAllTimers(): void,
|
||||
currentTestPath(): void,
|
||||
disableAutomock(): Jest,
|
||||
doMock(moduleName: string, moduleFactory?: any): void,
|
||||
dontMock(moduleName: string): Jest,
|
||||
enableAutomock(): Jest,
|
||||
fn(implementation?: Function): JestMockFn,
|
||||
genMockFromModule(moduleName: string): any,
|
||||
isMockFunction(fn: Function): boolean,
|
||||
mock(
|
||||
moduleName: string,
|
||||
moduleFactory?: any,
|
||||
options?: {virtual?: boolean},
|
||||
): Jest,
|
||||
resetModuleRegistry(): Jest, // undocumented alias for resetModuleRegistry
|
||||
resetModules(): Jest,
|
||||
restoreAllMocks(): Jest,
|
||||
runAllTicks(): Jest,
|
||||
runAllTimers(): Jest,
|
||||
runTimersToTime(msToRun: number): Jest,
|
||||
runOnlyPendingTimers(): Jest,
|
||||
setMock(moduleName: string, moduleExports: any): Jest,
|
||||
unmock(moduleName: string): Jest,
|
||||
useFakeTimers(): Jest,
|
||||
useRealTimers(): Jest,
|
||||
};
|
||||
|
||||
declare var jest: Jest;
|
||||
/** Holds all functions related to manipulating test runner */
|
||||
declare var jest: JestObjectType;
|
||||
|
||||
/**
|
||||
* The global Jamine object, this is generally not exposed as the public API,
|
||||
* using features inside here could break in later versions of Jest.
|
||||
*/
|
||||
declare var jasmine: {
|
||||
DEFAULT_TIMEOUT_INTERVAL: number,
|
||||
any(value: mixed): JestAsymmetricEqualityType,
|
||||
@@ -174,6 +634,10 @@ declare var jasmine: {
|
||||
arrayContaining(value: Array<mixed>): void,
|
||||
clock(): JestClockType,
|
||||
createSpy(name: string): JestSpyType,
|
||||
createSpyObj(
|
||||
baseName: string,
|
||||
methodNames: Array<string>,
|
||||
): {[methodName: string]: JestSpyType},
|
||||
objectContaining(value: Object): void,
|
||||
stringMatching(value: string): void,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:0.18.+"
|
||||
compile "com.android.support:appcompat-v7:26.1.0"
|
||||
compile "com.facebook.react:react-native:+"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ dependencies {
|
||||
exclude(group: 'org.unwanted', module: 'test10')
|
||||
}
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:0.18.+"
|
||||
compile "com.android.support:appcompat-v7:26.1.0"
|
||||
compile "com.facebook.react:react-native:+"
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ def enableSeparateBuildPerCPUArchitecture = false
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.2"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.helloworld"
|
||||
@@ -138,7 +138,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.android.support:appcompat-v7:26.1.0"
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "1000.0.0",
|
||||
"version": "0.56.0-rc.1",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -221,4 +221,4 @@
|
||||
"shelljs": "^0.7.8",
|
||||
"sinon": "^2.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -4,9 +4,9 @@
|
||||
|
||||
## ANDROID ##
|
||||
# Android SDK Build Tools revision
|
||||
export ANDROID_SDK_BUILD_TOOLS_REVISION=23.0.1
|
||||
export ANDROID_SDK_BUILD_TOOLS_REVISION=26.0.2
|
||||
# Android API Level we build with
|
||||
export ANDROID_SDK_BUILD_API_LEVEL="23"
|
||||
export ANDROID_SDK_BUILD_API_LEVEL="26"
|
||||
# Google APIs for Android level
|
||||
export ANDROID_GOOGLE_API_LEVEL="23"
|
||||
# Minimum Android API Level we target
|
||||
|
||||
Reference in New Issue
Block a user