diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-06 09:10:02 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-06 09:10:02 +0000 |
commit | 51c20446a0dcf2f5f4a0254230876bd472a254e7 (patch) | |
tree | 5fc1658c6a240e49d9bc76113f33c500b45500bc /spec | |
parent | 0c4b9cacd575b3e71e41a13f042062b3adcb4caf (diff) | |
download | gitlab-ce-51c20446a0dcf2f5f4a0254230876bd472a254e7.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/profiles/emails_controller_spec.rb | 18 | ||||
-rw-r--r-- | spec/features/help_pages_spec.rb | 32 | ||||
-rw-r--r-- | spec/features/profiles/emails_spec.rb | 9 | ||||
-rw-r--r-- | spec/features/projects/snippets/create_snippet_spec.rb | 15 | ||||
-rw-r--r-- | spec/features/snippets/spam_snippets_spec.rb | 15 | ||||
-rw-r--r-- | spec/features/snippets/user_creates_snippet_spec.rb | 17 | ||||
-rw-r--r-- | spec/fixtures/sample_doc.md | 1 | ||||
-rw-r--r-- | spec/frontend/blob/utils_spec.js | 42 | ||||
-rw-r--r-- | spec/frontend/pipelines/pipelines_artifacts_spec.js | 46 | ||||
-rw-r--r-- | spec/frontend/pipelines/stage_spec.js | 156 | ||||
-rw-r--r-- | spec/frontend/releases/components/release_block_milestone_info_spec.js | 4 | ||||
-rw-r--r-- | spec/frontend/snippet/snippet_bundle_spec.js | 141 | ||||
-rw-r--r-- | spec/javascripts/pipelines/pipelines_artifacts_spec.js | 38 | ||||
-rw-r--r-- | spec/javascripts/pipelines/stage_spec.js | 136 | ||||
-rw-r--r-- | spec/models/email_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 3 | ||||
-rw-r--r-- | spec/support/shared_examples/models/email_format_shared_examples.rb | 41 |
17 files changed, 346 insertions, 370 deletions
diff --git a/spec/controllers/profiles/emails_controller_spec.rb b/spec/controllers/profiles/emails_controller_spec.rb index 7c6b1863202..91850e429a5 100644 --- a/spec/controllers/profiles/emails_controller_spec.rb +++ b/spec/controllers/profiles/emails_controller_spec.rb @@ -10,12 +10,20 @@ describe Profiles::EmailsController do end describe '#create' do - let(:email_params) { { email: "add_email@example.com" } } + context 'when email address is valid' do + let(:email_params) { { email: "add_email@example.com" } } - it 'sends an email confirmation' do - expect { post(:create, params: { email: email_params }) }.to change { ActionMailer::Base.deliveries.size } - expect(ActionMailer::Base.deliveries.last.to).to eq [email_params[:email]] - expect(ActionMailer::Base.deliveries.last.subject).to match "Confirmation instructions" + it 'sends an email confirmation' do + expect { post(:create, params: { email: email_params }) }.to change { ActionMailer::Base.deliveries.size } + end + end + + context 'when email address is invalid' do + let(:email_params) { { email: "test.@example.com" } } + + it 'does not send an email confirmation' do + expect { post(:create, params: { email: email_params }) }.not_to change { ActionMailer::Base.deliveries.size } + end end end diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb index 88a7aa51326..1ba3849fe2c 100644 --- a/spec/features/help_pages_spec.rb +++ b/spec/features/help_pages_spec.rb @@ -4,35 +4,9 @@ require 'spec_helper' describe 'Help Pages' do describe 'Get the main help page' do - shared_examples_for 'help page' do |prefix: ''| - it 'prefixes links correctly' do - expect(page).to have_selector(%(div.documentation-index > table tbody tr td a[href="#{prefix}/help/api/README.md"])) - end - end - - context 'without a trailing slash' do - before do - visit help_path - end - - it_behaves_like 'help page' - end - - context 'with a trailing slash' do - before do - visit help_path + '/' - end - - it_behaves_like 'help page' - end - - context 'with a relative installation' do - before do - stub_config_setting(relative_url_root: '/gitlab') - visit help_path - end - - it_behaves_like 'help page', prefix: '/gitlab' + before do + allow(File).to receive(:read).and_call_original + allow(File).to receive(:read).with(Rails.root.join('doc', 'README.md')).and_return(fixture_file('sample_doc.md')) end context 'quick link shortcuts', :js do diff --git a/spec/features/profiles/emails_spec.rb b/spec/features/profiles/emails_spec.rb index 4d2cd0f8b56..5dfc03d711a 100644 --- a/spec/features/profiles/emails_spec.rb +++ b/spec/features/profiles/emails_spec.rb @@ -31,6 +31,15 @@ describe 'Profile > Emails' do expect(email).to be_nil expect(page).to have_content('Email has already been taken') end + + it 'does not add an invalid email' do + fill_in('Email', with: 'test.@example.com') + click_button('Add email address') + + email = user.emails.find_by(email: email) + expect(email).to be_nil + expect(page).to have_content('Email is invalid') + end end it 'User removes email' do diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb index d883a1fc39c..794b1fdb97c 100644 --- a/spec/features/projects/snippets/create_snippet_spec.rb +++ b/spec/features/projects/snippets/create_snippet_spec.rb @@ -5,7 +5,6 @@ require 'spec_helper' shared_examples_for 'snippet editor' do before do stub_feature_flags(snippets_edit_vue: false) - stub_feature_flags(monaco_snippets: flag) end def description_field @@ -20,7 +19,7 @@ shared_examples_for 'snippet editor' do fill_in 'project_snippet_description', with: 'My Snippet **Description**' page.within('.file-editor') do - el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false) + el = find('.inputarea') el.send_keys 'Hello World!' end end @@ -145,15 +144,5 @@ describe 'Projects > Snippets > Create Snippet', :js do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :public) } - context 'when using Monaco' do - it_behaves_like "snippet editor" do - let(:flag) { true } - end - end - - context 'when using ACE' do - it_behaves_like "snippet editor" do - let(:flag) { false } - end - end + it_behaves_like "snippet editor" end diff --git a/spec/features/snippets/spam_snippets_spec.rb b/spec/features/snippets/spam_snippets_spec.rb index bdcc1cc56e7..d7b181dc678 100644 --- a/spec/features/snippets/spam_snippets_spec.rb +++ b/spec/features/snippets/spam_snippets_spec.rb @@ -13,7 +13,6 @@ shared_examples_for 'snippet editor' do stub_feature_flags(allow_possible_spam: false) stub_feature_flags(snippets_vue: false) stub_feature_flags(snippets_edit_vue: false) - stub_feature_flags(monaco_snippets: flag) stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') Gitlab::CurrentSettings.update!( @@ -35,7 +34,7 @@ shared_examples_for 'snippet editor' do find('#personal_snippet_visibility_level_20').set(true) page.within('.file-editor') do - el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false) + el = find('.inputarea') el.send_keys 'Hello World!' end end @@ -126,15 +125,5 @@ end describe 'User creates snippet', :js do let_it_be(:user) { create(:user) } - context 'when using Monaco' do - it_behaves_like "snippet editor" do - let(:flag) { true } - end - end - - context 'when using ACE' do - it_behaves_like "snippet editor" do - let(:flag) { false } - end - end + it_behaves_like "snippet editor" end diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb index 5d3a84dd7bc..c4279bdb212 100644 --- a/spec/features/snippets/user_creates_snippet_spec.rb +++ b/spec/features/snippets/user_creates_snippet_spec.rb @@ -6,7 +6,6 @@ shared_examples_for 'snippet editor' do before do stub_feature_flags(snippets_vue: false) stub_feature_flags(snippets_edit_vue: false) - stub_feature_flags(monaco_snippets: flag) sign_in(user) visit new_snippet_path end @@ -23,7 +22,7 @@ shared_examples_for 'snippet editor' do fill_in 'personal_snippet_description', with: 'My Snippet **Description**' page.within('.file-editor') do - el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false) + el = find('.inputarea') el.send_keys 'Hello World!' end end @@ -136,7 +135,7 @@ shared_examples_for 'snippet editor' do fill_in 'personal_snippet_title', with: 'My Snippet Title' page.within('.file-editor') do find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name' - el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false) + el = find('.inputarea') el.send_keys 'Hello World!' end @@ -154,15 +153,5 @@ describe 'User creates snippet', :js do let_it_be(:user) { create(:user) } - context 'when using Monaco' do - it_behaves_like "snippet editor" do - let(:flag) { true } - end - end - - context 'when using ACE' do - it_behaves_like "snippet editor" do - let(:flag) { false } - end - end + it_behaves_like "snippet editor" end diff --git a/spec/fixtures/sample_doc.md b/spec/fixtures/sample_doc.md new file mode 100644 index 00000000000..84080dd1089 --- /dev/null +++ b/spec/fixtures/sample_doc.md @@ -0,0 +1 @@ +[GitLab API](api/README.md) diff --git a/spec/frontend/blob/utils_spec.js b/spec/frontend/blob/utils_spec.js index 39a73aae444..119ed2dfe7a 100644 --- a/spec/frontend/blob/utils_spec.js +++ b/spec/frontend/blob/utils_spec.js @@ -8,11 +8,6 @@ jest.mock('~/editor/editor_lite', () => { }); }); -const mockCreateAceInstance = jest.fn(); -global.ace = { - edit: mockCreateAceInstance, -}; - describe('Blob utilities', () => { beforeEach(() => { Editor.mockClear(); @@ -29,21 +24,6 @@ describe('Blob utilities', () => { }); describe('Monaco editor', () => { - let origProp; - - beforeEach(() => { - origProp = window.gon; - window.gon = { - features: { - monacoSnippets: true, - }, - }; - }); - - afterEach(() => { - window.gon = origProp; - }); - it('initializes the Editor Lite', () => { utils.initEditorLite({ el: editorEl }); expect(Editor).toHaveBeenCalled(); @@ -69,27 +49,5 @@ describe('Blob utilities', () => { ]); }); }); - describe('ACE editor', () => { - let origProp; - - beforeEach(() => { - origProp = window.gon; - window.gon = { - features: { - monacoSnippets: false, - }, - }; - }); - - afterEach(() => { - window.gon = origProp; - }); - - it('does not initialize the Editor Lite', () => { - utils.initEditorLite({ el: editorEl }); - expect(Editor).not.toHaveBeenCalled(); - expect(mockCreateAceInstance).toHaveBeenCalledWith(editorEl); - }); - }); }); }); diff --git a/spec/frontend/pipelines/pipelines_artifacts_spec.js b/spec/frontend/pipelines/pipelines_artifacts_spec.js new file mode 100644 index 00000000000..a93cc8a62ab --- /dev/null +++ b/spec/frontend/pipelines/pipelines_artifacts_spec.js @@ -0,0 +1,46 @@ +import { shallowMount } from '@vue/test-utils'; +import PipelineArtifacts from '~/pipelines/components/pipelines_artifacts.vue'; +import { GlLink } from '@gitlab/ui'; + +describe('Pipelines Artifacts dropdown', () => { + let wrapper; + + const createComponent = () => { + wrapper = shallowMount(PipelineArtifacts, { + propsData: { + artifacts: [ + { + name: 'artifact', + path: '/download/path', + }, + { + name: 'artifact two', + path: '/download/path-two', + }, + ], + }, + }); + }; + + const findGlLink = () => wrapper.find(GlLink); + const findAllGlLinks = () => wrapper.find('.dropdown-menu').findAll(GlLink); + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('should render a dropdown with all the provided artifacts', () => { + expect(findAllGlLinks()).toHaveLength(2); + }); + + it('should render a link with the provided path', () => { + expect(findGlLink().attributes('href')).toEqual('/download/path'); + + expect(findGlLink().text()).toContain('artifact'); + }); +}); diff --git a/spec/frontend/pipelines/stage_spec.js b/spec/frontend/pipelines/stage_spec.js new file mode 100644 index 00000000000..b020aaedd06 --- /dev/null +++ b/spec/frontend/pipelines/stage_spec.js @@ -0,0 +1,156 @@ +import { mount } from '@vue/test-utils'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import StageComponent from '~/pipelines/components/stage.vue'; +import eventHub from '~/pipelines/event_hub'; +import { stageReply } from './mock_data'; +import waitForPromises from 'helpers/wait_for_promises'; + +describe('Pipelines stage component', () => { + let wrapper; + let mock; + + const defaultProps = { + stage: { + status: { + group: 'success', + icon: 'status_success', + title: 'success', + }, + dropdown_path: 'path.json', + }, + updateDropdown: false, + }; + + const createComponent = (props = {}) => { + wrapper = mount(StageComponent, { + propsData: { + ...defaultProps, + ...props, + }, + }); + }; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + + mock.restore(); + }); + + describe('default', () => { + beforeEach(() => { + createComponent(); + }); + + it('should render a dropdown with the status icon', () => { + expect(wrapper.attributes('class')).toEqual('dropdown'); + expect(wrapper.find('svg').exists()).toBe(true); + expect(wrapper.find('button').attributes('data-toggle')).toEqual('dropdown'); + }); + }); + + describe('with successful request', () => { + beforeEach(() => { + mock.onGet('path.json').reply(200, stageReply); + createComponent(); + }); + + it('should render the received data and emit `clickedDropdown` event', () => { + jest.spyOn(eventHub, '$emit'); + wrapper.find('button').trigger('click'); + + return waitForPromises().then(() => { + expect(wrapper.find('.js-builds-dropdown-container ul').text()).toContain( + stageReply.latest_statuses[0].name, + ); + + expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); + }); + }); + }); + + describe('when request fails', () => { + beforeEach(() => { + mock.onGet('path.json').reply(500); + createComponent(); + }); + + it('should close the dropdown', () => { + wrapper.setMethods({ + closeDropdown: jest.fn(), + isDropdownOpen: jest.fn().mockReturnValue(false), + }); + + wrapper.find('button').trigger('click'); + + return waitForPromises().then(() => { + expect(wrapper.vm.closeDropdown).toHaveBeenCalled(); + }); + }); + }); + + describe('update endpoint correctly', () => { + beforeEach(() => { + const copyStage = Object.assign({}, stageReply); + copyStage.latest_statuses[0].name = 'this is the updated content'; + mock.onGet('bar.json').reply(200, copyStage); + createComponent({ + stage: { + status: { + group: 'running', + icon: 'status_running', + title: 'running', + }, + dropdown_path: 'bar.json', + }, + }); + }); + + it('should update the stage to request the new endpoint provided', () => { + return wrapper.vm + .$nextTick() + .then(() => { + wrapper.find('button').trigger('click'); + return waitForPromises(); + }) + .then(() => { + expect(wrapper.find('.js-builds-dropdown-container ul').text()).toContain( + 'this is the updated content', + ); + }); + }); + }); + + describe('pipelineActionRequestComplete', () => { + beforeEach(() => { + mock.onGet('path.json').reply(200, stageReply); + + mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200); + + createComponent({ type: 'PIPELINES_TABLE' }); + }); + + describe('within pipeline table', () => { + it('emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered', () => { + jest.spyOn(eventHub, '$emit'); + + wrapper.find('button').trigger('click'); + + return waitForPromises() + .then(() => { + wrapper.find('.js-ci-action').trigger('click'); + + return waitForPromises(); + }) + .then(() => { + expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); + }); + }); + }); + }); +}); diff --git a/spec/frontend/releases/components/release_block_milestone_info_spec.js b/spec/frontend/releases/components/release_block_milestone_info_spec.js index 0b65b6cab96..0e79c45b337 100644 --- a/spec/frontend/releases/components/release_block_milestone_info_spec.js +++ b/spec/frontend/releases/components/release_block_milestone_info_spec.js @@ -1,5 +1,5 @@ import { mount } from '@vue/test-utils'; -import { GlProgressBar, GlLink, GlBadge, GlDeprecatedButton } from '@gitlab/ui'; +import { GlProgressBar, GlLink, GlBadge, GlButton } from '@gitlab/ui'; import { trimText } from 'helpers/text_helper'; import ReleaseBlockMilestoneInfo from '~/releases/components/release_block_milestone_info.vue'; import { milestones as originalMilestones } from '../mock_data'; @@ -106,7 +106,7 @@ describe('Release block milestone info', () => { const clickShowMoreFewerButton = () => { milestoneListContainer() - .find(GlDeprecatedButton) + .find(GlButton) .trigger('click'); return wrapper.vm.$nextTick(); diff --git a/spec/frontend/snippet/snippet_bundle_spec.js b/spec/frontend/snippet/snippet_bundle_spec.js index 12d20d5cd85..38d05243c65 100644 --- a/spec/frontend/snippet/snippet_bundle_spec.js +++ b/spec/frontend/snippet/snippet_bundle_spec.js @@ -1,94 +1,85 @@ import Editor from '~/editor/editor_lite'; -import { initEditor } from '~/snippet/snippet_bundle'; +import initEditor from '~/snippet/snippet_bundle'; import { setHTMLFixture } from 'helpers/fixtures'; jest.mock('~/editor/editor_lite', () => jest.fn()); describe('Snippet editor', () => { - describe('Monaco editor for Snippets', () => { - let oldGon; - let editorEl; - let contentEl; - let fileNameEl; - let form; - - const mockName = 'foo.bar'; - const mockContent = 'Foo Bar'; - const updatedMockContent = 'New Foo Bar'; - - const mockEditor = { - createInstance: jest.fn(), - updateModelLanguage: jest.fn(), - getValue: jest.fn().mockReturnValueOnce(updatedMockContent), - }; - Editor.mockImplementation(() => mockEditor); - - function setUpFixture(name, content) { - setHTMLFixture(` - <div class="snippet-form-holder"> - <form> - <input class="js-snippet-file-name" type="text" value="${name}"> - <input class="snippet-file-content" type="hidden" value="${content}"> - <pre id="editor"></pre> - </form> - </div> - `); - } - - function bootstrap(name = '', content = '') { - setUpFixture(name, content); - editorEl = document.getElementById('editor'); - contentEl = document.querySelector('.snippet-file-content'); - fileNameEl = document.querySelector('.js-snippet-file-name'); - form = document.querySelector('.snippet-form-holder form'); - - initEditor(); - } - - function createEvent(name) { - return new Event(name, { - view: window, - bubbles: true, - cancelable: true, - }); - } - - beforeEach(() => { - oldGon = window.gon; - window.gon = { features: { monacoSnippets: true } }; - bootstrap(mockName, mockContent); + let editorEl; + let contentEl; + let fileNameEl; + let form; + + const mockName = 'foo.bar'; + const mockContent = 'Foo Bar'; + const updatedMockContent = 'New Foo Bar'; + + const mockEditor = { + createInstance: jest.fn(), + updateModelLanguage: jest.fn(), + getValue: jest.fn().mockReturnValueOnce(updatedMockContent), + }; + Editor.mockImplementation(() => mockEditor); + + function setUpFixture(name, content) { + setHTMLFixture(` + <div class="snippet-form-holder"> + <form> + <input class="js-snippet-file-name" type="text" value="${name}"> + <input class="snippet-file-content" type="hidden" value="${content}"> + <pre id="editor"></pre> + </form> + </div> + `); + } + + function bootstrap(name = '', content = '') { + setUpFixture(name, content); + editorEl = document.getElementById('editor'); + contentEl = document.querySelector('.snippet-file-content'); + fileNameEl = document.querySelector('.js-snippet-file-name'); + form = document.querySelector('.snippet-form-holder form'); + + initEditor(); + } + + function createEvent(name) { + return new Event(name, { + view: window, + bubbles: true, + cancelable: true, }); + } - afterEach(() => { - window.gon = oldGon; - }); + beforeEach(() => { + bootstrap(mockName, mockContent); + }); - it('correctly initializes Editor', () => { - expect(mockEditor.createInstance).toHaveBeenCalledWith({ - el: editorEl, - blobPath: mockName, - blobContent: mockContent, - }); + it('correctly initializes Editor', () => { + expect(mockEditor.createInstance).toHaveBeenCalledWith({ + el: editorEl, + blobPath: mockName, + blobContent: mockContent, }); + }); - it('listens to file name changes and updates syntax highlighting of code', () => { - expect(mockEditor.updateModelLanguage).not.toHaveBeenCalled(); + it('listens to file name changes and updates syntax highlighting of code', () => { + expect(mockEditor.updateModelLanguage).not.toHaveBeenCalled(); - const event = createEvent('change'); + const event = createEvent('change'); - fileNameEl.value = updatedMockContent; - fileNameEl.dispatchEvent(event); + fileNameEl.value = updatedMockContent; + fileNameEl.dispatchEvent(event); - expect(mockEditor.updateModelLanguage).toHaveBeenCalledWith(updatedMockContent); - }); + expect(mockEditor.updateModelLanguage).toHaveBeenCalledWith(updatedMockContent); + }); - it('listens to form submit event and populates the hidden field with most recent version of the content', () => { - expect(contentEl.value).toBe(mockContent); + it('listens to form submit event and populates the hidden field with most recent version of the content', () => { + expect(contentEl.value).toBe(mockContent); - const event = createEvent('submit'); + const event = createEvent('submit'); - form.dispatchEvent(event); - expect(contentEl.value).toBe(updatedMockContent); - }); + form.dispatchEvent(event); + expect(contentEl.value).toBe(updatedMockContent); }); }); diff --git a/spec/javascripts/pipelines/pipelines_artifacts_spec.js b/spec/javascripts/pipelines/pipelines_artifacts_spec.js deleted file mode 100644 index 7705d5a19bf..00000000000 --- a/spec/javascripts/pipelines/pipelines_artifacts_spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import Vue from 'vue'; -import artifactsComp from '~/pipelines/components/pipelines_artifacts.vue'; - -describe('Pipelines Artifacts dropdown', () => { - let component; - let artifacts; - - beforeEach(() => { - const ArtifactsComponent = Vue.extend(artifactsComp); - - artifacts = [ - { - name: 'artifact', - path: '/download/path', - }, - ]; - - component = new ArtifactsComponent({ - propsData: { - artifacts, - }, - }).$mount(); - }); - - it('should render a dropdown with the provided artifacts', () => { - expect(component.$el.querySelectorAll('.dropdown-menu li').length).toEqual(artifacts.length); - }); - - it('should render a link with the provided path', () => { - expect(component.$el.querySelector('.dropdown-menu li a').getAttribute('href')).toEqual( - artifacts[0].path, - ); - - expect(component.$el.querySelector('.dropdown-menu li a').textContent).toContain( - artifacts[0].name, - ); - }); -}); diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js deleted file mode 100644 index b99688ec371..00000000000 --- a/spec/javascripts/pipelines/stage_spec.js +++ /dev/null @@ -1,136 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import axios from '~/lib/utils/axios_utils'; -import stage from '~/pipelines/components/stage.vue'; -import eventHub from '~/pipelines/event_hub'; -import { stageReply } from './mock_data'; - -describe('Pipelines stage component', () => { - let StageComponent; - let component; - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - - StageComponent = Vue.extend(stage); - - component = mountComponent(StageComponent, { - stage: { - status: { - group: 'success', - icon: 'status_success', - title: 'success', - }, - dropdown_path: 'path.json', - }, - updateDropdown: false, - }); - }); - - afterEach(() => { - component.$destroy(); - mock.restore(); - }); - - it('should render a dropdown with the status icon', () => { - expect(component.$el.getAttribute('class')).toEqual('dropdown'); - expect(component.$el.querySelector('svg')).toBeDefined(); - expect(component.$el.querySelector('button').getAttribute('data-toggle')).toEqual('dropdown'); - }); - - describe('with successful request', () => { - beforeEach(() => { - mock.onGet('path.json').reply(200, stageReply); - }); - - it('should render the received data and emit `clickedDropdown` event', done => { - spyOn(eventHub, '$emit'); - component.$el.querySelector('button').click(); - - setTimeout(() => { - expect( - component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(), - ).toContain(stageReply.latest_statuses[0].name); - - expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); - done(); - }, 0); - }); - }); - - describe('when request fails', () => { - beforeEach(() => { - mock.onGet('path.json').reply(500); - }); - - it('should close the dropdown', () => { - component.$el.click(); - - setTimeout(() => { - expect(component.$el.classList.contains('open')).toEqual(false); - }, 0); - }); - }); - - describe('update endpoint correctly', () => { - beforeEach(() => { - const copyStage = Object.assign({}, stageReply); - copyStage.latest_statuses[0].name = 'this is the updated content'; - mock.onGet('bar.json').reply(200, copyStage); - }); - - it('should update the stage to request the new endpoint provided', done => { - component.stage = { - status: { - group: 'running', - icon: 'status_running', - title: 'running', - }, - dropdown_path: 'bar.json', - }; - - Vue.nextTick(() => { - component.$el.querySelector('button').click(); - - setTimeout(() => { - expect( - component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(), - ).toContain('this is the updated content'); - done(); - }); - }); - }); - }); - - describe('pipelineActionRequestComplete', () => { - beforeEach(() => { - mock.onGet('path.json').reply(200, stageReply); - - mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200); - }); - - describe('within pipeline table', () => { - it('emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered', done => { - spyOn(eventHub, '$emit'); - - component.type = 'PIPELINES_TABLE'; - component.$el.querySelector('button').click(); - - setTimeout(() => { - component.$el.querySelector('.js-ci-action').click(); - setTimeout(() => { - component - .$nextTick() - .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); - }) - .then(done) - .catch(done.fail); - }, 0); - }, 0); - }); - }); - }); -}); diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb index aa3a60b867a..dabf2bb80b5 100644 --- a/spec/models/email_spec.rb +++ b/spec/models/email_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe Email do describe 'validations' do - it_behaves_like 'an object with email-formated attributes', :email do + it_behaves_like 'an object with RFC3696 compliant email-formated attributes', :email do subject { build(:email) } end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 7649b09aa8e..83f274f1894 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -296,7 +296,7 @@ describe User, :do_not_mock_admin_mode do subject { build(:user) } end - it_behaves_like 'an object with email-formated attributes', :public_email, :notification_email do + it_behaves_like 'an object with RFC3696 compliant email-formated attributes', :public_email, :notification_email do subject { build(:user).tap { |user| user.emails << build(:email, email: email_value) } } end @@ -916,7 +916,6 @@ describe User, :do_not_mock_admin_mode do user.tap { |u| u.update!(email: new_email) }.reload end.to change(user, :unconfirmed_email).to(new_email) end - it 'does not change :notification_email' do expect do user.tap { |u| u.update!(email: new_email) }.reload diff --git a/spec/support/shared_examples/models/email_format_shared_examples.rb b/spec/support/shared_examples/models/email_format_shared_examples.rb index 6797836e383..a8115e440a4 100644 --- a/spec/support/shared_examples/models/email_format_shared_examples.rb +++ b/spec/support/shared_examples/models/email_format_shared_examples.rb @@ -44,3 +44,44 @@ RSpec.shared_examples 'an object with email-formated attributes' do |*attributes end end end + +RSpec.shared_examples 'an object with RFC3696 compliant email-formated attributes' do |*attributes| + attributes.each do |attribute| + describe "specifically its :#{attribute} attribute" do + %w[ + info@example.com + info+test@example.com + o'reilly@example.com + ].each do |valid_email| + context "with a value of '#{valid_email}'" do + let(:email_value) { valid_email } + + it 'is valid' do + subject.send("#{attribute}=", valid_email) + + expect(subject).to be_valid + end + end + end + + %w[ + foobar + test@test@example.com + test.test.@example.com + .test.test@example.com + mailto:test@example.com + lol!'+=?><#$%^&*()@gmail.com + ].each do |invalid_email| + context "with a value of '#{invalid_email}'" do + let(:email_value) { invalid_email } + + it 'is invalid' do + subject.send("#{attribute}=", invalid_email) + + expect(subject).to be_invalid + end + end + end + end + end +end |