import React from 'react';
import { mount } from 'enzyme';

import Staging from '.';
import { DashHeader, StagingFeed, Log } from '@ui/compositions';
import { Button, Modal } from '@ui/components';

// Helper function returns a promise that resolves after all other promise mocks,
// even if they are chained like Promise.resolve().then(...)
// Technically: this is designed to resolve on the next macrotask
function tick() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

const auditlog = {
  _id: '5f1f1088f335620189768998',
  description: 'Edited \n page "Versions"',
  project: 'guides',
  link: 'https://dash.readme.io/project/guides/v2.0/docs/versions',
  filter: {
    project: 'Product Guide',
    user: 'Andy Trattner\nandy@readme.io',
    page: 'Versions',
    version: '2.0',
    action: 'EDIT',
  },
  createdAt: '2020-07-27T17:36:29.752+0000',
};
const changelog = {
  _id: '5f0cddddc53967004d2fad39',
  link: 'https://docs.readme.com/guides/changelog/2020-07-13-v3-5-0', // must generate on the backend
  metadata: {
    image: [],
    title: '',
    description: '',
  },
  title: 'Owlet Weekly Update (Week of July 13)',
  slug: '2020-07-13-v3-5-0',
  body:
    "> 🎉\n> Welcome to another update!\n> We have exciting news to share.\n> This week, we updated the [ReadMe API](https://docs.readme.com/developers/reference/intro)\n> To include poems with every err'!\n\nAlas, I'm not nearly as good at writing poems as Greg (our CEO) is — who literally wrote over 30 unique poems for each and every error code that is returned from the [ReadMe HTTP API](https://docs.readme.com/developers/reference/intro)! 🖋️\n\nIn other non-poetry-related (but nevertheless exciting) news, we also shipped some exciting updates to our icons, made a ton of usability improvements to our GitHub integration, and lots more. More information below! :owlbert-reading:\n\n## ✨",
  type: 'added',
  hidden: false,
  pendingAlgoliaPublish: false,
  user: '560efaf7a8e6680d00b2e3f9',
  createdAt: '2020-07-14T22:19:00.000Z',
  project: '542334961972e21d7dae4e00',
  __v: 0,
};

describe('Staging', () => {
  describe('Defaults', () => {
    it('should display empty state by default', () => {
      const node = mount(
        <Staging awaiting={[]} deploy={() => {}} deployed={[]} deployments={[]} modalTarget={'body'} />
      );
      expect(node.find('.Staging')).toHaveLength(1);
      expect(node.find(DashHeader)).toHaveLength(1);
      expect(node.find(Button)).toHaveLength(3);
      expect(node.find(Modal)).toHaveLength(1);
      expect(node.find(StagingFeed)).toHaveLength(2);
      expect(node.find('.StagingFeed-Timeline-Day-Deployed_empty')).toHaveLength(1);
      expect(node.containsMatchingElement(<div>No deployment history</div>)).toBe(true);
    });

    it('should display logs if logs exist', () => {
      const node = mount(
        <Staging
          awaiting={[
            {
              date: new Date('2020-07-19'),
              changelogs: [],
              auditlogs: [auditlog],
            },
          ]}
          deploy={() => {}}
          deployed={[
            {
              date: new Date('2020-07-01'),
              changelogs: [changelog],
              auditlogs: [],
            },
          ]}
          deployments={[new Date('2020-07-01')]}
          loadMore={() => {}}
          modalTarget={'body'}
        />
      );
      expect(node.find(Log)).toHaveLength(2);
      expect(node.find('.Log-Link')).toHaveLength(2);
      expect(node.find('.Log-Link').at(0).text().trim()).toBe('page "Versions"');
      expect(node.find('.Log-Link').at(1).text().trim()).toBe("What's New");
      expect(node.find('.StagingFeed_deployed')).toHaveLength(1);
      // .find() results in both React nodes & DOM nodes - the below tests were resulting in length = 2
      // using .hostNodes() filters so that only the DOM nodes come through
      // https://github.com/enzymejs/enzyme/issues/836
      expect(node.find('.Log_Owlet').hostNodes()).toHaveLength(1);
      expect(node.find('.Log_AuditLog').hostNodes()).toHaveLength(1);
    });
  });

  describe('Staging modal', () => {
    it('should open modal when deploy button is clicked', () => {
      const node = mount(
        <Staging
          awaiting={[
            {
              date: new Date('2020-07-19'),
              changelogs: [],
              auditlogs: [auditlog],
            },
          ]}
          deploy={() => {}}
          deployed={[
            {
              date: new Date('2020-07-01'),
              changelogs: [changelog],
              auditlogs: [],
            },
          ]}
          deployments={[]}
          loadMore={() => {}}
          modalTarget={'body'}
        />
      );
      // before opening the modal
      const deployButton = node.findWhere(n => n.hasClass('Button') && n.text() === 'Deploy');
      expect(deployButton).toHaveLength(1);
      deployButton.simulate('click');
      // after opening the modal
      expect(node.find('.Modal-Header').text()).toBe('Deploying Changes');
      expect(node.findWhere(n => n.hasClass('Button') && n.text() === 'Deploy')).toHaveLength(2);
      expect(node.find('.Modal-Body').text()).toContain('Deploy your staged changes');
    });

    it('should show success message after successful deploy', async () => {
      const deployMock = jest.fn(() => {
        return new Promise(resolve => {
          resolve();
        });
      });
      const node = mount(
        <Staging
          awaiting={[
            {
              date: new Date('2020-07-19'),
              changelogs: [],
              auditlogs: [auditlog],
            },
          ]}
          deploy={deployMock}
          deployed={[
            {
              date: new Date('2020-07-01'),
              changelogs: [changelog],
              auditlogs: [],
            },
          ]}
          deployments={[new Date('2020-07-01')]}
          loadMore={() => {}}
          modalTarget={'body'}
        />
      );
      // before opening the modal
      const deployButton = node.findWhere(n => n.hasClass('Button') && n.text() === 'Deploy');
      expect(deployButton).toHaveLength(1);
      deployButton.simulate('click');
      // after opening the modal
      expect(node.find('.Modal-Header').text()).toBe('Deploying Changes');
      const modal = node.find(Modal);
      const modalDeployButton = modal.findWhere(n => n.hasClass('Button') && n.text() === 'Deploy');
      expect(modalDeployButton).toHaveLength(1);
      modalDeployButton.simulate('click');
      await tick();
      // successful deploy
      expect(deployMock).toHaveBeenCalledTimes(1);
      expect(node.find('.Modal-Header').text()).toContain('Hoot Hoot Hooray');
    });

    it('should show error message after failed deploy', async () => {
      const deployMock = jest.fn(() => {
        return new Promise((_, reject) => {
          // eslint-disable-next-line prefer-promise-reject-errors
          reject({
            data: {
              errors: {
                $all: 'Some error',
              },
            },
          });
        });
      });
      const node = mount(
        <Staging
          awaiting={[
            {
              date: new Date('2020-07-19'),
              changelogs: [],
              auditlogs: [auditlog],
            },
          ]}
          deploy={deployMock}
          deployed={[
            {
              date: new Date('2020-07-01'),
              changelogs: [changelog],
              auditlogs: [],
            },
          ]}
          deployments={[new Date('2020-07-01')]}
          loadMore={() => {}}
          modalTarget={'body'}
        />
      );
      // before opening the modal
      const deployButton = node.findWhere(n => n.hasClass('Button') && n.text() === 'Deploy');
      expect(deployButton).toHaveLength(1);
      deployButton.simulate('click');
      // after opening the modal
      expect(node.find('.Modal-Header').text()).toBe('Deploying Changes');
      const modal = node.find(Modal);
      const modalDeployButton = modal.findWhere(n => n.hasClass('Button') && n.text() === 'Deploy');
      expect(modalDeployButton).toHaveLength(1);
      modalDeployButton.simulate('click');
      await tick();
      // unsuccessful deploy
      expect(deployMock).toHaveBeenCalledTimes(1);
      expect(node.find('.Modal-Header').text()).toContain('Error');
    });
  });

  it('disables the loadMore button within <Staging> if deployments is empty', () => {
    const node = mount(<Staging awaiting={[]} deploy={() => {}} deployed={[]} deployments={[]} modalTarget={'body'} />);
    expect(node.find('.Staging-LoadMore button')).toHaveLength(1);
    expect(node.find('.Staging-LoadMore button').getDOMNode()).toBeDisabled();
  });
});
