summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-06 09:10:02 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-06 09:10:02 +0000
commit51c20446a0dcf2f5f4a0254230876bd472a254e7 (patch)
tree5fc1658c6a240e49d9bc76113f33c500b45500bc /spec
parent0c4b9cacd575b3e71e41a13f042062b3adcb4caf (diff)
downloadgitlab-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.rb18
-rw-r--r--spec/features/help_pages_spec.rb32
-rw-r--r--spec/features/profiles/emails_spec.rb9
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb15
-rw-r--r--spec/features/snippets/spam_snippets_spec.rb15
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb17
-rw-r--r--spec/fixtures/sample_doc.md1
-rw-r--r--spec/frontend/blob/utils_spec.js42
-rw-r--r--spec/frontend/pipelines/pipelines_artifacts_spec.js46
-rw-r--r--spec/frontend/pipelines/stage_spec.js156
-rw-r--r--spec/frontend/releases/components/release_block_milestone_info_spec.js4
-rw-r--r--spec/frontend/snippet/snippet_bundle_spec.js141
-rw-r--r--spec/javascripts/pipelines/pipelines_artifacts_spec.js38
-rw-r--r--spec/javascripts/pipelines/stage_spec.js136
-rw-r--r--spec/models/email_spec.rb2
-rw-r--r--spec/models/user_spec.rb3
-rw-r--r--spec/support/shared_examples/models/email_format_shared_examples.rb41
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