import { Enumify } from './enumify';

describe('enumify', () => {
  it('Color: basic features', () => {
    class Color extends Enumify {
      static red = new Color();
      static orange = new Color();
      static yellow = new Color();
      static green = new Color();
      static blue = new Color();
      static purple = new Color();
      static _ = Color.closeEnum();
    }

    // Instance properties
    expect(Color.red.enumKey).toBe('red');
    expect(Color.red.enumOrdinal).toBe(0);

    // Prototype methods
    expect(
      'Color: ' + Color.red // .toString()
    ).toBe('Color: Color.red');

    // Static `.enumKeys` and static `.enumValues`
    expect(Color.enumKeys).toEqual(['red', 'orange', 'yellow', 'green', 'blue', 'purple']);
    expect(Color.enumValues).toEqual([Color.red, Color.orange, Color.yellow, Color.green, Color.blue, Color.purple]);

    // Static `.enumValueOf()`
    expect(Color.enumValueOf('yellow')).toBe(Color.yellow);

    // Iterability
    const result = [];
    const iterated = [...Color];
    for (const c of Color) {
      result.push('Color: ' + c);
    }
    expect(iterated).toEqual([Color.red, Color.orange, Color.yellow, Color.green, Color.blue, Color.purple]);
  });

  it('Instance properties', () => {
    class Weekday extends Enumify {
      static monday = new Weekday(true);
      static tuesday = new Weekday(true);
      static wednesday = new Weekday(true);
      static thursday = new Weekday(true);
      static friday = new Weekday(true);
      static saturday = new Weekday(false);
      static sunday = new Weekday(false);
      static _ = Weekday.closeEnum();

      isWorkDay: boolean;
      constructor(isWorkDay: boolean) {
        super();
        this.isWorkDay = isWorkDay;
      }
    }
    expect(Weekday.sunday.isWorkDay).toBe(false);
    expect(Weekday.wednesday.isWorkDay).toBe(true);
  });

  it('switch', () => {
    class Weekday extends Enumify {
      static monday = new Weekday();
      static tuesday = new Weekday();
      static wednesday = new Weekday();
      static thursday = new Weekday();
      static friday = new Weekday();
      static saturday = new Weekday();
      static sunday = new Weekday();
      static _ = Weekday.closeEnum();
    }
    function nextDay(weekday: Weekday) {
      switch (weekday) {
        case Weekday.monday:
          return Weekday.tuesday;
        case Weekday.tuesday:
          return Weekday.wednesday;
        case Weekday.wednesday:
          return Weekday.thursday;
        case Weekday.thursday:
          return Weekday.friday;
        case Weekday.friday:
          return Weekday.saturday;
        case Weekday.saturday:
          return Weekday.sunday;
        case Weekday.sunday:
          return Weekday.monday;
        default:
          throw new Error();
      }
    }
    expect(nextDay(Weekday.tuesday)).toBe(Weekday.wednesday);
    expect(nextDay(Weekday.sunday)).toBe(Weekday.monday);
  });

  it('Instance getters', () => {
    class Weekday extends Enumify {
      static monday = new Weekday({
        get nextDay() {
          return Weekday.tuesday;
        },
      });
      static tuesday = new Weekday({
        get nextDay() {
          return Weekday.wednesday;
        },
      });
      static wednesday = new Weekday({
        get nextDay() {
          return Weekday.thursday;
        },
      });
      static thursday = new Weekday({
        get nextDay() {
          return Weekday.friday;
        },
      });
      static friday = new Weekday({
        get nextDay() {
          return Weekday.saturday;
        },
      });
      static saturday = new Weekday({
        get nextDay() {
          return Weekday.sunday;
        },
      });
      static sunday = new Weekday({
        get nextDay() {
          return Weekday.monday;
        },
      });
      static _ = Weekday.closeEnum();
      nextDay!: Weekday;
      constructor(props: {[k: string]: any}) {
        super();
        Object.defineProperties(this, Object.getOwnPropertyDescriptors(props));
      }
    }
    expect(Weekday.friday.nextDay).toBe(Weekday.saturday);
    expect(Weekday.sunday.nextDay).toBe(Weekday.monday);
  });

  test('Arbitrary enum values', () => {
    class Mode extends Enumify {
      static user_r = new Mode(0b100000000);
      static user_w = new Mode(0b010000000);
      static user_x = new Mode(0b001000000);
      static group_r = new Mode(0b000100000);
      static group_w = new Mode(0b000010000);
      static group_x = new Mode(0b000001000);
      static all_r = new Mode(0b000000100);
      static all_w = new Mode(0b000000010);
      static all_x = new Mode(0b000000001);
      static _ = Mode.closeEnum();

      constructor(public n: number) {
        super();
      }
    }
    expect(
      Mode.user_r.n | Mode.user_w.n | Mode.user_x.n | Mode.group_r.n | Mode.group_x.n | Mode.all_r.n | Mode.all_x.n
    ).toBe(
      0o755
    );
    expect(Mode.user_r.n | Mode.user_w.n | Mode.user_x.n | Mode.group_r.n).toBe(0o740);
  });
});
