import { expect } from 'chai';
import fc from 'fast-check';
import 'mocha';
import { c, s, t } from '../../../src/renderer/services/utils';

describe('Frontend Utils', function() {
  this.timeout(1000);

  it("does 'translate' text", () => {
    expect(t('translate this')).to.equal('translate this');
  });

  describe('Class Util', () => {
    beforeEach(function() {
      const numeric = fc.integer(0x30, 0x39);
      const alphaCapital = fc.integer(0x41, 0x5a);
      const alphaSmall = fc.integer(0x61, 0x7a);
      const alpha = fc.oneof(alphaCapital, alphaSmall).map(String.fromCharCode);
      const alphaNumeric = fc.oneof(numeric, alphaCapital, alphaSmall).map(String.fromCharCode);

      this.className = fc
        .stringOf(fc.oneof(alpha, alphaNumeric, fc.constantFrom('-', '_')))
        .filter((name: string) => !!name.charAt(0).match(/[a-zA-Z]/));
      this.anything = fc
        .anything({
          maxDepth: 1,
          maxKeys: 2,
        })
        .filter((thing) => typeof thing !== 'string');
    });

    it('compiles classes out of an array', function() {
      const classArrayArbitrary = fc.array(fc.oneof(this.className, this.anything));

      fc.assert(
        fc.property(classArrayArbitrary, (classArray) => {
          const expectedClasses = classArray.filter((potentialClass) => typeof potentialClass === 'string');
          const compiled = c(classArray);

          expect(compiled).to.be.a('string', 'class util does not return a string');
          expectedClasses.forEach((expectedClassName) => {
            expect(compiled).to.contain(expectedClassName, 'class util does not add all classes');
          });
        })
      );
    });

    it('compiles classes out of an object', function() {
      const classObjectArbitrary = fc.object({
        maxDepth: 0,
        key: this.className,
        values: [fc.boolean()],
      });

      fc.assert(
        fc.property(classObjectArbitrary, (classObject: any) => {
          const expectedClasses = Object.keys(classObject).filter((key) => classObject[key]);
          const compiled = c(classObject);

          expect(compiled).to.be.a('string', 'class util does not return a string');
          expectedClasses.forEach((className) => {
            expect(compiled).to.contain(className, 'class util does not add all classes');
          });
        })
      );
    });
  });

  describe('Style Util', () => {
    it('compiles an object into an html style property', () => {
      const styleObjectArbitrary = fc.object({
        maxDepth: 0,
        key: fc.string(1, 10).filter((str) => !str.match(/\s/)),
        values: [fc.string(), fc.integer(), fc.boolean()],
      });

      fc.assert(
        fc.property(styleObjectArbitrary, (styleObject: any) => {
          const expectedString = Object.keys(styleObject)
            .filter((key) => {
              const value = styleObject[key];
              if (typeof value === 'string') {
                return !!value;
              }
              return typeof value === 'number';
            })
            .map((key) => {
              return `${key}:${styleObject[key]}`;
            })
            .join(';');

          expect(s(styleObject)).to.equal(expectedString);
        })
      );
    });
  });
});
