import * as lolex from 'lolex'
import * as React from 'react'
import { render } from 'react-testing-library'

import { loadable } from '.'

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))

const sleepForever: Promise<any> = new Promise(() => {})

const AsyncChildren = async ({
  children,
}: {
  children?: React.ReactNode
}): Promise<React.ReactNode> => {
  await sleep(1000)
  return children
}

describe('loadable', () => {
  describe('before loading content', () => {
    describe('given 200ms have not yet elapsed', () => {
      it('should render nothing', () => {
        const clock = lolex.install()
        const Loading = loadable()(AsyncChildren)
        const { container } = render(<Loading>Loaded!</Loading>)
        expect(container).toBeEmpty()
        clock.tick(199)
        expect(container).toBeEmpty()
        clock.uninstall()
      })
    })

    describe('given 200ms have elapsed', () => {
      it('should render stub component', () => {
        const clock = lolex.install()
        const Loading = loadable()(AsyncChildren)
        const { container } = render(<Loading>Loaded!</Loading>)
        clock.tick(200)
        expect(container).toHaveTextContent('Loading...')
        expect(clock.countTimers()).toBe(1)
        clock.uninstall()
      })
    })

    it('should respect configurable delay', () => {
      const clock = lolex.install()
      const Loading = loadable({ delay: 400 })(AsyncChildren)
      const { container } = render(<Loading>Loaded!</Loading>)
      clock.tick(200)
      expect(container).toBeEmpty()
      clock.tick(200)
      expect(container).toHaveTextContent('Loading...')
      expect(clock.countTimers()).toBe(1)
      clock.uninstall()
    })
  })

  describe('after loading content', () => {
    // The HOC works, but this test fails. Why?
    it.skip('should render it with children', () => {
      const clock = lolex.install()
      const Loading = loadable()(AsyncChildren)
      const { container } = render(<Loading>Loaded!</Loading>)
      clock.tick(1000)
      expect(container).toHaveTextContent('Loaded!')
      clock.uninstall()
    })
  })

  describe('on unmount', () => {
    it('should clear timeout', () => {
      const clock = lolex.install()
      const Loading = loadable()(() => sleepForever)
      const { unmount } = render(<Loading>Loaded!</Loading>)
      unmount()
      expect(clock.countTimers()).toBe(0)
      clock.uninstall()
    })
  })
})
