text: 'use strict';
  
  const { BaseInput } = require('../../../../../src/hosted-fields/internal/components/base-input');
  const constants = require('../../../../../src/hosted-fields/shared/constants');
  const browserDetection = require('../../../../../src/hosted-fields/shared/browser-detection');
  const RestrictedInput = require('restricted-input');
  const FakeRestrictedInput = require('../../../../../src/lib/fake-restricted-input');
  const { triggerEvent } = require('../../helpers');
  
  describe('Base Input', () => {
    let testContext;
  
    beforeEach(() => {
      testContext = {};
    });
  
    describe.each(Object.keys(constants.allowedFields))('%p', (key) => {
      beforeEach(() => {
        const config = {};
  
        testContext.config = config;
        jest.spyOn(BaseInput.prototype, 'getConfiguration').mockReturnValue(config);
  
        jest.spyOn(BaseInput.prototype, 'addDOMEventListeners').mockReturnValue(null);
        jest.spyOn(BaseInput.prototype, 'addModelEventListeners').mockReturnValue(null);
        jest.spyOn(BaseInput.prototype, 'render').mockReturnValue(null);
  
        testContext.model = {
          configuration: {},
          set: jest.fn()
        };
        testContext.type = key;
  
        testContext.instance = new BaseInput({
          model: testContext.model,
          type: testContext.type
        });
      });
  
      describe('formatter', () => {
        it('creates a RestrictedInput formatter by default', () => {
          expect(testContext.instance.formatter).toBeInstanceOf(RestrictedInput);
        });
  
        it('creates a FakeRestrictedInput formatter if formatting is disabled', () => {
          let instance;
  
          BaseInput.prototype.getConfiguration.mockRestore();
          jest.spyOn(BaseInput.prototype, 'getConfiguration').mockReturnValue({ formatInput: false });
  
          instance = new BaseInput({
            model: testContext.model,
            type: testContext.type
          });
  
          expect(instance.formatter).toBeInstanceOf(FakeRestrictedInput);
        });
      });
  
      describe('element', () => {
        it('is an input', () => {
          expect(testContext.instance.element).toBeInstanceOf(HTMLInputElement);
        });
  
        describe('attributes', () => {
          afterEach(() => {
            delete BaseInput.prototype.maxLength;
          });
  
          describe('prefill', () => {
            it('applies prefill provided', () => {
              let instance;
  
              testContext.config.prefill = 'value';
              instance = new BaseInput({
                model: testContext.model,
                type: testContext.type
              });
  
              expect(instance.element.value).toBe('value');
              expect(testContext.model.set).toHaveBeenCalledTimes(1);
              expect(testContext.model.set).toHaveBeenCalledWith(`${testContext.type}.value`, 'value');
            });
  
            it('coerces prefill to a string', () => {
              let instance;
  
              testContext.config.prefill = 1;
              instance = new BaseInput({
                model: testContext.model,
                type: testContext.type
              });
  
              expect(instance.element.value).toBe('1');
              expect(testContext.model.set).toHaveBeenCalledTimes(1);
              expect(testContext.model.set).toHaveBeenCalledWith(`${testContext.type}.value`, '1');
            });
          });
  
          describe('placeholder', () => {
            it('applies if provided', () => {
              let instance;
  
              testContext.config.placeholder = key.toUpperCase();
              instance = new BaseInput({ model: testContext.model });
  
              expect(instance.element.getAttribute('placeholder')).toBe(key.toUpperCase());
            });
  
            it('does not apply if not defined', () => {
              expect(testContext.instance.element.getAttribute('placeholder')).toBeNull();
            });
          });
  
          describe('masking', () => {
            beforeEach(() => {
              jest.spyOn(BaseInput.prototype, 'updateModel').mockReturnValue(null);
              // prevents adding listeners to document
              // when calling _addDOMFocusListeners
              // and polluting other tests
              jest.spyOn(document.documentElement, 'addEventListener').mockReturnValue(null);
              jest.spyOn(document, 'addEventListener').mockReturnValue(null);
            });
  
            it('applies if provided', () => {
              let instance;
  
              testContext.config.maskInput = true;
              instance = new BaseInput({ model: testContext.model });
              instance._addDOMFocusListeners();
  
              triggerEvent('focus', global);
              instance.element.value = 'abc 1234-asdf /asdf';
              triggerEvent('blur', global);
  
              expect(instance.element.value).toBe('••• ••••-•••• /••••');
              triggerEvent('focus', global);
              expect(instance.element.value).toBe('abc 1234-asdf /asdf');
            });
  
            it('does not apply if not defined', () => {
              testContext.instance._addDOMFocusListeners();
  
              triggerEvent('focus', global);
              testContext.instance.element.value = 'abc 1234-asdf /asdf';
              triggerEvent('blur', global);
  
              expect(testContext.instance.element.value).toBe('abc 1234-asdf /asdf');
            });
          });
  
          describe('type', () => {
            it('applies if provided', () => {
              let instance;
  
              testContext.config.type = 'password';
              instance = new BaseInput({
                model: testContext.model,
                type: testContext.type
              });
  
              expect(instance.element.getAttribute('type')).toBe('password');
            });
  
            it('uses "tel" if not provided', () => {
              expect(testContext.instance.element.getAttribute('type')).toBe('tel');
            });
  
            it('uses "text" with pattern for iOS', () => {
              let instance;
  
              jest.spyOn(browserDetection, 'isIos').mockReturnValue(true);
              instance = new BaseInput({
                model: testContext.model,
                type: testContext.type
              });
  
              expect(instance.element.getAttribute('type')).toBe('text');
              expect(instance.element.getAttribute('pattern')).toBe('\\d*');
            });
          });
  
          describe('autocomplete', () => {
            it('opts out if configured', () => {
              let instance;
  
              testContext.model.configuration.preventAutofill = true;
              instance = new BaseInput({
                model: testContext.model,
                type: testContext.type
              });
  
              expect(instance.element.getAttribute('autocomplete')).toBe('off');
              expect(instance.element.getAttribute('name')).toBe('field');
            });
          });
  
          describe('defaults', () => {
            it('applies type', () => {
              expect(testContext.instance.element.getAttribute('type')).toBe('tel');
            });
  
            it('applies autocomplete', () => {
              expect(testContext.instance.element.getAttribute('autocomplete')).toBeDefined();
              expect(testContext.instance.element.getAttribute('autocomplete')).not.toBe('off');
            });
  
            it('applies autocorrect', () => {
              expect(testContext.instance.element.getAttribute('autocorrect')).toBe('off');
            });
  
            it('applies autocapitalize', () => {
              expect(testContext.instance.element.getAttribute('autocapitalize')).toBe('none');
            });
  
            it('applies spellcheck', () => {
              expect(testContext.instance.element.getAttribute('spellcheck')).toBe('false');
            });
  
            it('applies name', () => {
              expect(testContext.instance.element.getAttribute('name')).toBe(constants.allowedFields[key].name);
            });
  
            it('applies id', () => {
              expect(testContext.instance.element.getAttribute('id')).toBe(constants.allowedFields[key].name);
            });
          });
  
          describe('class', () => {
            it('applies as type', () => {
              expect(testContext.instance.element.getAttribute('class')).toBe(key);
            });
          });
  
          describe('data-braintree-name', () => {
            it('applies based on type', () => {
              expect(testContext.instance.element.getAttribute('data-braintree-name')).toBe(key);
            });
          });
  
          describe('setAttribute', () => {
            beforeEach(() => {
              jest.spyOn(testContext.instance.element, 'setAttribute').mockReturnValue(null);
            });
  
            it('calls element.setAttribute', () => {
              testContext.instance.setAttribute(testContext.type, 'disabled', true);
  
              expect(testContext.instance.element.setAttribute).toHaveBeenCalledWith('disabled', true);
            });
  
            it('does not call element.setAttribute when type does not match', () => {
              testContext.instance.setAttribute('not-my-type', 'disabled', true);
  
              expect(testContext.instance.element.setAttribute).not.toHaveBeenCalled();
            });
  
            it('does not call element.setAttribute when attribute is not allowed', () => {
              testContext.instance.setAttribute(testContext.type, 'maxlength', 0);
  
              expect(testContext.instance.element.setAttribute).not.toHaveBeenCalled();
            });
  
            it('does not call element.setAttribute when value does not match attribute type', () => {
              testContext.instance.setAttribute(testContext.type, 'maxlength', false);
  
              expect(testContext.instance.element.setAttribute).not.toHaveBeenCalled();
            });
          });
  
          describe('maskValue', () => {
            it('masks element value', () => {
              testContext.instance.maskValue('abc 1234-asdf /asdf');
  
              expect(testContext.instance.element.value).toBe('••• ••••-•••• /••••');
            });
  
            it('can use custom mask-deliminator', () => {
              testContext.instance.maskCharacter = ':)';
  
              testContext.instance.maskValue('abc 1234-asdf /asdf');
  
              expect(testContext.instance.element.value).toBe(':):):) :):):):)-:):):):) /:):):):)');
            });
          });
  
          describe('unmaskValue', () => {
            it('sets element value to hiddenMaskedValue', () => {
              testContext.instance.hiddenMaskedValue = 'abc';
              testContext.instance.element.value = '123';
  
              testContext.instance.unmaskValue();
  
              expect(testContext.instance.element.value).toBe('abc');
            });
          });
  
          describe('removeAttribute', () => {
            beforeEach(() => {
              jest.spyOn(testContext.instance.element, 'removeAttribute').mockReturnValue(null);
            });
  
            it('calls element.removeAttribute', () => {
              testContext.instance.setAttribute('disabled', true);
  
              testContext.instance.removeAttribute(testContext.type, 'disabled');
  
              expect(testContext.instance.element.removeAttribute).toHaveBeenCalledWith('disabled');
            });
  
            it('does not call element.removeAttribute when type does not match', () => {
              testContext.instance.setAttribute('disabled', true);
  
              testContext.instance.removeAttribute('not-my-type', 'disabled');
  
              expect(testContext.instance.element.removeAttribute).not.toHaveBeenCalled();
            });
  
            it('does not call element.removeAttribute when attribute is not allowed', () => {
              jest.spyOn(testContext.instance.element, 'removeAttribute').mockReturnValue(null);
  
              testContext.instance.removeAttribute(testContext.type, 'maxlength');
  
              expect(testContext.instance.element.removeAttribute).not.toHaveBeenCalled();
            });
          });
        });
      });
    });
  });
  