// Components
import textareaQuestion from '@/digital-components/textarea-question/textarea-question.vue';

// Supporting Files
import { shallowMount } from '@vue/test-utils';
import { getRandomString, getRandomInt } from '@/helpers/data-generation.js';
import { nextTick } from 'vue';
import { defineRule } from 'vee-validate';
import { required } from '@/helpers/validation-rules.js';

describe('textarea-question.vue', () => {
    const mockMixin = {
        methods: {
            getCmsContent: jest.fn().mockImplementation(() => getRandomString(10, 20)),
            setCmsContent: jest.fn()
        }
    };
    describe('Expected components rendered', () => {
        test('Label rendered', () => {
            // Arrange
            const questionText = getRandomString(10, 20);
            const mixinWithQuestion = {
                methods: {
                    getCmsContent: jest.fn().mockImplementation(() => questionText),
                    setCmsContent: jest.fn()
                }
            };
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20)
                },
                mixins: [mixinWithQuestion]
            });

            // Act
            const label = wrapper.find('label');

            // Assert
            expect(label.exists()).toBe(true);
            expect(wrapper.text()).toContain(questionText);
        });
        test('Textarea rendered', () => {
            // Arrange
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20)
                },
                mixins: [mockMixin]
            });

            // Act
            const textarea = wrapper.find('textarea');

            // Assert
            expect(textarea.exists()).toBe(true);
        });
        test('Character count rendered', () => {
            // Arrange
            const maxLength = getRandomInt(100, 900);
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    maxLength
                },
                mixins: [mockMixin]
            });
            const expected = `${maxLength}/${maxLength}`;

            // Act
            // Assert
            expect(wrapper.text()).toContain(expected);
        });
        test('Error rendered', async () => {
            // Arrange
            const maxLength = getRandomInt(100, 900);
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    maxLength
                },
                mixins: [mockMixin]
            });

            // Act
            const errorComponent = wrapper.find('#error-message');
            expect(errorComponent.text()).toBe('');

            const errorMessage = getRandomString(20, 30);
            wrapper.vm.resetField({
                errors: [errorMessage]
            });
            await nextTick();

            // Assert
            expect(errorComponent.text()).toBe(errorMessage);
        });
        test('Not required => optional text rendered', () => {
            // Arrange
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    isRequired: false
                },
                mixins: [mockMixin]
            });

            // Act
            // Assert
            expect(wrapper.text()).toContain('Optional');
        });
        test('Required => optional text not rendered', () => {
            // Arrange
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    isRequired: true
                },
                mixins: [mockMixin]
            });

            // Act
            // Assert
            expect(wrapper.text()).not.toContain('Optional');
        });
    });
    describe('Computed properties rendered', () => {
        describe('characterCount', () => {
            test('Model value null => character count is 0 ', () => {
                // Arrange
                const wrapper = shallowMount(textareaQuestion, {
                    propsData: {
                        inputId: getRandomString(10, 20),
                        cmsWidgetName: getRandomString(10, 20),
                        modelValue: null
                    },
                    mixins: [mockMixin]
                });

                // Act
                // Assert
                expect(wrapper.vm.characterCount).toBe(0);
            });
            test('Model value undefined => character count is 0', () => {
                // Arrange
                const wrapper = shallowMount(textareaQuestion, {
                    propsData: {
                        inputId: getRandomString(10, 20),
                        cmsWidgetName: getRandomString(10, 20),
                        modelValue: undefined
                    },
                    mixins: [mockMixin]
                });

                // Act
                // Assert
                expect(wrapper.vm.characterCount).toBe(0);
            });
            test('Model value empty string => character count is 0', () => {
                // Arrange
                const wrapper = shallowMount(textareaQuestion, {
                    propsData: {
                        inputId: getRandomString(10, 20),
                        cmsWidgetName: getRandomString(10, 20),
                        modelValue: ''
                    },
                    mixins: [mockMixin]
                });

                // Act
                // Assert
                expect(wrapper.vm.characterCount).toBe(0);
            });
            test('Model value non-empty string => character count is as expected', () => {
                // Arrange
                const length = getRandomInt(10, 30);
                const value = getRandomString(length, length);
                const wrapper = shallowMount(textareaQuestion, {
                    propsData: {
                        inputId: getRandomString(10, 20),
                        cmsWidgetName: getRandomString(10, 20),
                        modelValue: value
                    },
                    mixins: [mockMixin]
                });

                // Act
                // Assert
                expect(wrapper.vm.characterCount).toBe(length);
            });
        });
        describe('value', () => {
            test('Get value corresponds to modelValue', () => {
                // Arrange
                const modelValue = getRandomString(10, 20);
                const wrapper = shallowMount(textareaQuestion, {
                    propsData: {
                        inputId: getRandomString(10, 20),
                        cmsWidgetName: getRandomString(10, 20),
                        modelValue
                    },
                    mixins: [mockMixin]
                });

                // Act
                // Assert
                expect(wrapper.vm.value).toBe(modelValue);
            });
            test('Set value => update modelValue event emitted with new value', () => {
                // Arrange
                const modelValue = getRandomString(10, 20);
                const wrapper = shallowMount(textareaQuestion, {
                    propsData: {
                        inputId: getRandomString(10, 20),
                        cmsWidgetName: getRandomString(10, 20),
                        modelValue
                    },
                    mixins: [mockMixin]
                });
                const newValue = getRandomString(10, 20);

                // Act
                wrapper.vm.value = newValue;

                // Assert
                expect(wrapper.emitted('update:modelValue')).toBeTruthy();
                expect(wrapper.emitted('update:modelValue')[0][0]).toEqual(newValue);
            });
        });
    });
    describe('Properties properly affect initial state', () => {
        test('Placeholder text sets placeholder text of text area', () => {
            // Arrange
            const placeholderText = getRandomString(10, 20);
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    placeholderText
                },
                mixins: [mockMixin]
            });

            // Act
            const textarea = wrapper.find('textarea');

            // Assert
            expect(textarea.attributes().placeholder).toBe(placeholderText);
        });
        test('isDisabled true => textarea disabled', () => {
            // Arrange
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    isDisabled: true
                },
                mixins: [mockMixin]
            });

            // Act
            const textarea = wrapper.find('textarea');

            // Assert
            expect(textarea.attributes().disabled).toBeDefined();
        });
        test('isRequired true => textarea required', () => {
            // Arrange
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    isRequired: true
                },
                mixins: [mockMixin]
            });

            // Act
            const textarea = wrapper.find('textarea');

            // Assert
            expect(textarea.attributes().required).toBeDefined();
        });
        test('validationRules set rules of text area', () => {
            // Arrange
            const rule = getRandomString(10, 20);
            const errorMessage = getRandomString(20, 30);
            defineRule(rule, required(errorMessage));
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    validationRules: rule
                },
                mixins: [mockMixin]
            });

            // Act
            const textarea = wrapper.find('textarea');

            // Assert
            expect(textarea.attributes().validationrules).toBe(rule);
        });
        test('maxLength set max length of text area', () => {
            // Arrange
            const maxLength = toString(getRandomInt(100, 900));
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    maxLength
                },
                mixins: [mockMixin]
            });

            // Act
            const textarea = wrapper.find('textarea');

            // Assert
            expect(textarea.attributes().maxlength).toBe(maxLength);
        });
        test('inputRows set input rows of text area', () => {
            // Arrange
            const inputRows = getRandomInt(10, 20);
            const wrapper = shallowMount(textareaQuestion, {
                propsData: {
                    inputId: getRandomString(10, 20),
                    cmsWidgetName: getRandomString(10, 20),
                    inputRows
                },
                mixins: [mockMixin]
            });

            // Act
            const textarea = wrapper.find('textarea');

            // Assert
            expect(textarea.attributes().rows).toBe(`${inputRows}`);
        });
    });
});
