unexpected-react icon indicating copy to clipboard operation
unexpected-react copied to clipboard

Improving error feedback: reducing the details

Open yormi opened this issue 9 years ago • 4 comments

Hi again !

I was wondering if it would be possible to have a flag to reduce the verbosity of the error. Or at least not showing the function definition... Rigth now, because of router stuff, for one test I have this error returned :P

UnexpectedError: 
expected
<IngredientList
   history={{
  listenBefore: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  listen: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  transitionTo: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  push: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  replace: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  go: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  goBack: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  goForward: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  createKey: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  createPath: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  createHref: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  createLocation: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  setState: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  registerTransitionHook: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  unregisterTransitionHook: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  pushState: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  replaceState: function () { reduce  reduce  reduce 
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined; reduce 
    return object[prop].apply(object, arguments);
  },
  unsubscribe: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  isActive: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  match: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  },
  listenBeforeLeavingRoute: function () {
    process.env.NODE_ENV !== 'production' ? _routerWarning2['default'](false, message) : undefined;
    return object[prop].apply(object, arguments);
  }
}}
   location={{ pathname: '/', search: '', hash: '', state: null, action: 'POP', key: 'jvueyt', query: {}, $searchBase: { search: '', searchBase: '' } }}
   params={{}}
   route={{
  component: function Connect(props, context) {
    _classCallCheck(this, Connect);

    var _this = _possibleConstructorReturn(this, _Component.call(this, props, context));
    // ... lines removed ...
    _this.state = { storeState: storeState };
    _this.clearCache();
    return _this;
  }
}}
   routeParams={{}}
   routes={[
  {
    path: '/',
    component: function App() {
      _classCallCheck(this, App);

      return _possibleConstructorReturn(this, Object.getPrototypeOf(App).apply(this, arguments));
    },
    indexRoute: ...,
    childRoutes: ...
  },
  {
    component: function Connect(props, context) {
      _classCallCheck(this, Connect);

      var _this = _possibleConstructorReturn(this, _Component.call(this, props, context));
      // ... lines removed ...
      _this.state = { storeState: storeState };
      _this.clearCache();
      return _this;
    }
  }
]}
   ingredients={[]} additionsNotConfirmed={[]} failedAdditions={[]}
   dispatch={function (action) { /* ... */ }}>
  <ul />
</IngredientList>
to contain <ul><li>Banana</li><li>Marshmallow</li></ul>

the best match was
<ul>
  // missing <li>Banana</li>
  // missing <li>Marshmallow</li>
</ul>

      at _callee2$ (ingredien_list_e2e.js:69:7)
      at tryCatch (node_modules/babel-regenerator-runtime/runtime.js:61:40)
      at GeneratorFunctionPrototype.invoke [as _invoke] (node_modules/babel-regenerator-runtime/runtime.js:329:22)
      at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (node_modules/babel-regenerator-runtime/runtime.js:94:21)
      at step (ingredien_list_e2e.js:29:1)
      at ingredien_list_e2e.js:29:1
      at new Promise (node_modules/core-js/modules/es6.promise.js:193:7)
      at ingredien_list_e2e.js:29:1
      at ingredien_list_e2e.js:60:75
      at _callee$ (node_modules/test-them-all/dist/src/async_it.js:44:22)
      at tryCatch (node_modules/babel-regenerator-runtime/runtime.js:61:40)
      at GeneratorFunctionPrototype.invoke [as _invoke] (node_modules/babel-regenerator-runtime/runtime.js:329:22)
      at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (node_modules/babel-regenerator-runtime/runtime.js:94:21)
      at step (node_modules/test-them-all/dist/src/async_it.js:12:191)
      at node_modules/test-them-all/dist/src/async_it.js:12:451
      at new Promise (node_modules/core-js/modules/es6.promise.js:193:7)
      at Context.<anonymous> (node_modules/test-them-all/dist/src/async_it.js:12:99)
      at Context.<anonymous> (node_modules/test-them-all/dist/src/async_it.js:65:18)
      set UNEXPECTED_FULL_TRACE=true to see the full stack trace

yormi avatar Apr 22 '16 10:04 yormi

Ah, I thought this was a version thing, but it doesn't look like it is - it's when you're passing an object that contains functions (functions passed directly are skipped), when you pass a whole object with functions, that's more difficult to skip over. I'll leave this issue open as there are some improvements in this area coming, but they probably won't be that soon as it's quite involved to work out which bits can be skipped and which not.

bruderstein avatar Apr 27 '16 18:04 bruderstein

Alright ! Thank you for considering :) I'm not aware of the complexity of the change but wouldn't it be possible to just do the same with the function properties of the "second-level" (since the properties of the "third-level" are replaced by ...). I try to look-up the code but I felt so lost with the htlmlike adapter and all that and I gave up...

Once again, thanks for the great work !

To reduce the pain: I'm currently designing my containers to only have logic in nothing more than the react-redux mapping mapStateToProps, etc. This is kind of presented here if anybody is interested: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.e0v1h6arf

This should help but I foresee some pain when developping components receiving other components as children :P

yormi avatar Apr 28 '16 14:04 yormi

Oh, I'm starting to stuggle with the output since I added inline png... It fills my command line with png jibberish :P

What would you think of adding a substring to limit the string props output to 20 char or something like that ?

I guess I'm not the only one using png. How do other people deal with that ? I'm using this for the test since webpack doesn't pass over the code for tests:

require.extensions['.png'] = function () { return null }

and I end up with the content in base64 of the png file in the output markup.

yormi avatar Jul 27 '16 15:07 yormi

I've run into this issue while integration testing components together with their Redux stores and getting the entire store dumped into my face in case of failure. Makes it very hard to see the actual problem.

salomvary avatar Sep 22 '20 13:09 salomvary