<commit_before>import React from 'react';
import ReactShallowRenderer from 'react-test-renderer/shallow';
import {Config} from '../config';
import {element} from './element';

const {ELEMENT_SEPARATOR, MODIFIER_SEPARATOR} = Config;

describe('BEM element decorator', () => {
    let renderer;
    let Bar;
    beforeEach(() => {
        renderer = new ReactShallowRenderer();
        Bar = () => <div />;
    });

    it('should inject [displayName] containing element name', () => {
        const WrappedBar = element('bar')(Bar);
        expect(WrappedBar.displayName).toEqual('element(bar)');
    });

    it('should inject [className] property containing element full name', () => {
        const WrappedBar = element('bar')(Bar);
        renderer.render(<WrappedBar />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.className).toBeDefined();
        expect(wrappedFooBar.props.className).toEqual(`foo${ELEMENT_SEPARATOR}bar`);
    });

    it('should mixin provided [className] property (passed to decorator)' +
            'into resulting [className] property (injected to wrapped/underlying component)', () => {
        const WrappedBar = element('bar')(Bar);
        renderer.render(<WrappedBar className="quux" />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.className).toBeDefined();
        const fooBarClasses = wrappedFooBar.props.className.split(' ');
        expect(fooBarClasses).toHaveLength(2);
        expect(fooBarClasses).toContain(`foo${ELEMENT_SEPARATOR}bar`);
        expect(fooBarClasses).toContain('quux');
    });

    it('should transduce properties to modifiers and mixin corresponding classes into [className]', () => {
        const WrappedBar = element(
            'bar',
            ({baz}) => `baz-${baz}` // transducer
        )(Bar);
        renderer.render(<WrappedBar baz="quux" />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.className).toBeDefined();
        const fooBarClasses = wrappedFooBar.props.className.split(' ');
        expect(fooBarClasses).toHaveLength(2);
        expect(fooBarClasses).toContain(`foo${ELEMENT_SEPARATOR}bar`);
        expect(fooBarClasses).toContain(`foo${ELEMENT_SEPARATOR}bar${MODIFIER_SEPARATOR}baz-quux`);
    });

    it('should accept "classnames" compatible structures as modifier', () => {
        const WrappedBar = element(
            'bar',
            ({baz}) => ({notBaz: !baz})
        )(Bar);
        renderer.render(<WrappedBar baz={false} />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.className).toBeDefined();
        const fooBarClasses = wrappedFooBar.props.className.split(' ');
        expect(fooBarClasses).toHaveLength(2);
        expect(fooBarClasses).toContain(`foo${ELEMENT_SEPARATOR}bar`);
        expect(fooBarClasses).toContain(`foo${ELEMENT_SEPARATOR}bar${MODIFIER_SEPARATOR}not-baz`);
    });

    it('should pass block modifiers to [mapPropsToModifiers] transducer as second argument', () => {
        const mapPropsToModifiers = jest.fn().mockReturnValue('quux');
        const WrappedBar = element(
            'bar',
            mapPropsToModifiers
        )(Bar);
        renderer.render(<WrappedBar />, {
            blockName: 'foo',
            blockModifiers: 'xyzzy plugh'
        });
        expect(mapPropsToModifiers).toBeCalledWith(
            {},
            ['xyzzy', 'plugh']
        );
    });

    describe('which wraps a component with modular css', () => {
        function checkClasses() {
            const wrappedFooBar = renderer.getRenderOutput();
            expect(wrappedFooBar.props.className).toBeDefined();
            const fooBarClasses = wrappedFooBar.props.className.split(' ');
            expect(fooBarClasses).toHaveLength(2);
            expect(fooBarClasses).toContain('foo#123');
            expect(fooBarClasses).toContain('quux#456');
        }

        it('should take a class mapping from the context property [blockStyles]', () => {
            const WrappedBar = element(
                'bar',
                ({baz}) => `baz-${baz}`
            )(Bar);
            renderer.render(<WrappedBar baz="quux" />, {
                blockName: 'foo',
                blockStyles: {
                    [`foo${ELEMENT_SEPARATOR}bar`]: 'foo#123',
                    [`foo${ELEMENT_SEPARATOR}bar${MODIFIER_SEPARATOR}baz-quux`]: 'quux#456'
                }
            });
            checkClasses();
        });

        it('should take a class mapping from decorator third arg [options]', () => {
            const styles = {
                [`foo${ELEMENT_SEPARATOR}bar`]: 'foo#123',
                [`foo${ELEMENT_SEPARATOR}bar${MODIFIER_SEPARATOR}baz-quux`]: 'quux#456'
            };
            const WrappedBar = element(
                'bar',
                ({baz}) => `baz-${baz}`,
                {styles} // options
            )(Bar);
            renderer.render(<WrappedBar baz="quux" />, {blockName: 'foo'});
            checkClasses();
        });

        it('should take a class mapping from decorator second arg [options] (overloaded version)', () => {
            const styles = {
                [`foo${ELEMENT_SEPARATOR}bar`]: 'foo#123'
            };
            const WrappedBar = element(
                'bar',
                {styles} // options
            )(Bar);
            renderer.render(<WrappedBar />, {blockName: 'foo'});
            const wrappedFooBar = renderer.getRenderOutput();
            expect(wrappedFooBar.props.className).toEqual('foo#123');
        });
    });

    it('should decorate components defined as tag name', () => {
        const WrappedBar = element('bar')('div');
        renderer.render(<WrappedBar />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.type).toEqual('div');
        expect(wrappedFooBar.props.className).toEqual(`foo${ELEMENT_SEPARATOR}bar`);
    });

    it('should fail in case of empty context', () => {
        const WrappedBar = element('bar')(Bar);
        expect(() => renderer.render(<WrappedBar />)).toThrow(/^\[BEM\].+/);
    });
});
<commit_msg>Element decorator tests refactoring<commit_after>import React from 'react';
import ReactShallowRenderer from 'react-test-renderer/shallow';
import {Config} from '../config';
import {element} from './element';

const {ELEMENT_SEPARATOR, MODIFIER_SEPARATOR} = Config;

describe('BEM element decorator', () => {
    let renderer;
    let Bar;
    beforeEach(() => {
        renderer = new ReactShallowRenderer();
        Bar = () => <div />;
    });

    it('should inject [className] property containing element name', () => {
        const WrappedBar = element('bar')(Bar);
        renderer.render(<WrappedBar />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.className).toBe(`foo${ELEMENT_SEPARATOR}bar`);
    });

    it('should pass through all props except [className] and [modifiers]', () => {
        const WrappedBar = element('bar')(Bar);
        renderer.render(<WrappedBar quux />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.quux).toBe(true);
    });

    it('should mixin provided [className] into resulting [className]', () => {
        const WrappedBar = element('bar')(Bar);
        renderer.render(<WrappedBar className="quux" />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.className).toBe(`foo${ELEMENT_SEPARATOR}bar quux`);
    });

    it('should map properties to modifiers and mixin corresponding classes to [className]', () => {
        const WrappedBar = element(
            'bar',
            ({baz}) => baz // properties to modifiers
        )(Bar);
        renderer.render(<WrappedBar baz="quux" />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        const baseName = `foo${ELEMENT_SEPARATOR}bar`;
        expect(wrappedFooBar.props.className).toBe(
            `${baseName} ${baseName}${MODIFIER_SEPARATOR}quux`
        );
    });

    it('should support "classnames" compatible structures as modifier', () => {
        const WrappedBar = element(
            'bar',
            ({baz, plugh}) => [baz, {plugh}] // properties to modifiers
        )(Bar);
        renderer.render(<WrappedBar baz="quux" plugh />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        const baseName = `foo${ELEMENT_SEPARATOR}bar`;
        expect(wrappedFooBar.props.className).toBe(
            `${baseName} ${baseName}${MODIFIER_SEPARATOR}quux ${baseName}${MODIFIER_SEPARATOR}plugh`
        );
    });

    it('should map properties to modifiers and inject [modifiers] property with active modifiers', () => {
        const WrappedBar = element(
            'bar',
            ({baz, plugh}) => [baz, {plugh}] // properties to modifiers
        )(Bar);
        renderer.render(<WrappedBar baz="quux" plugh />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.props.modifiers).toBe('quux plugh');
    });

    it('should pass block modifiers to [mapPropsToModifiers] function as second argument', () => {
        const mapPropsToModifiers = jest.fn().mockReturnValue('quux');
        const WrappedBar = element('bar', mapPropsToModifiers)(Bar);
        renderer.render(<WrappedBar />, {
            blockName: 'foo',
            blockModifiers: 'xyzzy plugh'
        });
        expect(mapPropsToModifiers).toBeCalledWith({}, ['xyzzy', 'plugh']);
    });

    describe('which wraps a component with modular CSS', () => {
        const styles = {
            [`foo${ELEMENT_SEPARATOR}bar`]: 'foo#123',
            [`foo${ELEMENT_SEPARATOR}bar${MODIFIER_SEPARATOR}quux`]: 'quux#456'
        };

        it('should use class names mapping from from component`s context', () => {
            const WrappedBar = element(
                'bar',
                ({baz}) => baz
            )(Bar);
            renderer.render(<WrappedBar baz="quux" />, {
                blockName: 'foo',
                blockStyles: styles
            });
            const wrappedFooBar = renderer.getRenderOutput();
            expect(wrappedFooBar.props.className).toBe('foo#123 quux#456');
        });

        it('should use class names mapping from [options] which is passed as third arg', () => {
            const WrappedBar = element(
                'bar',
                ({baz}) => baz,
                {styles} // options
            )(Bar);
            renderer.render(<WrappedBar baz="quux" />, {blockName: 'foo'});
            const wrappedFooBar = renderer.getRenderOutput();
            expect(wrappedFooBar.props.className).toBe('foo#123 quux#456');
        });

        it('should use class names mapping from [options] which is passed as second arg', () => {
            const WrappedBar = element(
                'bar',
                {styles} // options
            )(Bar);
            renderer.render(<WrappedBar />, {blockName: 'foo'});
            const wrappedFooBar = renderer.getRenderOutput();
            expect(wrappedFooBar.props.className).toBe('foo#123');
        });
    });

    it('should support DOM components decoration', () => {
        const WrappedBar = element('bar')('div');
        renderer.render(<WrappedBar />, {blockName: 'foo'});
        const wrappedFooBar = renderer.getRenderOutput();
        expect(wrappedFooBar.type).toBe('div');
        expect(wrappedFooBar.props.className).toBe(`foo${ELEMENT_SEPARATOR}bar`);
    });

    it('should declare [displayName] containing element name', () => {
        const WrappedBar = element('bar')(Bar);
        expect(WrappedBar.displayName).toBe('element(bar)');
    });

    it('should fail in case of empty context', () => {
        const WrappedBar = element('bar')(Bar);
        expect(() => renderer.render(<WrappedBar />)).toThrow(/^\[BEM\].+/);
    });
});

{'fraction_non_alphanumeric': 0.15162188728702491, 'fraction_numerical': 0.0040137614678899085, 'mean_word_length': 2.589826521611291, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 70, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4012428', 'n_tokens_mistral': 3626, 'n_tokens_neox': 3459, 'n_words': 679}