@gitlab/ui
Version:
GitLab UI Components
203 lines (173 loc) • 5.98 kB
JavaScript
import { mount } from '@vue/test-utils';
import GlLoadingIcon from '../loading_icon/loading_icon.vue';
import GlButton from './button.vue';
describe('button component', () => {
let wrapper;
const buildWrapper = (options = {}) => {
wrapper = mount(GlButton, options);
};
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
it('renders a button', () => {
buildWrapper();
expect(wrapper.element.tagName).toBe('BUTTON');
});
describe('ellipsis button', () => {
beforeEach(() => {
buildWrapper({
propsData: {
icon: 'ellipsis_h',
},
});
});
it('should add `button-ellipsis-horizontal` class', () => {
expect(wrapper.classes()).toContain('button-ellipsis-horizontal');
});
});
describe('label button', () => {
describe('default', () => {
beforeEach(() => {
buildWrapper({
propsData: {
label: true,
},
});
});
it('renders as a span', () => {
expect(wrapper.element.tagName).toBe('SPAN');
});
it('should add `btn` and `btn-label` classes', () => {
const classes = wrapper.classes();
expect(classes).toContain('btn');
expect(classes).toContain('btn-label');
});
});
it.each`
size | expectedClass
${undefined} | ${'btn-md'}
${'small'} | ${'btn-sm'}
`('applies $expectedClass class when size is $size', ({ size, expectedClass }) => {
buildWrapper({
propsData: {
label: true,
size,
},
});
expect(wrapper.classes()).toContain(expectedClass);
});
});
describe('loading indicator', () => {
beforeEach(() => {
buildWrapper({
propsData: {
loading: true,
},
});
});
it('should render the loading indicator', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
it('should render the loading indicator with the `gl-button-loading-indicator` class', () => {
expect(findLoadingIcon().classes()).toContain('gl-button-loading-indicator');
});
});
it.each`
variant | category | expectedClass
${'default'} | ${'primary'} | ${'btn-default'}
${'default'} | ${'secondary'} | ${'btn-default-secondary'}
${'default'} | ${'tertiary'} | ${'btn-default-tertiary'}
${'confirm'} | ${'primary'} | ${'btn-confirm'}
${'confirm'} | ${'secondary'} | ${'btn-confirm-secondary'}
${'confirm'} | ${'tertiary'} | ${'btn-confirm-tertiary'}
${'info'} | ${'primary'} | ${'btn-info'}
${'info'} | ${'secondary'} | ${'btn-info-secondary'}
${'info'} | ${'tertiary'} | ${'btn-info-tertiary'}
${'success'} | ${'primary'} | ${'btn-success'}
${'success'} | ${'secondary'} | ${'btn-success-secondary'}
${'success'} | ${'tertiary'} | ${'btn-success-tertiary'}
${'warning'} | ${'primary'} | ${'btn-warning'}
${'warning'} | ${'secondary'} | ${'btn-warning-secondary'}
${'warning'} | ${'tertiary'} | ${'btn-warning-tertiary'}
${'danger'} | ${'primary'} | ${'btn-danger'}
${'danger'} | ${'secondary'} | ${'btn-danger-secondary'}
${'danger'} | ${'tertiary'} | ${'btn-danger-tertiary'}
`(
'adds $expectedClass class when variant=$variant and category=$category',
({ variant, category, expectedClass }) => {
buildWrapper({
propsData: {
icon: 'ellipsis_h',
variant,
category,
},
});
expect(wrapper.classes()).toContain(expectedClass);
}
);
describe('link button', () => {
describe('link to #', () => {
beforeEach(() => {
buildWrapper({
propsData: {
href: '#',
},
});
});
it('should not have a target attribute', () => {
expect(wrapper.attributes('target')).toBeUndefined();
});
it('should not have a rel attribute', () => {
expect(wrapper.attributes('rel')).toBeUndefined();
});
});
describe('target blank', () => {
it('should set secure rels for hrefs in a different domain', () => {
buildWrapper({
propsData: {
target: '_blank',
href: 'http://example.com',
},
});
expect(wrapper.attributes('rel')).toBe('noopener noreferrer');
});
it('should set noopener rel for hrefs for the same domain', () => {
// This behavior is due to the fact that
// bootstrap-vue link component adds rel="noopener" when target is set as "_blank"
// https://github.com/bootstrap-vue/bootstrap-vue/pull/418/files#diff-997fcb1eb150236aec5306a4d72229beR25
buildWrapper({
propsData: {
target: '_blank',
href: window.location.hostname,
},
});
expect(wrapper.attributes('rel')).toBe('noopener');
});
it('should keep rel attribute for hrefs in the same domain', () => {
buildWrapper({ attrs: { rel: 'nofollow', href: window.location.hostname } });
expect(wrapper.attributes('rel')).toBe('nofollow');
});
});
describe('unsafe urls', () => {
// eslint-disable-next-line no-script-url
const unsafeUrl = 'javascript:alert(1)';
// GlSafeLinkDirective is actually responsible to handle the unsafe URLs
// and GlButton uses this directive to make all the links secure by default
it('should set href to blank ', () => {
buildWrapper({
propsData: {
href: unsafeUrl,
},
});
expect(wrapper.attributes('href')).toBe('about:blank');
});
it('should allow unsafe URL if isUnsafeLink is true', () => {
buildWrapper({
propsData: {
href: unsafeUrl,
isUnsafeLink: true,
},
});
expect(wrapper.attributes('href')).toBe(unsafeUrl);
});
});
});
});