Commit f6932e40 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'leipert-absolute-scss-paths-ee' into 'master'

EE-Port of https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/30161

See merge request gitlab-org/gitlab-ee!14454
parents 888fabf5 8dc6d176
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
// like a table or typography then make changes in the framework/ directory. // like a table or typography then make changes in the framework/ directory.
// If you need to add unique style that should affect only one page - use pages/ // If you need to add unique style that should affect only one page - use pages/
// directory. // directory.
@import "../../../node_modules/at.js/dist/css/jquery.atwho"; @import "at.js/dist/css/jquery.atwho";
@import "../../../node_modules/pikaday/scss/pikaday"; @import "pikaday/scss/pikaday";
@import "../../../node_modules/dropzone/dist/basic"; @import "dropzone/dist/basic";
@import "../../../node_modules/select2/select2"; @import "select2/select2";
// GitLab UI framework // GitLab UI framework
@import "framework"; @import "framework";
......
@import "../../../node_modules/@gitlab/csslab/dist/css/csslab-slim"; @import "@gitlab/csslab/dist/css/csslab-slim";
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
* This is a minimal stylesheet, meant to be used for error pages. * This is a minimal stylesheet, meant to be used for error pages.
*/ */
@import 'framework/variables'; @import 'framework/variables';
@import '../../../node_modules/bootstrap/scss/functions'; @import 'bootstrap/scss/functions';
@import '../../../node_modules/bootstrap/scss/variables'; @import 'bootstrap/scss/variables';
@import '../../../node_modules/bootstrap/scss/mixins'; @import 'bootstrap/scss/mixins';
@import '../../../node_modules/bootstrap/scss/reboot'; @import 'bootstrap/scss/reboot';
@import '../../../node_modules/bootstrap/scss/buttons'; @import 'bootstrap/scss/buttons';
@import '../../../node_modules/bootstrap/scss/forms'; @import 'bootstrap/scss/forms';
$body-color: #666; $body-color: #666;
$header-color: #456; $header-color: #456;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
@import 'framework/variables_overrides'; @import 'framework/variables_overrides';
@import 'framework/mixins'; @import 'framework/mixins';
@import '../../../node_modules/@gitlab/ui/scss/gitlab_ui'; @import '@gitlab/ui/scss/gitlab_ui';
@import 'bootstrap_migration'; @import 'bootstrap_migration';
@import 'framework/layout'; @import 'framework/layout';
......
...@@ -48,8 +48,8 @@ export default { ...@@ -48,8 +48,8 @@ export default {
isSelected(option) { isSelected(option) {
return this.selection.has(option.id); return this.selection.has(option.id);
}, },
closeDropdown(event) { closeDropdown() {
this.$root.$emit('clicked::link', event); this.$refs.dropdown.$children[0].hide(true);
}, },
}, },
}; };
...@@ -58,7 +58,7 @@ export default { ...@@ -58,7 +58,7 @@ export default {
<template> <template>
<div class="dashboard-filter"> <div class="dashboard-filter">
<strong class="js-name">{{ filter.name }}</strong> <strong class="js-name">{{ filter.name }}</strong>
<gl-dropdown class="d-block mt-1" menu-class="dropdown-extended-height"> <gl-dropdown ref="dropdown" class="d-block mt-1" menu-class="dropdown-extended-height">
<template slot="button-content"> <template slot="button-content">
<span class="text-truncate"> <span class="text-truncate">
{{ selectedOptionText.firstOption }} {{ selectedOptionText.firstOption }}
......
...@@ -12,6 +12,7 @@ describe('custom metrics form fields component', () => { ...@@ -12,6 +12,7 @@ describe('custom metrics form fields component', () => {
const validQueryResponse = { data: { success: true, query: { valid: true, error: '' } } }; const validQueryResponse = { data: { success: true, query: { valid: true, error: '' } } };
const csrfToken = 'mockToken'; const csrfToken = 'mockToken';
const formOperation = 'post'; const formOperation = 'post';
const debouncedValidateQueryMock = jest.fn();
const makeFormData = (data = {}) => ({ const makeFormData = (data = {}) => ({
formData: { formData: {
title: '', title: '',
...@@ -32,6 +33,9 @@ describe('custom metrics form fields component', () => { ...@@ -32,6 +33,9 @@ describe('custom metrics form fields component', () => {
}, },
csrfToken, csrfToken,
sync: false, sync: false,
methods: {
debouncedValidateQuery: debouncedValidateQueryMock,
},
}); });
}; };
...@@ -120,18 +124,14 @@ describe('custom metrics form fields component', () => { ...@@ -120,18 +124,14 @@ describe('custom metrics form fields component', () => {
jest.runAllTimers(); jest.runAllTimers();
}); });
it('checks validity on user input', done => { it('checks validity on user input', () => {
const query = 'changedQuery'; const query = 'changedQuery';
mountComponent(); mountComponent();
const spy = jest.spyOn(component.vm, 'debouncedValidateQuery'); const queryInput = component.find(`input[name="${name}"]`);
const queryInput = getNamedInput(name); queryInput.setValue(query);
queryInput.value = query; queryInput.trigger('input');
queryInput.dispatchEvent(new Event('input'));
component.vm.$nextTick(() => { expect(debouncedValidateQueryMock).toHaveBeenCalledWith(query);
expect(spy).toHaveBeenCalledWith(query);
done();
});
}); });
describe('when query is invalid', () => { describe('when query is invalid', () => {
......
...@@ -42,7 +42,9 @@ describe('Batch comments review bar component', () => { ...@@ -42,7 +42,9 @@ describe('Batch comments review bar component', () => {
vm.$el.querySelector('.btn.btn-align-content').click(); vm.$el.querySelector('.btn.btn-align-content').click();
vm.$nextTick(() => { vm.$nextTick(() => {
vm.$el.querySelector('.modal .btn-danger').click(); const modal = document.querySelector('#discard-draft-review');
modal.querySelector('.btn-danger').click();
expect(vm.$store.dispatch).toHaveBeenCalled(); expect(vm.$store.dispatch).toHaveBeenCalled();
......
...@@ -17,6 +17,9 @@ describe('LicenseManagementRow', () => { ...@@ -17,6 +17,9 @@ describe('LicenseManagementRow', () => {
let store; let store;
let actions; let actions;
const findNthDropdown = num => [...vm.$el.querySelectorAll('.dropdown-item')][num];
const findNthDropdownIcon = num => findNthDropdown(num).querySelector('svg');
beforeEach(() => { beforeEach(() => {
actions = { actions = {
setLicenseInModal: jasmine.createSpy('setLicenseInModal'), setLicenseInModal: jasmine.createSpy('setLicenseInModal'),
...@@ -60,13 +63,13 @@ describe('LicenseManagementRow', () => { ...@@ -60,13 +63,13 @@ describe('LicenseManagementRow', () => {
describe('template', () => { describe('template', () => {
it('first dropdown element should have a visible icon', () => { it('first dropdown element should have a visible icon', () => {
const firstOption = vm.$el.querySelector('.dropdown-item:nth-child(1) svg'); const firstOption = findNthDropdownIcon(0);
expect(firstOption.classList).toContain(visibleClass); expect(firstOption.classList).toContain(visibleClass);
}); });
it('second dropdown element should have no visible icon', () => { it('second dropdown element should have no visible icon', () => {
const secondOption = vm.$el.querySelector('.dropdown-item:nth-child(2) svg'); const secondOption = findNthDropdownIcon(1);
expect(secondOption.classList).toContain(invisibleClass); expect(secondOption.classList).toContain(invisibleClass);
}); });
...@@ -95,13 +98,13 @@ describe('LicenseManagementRow', () => { ...@@ -95,13 +98,13 @@ describe('LicenseManagementRow', () => {
describe('template', () => { describe('template', () => {
it('first dropdown element should have no visible icon', () => { it('first dropdown element should have no visible icon', () => {
const firstOption = vm.$el.querySelector('.dropdown-item:nth-child(1) svg'); const firstOption = findNthDropdownIcon(0);
expect(firstOption.classList).toContain(invisibleClass); expect(firstOption.classList).toContain(invisibleClass);
}); });
it('second dropdown element should have a visible icon', () => { it('second dropdown element should have a visible icon', () => {
const secondOption = vm.$el.querySelector('.dropdown-item:nth-child(2) svg'); const secondOption = findNthDropdownIcon(1);
expect(secondOption.classList).toContain(visibleClass); expect(secondOption.classList).toContain(visibleClass);
}); });
...@@ -117,14 +120,14 @@ describe('LicenseManagementRow', () => { ...@@ -117,14 +120,14 @@ describe('LicenseManagementRow', () => {
}); });
it('triggering approveLicense by clicking the first dropdown option', () => { it('triggering approveLicense by clicking the first dropdown option', () => {
const linkEl = vm.$el.querySelector('.dropdown-item:nth-child(1)'); const linkEl = findNthDropdown(0);
linkEl.click(); linkEl.click();
expect(actions.approveLicense).toHaveBeenCalled(); expect(actions.approveLicense).toHaveBeenCalled();
}); });
it('triggering approveLicense blacklistLicense by clicking the second dropdown option', () => { it('triggering approveLicense blacklistLicense by clicking the second dropdown option', () => {
const linkEl = vm.$el.querySelector('.dropdown-item:nth-child(2)'); const linkEl = findNthDropdown(1);
linkEl.click(); linkEl.click();
expect(actions.blacklistLicense).toHaveBeenCalled(); expect(actions.blacklistLicense).toHaveBeenCalled();
...@@ -166,12 +169,12 @@ describe('LicenseManagementRow', () => { ...@@ -166,12 +169,12 @@ describe('LicenseManagementRow', () => {
expect(dropdownEl).not.toBeNull(); expect(dropdownEl).not.toBeNull();
const firstOption = dropdownEl.querySelector('.dropdown-item:nth-child(1)'); const firstOption = findNthDropdown(0);
expect(firstOption).not.toBeNull(); expect(firstOption).not.toBeNull();
expect(firstOption.innerText.trim()).toBe('Approved'); expect(firstOption.innerText.trim()).toBe('Approved');
const secondOption = dropdownEl.querySelector('.dropdown-item:nth-child(2)'); const secondOption = findNthDropdown(1);
expect(secondOption).not.toBeNull(); expect(secondOption).not.toBeNull();
expect(secondOption.innerText.trim()).toBe('Blacklisted'); expect(secondOption.innerText.trim()).toBe('Blacklisted');
......
...@@ -2,6 +2,7 @@ import Vue from 'vue'; ...@@ -2,6 +2,7 @@ import Vue from 'vue';
import AlertWidget from 'ee/monitoring/components/alert_widget.vue'; import AlertWidget from 'ee/monitoring/components/alert_widget.vue';
import AlertsService from 'ee/monitoring/services/alerts_service'; import AlertsService from 'ee/monitoring/services/alerts_service';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import waitForPromises from 'spec/helpers/wait_for_promises';
describe('AlertWidget', () => { describe('AlertWidget', () => {
let AlertWidgetComponent; let AlertWidgetComponent;
...@@ -184,10 +185,13 @@ describe('AlertWidget', () => { ...@@ -184,10 +185,13 @@ describe('AlertWidget', () => {
vm.$refs.widgetForm.$emit('create', alertParams); vm.$refs.widgetForm.$emit('create', alertParams);
expect(AlertsService.prototype.createAlert).toHaveBeenCalledWith(alertParams); expect(AlertsService.prototype.createAlert).toHaveBeenCalledWith(alertParams);
Vue.nextTick(() => {
waitForPromises()
.then(() => {
expect(vm.isLoading).toEqual(false); expect(vm.isLoading).toEqual(false);
done(); done();
}); })
.catch(done.fail);
}); });
it('updates an alert with an appropriate handler', done => { it('updates an alert with an appropriate handler', done => {
...@@ -195,7 +199,9 @@ describe('AlertWidget', () => { ...@@ -195,7 +199,9 @@ describe('AlertWidget', () => {
const newAlertParams = { operator: '=', threshold: 12 }; const newAlertParams = { operator: '=', threshold: 12 };
spyOn(AlertsService.prototype, 'readAlert').and.returnValue(Promise.resolve(alertParams)); spyOn(AlertsService.prototype, 'readAlert').and.returnValue(Promise.resolve(alertParams));
spyOn(AlertsService.prototype, 'updateAlert').and.returnValue(Promise.resolve({})); spyOn(AlertsService.prototype, 'updateAlert').and.returnValue(
Promise.resolve({ ...alertParams, ...newAlertParams }),
);
vm = mountComponent(AlertWidgetComponent, propsWithAlertData); vm = mountComponent(AlertWidgetComponent, propsWithAlertData);
vm.$on('setAlerts', mockSetAlerts); vm.$on('setAlerts', mockSetAlerts);
...@@ -207,10 +213,12 @@ describe('AlertWidget', () => { ...@@ -207,10 +213,12 @@ describe('AlertWidget', () => {
}); });
expect(AlertsService.prototype.updateAlert).toHaveBeenCalledWith(alertPath, newAlertParams); expect(AlertsService.prototype.updateAlert).toHaveBeenCalledWith(alertPath, newAlertParams);
Vue.nextTick(() => { waitForPromises()
.then(() => {
expect(vm.isLoading).toEqual(false); expect(vm.isLoading).toEqual(false);
done(); done();
}); })
.catch(done.fail);
}); });
it('deletes an alert with an appropriate handler', done => { it('deletes an alert with an appropriate handler', done => {
...@@ -225,10 +233,12 @@ describe('AlertWidget', () => { ...@@ -225,10 +233,12 @@ describe('AlertWidget', () => {
vm.$refs.widgetForm.$emit('delete', { alert: alertPath }); vm.$refs.widgetForm.$emit('delete', { alert: alertPath });
expect(AlertsService.prototype.deleteAlert).toHaveBeenCalledWith(alertPath); expect(AlertsService.prototype.deleteAlert).toHaveBeenCalledWith(alertPath);
Vue.nextTick(() => { waitForPromises()
.then(() => {
expect(vm.isLoading).toEqual(false); expect(vm.isLoading).toEqual(false);
expect(vm.alertSummary).toBeFalsy(); expect(vm.alertSummary).toBeFalsy();
done(); done();
}); })
.catch(done.fail);
}); });
}); });
...@@ -3,6 +3,7 @@ import * as jqueryMatchers from 'custom-jquery-matchers'; ...@@ -3,6 +3,7 @@ import * as jqueryMatchers from 'custom-jquery-matchers';
import $ from 'jquery'; import $ from 'jquery';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { config as testUtilsConfig } from '@vue/test-utils';
import { initializeTestTimeout } from './helpers/timeout'; import { initializeTestTimeout } from './helpers/timeout';
import { loadHTMLFixture, setHTMLFixture } from './helpers/fixtures'; import { loadHTMLFixture, setHTMLFixture } from './helpers/fixtures';
...@@ -60,9 +61,21 @@ Object.assign(global, { ...@@ -60,9 +61,21 @@ Object.assign(global, {
preloadFixtures() {}, preloadFixtures() {},
}); });
Object.assign(global, {
MutationObserver() {
return {
disconnect() {},
observe() {},
};
},
});
// custom-jquery-matchers was written for an old Jest version, we need to make it compatible // custom-jquery-matchers was written for an old Jest version, we need to make it compatible
Object.entries(jqueryMatchers).forEach(([matcherName, matcherFactory]) => { Object.entries(jqueryMatchers).forEach(([matcherName, matcherFactory]) => {
expect.extend({ expect.extend({
[matcherName]: matcherFactory().compare, [matcherName]: matcherFactory().compare,
}); });
}); });
// Tech debt issue TBD
testUtilsConfig.logModifiedComponents = false;
...@@ -241,7 +241,7 @@ describe('Dashboard', () => { ...@@ -241,7 +241,7 @@ describe('Dashboard', () => {
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
const dropdownItems = component.$el.querySelectorAll( const dropdownItems = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item.is-active', '.js-environments-dropdown .dropdown-item.active',
); );
expect(dropdownItems.length).toEqual(1); expect(dropdownItems.length).toEqual(1);
......
...@@ -72,21 +72,15 @@ describe('collapsible registry container', () => { ...@@ -72,21 +72,15 @@ describe('collapsible registry container', () => {
expect(findDeleteBtn()).not.toBeNull(); expect(findDeleteBtn()).not.toBeNull();
}); });
describe('clicked on delete', () => { it('should call deleteItem when confirming deletion', done => {
beforeEach(done => {
findDeleteBtn().click(); findDeleteBtn().click();
Vue.nextTick(done);
});
it('should open confirmation modal', () => {
expect(vm.$el.querySelector('#confirm-repo-deletion-modal')).not.toBeNull();
});
it('should call deleteItem when confirming deletion', () => {
spyOn(vm, 'deleteItem').and.returnValue(Promise.resolve()); spyOn(vm, 'deleteItem').and.returnValue(Promise.resolve());
vm.$el.querySelector('#confirm-repo-deletion-modal .btn-danger').click();
Vue.nextTick(() => {
document.querySelector('#confirm-repo-deletion-modal .btn-danger').click();
expect(vm.deleteItem).toHaveBeenCalledWith(vm.repo); expect(vm.deleteItem).toHaveBeenCalledWith(vm.repo);
done();
}); });
}); });
}); });
......
...@@ -46,23 +46,16 @@ describe('table registry', () => { ...@@ -46,23 +46,16 @@ describe('table registry', () => {
expect(findDeleteBtn()).toBeDefined(); expect(findDeleteBtn()).toBeDefined();
}); });
describe('clicked on delete', () => { it('should call deleteItem and reset itemToBeDeleted when confirming deletion', done => {
beforeEach(done => {
findDeleteBtn().click(); findDeleteBtn().click();
Vue.nextTick(done);
});
it('should open confirmation modal and set itemToBeDeleted properly', () => {
expect(vm.itemToBeDeleted).toEqual(firstImage);
expect(vm.$el.querySelector('#confirm-image-deletion-modal')).not.toBeNull();
});
it('should call deleteItem and reset itemToBeDeleted when confirming deletion', () => {
spyOn(vm, 'deleteItem').and.returnValue(Promise.resolve()); spyOn(vm, 'deleteItem').and.returnValue(Promise.resolve());
vm.$el.querySelector('#confirm-image-deletion-modal .btn-danger').click();
Vue.nextTick(() => {
document.querySelector('#confirm-image-deletion-modal .btn-danger').click();
expect(vm.deleteItem).toHaveBeenCalledWith(firstImage); expect(vm.deleteItem).toHaveBeenCalledWith(firstImage);
expect(vm.itemToBeDeleted).toBeNull(); expect(vm.itemToBeDeleted).toBeNull();
done();
}); });
}); });
}); });
......
...@@ -10,12 +10,16 @@ import VueResource from 'vue-resource'; ...@@ -10,12 +10,16 @@ import VueResource from 'vue-resource';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import CheckEE from '~/vue_shared/mixins/is_ee'; import CheckEE from '~/vue_shared/mixins/is_ee';
import jasmineDiff from 'jasmine-diff'; import jasmineDiff from 'jasmine-diff';
import { config as testUtilsConfig } from '@vue/test-utils';
import { getDefaultAdapter } from '~/lib/utils/axios_utils'; import { getDefaultAdapter } from '~/lib/utils/axios_utils';
import { FIXTURES_PATH, TEST_HOST } from './test_constants'; import { FIXTURES_PATH, TEST_HOST } from './test_constants';
import customMatchers from './matchers'; import customMatchers from './matchers';
// Tech debt issue TBD
testUtilsConfig.logModifiedComponents = false;
const isHeadlessChrome = /\bHeadlessChrome\//.test(navigator.userAgent); const isHeadlessChrome = /\bHeadlessChrome\//.test(navigator.userAgent);
Vue.config.devtools = !isHeadlessChrome; Vue.config.devtools = !isHeadlessChrome;
Vue.config.productionTip = false; Vue.config.productionTip = false;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment