Convert to TypeScript

Summary: Converted the Crash Reporter plugin to Typescript while fixing all linting errors

Reviewed By: jknoxville

Differential Revision: D22571332

fbshipit-source-id: d3092ffc480827b5eb1beb5c3cc91dad6993e267
This commit is contained in:
Yann Noutary
2020-07-22 00:59:25 -07:00
committed by Facebook GitHub Bot
parent 9f4f547ef4
commit c13593bc7e
4 changed files with 182 additions and 121 deletions

View File

@@ -12,7 +12,7 @@ import CrashReporterPlugin from '..';
import type {PersistedState, Crash} from '..'; import type {PersistedState, Crash} from '..';
import { import {
parseCrashLog, parseCrashLog,
getNewPersisitedStateFromCrashLog, getNewPersistedStateFromCrashLog,
parsePath, parsePath,
shouldShowCrashNotification, shouldShowCrashNotification,
} from '..'; } from '..';
@@ -70,7 +70,7 @@ afterAll(() => {
test('test the parsing of the date and crash info for the log which matches the predefined regex', () => { test('test the parsing of the date and crash info for the log which matches the predefined regex', () => {
const log = const log =
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa Date/Time: 2019-03-21 12:07:00.861 +0000 \n Blaa balaaa'; 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa Date/Time: 2019-03-21 12:07:00.861 +0000 \n Blaa balaaa';
const crash = parseCrashLog(log, 'iOS'); const crash = parseCrashLog(log, 'iOS', null);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('SIGSEGV'); expect(crash.reason).toEqual('SIGSEGV');
expect(crash.name).toEqual('SIGSEGV'); expect(crash.name).toEqual('SIGSEGV');
@@ -80,7 +80,7 @@ test('test the parsing of the date and crash info for the log which matches the
test('test the parsing of the reason for crash when log matches the crash regex, but there is no mention of date', () => { test('test the parsing of the reason for crash when log matches the crash regex, but there is no mention of date', () => {
const log = const log =
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa'; 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa';
const crash = parseCrashLog(log, 'iOS'); const crash = parseCrashLog(log, 'iOS', undefined);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('SIGSEGV'); expect(crash.reason).toEqual('SIGSEGV');
expect(crash.name).toEqual('SIGSEGV'); expect(crash.name).toEqual('SIGSEGV');
@@ -89,7 +89,7 @@ test('test the parsing of the reason for crash when log matches the crash regex,
test('test the parsing of the crash log when log does not match the predefined regex but is alphanumeric', () => { test('test the parsing of the crash log when log does not match the predefined regex but is alphanumeric', () => {
const log = 'Blaa Blaaa \n Blaa Blaaa \n Blaa Blaaa'; const log = 'Blaa Blaaa \n Blaa Blaaa \n Blaa Blaaa';
const crash = parseCrashLog(log, 'iOS'); const crash = parseCrashLog(log, 'iOS', undefined);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause');
@@ -98,7 +98,7 @@ test('test the parsing of the crash log when log does not match the predefined r
test('test the parsing of the reason for crash when log does not match the predefined regex contains unicode character', () => { test('test the parsing of the reason for crash when log does not match the predefined regex contains unicode character', () => {
const log = const log =
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: 🍕🐬 \n Blaa Blaa \n Blaa Blaa'; 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: 🍕🐬 \n Blaa Blaa \n Blaa Blaa';
const crash = parseCrashLog(log, 'iOS'); const crash = parseCrashLog(log, 'iOS', undefined);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause');
@@ -106,7 +106,7 @@ test('test the parsing of the reason for crash when log does not match the prede
}); });
test('test the parsing of the reason for crash when log is empty', () => { test('test the parsing of the reason for crash when log is empty', () => {
const log = ''; const log = '';
const crash = parseCrashLog(log, 'iOS'); const crash = parseCrashLog(log, 'iOS', undefined);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause');
@@ -126,7 +126,7 @@ test('test the parsing of the Android crash log for the proper android crash for
}); });
test('test the parsing of the Android crash log for the unknown crash format and no date', () => { test('test the parsing of the Android crash log for the unknown crash format and no date', () => {
const log = 'Blaa Blaa Blaa'; const log = 'Blaa Blaa Blaa';
const crash = parseCrashLog(log, 'Android'); const crash = parseCrashLog(log, 'Android', undefined);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause');
@@ -134,7 +134,7 @@ test('test the parsing of the Android crash log for the unknown crash format and
}); });
test('test the parsing of the Android crash log for the partial format matching the crash format', () => { test('test the parsing of the Android crash log for the partial format matching the crash format', () => {
const log = 'First Line Break \n Blaa Blaa \n Blaa Blaa '; const log = 'First Line Break \n Blaa Blaa \n Blaa Blaa ';
const crash = parseCrashLog(log, 'Android'); const crash = parseCrashLog(log, 'Android', null);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('First Line Break '); expect(crash.name).toEqual('First Line Break ');
@@ -142,26 +142,26 @@ test('test the parsing of the Android crash log for the partial format matching
test('test the parsing of the Android crash log with os being iOS', () => { test('test the parsing of the Android crash log with os being iOS', () => {
const log = const log =
'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n'; 'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n';
const crash = parseCrashLog(log, 'iOS'); const crash = parseCrashLog(log, 'iOS', null);
expect(crash.callstack).toEqual(log); expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause'); expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause'); expect(crash.name).toEqual('Cannot figure out the cause');
}); });
test('test the getter of pluginKey with proper input', () => { test('test the getter of pluginKey with proper input', () => {
const device = new BaseDevice('serial', 'emulator', 'test device'); const device = new BaseDevice('serial', 'emulator', 'test device', 'iOS');
const pluginKey = getPluginKey(null, device, 'CrashReporter'); const pluginKey = getPluginKey(null, device, 'CrashReporter');
expect(pluginKey).toEqual('serial#CrashReporter'); expect(pluginKey).toEqual('serial#CrashReporter');
}); });
test('test the getter of pluginKey with undefined input', () => { test('test the getter of pluginKey with undefined input', () => {
const pluginKey = getPluginKey(null, undefined, 'CrashReporter'); const pluginKey = getPluginKey(null, null, 'CrashReporter');
expect(pluginKey).toEqual('unknown#CrashReporter'); expect(pluginKey).toEqual('unknown#CrashReporter');
}); });
test('test the getter of pluginKey with defined selected app', () => { test('test the getter of pluginKey with defined selected app', () => {
const pluginKey = getPluginKey('selectedApp', undefined, 'CrashReporter'); const pluginKey = getPluginKey('selectedApp', null, 'CrashReporter');
expect(pluginKey).toEqual('selectedApp#CrashReporter'); expect(pluginKey).toEqual('selectedApp#CrashReporter');
}); });
test('test the getter of pluginKey with defined selected app and defined base device', () => { test('test the getter of pluginKey with defined selected app and defined base device', () => {
const device = new BaseDevice('serial', 'emulator', 'test device'); const device = new BaseDevice('serial', 'emulator', 'test device', 'iOS');
const pluginKey = getPluginKey('selectedApp', device, 'CrashReporter'); const pluginKey = getPluginKey('selectedApp', device, 'CrashReporter');
expect(pluginKey).toEqual('selectedApp#CrashReporter'); expect(pluginKey).toEqual('selectedApp#CrashReporter');
}); });
@@ -177,12 +177,12 @@ test('test getPersistedState for non-empty defaultPersistedState and undefined p
const crash = getCrash(0, 'callstack', 'crash0', 'crash0'); const crash = getCrash(0, 'callstack', 'crash0', 'crash0');
setDefaultPersistedState({crashes: [crash]}); setDefaultPersistedState({crashes: [crash]});
const pluginStates = {}; const pluginStates = {};
const perisistedState = getPersistedState( const persistedState = getPersistedState(
getPluginKey(null, null, CrashReporterPlugin.id), getPluginKey(null, null, CrashReporterPlugin.id),
CrashReporterPlugin, CrashReporterPlugin,
pluginStates, pluginStates,
); );
expect(perisistedState).toEqual({crashes: [crash]}); expect(persistedState).toEqual({crashes: [crash]});
}); });
test('test getPersistedState for non-empty defaultPersistedState and defined pluginState', () => { test('test getPersistedState for non-empty defaultPersistedState and defined pluginState', () => {
const crash = getCrash(0, 'callstack', 'crash0', 'crash0'); const crash = getCrash(0, 'callstack', 'crash0', 'crash0');
@@ -190,40 +190,42 @@ test('test getPersistedState for non-empty defaultPersistedState and defined plu
setDefaultPersistedState({crashes: [crash]}); setDefaultPersistedState({crashes: [crash]});
const pluginStateCrash = getCrash(1, 'callstack', 'crash1', 'crash1'); const pluginStateCrash = getCrash(1, 'callstack', 'crash1', 'crash1');
const pluginStates = {'unknown#CrashReporter': {crashes: [pluginStateCrash]}}; const pluginStates = {'unknown#CrashReporter': {crashes: [pluginStateCrash]}};
const perisistedState = getPersistedState( const persistedState = getPersistedState(
pluginKey, pluginKey,
CrashReporterPlugin, CrashReporterPlugin,
pluginStates, pluginStates,
); );
expect(perisistedState).toEqual({crashes: [pluginStateCrash]}); expect(persistedState).toEqual({crashes: [pluginStateCrash]});
}); });
test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState and defined pluginState', () => { test('test getNewPersistedStateFromCrashLog for non-empty defaultPersistedState and defined pluginState', () => {
const crash = getCrash(0, 'callstack', 'crash0', 'crash0'); const crash = getCrash(0, 'callstack', 'crash0', 'crash0');
const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id); const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id);
setDefaultPersistedState({crashes: [crash]}); setDefaultPersistedState({crashes: [crash]});
const pluginStateCrash = getCrash(1, 'callstack', 'crash1', 'crash1'); const pluginStateCrash = getCrash(1, 'callstack', 'crash1', 'crash1');
const pluginStates = {'unknown#CrashReporter': {crashes: [pluginStateCrash]}}; const pluginStates = {'unknown#CrashReporter': {crashes: [pluginStateCrash]}};
const perisistedState = getPersistedState( const persistedState = getPersistedState(
pluginKey, pluginKey,
CrashReporterPlugin, CrashReporterPlugin,
pluginStates, pluginStates,
); );
const content = const content =
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa'; 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa';
expect(perisistedState).toBeDefined(); expect(persistedState).toBeDefined();
const {crashes} = perisistedState; const definedState = persistedState as PersistedState;
const {crashes} = definedState;
expect(crashes).toBeDefined(); expect(crashes).toBeDefined();
expect(crashes.length).toEqual(1); expect(crashes.length).toEqual(1);
expect(crashes[0]).toEqual(pluginStateCrash); expect(crashes[0]).toEqual(pluginStateCrash);
const newPersistedState = getNewPersisitedStateFromCrashLog( const newPersistedState = getNewPersistedStateFromCrashLog(
perisistedState, definedState,
CrashReporterPlugin, CrashReporterPlugin,
content, content,
'iOS', 'iOS',
null,
); );
expect(newPersistedState).toBeDefined(); expect(newPersistedState).toBeDefined();
// $FlowFixMe: Checked if perisistedState is defined or not const newDefinedState = newPersistedState as PersistedState;
const newPersistedStateCrashes = newPersistedState.crashes; const newPersistedStateCrashes = newDefinedState.crashes;
expect(newPersistedStateCrashes).toBeDefined(); expect(newPersistedStateCrashes).toBeDefined();
expect(newPersistedStateCrashes.length).toEqual(2); expect(newPersistedStateCrashes.length).toEqual(2);
assertCrash(newPersistedStateCrashes[0], pluginStateCrash); assertCrash(newPersistedStateCrashes[0], pluginStateCrash);
@@ -232,34 +234,35 @@ test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState
getCrash(1, content, 'SIGSEGV', 'SIGSEGV'), getCrash(1, content, 'SIGSEGV', 'SIGSEGV'),
); );
}); });
test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState and undefined pluginState', () => { test('test getNewPersistedStateFromCrashLog for non-empty defaultPersistedState and undefined pluginState', () => {
setNotificationID(0); setNotificationID(0);
const crash = getCrash(0, 'callstack', 'crash0', 'crash0'); const crash = getCrash(0, 'callstack', 'crash0', 'crash0');
const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id); const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id);
setDefaultPersistedState({crashes: [crash]}); setDefaultPersistedState({crashes: [crash]});
const pluginStates = {}; const pluginStates = {};
const perisistedState = getPersistedState( const persistedState = getPersistedState(
pluginKey, pluginKey,
CrashReporterPlugin, CrashReporterPlugin,
pluginStates, pluginStates,
); );
const content = 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV'; const content = 'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV';
expect(perisistedState).toEqual({crashes: [crash]}); expect(persistedState).toEqual({crashes: [crash]});
const newPersistedState = getNewPersisitedStateFromCrashLog( const newPersistedState = getNewPersistedStateFromCrashLog(
perisistedState, persistedState as PersistedState,
CrashReporterPlugin, CrashReporterPlugin,
content, content,
'iOS', 'iOS',
null,
); );
expect(newPersistedState).toBeDefined(); expect(newPersistedState).toBeDefined();
// $FlowFixMe: Checked if perisistedState is defined or not // $FlowFixMe: Checked if perisistedState is defined or not
const {crashes} = newPersistedState; const {crashes} = newPersistedState as PersistedState;
expect(crashes).toBeDefined(); expect(crashes).toBeDefined();
expect(crashes.length).toEqual(2); expect(crashes.length).toEqual(2);
assertCrash(crashes[0], crash); assertCrash(crashes[0], crash);
assertCrash(crashes[1], getCrash(1, content, 'SIGSEGV', 'SIGSEGV')); assertCrash(crashes[1], getCrash(1, content, 'SIGSEGV', 'SIGSEGV'));
}); });
test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState and defined pluginState and improper crash log', () => { test('test getNewPersistedStateFromCrashLog for non-empty defaultPersistedState and defined pluginState and improper crash log', () => {
setNotificationID(0); setNotificationID(0);
const crash = getCrash(0, 'callstack', 'crash0', 'crash0'); const crash = getCrash(0, 'callstack', 'crash0', 'crash0');
const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id); const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id);
@@ -273,15 +276,16 @@ test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState
); );
const content = 'Blaa Blaaa \n Blaa Blaaa'; const content = 'Blaa Blaaa \n Blaa Blaaa';
expect(perisistedState).toEqual({crashes: [pluginStateCrash]}); expect(perisistedState).toEqual({crashes: [pluginStateCrash]});
const newPersistedState = getNewPersisitedStateFromCrashLog( const newPersistedState = getNewPersistedStateFromCrashLog(
perisistedState, perisistedState as PersistedState,
CrashReporterPlugin, CrashReporterPlugin,
content, content,
'iOS', 'iOS',
null,
); );
expect(newPersistedState).toBeDefined(); expect(newPersistedState).toBeDefined();
// $FlowFixMe: Checked if perisistedState is defined or not // $FlowFixMe: Checked if perisistedState is defined or not
const {crashes} = newPersistedState; const {crashes} = newPersistedState as PersistedState;
expect(crashes).toBeDefined(); expect(crashes).toBeDefined();
expect(crashes.length).toEqual(2); expect(crashes.length).toEqual(2);
assertCrash(crashes[0], pluginStateCrash); assertCrash(crashes[0], pluginStateCrash);
@@ -295,23 +299,25 @@ test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState
), ),
); );
}); });
test('test getNewPersisitedStateFromCrashLog when os is undefined', () => { test('test getNewPersistedStateFromCrashLog when os is undefined', () => {
setNotificationID(0); setNotificationID(0);
const crash = getCrash(0, 'callstack', 'crash0', 'crash0'); const crash = getCrash(0, 'callstack', 'crash0', 'crash0');
const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id); const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id);
setDefaultPersistedState({crashes: [crash]}); setDefaultPersistedState({crashes: [crash]});
const pluginStateCrash = getCrash(1, 'callstack', 'crash1', 'crash1'); const pluginStateCrash = getCrash(1, 'callstack', 'crash1', 'crash1');
const pluginStates = {'unknown#CrashReporter': {crashes: [pluginStateCrash]}}; const pluginStates = {'unknown#CrashReporter': {crashes: [pluginStateCrash]}};
const perisistedState = getPersistedState( const persistedState = getPersistedState(
pluginKey, pluginKey,
CrashReporterPlugin, CrashReporterPlugin,
pluginStates, pluginStates,
); );
const content = 'Blaa Blaaa \n Blaa Blaaa'; const content = 'Blaa Blaaa \n Blaa Blaaa';
const newPersistedState = getNewPersisitedStateFromCrashLog( const newPersistedState = getNewPersistedStateFromCrashLog(
perisistedState, persistedState as PersistedState,
CrashReporterPlugin, CrashReporterPlugin,
content, content,
undefined,
null,
); );
expect(newPersistedState).toEqual(null); expect(newPersistedState).toEqual(null);
}); });
@@ -347,22 +353,44 @@ test('test parsing of path when a regex is not present', () => {
expect(id).toEqual(null); expect(id).toEqual(null);
}); });
test('test shouldShowCrashNotification function for all correct inputs', () => { test('test shouldShowCrashNotification function for all correct inputs', () => {
const device = new BaseDevice('TH1S-15DEV1CE-1D', 'emulator', 'test device'); const device = new BaseDevice(
'TH1S-15DEV1CE-1D',
'emulator',
'test device',
'iOS',
);
const content = const content =
'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-15DEV1CE-1D/App Name.app/App Name \n Blaa Blaa \n Blaa Blaa'; 'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-15DEV1CE-1D/App Name.app/App Name \n Blaa Blaa \n Blaa Blaa';
const shouldShowNotification = shouldShowCrashNotification(device, content); const shouldShowNotification = shouldShowCrashNotification(
device,
content,
'iOS',
);
expect(shouldShowNotification).toEqual(true); expect(shouldShowNotification).toEqual(true);
}); });
test('test shouldShowCrashNotification function for all correct inputs but incorrect id', () => { test('test shouldShowCrashNotification function for all correct inputs but incorrect id', () => {
const device = new BaseDevice('TH1S-15DEV1CE-1D', 'emulator', 'test device'); const device = new BaseDevice(
'TH1S-15DEV1CE-1D',
'emulator',
'test device',
'iOS',
);
const content = const content =
'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-1598DEV1CE-2D/App Name.app/App Name \n Blaa Blaa \n Blaa Blaa'; 'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-1598DEV1CE-2D/App Name.app/App Name \n Blaa Blaa \n Blaa Blaa';
const shouldShowNotification = shouldShowCrashNotification(device, content); const shouldShowNotification = shouldShowCrashNotification(
device,
content,
'iOS',
);
expect(shouldShowNotification).toEqual(false); expect(shouldShowNotification).toEqual(false);
}); });
test('test shouldShowCrashNotification function for undefined device', () => { test('test shouldShowCrashNotification function for undefined device', () => {
const content = const content =
'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-1598DEV1CE-2D/App Name.app/App Name \n Blaa Blaa \n Blaa Blaa'; 'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-1598DEV1CE-2D/App Name.app/App Name \n Blaa Blaa \n Blaa Blaa';
const shouldShowNotification = shouldShowCrashNotification(null, content); const shouldShowNotification = shouldShowCrashNotification(
null,
content,
'iOS',
);
expect(shouldShowNotification).toEqual(false); expect(shouldShowNotification).toEqual(false);
}); });

View File

@@ -9,6 +9,7 @@
*/ */
import { import {
FlipperBasePlugin,
FlipperDevicePlugin, FlipperDevicePlugin,
Device, Device,
View, View,
@@ -37,57 +38,63 @@ import path from 'path';
import {promisify} from 'util'; import {promisify} from 'util';
import type {Notification} from 'flipper'; import type {Notification} from 'flipper';
import type {Store, DeviceLogEntry, OS, Props} from 'flipper'; import type {Store, DeviceLogEntry, OS, Props} from 'flipper';
import React from 'react';
import {Component} from 'react'; import {Component} from 'react';
type Maybe<T> = T | null | undefined;
type HeaderRowProps = { type HeaderRowProps = {
title: string, title: string;
value: string, value: string;
}; };
type openLogsCallbackType = () => void; type openLogsCallbackType = () => void;
type CrashReporterBarProps = {| type CrashReporterBarProps = {
openLogsCallback?: openLogsCallbackType, openLogsCallback?: openLogsCallbackType;
crashSelector: CrashSelectorProps, crashSelector: CrashSelectorProps;
|}; };
type CrashSelectorProps = {| type CrashSelectorProps = {
crashes: ?{[key: string]: string}, crashes?: {[key: string]: string};
orderedIDs: ?Array<string>, orderedIDs?: Array<string>;
selectedCrashID: ?string, selectedCrashID?: string;
onCrashChange: ?(string) => void, onCrashChange: (name: Maybe<string>) => void;
|}; };
export type Crash = {| export type Crash = {
notificationID: string, notificationID: string;
callstack: ?string, callstack?: string;
reason: string, reason: string;
name: string, name: string;
date: Date, date: Date;
|}; };
export type CrashLog = {| export type CrashLog = {
callstack: string, callstack: string;
reason: string, reason: string;
name: string, name: string;
date: ?Date, date: Maybe<Date>;
|}; };
export type PersistedState = { export type PersistedState = {
crashes: Array<Crash>, crashes: Array<Crash>;
}; };
type State = { type State = {
crash: ?Crash, crash?: Crash;
}; };
const Padder = styled.div( const Padder = styled.div<{
({paddingLeft, paddingRight, paddingBottom, paddingTop}) => ({ paddingLeft?: number;
paddingLeft: paddingLeft || 0, paddingRight?: number;
paddingRight: paddingRight || 0, paddingBottom?: number;
paddingBottom: paddingBottom || 0, paddingTop?: number;
paddingTop: paddingTop || 0, }>(({paddingLeft, paddingRight, paddingBottom, paddingTop}) => ({
}), paddingLeft: paddingLeft || 0,
); paddingRight: paddingRight || 0,
paddingBottom: paddingBottom || 0,
paddingTop: paddingTop || 0,
}));
const Title = styled(Text)({ const Title = styled(Text)({
fontWeight: 'bold', fontWeight: 'bold',
@@ -183,13 +190,13 @@ const StackTraceContainer = styled(FlexColumn)({
const UNKNOWN_CRASH_REASON = 'Cannot figure out the cause'; const UNKNOWN_CRASH_REASON = 'Cannot figure out the cause';
export function getNewPersisitedStateFromCrashLog( export function getNewPersistedStateFromCrashLog(
persistedState: ?PersistedState, persistedState: Maybe<PersistedState>,
persistingPlugin: Class<FlipperDevicePlugin<> | FlipperPlugin<>>, persistingPlugin: typeof FlipperBasePlugin,
content: string, content: string,
os: ?OS, os: Maybe<OS>,
logDate: ?Date, logDate: Maybe<Date>,
): ?PersistedState { ): Maybe<PersistedState> {
const persistedStateReducer = persistingPlugin.persistedStateReducer; const persistedStateReducer = persistingPlugin.persistedStateReducer;
if (!os || !persistedStateReducer) { if (!os || !persistedStateReducer) {
return null; return null;
@@ -208,9 +215,9 @@ export function parseCrashLogAndUpdateState(
content: string, content: string,
setPersistedState: ( setPersistedState: (
pluginKey: string, pluginKey: string,
newPluginState: ?PersistedState, newPluginState: Maybe<PersistedState>,
) => void, ) => void,
logDate: ?Date, logDate: Maybe<Date>,
) { ) {
const os = store.getState().connections.selectedDevice?.os; const os = store.getState().connections.selectedDevice?.os;
if ( if (
@@ -228,9 +235,11 @@ export function parseCrashLogAndUpdateState(
store.getState().connections.selectedDevice, store.getState().connections.selectedDevice,
pluginID, pluginID,
); );
const persistingPlugin: ?Class< const persistingPlugin:
FlipperDevicePlugin<> | FlipperPlugin<>, | typeof FlipperBasePlugin
> = store.getState().plugins.devicePlugins.get(CrashReporterPlugin.id); | undefined = store
.getState()
.plugins.devicePlugins.get(CrashReporterPlugin.id);
if (!persistingPlugin) { if (!persistingPlugin) {
return; return;
} }
@@ -240,8 +249,11 @@ export function parseCrashLogAndUpdateState(
persistingPlugin, persistingPlugin,
pluginStates, pluginStates,
); );
const newPluginState = getNewPersisitedStateFromCrashLog( if (!persistedState) {
persistedState, return;
}
const newPluginState = getNewPersistedStateFromCrashLog(
persistedState as PersistedState,
persistingPlugin, persistingPlugin,
content, content,
os, os,
@@ -251,9 +263,9 @@ export function parseCrashLogAndUpdateState(
} }
export function shouldShowCrashNotification( export function shouldShowCrashNotification(
baseDevice: ?BaseDevice, baseDevice: Maybe<BaseDevice>,
content: string, content: string,
os: ?OS, os: Maybe<OS>,
): boolean { ): boolean {
if (os && os === 'Android') { if (os && os === 'Android') {
return true; return true;
@@ -270,7 +282,7 @@ export function shouldShowCrashNotification(
export function parseCrashLog( export function parseCrashLog(
content: string, content: string,
os: OS, os: OS,
logDate: ?Date, logDate: Maybe<Date>,
): CrashLog { ): CrashLog {
const fallbackReason = UNKNOWN_CRASH_REASON; const fallbackReason = UNKNOWN_CRASH_REASON;
switch (os) { switch (os) {
@@ -289,12 +301,12 @@ export function parseCrashLog(
const dateString = dateArr ? dateArr[0] : ''; const dateString = dateArr ? dateArr[0] : '';
const dateRegex2 = /[\w\s\.:-]*$/; const dateRegex2 = /[\w\s\.:-]*$/;
const tmp1 = dateRegex2.exec(dateString); const tmp1 = dateRegex2.exec(dateString);
const extractedDateString: ?string = const extractedDateString: Maybe<string> =
tmp1 && tmp1[0].length ? tmp1[0] : null; tmp1 && tmp1[0].length ? tmp1[0] : null;
date = extractedDateString ? new Date(extractedDateString) : logDate; date = extractedDateString ? new Date(extractedDateString) : logDate;
} }
const crash = { const crash: CrashLog = {
callstack: content, callstack: content,
name: exception, name: exception,
reason: exception, reason: exception,
@@ -314,14 +326,15 @@ export function parseCrashLog(
if (remainingString[remainingString.length - 1] === '\n') { if (remainingString[remainingString.length - 1] === '\n') {
remainingString = remainingString.slice(0, -1); remainingString = remainingString.slice(0, -1);
} }
const reason = const reasonText =
remainingString.length > 0 remainingString.length > 0
? remainingString.split('\n').pop() ? remainingString.split('\n').pop()
: fallbackReason; : fallbackReason;
const reason = reasonText ? reasonText : fallbackReason;
if (name[name.length - 1] === '\n') { if (name[name.length - 1] === '\n') {
name = name.slice(0, -1); name = name.slice(0, -1);
} }
const crash = { const crash: CrashLog = {
callstack: content, callstack: content,
name: name, name: name,
reason: reason, reason: reason,
@@ -343,7 +356,7 @@ function truncate(baseString: string, numOfChars: number): string {
return truncated_string + '\u2026'; return truncated_string + '\u2026';
} }
export function parsePath(content: string): ?string { export function parsePath(content: string): Maybe<string> {
const regex = /Path: *[\w\-\/\.\t\ \_\%]*\n/; const regex = /Path: *[\w\-\/\.\t\ \_\%]*\n/;
const arr = regex.exec(content); const arr = regex.exec(content);
if (!arr || arr.length <= 0) { if (!arr || arr.length <= 0) {
@@ -363,7 +376,7 @@ function addFileWatcherForiOSCrashLogs(
store: Store, store: Store,
setPersistedState: ( setPersistedState: (
pluginKey: string, pluginKey: string,
newPluginState: ?PersistedState, newPluginState: Maybe<PersistedState>,
) => void, ) => void,
) { ) {
const dir = path.join(os.homedir(), 'Library', 'Logs', 'DiagnosticReports'); const dir = path.join(os.homedir(), 'Library', 'Logs', 'DiagnosticReports');
@@ -395,6 +408,7 @@ function addFileWatcherForiOSCrashLogs(
store, store,
util.format(data), util.format(data),
setPersistedState, setPersistedState,
null,
); );
}); });
}); });
@@ -412,8 +426,8 @@ class CrashSelector extends Component<CrashSelectorProps> {
disabled={Boolean(!orderedIDs || orderedIDs.length <= 1)} disabled={Boolean(!orderedIDs || orderedIDs.length <= 1)}
compact={true} compact={true}
onClick={() => { onClick={() => {
if (onCrashChange && orderedIDs) { if (onCrashChange && orderedIDs && selectedCrashID) {
const index = orderedIDs.indexOf(selectedCrashID); const index = orderedIDs.indexOf(selectedCrashID as string);
const nextIndex = const nextIndex =
index < 1 ? orderedIDs.length - 1 : index - 1; index < 1 ? orderedIDs.length - 1 : index - 1;
const nextID = orderedIDs[nextIndex]; const nextID = orderedIDs[nextIndex];
@@ -430,8 +444,8 @@ class CrashSelector extends Component<CrashSelectorProps> {
disabled={Boolean(!orderedIDs || orderedIDs.length <= 1)} disabled={Boolean(!orderedIDs || orderedIDs.length <= 1)}
compact={true} compact={true}
onClick={() => { onClick={() => {
if (onCrashChange && orderedIDs) { if (onCrashChange && orderedIDs && selectedCrashID) {
const index = orderedIDs.indexOf(selectedCrashID); const index = orderedIDs.indexOf(selectedCrashID as string);
const nextIndex = const nextIndex =
index >= orderedIDs.length - 1 ? 0 : index + 1; index >= orderedIDs.length - 1 ? 0 : index + 1;
const nextID = orderedIDs[nextIndex]; const nextID = orderedIDs[nextIndex];
@@ -506,7 +520,7 @@ class HeaderRow extends Component<HeaderRowProps> {
} }
type StackTraceComponentProps = { type StackTraceComponentProps = {
stacktrace: string, stacktrace: string;
}; };
class StackTraceComponent extends Component<StackTraceComponentProps> { class StackTraceComponent extends Component<StackTraceComponentProps> {
@@ -525,10 +539,12 @@ class StackTraceComponent extends Component<StackTraceComponentProps> {
export default class CrashReporterPlugin extends FlipperDevicePlugin< export default class CrashReporterPlugin extends FlipperDevicePlugin<
State, State,
void, any,
PersistedState, PersistedState
> { > {
static defaultPersistedState = {crashes: []}; static defaultPersistedState: PersistedState = {
crashes: [],
};
static supportsDevice(device: Device) { static supportsDevice(device: Device) {
return ( return (
@@ -544,7 +560,7 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
static persistedStateReducer = ( static persistedStateReducer = (
persistedState: PersistedState, persistedState: PersistedState,
method: string, method: string,
payload: Object, payload: CrashLog | Crash,
): PersistedState => { ): PersistedState => {
if (method === 'crash-report' || method === 'flipper-crash-report') { if (method === 'crash-report' || method === 'flipper-crash-report') {
CrashReporterPlugin.notificationID++; CrashReporterPlugin.notificationID++;
@@ -615,7 +631,7 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
baseDevice: BaseDevice, baseDevice: BaseDevice,
setPersistedState: ( setPersistedState: (
pluginKey: string, pluginKey: string,
newPluginState: ?PersistedState, newPluginState: Maybe<PersistedState>,
) => void, ) => void,
): void => { ): void => {
if (baseDevice.os.includes('iOS')) { if (baseDevice.os.includes('iOS')) {
@@ -627,12 +643,12 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
date: Date, date: Date,
setPersistedState: ( setPersistedState: (
pluginKey: string, pluginKey: string,
newPluginState: ?PersistedState, newPluginState: Maybe<PersistedState>,
) => void, ) => void,
) { ) {
let androidLog: string = ''; let androidLog: string = '';
let androidLogUnderProcess = false; let androidLogUnderProcess = false;
let timer = null; let timer: Maybe<NodeJS.Timeout> = null;
baseDevice.addLogListener((entry: DeviceLogEntry) => { baseDevice.addLogListener((entry: DeviceLogEntry) => {
if (shouldParseAndroidLog(entry, referenceDate)) { if (shouldParseAndroidLog(entry, referenceDate)) {
if (androidLogUnderProcess) { if (androidLogUnderProcess) {
@@ -669,7 +685,7 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
constructor(props: Props<PersistedState>) { constructor(props: Props<PersistedState>) {
// Required step: always call the parent class' constructor // Required step: always call the parent class' constructor
super(props); super(props);
let crash: ?Crash = null; let crash: Crash | undefined = undefined;
if ( if (
this.props.persistedState.crashes && this.props.persistedState.crashes &&
this.props.persistedState.crashes.length > 0 this.props.persistedState.crashes.length > 0
@@ -679,7 +695,7 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
]; ];
} }
let deeplinkedCrash = null; let deeplinkedCrash: Crash | undefined = undefined;
if (this.props.deepLinkPayload) { if (this.props.deepLinkPayload) {
const id = this.props.deepLinkPayload; const id = this.props.deepLinkPayload;
const index = this.props.persistedState.crashes.findIndex((elem) => { const index = this.props.persistedState.crashes.findIndex((elem) => {
@@ -720,7 +736,7 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
(persistedCrash) => persistedCrash.notificationID, (persistedCrash) => persistedCrash.notificationID,
); );
const selectedCrashID = crash.notificationID; const selectedCrashID = crash.notificationID;
const onCrashChange = (id) => { const onCrashChange = (id: Maybe<string>) => {
const newSelectedCrash = crashes.find( const newSelectedCrash = crashes.find(
(element) => element.notificationID === id, (element) => element.notificationID === id,
); );
@@ -784,10 +800,10 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
); );
} }
const crashSelector = { const crashSelector = {
crashes: null, crashes: undefined,
orderedIDs: null, orderedIDs: undefined,
selectedCrashID: null, selectedCrashID: undefined,
onCrashChange: null, onCrashChange: () => void {},
}; };
return ( return (
<StyledFlexGrowColumn> <StyledFlexGrowColumn>

View File

@@ -5,7 +5,7 @@
"version": "0.50.0", "version": "0.50.0",
"description": "A plugin which will display a crash", "description": "A plugin which will display a crash",
"main": "dist/bundle.js", "main": "dist/bundle.js",
"flipperBundlerEntry": "index.js", "flipperBundlerEntry": "index.tsx",
"repository": "https://github.com/facebook/flipper", "repository": "https://github.com/facebook/flipper",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
declare module 'unicode-substring' {
const unicodeSubstring: (
string: string,
start: number,
end: number,
) => string;
export default unicodeSubstring;
}