/// <reference types="jest-enzyme" />
/// <reference path="../../../utilities/jest/extensions.ts" />

import * as fc from 'fast-check'
import * as Enzyme from 'enzyme'
import * as Axe from 'jest-axe'
import * as React from 'react'
import Button, { Variant, ButtonElement } from './Button'
import stringify from '../../services/stringify'

const CUSTOM_VARIANTS: string[] = ['primary']

const AVAILABLE_VARIANTS: string[] = ['outline', 'primary']

it('should exist', () => {
  expect(Button).toBeDefined()
})

it('should be a component', () => {
  expect(Button).toBeComponent()
})

it('should render without crashing given minimum required props', () => {
  expect(() => <Button />).not.toThrow()
})

it('should render a button element for button kind', () => {
  const wrapper = Enzyme.shallow(<Button element="button" />)
  expect(wrapper.name()).toBe('button')
})

it('should render an a element for anchor kind', () => {
  const wrapper = Enzyme.shallow(<Button element="a" />)
  expect(wrapper.name()).toBe('a')
})

describe('on unknown kinds', () => {
  let originalConsoleError: typeof console.error

  beforeEach(() => {
    originalConsoleError = console.error
    console.error = () => {}
  })

  afterEach(() => {
    console.error = originalConsoleError
  })

  it('should render null', () => {
    fc.assert(
      fc.property(fc.string().filter(s => !['button', 'a'].includes(s)), (element) => {
        const wrapper = Enzyme.shallow(<Button element={element as ButtonElement} />)

        expect(wrapper.isEmptyRender()).toBe(true)
      }),
      {
        numRuns: 300,
      },
    )
  })
})

it('should render a border', () => {
  const wrapper = Enzyme.shallow(<Button border />)
  expect(wrapper.find('button').find('span')).toHaveLength(1)
})

it('should render a label', () => {
  fc.assert(
    fc.property(fc.anything(), (label) => {
      const wrapper = Enzyme.shallow(<Button>{label}</Button>)

      expect(wrapper).toHaveText(stringify(label))
    }),
    {
      numRuns: 300,
    },
  )
})

describe('on disabled', () => {
  it('should render a disabled button element for button kind', () => {
    const wrapper = Enzyme.shallow(<Button disabled element="button" />)
    expect(wrapper.find('button')).toHaveProp('disabled', true)
  })

  it('should render a span element for anchor kind', () => {
    const wrapper = Enzyme.shallow(<Button disabled element="a" />)
    expect(wrapper.name()).toBe('span')
  })
})

describe.each(AVAILABLE_VARIANTS)('with %p variant', (rawVariant) => {
  const variant = rawVariant as Variant

  it('should render background color', () => {
    const wrapper = Enzyme.shallow(<Button variant={variant} />)
    const button = wrapper.find('button')

    if (CUSTOM_VARIANTS.includes(variant)) {
      expect(button).not.toHaveStyle('backgroundColor', 'transparent')
      return
    }

    expect(button).toHaveStyle('backgroundColor', 'transparent')
  })

  it('should render foreground color', () => {
    const wrapper = Enzyme.shallow(<Button variant={variant} />)
    const button = wrapper.find('button')

    expect(button).toHaveStyle('color', expect.any(String))
  })
})

describe('with unknown variants', () => {
  let originalConsoleError: typeof console.error

  beforeEach(() => {
    // silence console.error() from prop type validation since
    // we're checking for unknown variants
    originalConsoleError = console.error
    console.error = () => {}
  })

  afterEach(() => {
    console.error = originalConsoleError
  })

  it('should render background color', () => {
    fc.assert(
      fc.property(fc.string().filter((s) => !AVAILABLE_VARIANTS.includes(s)), (rawVariant) => {
        const variant = rawVariant as Variant
        const wrapper = Enzyme.shallow(<Button variant={variant} />)
        const button = wrapper.find('button')

        expect(button).toHaveStyle('backgroundColor', 'transparent')
      }),
    )
  })

  it('should render foreground color', () => {
    fc.assert(
      fc.property(fc.string().filter((s) => !AVAILABLE_VARIANTS.includes(s)), (rawVariant) => {
        const variant = rawVariant as Variant
        const wrapper = Enzyme.shallow(<Button variant={variant} />)
        const button = wrapper.find('button')

        expect(button).toHaveStyle('color', expect.any(String))
      }),
    )
  })
})

it('should guarantee minimal accessibility', () => {
  fc.assert(
    fc.asyncProperty(fc.string(1, 20), async (s) => {
      const wrapper = Enzyme.mount(<Button>{s}</Button>)
      const results = await Axe.axe(wrapper.getDOMNode())

      expect(results).toHaveNoViolations()
    }),
  )
})
