diff --git a/spec/javascripts/environments/confirm_rollback_modal_spec.js b/spec/frontend/environments/confirm_rollback_modal_spec.js similarity index 97% rename from spec/javascripts/environments/confirm_rollback_modal_spec.js rename to spec/frontend/environments/confirm_rollback_modal_spec.js index 05715bce38f..a1a22274e8f 100644 --- a/spec/javascripts/environments/confirm_rollback_modal_spec.js +++ b/spec/frontend/environments/confirm_rollback_modal_spec.js @@ -61,7 +61,7 @@ describe('Confirm Rollback Modal Component', () => { environment, }, }); - const eventHubSpy = spyOn(eventHub, '$emit'); + const eventHubSpy = jest.spyOn(eventHub, '$emit'); const modal = component.find(GlModal); modal.vm.$emit('ok'); diff --git a/spec/javascripts/environments/environment_rollback_spec.js b/spec/frontend/environments/environment_rollback_spec.js similarity index 55% rename from spec/javascripts/environments/environment_rollback_spec.js rename to spec/frontend/environments/environment_rollback_spec.js index 8c47f6a12c0..fb62a096c3d 100644 --- a/spec/javascripts/environments/environment_rollback_spec.js +++ b/spec/frontend/environments/environment_rollback_spec.js @@ -1,46 +1,38 @@ -import Vue from 'vue'; -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, mount } from '@vue/test-utils'; import { GlButton } from '@gitlab/ui'; import eventHub from '~/environments/event_hub'; -import rollbackComp from '~/environments/components/environment_rollback.vue'; +import RollbackComponent from '~/environments/components/environment_rollback.vue'; describe('Rollback Component', () => { const retryUrl = 'https://gitlab.com/retry'; - let RollbackComponent; - - beforeEach(() => { - RollbackComponent = Vue.extend(rollbackComp); - }); it('Should render Re-deploy label when isLastDeployment is true', () => { - const component = new RollbackComponent({ - el: document.querySelector('.test-dom-element'), + const wrapper = mount(RollbackComponent, { propsData: { retryUrl, isLastDeployment: true, environment: {}, }, - }).$mount(); + }); - expect(component.$el).toHaveSpriteIcon('repeat'); + expect(wrapper.element).toHaveSpriteIcon('repeat'); }); it('Should render Rollback label when isLastDeployment is false', () => { - const component = new RollbackComponent({ - el: document.querySelector('.test-dom-element'), + const wrapper = mount(RollbackComponent, { propsData: { retryUrl, isLastDeployment: false, environment: {}, }, - }).$mount(); + }); - expect(component.$el).toHaveSpriteIcon('redo'); + expect(wrapper.element).toHaveSpriteIcon('redo'); }); it('should emit a "rollback" event on button click', () => { - const eventHubSpy = spyOn(eventHub, '$emit'); - const component = shallowMount(RollbackComponent, { + const eventHubSpy = jest.spyOn(eventHub, '$emit'); + const wrapper = shallowMount(RollbackComponent, { propsData: { retryUrl, environment: { @@ -48,7 +40,7 @@ describe('Rollback Component', () => { }, }, }); - const button = component.find(GlButton); + const button = wrapper.find(GlButton); button.vm.$emit('click'); diff --git a/spec/frontend/matchers.js b/spec/frontend/matchers.js new file mode 100644 index 00000000000..35c362d0bf5 --- /dev/null +++ b/spec/frontend/matchers.js @@ -0,0 +1,38 @@ +export default { + toHaveSpriteIcon: (element, iconName) => { + if (!iconName) { + throw new Error('toHaveSpriteIcon is missing iconName argument!'); + } + + if (!(element instanceof HTMLElement)) { + throw new Error(`${element} is not a DOM element!`); + } + + const iconReferences = [].slice.apply(element.querySelectorAll('svg use')); + const matchingIcon = iconReferences.find(reference => + reference.getAttribute('xlink:href').endsWith(`#${iconName}`), + ); + + const pass = Boolean(matchingIcon); + + let message; + if (pass) { + message = `${element.outerHTML} contains the sprite icon "${iconName}"!`; + } else { + message = `${element.outerHTML} does not contain the sprite icon "${iconName}"!`; + + const existingIcons = iconReferences.map(reference => { + const iconUrl = reference.getAttribute('xlink:href'); + return `"${iconUrl.replace(/^.+#/, '')}"`; + }); + if (existingIcons.length > 0) { + message += ` (only found ${existingIcons.join(',')})`; + } + } + + return { + pass, + message: () => message, + }; + }, +}; diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js index 8b6f7802b15..df8a625319b 100644 --- a/spec/frontend/test_setup.js +++ b/spec/frontend/test_setup.js @@ -6,6 +6,7 @@ import { config as testUtilsConfig } from '@vue/test-utils'; import { initializeTestTimeout } from './helpers/timeout'; import { loadHTMLFixture, setHTMLFixture } from './helpers/fixtures'; import { setupManualMocks } from './mocks/mocks_helper'; +import customMatchers from './matchers'; // Expose jQuery so specs using jQuery plugins can be imported nicely. // Here is an issue to explore better alternatives: @@ -67,6 +68,8 @@ Object.entries(jqueryMatchers).forEach(([matcherName, matcherFactory]) => { }); }); +expect.extend(customMatchers); + // Tech debt issue TBD testUtilsConfig.logModifiedComponents = false;