diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-23 18:08:53 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-23 18:08:53 +0000 |
commit | d933bc5a8738d24898c5a82cc72ee9bd050425e6 (patch) | |
tree | 6d4c5ffedc32dc82c3fd6e4e3031f7981505655a /spec/frontend/vue_shared | |
parent | 3f9e1b261121f4dbd045341241f81b47356c99cf (diff) | |
download | gitlab-ce-d933bc5a8738d24898c5a82cc72ee9bd050425e6.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/vue_shared')
12 files changed, 412 insertions, 231 deletions
diff --git a/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap new file mode 100644 index 00000000000..5347d1efc48 --- /dev/null +++ b/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Code Block matches snapshot 1`] = ` +<pre + class="code-block rounded" +> + + <code + class="d-block" + > + test-code + </code> + + +</pre> +`; diff --git a/spec/frontend/vue_shared/components/__snapshots__/identicon_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/identicon_spec.js.snap new file mode 100644 index 00000000000..72370cb5b52 --- /dev/null +++ b/spec/frontend/vue_shared/components/__snapshots__/identicon_spec.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Identicon matches snapshot 1`] = ` +<div + class="avatar identicon s40 bg2" +> + + E + +</div> +`; diff --git a/spec/frontend/vue_shared/components/code_block_spec.js b/spec/frontend/vue_shared/components/code_block_spec.js index 6b91a20ff76..0d21dd94f7c 100644 --- a/spec/frontend/vue_shared/components/code_block_spec.js +++ b/spec/frontend/vue_shared/components/code_block_spec.js @@ -1,33 +1,25 @@ -import Vue from 'vue'; -import component from '~/vue_shared/components/code_block.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; +import { shallowMount } from '@vue/test-utils'; +import CodeBlock from '~/vue_shared/components/code_block.vue'; describe('Code Block', () => { - const Component = Vue.extend(component); - let vm; + let wrapper; - afterEach(() => { - vm.$destroy(); - }); - - it('renders a code block with the provided code', () => { - const code = - "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in \u003ctop (required)\u003e'"; - - vm = mountComponent(Component, { - code, + const createComponent = () => { + wrapper = shallowMount(CodeBlock, { + propsData: { + code: 'test-code', + }, }); + }; - expect(vm.$el.querySelector('code').textContent).toEqual(code); + afterEach(() => { + wrapper.destroy(); + wrapper = null; }); - it('escapes XSS injections', () => { - const code = 'CCC<img src=x onerror=alert(document.domain)>'; - - vm = mountComponent(Component, { - code, - }); + it('matches snapshot', () => { + createComponent(); - expect(vm.$el.querySelector('code').textContent).toEqual(code); + expect(wrapper.element).toMatchSnapshot(); }); }); diff --git a/spec/frontend/vue_shared/components/identicon_spec.js b/spec/frontend/vue_shared/components/identicon_spec.js index 0b3dbb61c96..5e8b013d480 100644 --- a/spec/frontend/vue_shared/components/identicon_spec.js +++ b/spec/frontend/vue_shared/components/identicon_spec.js @@ -1,65 +1,33 @@ -import Vue from 'vue'; -import identiconComponent from '~/vue_shared/components/identicon.vue'; - -const createComponent = sizeClass => { - const Component = Vue.extend(identiconComponent); - - return new Component({ - propsData: { - entityId: 1, - entityName: 'entity-name', - sizeClass, - }, - }).$mount(); -}; - -describe('IdenticonComponent', () => { - describe('computed', () => { - let vm; - - beforeEach(() => { - vm = createComponent(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('identiconBackgroundClass', () => { - it('should return bg class based on entityId', () => { - vm.entityId = 4; - - expect(vm.identiconBackgroundClass).toBeDefined(); - expect(vm.identiconBackgroundClass).toBe('bg5'); - }); +import { shallowMount } from '@vue/test-utils'; +import IdenticonComponent from '~/vue_shared/components/identicon.vue'; + +describe('Identicon', () => { + let wrapper; + + const createComponent = () => { + wrapper = shallowMount(IdenticonComponent, { + propsData: { + entityId: 1, + entityName: 'entity-name', + sizeClass: 's40', + }, }); + }; - describe('identiconTitle', () => { - it('should return first letter of entity title in uppercase', () => { - vm.entityName = 'dummy-group'; - - expect(vm.identiconTitle).toBeDefined(); - expect(vm.identiconTitle).toBe('D'); - }); - }); + afterEach(() => { + wrapper.destroy(); + wrapper = null; }); - describe('template', () => { - it('should render identicon', () => { - const vm = createComponent(); + it('matches snapshot', () => { + createComponent(); - expect(vm.$el.nodeName).toBe('DIV'); - expect(vm.$el.classList.contains('identicon')).toBeTruthy(); - expect(vm.$el.classList.contains('s40')).toBeTruthy(); - expect(vm.$el.classList.contains('bg2')).toBeTruthy(); - vm.$destroy(); - }); + expect(wrapper.element).toMatchSnapshot(); + }); - it('should render identicon with provided sizing class', () => { - const vm = createComponent('s32'); + it('adds a correct class to identicon', () => { + createComponent(); - expect(vm.$el.classList.contains('s32')).toBeTruthy(); - vm.$destroy(); - }); + expect(wrapper.find({ ref: 'identicon' }).classes()).toContain('bg2'); }); }); diff --git a/spec/frontend/vue_shared/components/issue/__snapshots__/issue_warning_spec.js.snap b/spec/frontend/vue_shared/components/issue/__snapshots__/issue_warning_spec.js.snap new file mode 100644 index 00000000000..49b18d3e106 --- /dev/null +++ b/spec/frontend/vue_shared/components/issue/__snapshots__/issue_warning_spec.js.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Issue Warning Component when issue is confidential but not locked renders information about confidential issue 1`] = ` +<span> + + This is a confidential issue. + People without permission will never get a notification. + + <gl-link-stub + href="confidential-path" + target="_blank" + > + + Learn more + + </gl-link-stub> +</span> +`; + +exports[`Issue Warning Component when issue is locked and confidential renders information about locked and confidential issue 1`] = ` +<span> + <span> + This issue is + <a + href="" + rel="noopener noreferrer" + target="_blank" + > + confidential + </a> + and + <a + href="" + rel="noopener noreferrer" + target="_blank" + > + locked + </a> + . + </span> + + People without permission will never get a notification and won't be able to comment. + +</span> +`; + +exports[`Issue Warning Component when issue is locked but not confidential renders information about locked issue 1`] = ` +<span> + + This issue is locked. + Only project members can comment. + + <gl-link-stub + href="locked-path" + target="_blank" + > + + Learn more + + </gl-link-stub> +</span> +`; diff --git a/spec/frontend/vue_shared/components/issue/issue_warning_spec.js b/spec/frontend/vue_shared/components/issue/issue_warning_spec.js index 7bb054b4e6c..891c70bcb5c 100644 --- a/spec/frontend/vue_shared/components/issue/issue_warning_spec.js +++ b/spec/frontend/vue_shared/components/issue/issue_warning_spec.js @@ -1,65 +1,105 @@ -import Vue from 'vue'; -import mountComponent from 'helpers/vue_mount_component_helper'; -import issueWarning from '~/vue_shared/components/issue/issue_warning.vue'; +import { shallowMount } from '@vue/test-utils'; +import IssueWarning from '~/vue_shared/components/issue/issue_warning.vue'; +import Icon from '~/vue_shared/components/icon.vue'; -const IssueWarning = Vue.extend(issueWarning); +describe('Issue Warning Component', () => { + let wrapper; -function formatWarning(string) { - // Replace newlines with a space then replace multiple spaces with one space - return string - .trim() - .replace(/\n/g, ' ') - .replace(/\s\s+/g, ' '); -} + const findIcon = () => wrapper.find(Icon); + const findLockedBlock = () => wrapper.find({ ref: 'locked' }); + const findConfidentialBlock = () => wrapper.find({ ref: 'confidential' }); + const findLockedAndConfidentialBlock = () => wrapper.find({ ref: 'lockedAndConfidential' }); -describe('Issue Warning Component', () => { - describe('isLocked', () => { - it('should render locked issue warning information', () => { - const props = { + const createComponent = props => { + wrapper = shallowMount(IssueWarning, { + propsData: { + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('when issue is locked but not confidential', () => { + beforeEach(() => { + createComponent({ isLocked: true, - lockedIssueDocsPath: 'docs/issues/locked', - }; - const vm = mountComponent(IssueWarning, props); - - expect( - vm.$el.querySelector('.icon use').getAttributeNS('http://www.w3.org/1999/xlink', 'href'), - ).toMatch(/lock$/); - expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual( - 'This issue is locked. Only project members can comment. Learn more', - ); - expect(vm.$el.querySelector('a').href).toContain(props.lockedIssueDocsPath); + lockedIssueDocsPath: 'locked-path', + isConfidential: false, + }); + }); + + it('renders information about locked issue', () => { + expect(findLockedBlock().exists()).toBe(true); + expect(findLockedBlock().element).toMatchSnapshot(); + }); + + it('renders warning icon', () => { + expect(findIcon().exists()).toBe(true); + }); + + it('does not render information about locked and confidential issue', () => { + expect(findLockedAndConfidentialBlock().exists()).toBe(false); + }); + + it('does not render information about confidential issue', () => { + expect(findConfidentialBlock().exists()).toBe(false); }); }); - describe('isConfidential', () => { - it('should render confidential issue warning information', () => { - const props = { + describe('when issue is confidential but not locked', () => { + beforeEach(() => { + createComponent({ + isLocked: false, isConfidential: true, - confidentialIssueDocsPath: '/docs/issues/confidential', - }; - const vm = mountComponent(IssueWarning, props); - - expect( - vm.$el.querySelector('.icon use').getAttributeNS('http://www.w3.org/1999/xlink', 'href'), - ).toMatch(/eye-slash$/); - expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual( - 'This is a confidential issue. People without permission will never get a notification. Learn more', - ); - expect(vm.$el.querySelector('a').href).toContain(props.confidentialIssueDocsPath); + confidentialIssueDocsPath: 'confidential-path', + }); + }); + + it('renders information about confidential issue', () => { + expect(findConfidentialBlock().exists()).toBe(true); + expect(findConfidentialBlock().element).toMatchSnapshot(); + }); + + it('renders warning icon', () => { + expect(wrapper.find(Icon).exists()).toBe(true); + }); + + it('does not render information about locked issue', () => { + expect(findLockedBlock().exists()).toBe(false); + }); + + it('does not render information about locked and confidential issue', () => { + expect(findLockedAndConfidentialBlock().exists()).toBe(false); }); }); - describe('isLocked and isConfidential', () => { - it('should render locked and confidential issue warning information', () => { - const vm = mountComponent(IssueWarning, { + describe('when issue is locked and confidential', () => { + beforeEach(() => { + createComponent({ isLocked: true, isConfidential: true, }); + }); + + it('renders information about locked and confidential issue', () => { + expect(findLockedAndConfidentialBlock().exists()).toBe(true); + expect(findLockedAndConfidentialBlock().element).toMatchSnapshot(); + }); + + it('does not render warning icon', () => { + expect(wrapper.find(Icon).exists()).toBe(false); + }); + + it('does not render information about locked issue', () => { + expect(findLockedBlock().exists()).toBe(false); + }); - expect(vm.$el.querySelector('.icon')).toBeFalsy(); - expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual( - "This issue is confidential and locked. People without permission will never get a notification and won't be able to comment.", - ); + it('does not render information about confidential issue', () => { + expect(findConfidentialBlock().exists()).toBe(false); }); }); }); diff --git a/spec/frontend/vue_shared/components/markdown/__snapshots__/suggestion_diff_spec.js.snap b/spec/frontend/vue_shared/components/markdown/__snapshots__/suggestion_diff_spec.js.snap new file mode 100644 index 00000000000..29ac754de49 --- /dev/null +++ b/spec/frontend/vue_shared/components/markdown/__snapshots__/suggestion_diff_spec.js.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Suggestion Diff component matches snapshot 1`] = ` +<div + class="md-suggestion" +> + <suggestion-diff-header-stub + class="qa-suggestion-diff-header js-suggestion-diff-header" + helppagepath="path_to_docs" + /> + + <table + class="mb-3 md-suggestion-diff js-syntax-highlight code" + > + <tbody> + <suggestion-diff-row-stub + line="[object Object]" + /> + <suggestion-diff-row-stub + line="[object Object]" + /> + <suggestion-diff-row-stub + line="[object Object]" + /> + </tbody> + </table> +</div> +`; diff --git a/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js b/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js index 3c5e7500ba7..162ac495385 100644 --- a/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js +++ b/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js @@ -1,9 +1,9 @@ -import Vue from 'vue'; +import { shallowMount } from '@vue/test-utils'; import SuggestionDiffComponent from '~/vue_shared/components/markdown/suggestion_diff.vue'; -import { selectDiffLines } from '~/vue_shared/components/lib/utils/diff_utils'; +import SuggestionDiffHeader from '~/vue_shared/components/markdown/suggestion_diff_header.vue'; +import SuggestionDiffRow from '~/vue_shared/components/markdown/suggestion_diff_row.vue'; const MOCK_DATA = { - canApply: true, suggestion: { id: 1, diff_lines: [ @@ -42,60 +42,45 @@ const MOCK_DATA = { helpPagePath: 'path_to_docs', }; -const lines = selectDiffLines(MOCK_DATA.suggestion.diff_lines); -const newLines = lines.filter(line => line.type === 'new'); - describe('Suggestion Diff component', () => { - let vm; - - beforeEach(done => { - const Component = Vue.extend(SuggestionDiffComponent); - - vm = new Component({ - propsData: MOCK_DATA, - }).$mount(); - - Vue.nextTick(done); - }); - - describe('init', () => { - it('renders a suggestion header', () => { - expect(vm.$el.querySelector('.js-suggestion-diff-header')).not.toBeNull(); - }); - - it('renders a diff table with syntax highlighting', () => { - expect(vm.$el.querySelector('.md-suggestion-diff.js-syntax-highlight.code')).not.toBeNull(); - }); + let wrapper; - it('renders the oldLineNumber', () => { - const fromLine = vm.$el.querySelector('.old_line').innerHTML; - - expect(parseInt(fromLine, 10)).toBe(lines[0].old_line); + const createComponent = () => { + wrapper = shallowMount(SuggestionDiffComponent, { + propsData: { + ...MOCK_DATA, + }, }); + }; - it('renders the oldLineContent', () => { - const fromContent = vm.$el.querySelector('.line_content.old').innerHTML; - - expect(fromContent.includes(lines[0].text)).toBe(true); - }); + beforeEach(() => { + createComponent(); + }); - it('renders new lines', () => { - const newLinesElements = vm.$el.querySelectorAll('.line_holder.new'); + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); - newLinesElements.forEach((line, i) => { - expect(newLinesElements[i].innerHTML.includes(newLines[i].new_line)).toBe(true); - expect(newLinesElements[i].innerHTML.includes(newLines[i].text)).toBe(true); - }); - }); + it('matches snapshot', () => { + expect(wrapper.element).toMatchSnapshot(); }); - describe('applySuggestion', () => { - it('emits apply event when applySuggestion is called', () => { - const callback = () => {}; - jest.spyOn(vm, '$emit').mockImplementation(() => {}); - vm.applySuggestion(callback); + it('renders a correct amount of suggestion diff rows', () => { + expect(wrapper.findAll(SuggestionDiffRow)).toHaveLength(3); + }); - expect(vm.$emit).toHaveBeenCalledWith('apply', { suggestionId: vm.suggestion.id, callback }); - }); + it('emits apply event on sugestion diff header apply', () => { + wrapper.find(SuggestionDiffHeader).vm.$emit('apply', 'test-event'); + + expect(wrapper.emitted('apply')).toBeDefined(); + expect(wrapper.emitted('apply')).toEqual([ + [ + { + callback: 'test-event', + suggestionId: 1, + }, + ], + ]); }); }); diff --git a/spec/frontend/vue_shared/components/notes/__snapshots__/placeholder_note_spec.js.snap b/spec/frontend/vue_shared/components/notes/__snapshots__/placeholder_note_spec.js.snap new file mode 100644 index 00000000000..f3ce03796f9 --- /dev/null +++ b/spec/frontend/vue_shared/components/notes/__snapshots__/placeholder_note_spec.js.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Issue placeholder note component matches snapshot 1`] = ` +<timeline-entry-item-stub + class="note note-wrapper being-posted fade-in-half" +> + <div + class="timeline-icon" + > + <user-avatar-link-stub + imgalt="" + imgcssclasses="" + imgsize="40" + imgsrc="mock_path" + linkhref="/root" + tooltipplacement="top" + tooltiptext="" + username="" + /> + </div> + + <div + class="timeline-content discussion" + > + <div + class="note-header" + > + <div + class="note-header-info" + > + <a + href="/root" + > + <span + class="d-none d-sm-inline-block bold" + > + Root + </span> + + <span + class="note-headline-light" + > + @root + </span> + </a> + </div> + </div> + + <div + class="note-body" + > + <div + class="note-text md" + > + <p> + Foo + </p> + </div> + </div> + </div> +</timeline-entry-item-stub> +`; diff --git a/spec/frontend/vue_shared/components/notes/__snapshots__/placeholder_system_note_spec.js.snap b/spec/frontend/vue_shared/components/notes/__snapshots__/placeholder_system_note_spec.js.snap new file mode 100644 index 00000000000..10c33269107 --- /dev/null +++ b/spec/frontend/vue_shared/components/notes/__snapshots__/placeholder_system_note_spec.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Placeholder system note component matches snapshot 1`] = ` +<timeline-entry-item-stub + class="note system-note being-posted fade-in-half" +> + <div + class="timeline-content" + > + <em> + This is a placeholder + </em> + </div> +</timeline-entry-item-stub> +`; diff --git a/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js b/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js index 45f131194ca..0f30b50da0b 100644 --- a/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js +++ b/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js @@ -1,51 +1,55 @@ -import Vue from 'vue'; -import issuePlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue'; -import createStore from '~/notes/stores'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import IssuePlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue'; import { userDataMock } from '../../../notes/mock_data'; -describe('issue placeholder system note component', () => { - let store; - let vm; - - beforeEach(() => { - const Component = Vue.extend(issuePlaceholderNote); - store = createStore(); - store.dispatch('setUserData', userDataMock); - vm = new Component({ - store, - propsData: { note: { body: 'Foo' } }, - }).$mount(); - }); +const localVue = createLocalVue(); +localVue.use(Vuex); + +const getters = { + getUserData: () => userDataMock, +}; + +describe('Issue placeholder note component', () => { + let wrapper; + + const findNote = () => wrapper.find({ ref: 'note' }); + + const createComponent = (isIndividual = false) => { + wrapper = shallowMount(IssuePlaceholderNote, { + localVue, + store: new Vuex.Store({ + getters, + }), + propsData: { + note: { + body: 'Foo', + individual_note: isIndividual, + }, + }, + }); + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); + wrapper = null; }); - describe('user information', () => { - it('should render user avatar with link', () => { - expect(vm.$el.querySelector('.user-avatar-link').getAttribute('href')).toEqual( - userDataMock.path, - ); + it('matches snapshot', () => { + createComponent(); - expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual( - `${userDataMock.avatar_url}?width=40`, - ); - }); + expect(wrapper.element).toMatchSnapshot(); }); - describe('note content', () => { - it('should render note header information', () => { - expect(vm.$el.querySelector('.note-header-info a').getAttribute('href')).toEqual( - userDataMock.path, - ); + it('does not add "discussion" class to individual notes', () => { + createComponent(true); - expect( - vm.$el.querySelector('.note-header-info .note-headline-light').textContent.trim(), - ).toEqual(`@${userDataMock.username}`); - }); + expect(findNote().classes()).not.toContain('discussion'); + }); - it('should render note body', () => { - expect(vm.$el.querySelector('.note-text p').textContent.trim()).toEqual('Foo'); - }); + it('adds "discussion" class to non-individual notes', () => { + createComponent(); + + expect(findNote().classes()).toContain('discussion'); }); }); diff --git a/spec/frontend/vue_shared/components/notes/placeholder_system_note_spec.js b/spec/frontend/vue_shared/components/notes/placeholder_system_note_spec.js index 81c5cd6a057..de6ab43bc41 100644 --- a/spec/frontend/vue_shared/components/notes/placeholder_system_note_spec.js +++ b/spec/frontend/vue_shared/components/notes/placeholder_system_note_spec.js @@ -1,27 +1,25 @@ -import Vue from 'vue'; -import mountComponent from 'helpers/vue_mount_component_helper'; -import placeholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue'; +import { shallowMount } from '@vue/test-utils'; +import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue'; -describe('placeholder system note component', () => { - let PlaceholderSystemNote; - let vm; +describe('Placeholder system note component', () => { + let wrapper; - beforeEach(() => { - PlaceholderSystemNote = Vue.extend(placeholderSystemNote); - }); + const createComponent = () => { + wrapper = shallowMount(PlaceholderSystemNote, { + propsData: { + note: { body: 'This is a placeholder' }, + }, + }); + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); + wrapper = null; }); - it('should render system note placeholder with plain text', () => { - vm = mountComponent(PlaceholderSystemNote, { - note: { body: 'This is a placeholder' }, - }); + it('matches snapshot', () => { + createComponent(); - expect(vm.$el.tagName).toEqual('LI'); - expect(vm.$el.querySelector('.timeline-content em').textContent.trim()).toEqual( - 'This is a placeholder', - ); + expect(wrapper.element).toMatchSnapshot(); }); }); |