📄 theme.test.ts
/home/palash/git/site/node_modules/react-activity-calendar/src/lib/theme.test.ts
Language: ts • Lines: 210
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Levels, Theme, ThemeInput } from '../types'
import { createTheme } from './theme'

describe('createTheme', () => {
  const defaultTheme = {
    light: [
      'hsl(0, 0%, 92%)',
      'color-mix(in oklab, hsl(0, 0%, 26%) 25%, hsl(0, 0%, 92%))',
      'color-mix(in oklab, hsl(0, 0%, 26%) 50%, hsl(0, 0%, 92%))',
      'color-mix(in oklab, hsl(0, 0%, 26%) 75%, hsl(0, 0%, 92%))',
      'hsl(0, 0%, 26%)',
    ],
    dark: [
      'hsl(0, 0%, 22%)',
      'color-mix(in oklab, hsl(0, 0%, 92%) 25%, hsl(0, 0%, 22%))',
      'color-mix(in oklab, hsl(0, 0%, 92%) 50%, hsl(0, 0%, 22%))',
      'color-mix(in oklab, hsl(0, 0%, 92%) 75%, hsl(0, 0%, 22%))',
      'hsl(0, 0%, 92%)',
    ],
  }

  const explicitTheme: Theme = {
    light: ['#f0f0f0', '#c4edde', '#7ac7c4', '#f73859', '#384259'],
    dark: ['hsl(0, 0%, 22%)', '#4D455D', '#7DB9B6', '#F5E9CF', '#E96479'],
  }

  const cssSupportsSpy = vi.fn<typeof global.CSS.supports>()

  beforeEach(() => {
    vi.stubGlobal('CSS', {
      supports: cssSupportsSpy.mockReturnValue(true),
    })
  })

  it('returns the default theme if no input is passed', () => {
    expect(createTheme()).toStrictEqual(defaultTheme)
  })

  it('throws an error if input is not an object', () => {
    // @ts-expect-error test invalid input
    expect(() => createTheme('invalid')).toThrow()
  })

  it('throws an error if neither "light" or "dark" inputs are set', () => {
    // @ts-expect-error test invalid input
    expect(() => createTheme({})).toThrow()
  })

  it('throws an error if too few colors are passed', () => {
    expect(() =>
      createTheme(
        {
          light: ['blue'],
        },
        { minLevel: 0, maxLevel: 2 },
      ),
    ).toThrow()
  })

  it('throws an error if too many colors are passed', () => {
    expect(() =>
      createTheme(
        {
          dark: Array(5).fill('green'),
        },
        { minLevel: 0, maxLevel: 3 },
      ),
    ).toThrow()
  })

  it('uses default dark color scale if undefined in input', () => {
    expect(
      createTheme({
        light: explicitTheme.light,
      }),
    ).toStrictEqual({
      light: explicitTheme.light,
      dark: defaultTheme.dark,
    })
  })

  it('uses default light color scale if undefined in input', () => {
    expect(
      createTheme({
        dark: explicitTheme.dark,
      }),
    ).toStrictEqual({
      light: defaultTheme.light,
      dark: explicitTheme.dark,
    })
  })

  it('throws if an invalid color is passed', () => {
    cssSupportsSpy.mockReturnValue(false)
    expect(() =>
      createTheme({
        dark: ['#333', '🙃'],
      }),
    ).toThrow()
  })

  it('returns the same theme for explicit inputs', () => {
    expect(createTheme(explicitTheme)).toStrictEqual(explicitTheme)
  })

  it('calculates color scales for two inputs', () => {
    const input: ThemeInput = {
      light: ['hsl(0, 0%, 92%)', 'hsl(0, 0%, 26%)'],
      dark: ['hsl(0, 0%, 22%)', 'hsl(0, 0%, 92%)'],
    }

    expect(createTheme(input)).toStrictEqual(defaultTheme)
  })

  it('calculates color scales for three inputs', () => {
    const input: ThemeInput = {
      light: ['hsl(0, 0%, 26%)', 'hsl(0, 0%, 92%)', 'hsl(0, 0%, 26%)'],
      dark: ['hsl(0, 0%, 92%)', 'hsl(0, 0%, 22%)', 'hsl(0, 0%, 92%)'],
    }

    expect(createTheme(input)).toStrictEqual(defaultTheme)
  })

  it('calculates color scale for specific color', () => {
    const input: ThemeInput = {
      light: ['hsl(0, 0%, 92%)', 'rebeccapurple'],
      dark: ['hsl(0, 0%, 22%)', 'hsl(225,92%,77%)'],
    }

    expect(createTheme(input)).toStrictEqual({
      light: [
        'hsl(0, 0%, 92%)',
        'color-mix(in oklab, rebeccapurple 25%, hsl(0, 0%, 92%))',
        'color-mix(in oklab, rebeccapurple 50%, hsl(0, 0%, 92%))',
        'color-mix(in oklab, rebeccapurple 75%, hsl(0, 0%, 92%))',
        'rebeccapurple',
      ],
      dark: [
        'hsl(0, 0%, 22%)',
        'color-mix(in oklab, hsl(225,92%,77%) 25%, hsl(0, 0%, 22%))',
        'color-mix(in oklab, hsl(225,92%,77%) 50%, hsl(0, 0%, 22%))',
        'color-mix(in oklab, hsl(225,92%,77%) 75%, hsl(0, 0%, 22%))',
        'hsl(225,92%,77%)',
      ],
    })
  })

  it.each([
    [
      { minLevel: 0, maxLevel: 3 },
      {
        light: [
          'hsl(0, 0%, 92%)',
          'color-mix(in oklab, hsl(0, 0%, 26%) 33.33%, hsl(0, 0%, 92%))',
          'color-mix(in oklab, hsl(0, 0%, 26%) 66.67%, hsl(0, 0%, 92%))',
          'hsl(0, 0%, 26%)',
        ],
        dark: [
          'hsl(0, 0%, 22%)',
          'color-mix(in oklab, hsl(0, 0%, 92%) 33.33%, hsl(0, 0%, 22%))',
          'color-mix(in oklab, hsl(0, 0%, 92%) 66.67%, hsl(0, 0%, 22%))',
          'hsl(0, 0%, 92%)',
        ],
      },
    ],
    [
      { minLevel: 1, maxLevel: 5 },
      {
        light: [
          'color-mix(in oklab, hsl(0, 0%, 26%) 20%, hsl(0, 0%, 92%))',
          'color-mix(in oklab, hsl(0, 0%, 26%) 40%, hsl(0, 0%, 92%))',
          'color-mix(in oklab, hsl(0, 0%, 26%) 60%, hsl(0, 0%, 92%))',
          'color-mix(in oklab, hsl(0, 0%, 26%) 80%, hsl(0, 0%, 92%))',
          'hsl(0, 0%, 26%)',
        ],
        dark: [
          'color-mix(in oklab, hsl(0, 0%, 92%) 20%, hsl(0, 0%, 22%))',
          'color-mix(in oklab, hsl(0, 0%, 92%) 40%, hsl(0, 0%, 22%))',
          'color-mix(in oklab, hsl(0, 0%, 92%) 60%, hsl(0, 0%, 22%))',
          'color-mix(in oklab, hsl(0, 0%, 92%) 80%, hsl(0, 0%, 22%))',
          'hsl(0, 0%, 92%)',
        ],
      },
    ],
    [
      { minLevel: -8, maxLevel: -7 },
      {
        light: ['hsl(0, 0%, 26%)', 'color-mix(in oklab, hsl(0, 0%, 92%) 12.5%, hsl(0, 0%, 26%))'],
        dark: ['hsl(0, 0%, 92%)', 'color-mix(in oklab, hsl(0, 0%, 22%) 12.5%, hsl(0, 0%, 92%))'],
      },
    ],
    [
      { minLevel: -1, maxLevel: 1 },
      {
        light: ['hsl(0, 0%, 26%)', 'hsl(0, 0%, 92%)', 'hsl(0, 0%, 26%)'],
        dark: ['hsl(0, 0%, 92%)', 'hsl(0, 0%, 22%)', 'hsl(0, 0%, 92%)'],
      },
    ],
  ] satisfies Array<[Levels, Theme]>)(
    'calculates correct color scales for levels %s',
    (levels, expected) => {
      const input: ThemeInput = {
        light: ['hsl(0, 0%, 92%)', 'hsl(0, 0%, 26%)'],
        dark: ['hsl(0, 0%, 22%)', 'hsl(0, 0%, 92%)'],
      }
      expect(createTheme(input, levels)).toStrictEqual(expected)
    },
  )
})