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

import AutoDocs from './';
import { hiddenMethods } from './methods-table';

const testkit = () => {
  let component;
  const baseUrl =
    'https://www.wix-pages.com/wix-style-react-public.pr_10984/?path=/story/';

  const byHook = hook => component.find(`[data-hook="${hook}"]`);

  const mockWindowLocation = () => {
    Object.defineProperties(window, {
      parent: {
        value: {
          location: {
            href: baseUrl,
          },
        },
      },
    });
  };

  mockWindowLocation();

  return {
    when: {
      created: props => {
        component = mount(<AutoDocs {...props} />);
        return component;
      },
    },

    get: {
      propsTables: () => byHook('autodocs-props-table'),
      propsRowsNames: () => byHook('autodocs-prop-row-name'),
      propRowsTypes: () => byHook('autodocs-prop-row-type'),
      propRowsTypesWithLink: () => byHook('autodocs-prop-row-linked-type'),
      methodsTable: () => byHook('autodocs-methods-table'),
      methodsTableRows: () => byHook('autodocs-methods-table-row'),
      baseUrl,
    },
  };
};

describe('AutoDocs', () => {
  it('should render props sorted alphabetically with required prioritized', () => {
    const type = { name: 'string' };
    const props = {
      metadata: {
        displayName: '',
        props: {
          d: { type },
          z: { type, required: true },
          a: { type },
          dz: { type },
          '42z': { type },
        },
      },
    };

    const expectedOrder = ['z', '42z', 'a', 'd', 'dz'];

    const driver = testkit();
    driver.when.created(props);
    const propNames = driver.get.propsRowsNames().map(node => node.text());

    expect(propNames).toEqual(expectedOrder);
  });

  describe('given metadata with deprecated props', () => {
    it('should display separate table for depreacted props', () => {
      const driver = testkit();
      driver.when.created({
        metadata: {
          props: { deprecated: { tags: [{ title: 'deprecated' }] } },
        },
      });

      expect(driver.get.propsTables().length).toEqual(2);
    });
  });

  describe('given metadata with linkTypeTo annotation', () => {
    it('should generate a link to the relevant storybook story', () => {
      const driver = testkit();
      const expectedTypeName = 'TooltipCommon';
      const expectedLink = `${driver.get.baseUrl}components-overlays--tooltip`;
      driver.when.created({
        metadata: {
          props: {
            tooltip: {
              type: { name: expectedTypeName },
              tags: [
                {
                  title: 'linkTypeTo',
                  description: `components-overlays--tooltip`,
                },
              ],
            },
          },
        },
      });

      const typesWithLink = driver.get.propRowsTypesWithLink();

      const names = typesWithLink.map(node => node.text());
      const links = typesWithLink.map(node => node.props().href);
      expect(names[0]).toBe(expectedTypeName);
      expect(links[0]).toBe(expectedLink);
    });

    it('should generate a link to external website', () => {
      const expectedLink = `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match`;
      const driver = testkit();
      driver.when.created({
        metadata: {
          props: {
            https: {
              type: { name: 'string' },
              tags: [
                {
                  title: 'linkTypeTo',
                  description: expectedLink,
                },
              ],
            },
          },
        },
      });

      const typesWithLink = driver.get.propRowsTypesWithLink();

      const links = typesWithLink.map(node => node.props().href);
      expect(links[0]).toBe(expectedLink);
    });
  });

  describe('given metadata with setTypeName annotation', () => {
    it('should display the given type name instead of the actual prop type ', () => {
      const expectedNames = ['firstName', 'secondName', 'thirdName'];
      const expectedLink = 'https://...';
      const driver = testkit();
      driver.when.created({
        metadata: {
          props: {
            firstProps: {
              type: { name: 'string' },
              tags: [
                {
                  title: 'setTypeName',
                  description: expectedNames[0],
                },
              ],
            },
            secondProp: {
              type: { name: 'string' },
              tags: [
                {
                  title: 'setTypeName',
                  description: expectedNames[1],
                },
                {
                  title: 'linkTypeTo',
                  description: expectedLink,
                },
              ],
            },
            thirdProp: {
              type: {
                name: 'shape',
                computed: true,
                value:
                  "import { TooltipCommonProps } from '../common/PropTypes/TooltipCommon';",
              },
              tags: [
                {
                  title: 'setTypeName',
                  description: expectedNames[2],
                },
                {
                  title: 'linkTypeTo',
                  description: expectedLink,
                },
              ],
            },
          },
        },
      });

      const types = driver.get.propRowsTypes().map(node => node.text());
      const typesWithLink = driver.get
        .propRowsTypesWithLink()
        .map(node => node.text());

      expect([...types, ...typesWithLink]).toEqual(expectedNames);
    });
  });

  describe('methods table', () => {
    it('should not render lifecycle methods', () => {
      const driver = testkit();
      const methods = [
        ...hiddenMethods,
        '_privateMethod',
        'publicCoolMethod',
        'publicAwesomeMethod',
      ].map(name => ({ name, params: [], description: '' }));

      driver.when.created({
        metadata: {
          props: {},
          methods,
        },
      });

      const rows = driver.get.methodsTableRows();
      expect(rows.length).toEqual(2);
      expect(rows.at(0).text()).toEqual('publicCoolMethod');
      expect(rows.at(1).text()).toEqual('publicAwesomeMethod');
    });
  });
});
