loki icon indicating copy to clipboard operation
loki copied to clipboard

Failed with "Error: TypeError: Reduce of empty array with no initial value" after 4 tries

Open ayatkevich opened this issue 7 years ago • 11 comments

Hi there,

Thanks for the awesome product!

I have a strangely behaving bug that prevents me from running some of the tests. All stories are valid and can be opened in local browser. And when I add one more story, the failing stories changes, so for example story A, that had error before I added story B, now is valid, and story B has the same error. Hope it makes sense.

> loki test

loki test v0.14.0
 ❯ Chrome (docker)
   ✔ Prepare environment
   ✔ Start
   ✔ Fetch list of stories
   ❯ Test chrome.laptop
     ❯ App
       ✖ sign in page
         → Failed with "Error: TypeError: Reduce of empty array with no initial value" after 4 tries
       ✖ waiting
         → Failed with "Error: TypeError: Reduce of empty array with no initial value" after 4 tries
       ✔ error
       ✖ dashboard
         → Failed with "Error: TypeError: Reduce of empty array with no initial value" after 4 tries
     ✔ Login page
     ❯ Dashboard
       ✖ default state
         → Failed with "Error: TypeError: Reduce of empty array with no initial value" after 4 tries
     ✔ Top header
     ✔ Left side panel
   ✔ Stop

I use storybook v4.0.6, react 16.6.3, docker v18.09.0, and run this all on Ubuntu 16.04.5 LTS

ayatkevich avatar Nov 16 '18 09:11 ayatkevich

Hey @ayatkevich Could you please create an example project in a repo where we can reproduce the issue?

techeverri avatar Nov 16 '18 10:11 techeverri

I encountered similar issues in the past. The changes in this PR fix it for me: https://github.com/oblador/loki/pull/102

@techeverri I'll try to post some examples of when this can happen

priomsrb avatar Nov 27 '18 23:11 priomsrb

Here are two examples where Loki will show an error, but the browser will work just fine:

const Message = ({ show }) => (show ? <h1>Hello</h1> : null);

storiesOf('LokiErrors', module)
    .add('Example 1', () => (
        <div>
            <h1 style={{ position: 'fixed' }}>Hello</h1>
        </div>
    ))
    .add('Example 2', () => <Message show={false} />);

Loki will report the following errors:

LokiErrors: Example 1 [failed]
→ Failed with "Error: TypeError: Reduce of empty array with no initial value" after 4 tries
LokiErrors: Example 2 [failed]
→ Failed with "Error: Unable to get position of selector "#root > *". Review the `chromeSelector` option and make sure your story doesn't crash." after 4 tries

In example 1, the parent div has zero dimensions because it's child is fixed positioned. Loki fails because it can't take a screenshot with zero dimensions.

In example 2, the story renders nothing. Loki gets confused because now the #root > * selector that it uses to capture screenshots matches nothing.

priomsrb avatar Nov 28 '18 01:11 priomsrb

@ayatkevich I worked around that error in my stories by adding a container element that has 100% width and height.

Basically, I changed my stories from this:

storiesOf('MyComponent', module).add('Example', () => (
    <MyComponent />
));

to this

storiesOf('MyComponent', module).add('Example', () => (
    <div style={{ width: '100%', height: '100%' }}>
        <MyComponent />
    </div>
));

priomsrb avatar Nov 28 '18 01:11 priomsrb

@priomsrb For the second example that's kind of intended behaviour, although there's been a few issues posted about it so might have to reconsider that decision. The thinking is that if we somehow can't find an element to screenshot then it's likely that something is wrong.

For the first example, it sounds like a bug. But for the sake of tracking down the bug, could you try changing the chromeSelector to be something that matches your fixed element? So something like this:

// chromeSelector: ".wrapper > *"
    .add('Example 1', () => (
        <div className="wrapper">
            <h1 style={{ position: 'fixed' }}>Hello</h1>
        </div>
    ))

oblador avatar Dec 02 '18 10:12 oblador

@oblador

For the second example that's kind of intended behaviour, although there's been a few issues posted about it so might have to reconsider that decision. The thinking is that if we somehow can't find an element to screenshot then it's likely that something is wrong.

It can be useful to take an empty screenshot when the page is empty. For example, I want to create a story to make sure that <MyComponent show={false}> doesn't render anything. That way if someone introduces a bug into to MyComponent so that it does render something when show={false}, then loki will flag it for me. If I skip creating the story for the show={false} case then I might miss this regression.

For the first example, it sounds like a bug.

Yup it is. I have a fix for it in this line of my PR: https://github.com/oblador/loki/pull/102/files#diff-22b2b3e305064426035bcb31d3bd573bR52

priomsrb avatar Dec 02 '18 15:12 priomsrb

@priomsrb Technically that's logic you're testing, not visual regression, but I agree that it could be useful.

About your fix, I'm not sure if actually fixes the real problem. I think the algorithm should be improved in that we'd detect which child elements that are outside of the regular rendering flow (position absolute/fixed) and add them to the viewport size.

oblador avatar Dec 02 '18 16:12 oblador

@priomsrb Thanks for that PR!

The 'Unable to find a visible, non-wrapper element' error is much clearer.

Defaulting to the body tag in my particular case still did not do what I needed (I am experimenting with some really weird things with absolute positioned elements in relative 0 x 0 wrappers for a personal project)

In my case, I ended up not raising an error if I did not find a visible non-wrapper, but using the window details (using https://github.com/oblador/loki/pull/102 as a starting point):

// throw new Error('Unable to find a visible, non-wrapper element');

const windowDefaults = {
  x: 0,
  y: 0,
  width: window.innerWidth,
  height: window.innerHeight,
};

return windowDefaults;

timkrins avatar Dec 02 '18 20:12 timkrins

I'm using a library which uses React Portals. It renders as a sibling of the app's root.

I've set the chromeSelector as #loki > * and the markup is like this:

<body>
  <div id="root"> <!-- React app -->
    <div id="loki">
      ...
    </div>
  </div>
  <div>...</div> <!-- React Portal -->
</body>

Since the React Portal is outside of the chromeSelector, I see the same error message as the OP.

I found a workaround that worked really well in this scenario. In the story, I appended the chromeSelector to the component that I wanted to capture. So in my case, I added id="loki" to the component.

JasonNutmeg avatar Dec 07 '18 00:12 JasonNutmeg

Has anyone got this working?

kavymi avatar Jan 30 '19 00:01 kavymi

I figured it out, you cannot use @storybook/addon-centered as a decorator

kavymi avatar Jan 30 '19 00:01 kavymi