summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/show/index.js8
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js94
-rw-r--r--app/assets/stylesheets/framework/buttons.scss4
-rw-r--r--app/assets/stylesheets/framework/forms.scss2
-rw-r--r--app/assets/stylesheets/pages/settings.scss14
-rw-r--r--app/helpers/mirror_helper.rb5
-rw-r--r--app/serializers/project_mirror_serializer.rb3
-rw-r--r--app/views/projects/mirrors/_instructions.html.haml12
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml63
-rw-r--r--app/views/projects/mirrors/_mirror_repos_form.html.haml18
-rw-r--r--app/views/projects/mirrors/_push.html.haml50
-rw-r--r--app/views/projects/mirrors/_show.html.haml4
-rw-r--r--app/views/shared/_remote_mirror_update_button.html.haml19
-rw-r--r--locale/gitlab.pot58
-rw-r--r--spec/features/projects/remote_mirror_spec.rb10
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb5
-rw-r--r--spec/serializers/project_mirror_serializer_spec.rb7
17 files changed, 295 insertions, 81 deletions
diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/index.js b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
index ffc84dc106b..78cf5406e43 100644
--- a/app/assets/javascripts/pages/projects/settings/repository/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
@@ -1,3 +1,9 @@
import initForm from '../form';
+import MirrorRepos from './mirror_repos';
-document.addEventListener('DOMContentLoaded', initForm);
+document.addEventListener('DOMContentLoaded', () => {
+ initForm();
+
+ const mirrorReposContainer = document.querySelector('.js-mirror-settings');
+ if (mirrorReposContainer) new MirrorRepos(mirrorReposContainer).init();
+});
diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js b/app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js
new file mode 100644
index 00000000000..76756f76642
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js
@@ -0,0 +1,94 @@
+import $ from 'jquery';
+import _ from 'underscore';
+import { __ } from '~/locale';
+import Flash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+
+export default class MirrorRepos {
+ constructor(container) {
+ this.$container = $(container);
+ this.$form = $('.js-mirror-form', this.$container);
+ this.$urlInput = $('.js-mirror-url', this.$form);
+ this.$protectedBranchesInput = $('.js-mirror-protected', this.$form);
+ this.$table = $('.js-mirrors-table-body', this.$container);
+ this.mirrorEndpoint = this.$form.data('projectMirrorEndpoint');
+ }
+
+ init() {
+ this.initMirrorPush();
+ this.registerUpdateListeners();
+ }
+
+ initMirrorPush() {
+ this.$passwordGroup = $('.js-password-group', this.$container);
+ this.$password = $('.js-password', this.$passwordGroup);
+ this.$authMethod = $('.js-auth-method', this.$form);
+
+ this.$authMethod.on('change', () => this.togglePassword());
+ this.$password.on('input.updateUrl', () => this.debouncedUpdateUrl());
+ }
+
+ updateUrl() {
+ let val = this.$urlInput.val();
+
+ if (this.$password) {
+ const password = this.$password.val();
+ if (password) val = val.replace('@', `:${password}@`);
+ }
+
+ $('.js-mirror-url-hidden', this.$form).val(val);
+ }
+
+ updateProtectedBranches() {
+ const val = this.$protectedBranchesInput.get(0).checked
+ ? this.$protectedBranchesInput.val()
+ : '0';
+ $('.js-mirror-protected-hidden', this.$form).val(val);
+ }
+
+ registerUpdateListeners() {
+ this.debouncedUpdateUrl = _.debounce(() => this.updateUrl(), 200);
+ this.$urlInput.on('input', () => this.debouncedUpdateUrl());
+ this.$protectedBranchesInput.on('change', () => this.updateProtectedBranches());
+ this.$table.on('click', '.js-delete-mirror', this.deleteMirror.bind(this));
+ }
+
+ togglePassword() {
+ const isPassword = this.$authMethod.val() === 'password';
+
+ if (!isPassword) {
+ this.$password.val('');
+ this.updateUrl();
+ }
+ this.$passwordGroup.collapse(isPassword ? 'show' : 'hide');
+ }
+
+ deleteMirror(event, existingPayload) {
+ const $target = $(event.currentTarget);
+ let payload = existingPayload;
+
+ if (!payload) {
+ payload = {
+ project: {
+ remote_mirrors_attributes: {
+ id: $target.data('mirrorId'),
+ enabled: 0,
+ },
+ },
+ };
+ }
+
+ return axios
+ .put(this.mirrorEndpoint, payload)
+ .then(() => this.removeRow($target))
+ .catch(() => Flash(__('Failed to remove mirror.')));
+ }
+
+ /* eslint-disable class-methods-use-this */
+ removeRow($target) {
+ const row = $target.closest('tr');
+ $('.js-delete-mirror', row).tooltip('hide');
+ row.remove();
+ }
+ /* eslint-enable class-methods-use-this */
+}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 646cedd79ed..a842ff92f8e 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -242,6 +242,10 @@
&:not(:last-child) {
margin-right: 5px;
}
+
+ &.hide {
+ display: none;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index d7149d93622..437fcff5c62 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -201,7 +201,7 @@ label {
}
.gl-show-field-errors {
- .form-control {
+ .form-control:not(textarea) {
height: 34px;
}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 839ac5ba59b..fb03970f64f 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -301,3 +301,17 @@
margin-bottom: 0;
}
}
+
+.mirror-error-badge {
+ background-color: $error-bg;
+ border-radius: $border-radius-default;
+ color: $white-light;
+}
+
+.push-pull-table {
+ margin-top: 1em;
+
+ .mirror-action-buttons {
+ padding-right: 0;
+ }
+}
diff --git a/app/helpers/mirror_helper.rb b/app/helpers/mirror_helper.rb
new file mode 100644
index 00000000000..93ed22513ac
--- /dev/null
+++ b/app/helpers/mirror_helper.rb
@@ -0,0 +1,5 @@
+module MirrorHelper
+ def mirrors_form_data_attributes
+ { project_mirror_endpoint: project_mirror_path(@project) }
+ end
+end
diff --git a/app/serializers/project_mirror_serializer.rb b/app/serializers/project_mirror_serializer.rb
new file mode 100644
index 00000000000..7e9ba592252
--- /dev/null
+++ b/app/serializers/project_mirror_serializer.rb
@@ -0,0 +1,3 @@
+class ProjectMirrorSerializer < BaseSerializer
+ entity ProjectMirrorEntity
+end
diff --git a/app/views/projects/mirrors/_instructions.html.haml b/app/views/projects/mirrors/_instructions.html.haml
index 64f0fde30cf..bc422543c88 100644
--- a/app/views/projects/mirrors/_instructions.html.haml
+++ b/app/views/projects/mirrors/_instructions.html.haml
@@ -1,10 +1,10 @@
.account-well.prepend-top-default.append-bottom-default
%ul
%li
- The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> or <code>git://</code>.
+ = _('The repository must be accessible over <code>http://</code>,
+ <code>https://</code>, <code>ssh://</code> and <code>git://</code>.').html_safe
+ %li= _('The update action will time out after 10 minutes. For big repositories, use a clone/push combination.')
+ %li= _('The Git LFS objects will <strong>not</strong> be synced.').html_safe
%li
- Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.
- %li
- The update action will time out after 10 minutes. For big repositories, use a clone/push combination.
- %li
- The Git LFS objects will <strong>not</strong> be synced.
+ = _('This user will be the author of all events in the activity feed that are the result of an update,
+ like new branches being created or new commits being pushed to existing branches.')
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
new file mode 100644
index 00000000000..53387b3a50c
--- /dev/null
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -0,0 +1,63 @@
+- expanded = Rails.env.test?
+- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
+
+%section.settings.project-mirror-settings.js-mirror-settings.no-animate{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4= _('Mirroring repositories')
+ %button.btn.js-settings-toggle
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _('Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically.')
+ = link_to _('Read more'), help_page_path('workflow/repository_mirroring'), target: '_blank'
+
+ .settings-content
+ = form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'false', data: mirrors_form_data_attributes } do |f|
+ .panel.panel-default
+ .panel-heading
+ %h3.panel-title= _('Mirror a repository')
+ .panel-body
+ %div= form_errors(@project)
+
+ .form-group.has-feedback
+ = label_tag :url, _('Git repository URL'), class: 'label-light'
+ = text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+"
+
+ = render 'projects/mirrors/instructions'
+
+ = render 'projects/mirrors/mirror_repos_form', f: f
+
+ .form-check.append-bottom-10
+ = check_box_tag :only_protected_branches, '1', false, class: 'js-mirror-protected form-check-input'
+ = label_tag :only_protected_branches, _('Only mirror protected branches'), class: 'form-check-label'
+ = link_to icon('question-circle'), help_page_path('user/project/protected_branches')
+
+ .panel-footer
+ = f.submit _('Mirror repository'), class: 'btn btn-create', name: :update_remote_mirror
+
+ .panel.panel-default
+ .table-responsive
+ %table.table.push-pull-table
+ %thead
+ %tr
+ %th
+ = _('Mirrored repositories')
+ = render_if_exists 'projects/mirrors/mirrored_repositories_count'
+ %th= _('Direction')
+ %th= _('Last update')
+ %th
+ %th
+ %tbody.js-mirrors-table-body
+ = render_if_exists 'projects/mirrors/table_pull_row'
+ - @project.remote_mirrors.each_with_index do |mirror, index|
+ - if mirror.enabled
+ %tr
+ %td= mirror.safe_url
+ %td= _('Push')
+ %td= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
+ %td
+ - if mirror.last_error.present?
+ .badge.mirror-error-badge{ data: { toggle: 'tooltip', html: 'true' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
+ %td.mirror-action-buttons
+ .btn-group.mirror-actions-group.pull-right{ role: 'group' }
+ = render 'shared/remote_mirror_update_button', remote_mirror: mirror
+ %button.js-delete-mirror.btn.btn-danger{ type: 'button', data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' }, title: _('Remove') }= icon('trash-o')
diff --git a/app/views/projects/mirrors/_mirror_repos_form.html.haml b/app/views/projects/mirrors/_mirror_repos_form.html.haml
new file mode 100644
index 00000000000..7927e459123
--- /dev/null
+++ b/app/views/projects/mirrors/_mirror_repos_form.html.haml
@@ -0,0 +1,18 @@
+- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
+
+.form-group
+ = label_tag :mirror_direction, _('Mirror direction'), class: 'label-light'
+ = select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control js-mirror-direction', disabled: true
+
+= f.fields_for :remote_mirrors, @remote_mirror do |rm_f|
+ = rm_f.hidden_field :enabled, value: '1'
+ = rm_f.hidden_field :url, class: 'js-mirror-url-hidden', required: true, pattern: "(#{protocols}):\/\/.+"
+ = rm_f.hidden_field :only_protected_branches, class: 'js-mirror-protected-hidden'
+
+.form-group
+ = label_tag :auth_method, _('Authentication method'), class: 'label-bold'
+ = select_tag :auth_method, options_for_select([[_('None'), 'none'], [_('Password'), 'password']], 'none'), { class: "form-control js-auth-method", disabled: true }
+
+.form-group.js-password-group.collapse
+ = label_tag :password, _('Password'), class: 'label-bold'
+ = text_field_tag :password, '', class: 'form-control js-password'
diff --git a/app/views/projects/mirrors/_push.html.haml b/app/views/projects/mirrors/_push.html.haml
deleted file mode 100644
index 08375e09816..00000000000
--- a/app/views/projects/mirrors/_push.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-- expanded = Rails.env.test?
-%section.settings.no-animate#js-push-remote-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- Push to a remote repository
- %button.btn.js-settings-toggle
- = expanded ? 'Collapse' : 'Expand'
- %p
- Set up the remote repository that you want to update with the content of the current repository
- every time someone pushes to it.
- = link_to 'Read more', help_page_path('workflow/repository_mirroring', anchor: 'pushing-to-a-remote-repository'), target: '_blank'
- .settings-content
- = form_for @project, url: project_mirror_path(@project) do |f|
- %div
- = form_errors(@project)
- = render "shared/remote_mirror_update_button", remote_mirror: @remote_mirror
- - if @remote_mirror.last_error.present?
- .panel.panel-danger
- .panel-heading
- - if @remote_mirror.last_update_at
- The remote repository failed to update #{time_ago_with_tooltip(@remote_mirror.last_update_at)}.
- - else
- The remote repository failed to update.
-
- - if @remote_mirror.last_successful_update_at
- Last successful update #{time_ago_with_tooltip(@remote_mirror.last_successful_update_at)}.
- .panel-body
- %pre
- :preserve
- #{h(@remote_mirror.last_error.strip)}
- = f.fields_for :remote_mirrors, @remote_mirror do |rm_form|
- .form-group
- = rm_form.check_box :enabled, class: "float-left"
- .prepend-left-20
- = rm_form.label :enabled, "Remote mirror repository", class: "label-bold append-bottom-0"
- %p.light.append-bottom-0
- Automatically update the remote mirror's branches, tags, and commits from this repository every time someone pushes to it.
- .form-group.has-feedback
- = rm_form.label :url, "Git repository URL", class: "label-bold"
- = rm_form.text_field :url, class: "form-control", placeholder: 'https://username:password@gitlab.company.com/group/project.git'
-
- = render "projects/mirrors/instructions"
-
- .form-group
- = rm_form.check_box :only_protected_branches, class: 'float-left'
- .prepend-left-20
- = rm_form.label :only_protected_branches, class: 'label-bold'
- = link_to icon('question-circle'), help_page_path('user/project/protected_branches')
-
- = f.submit 'Save changes', class: 'btn btn-create', name: 'update_remote_mirror'
diff --git a/app/views/projects/mirrors/_show.html.haml b/app/views/projects/mirrors/_show.html.haml
index de77701a373..8318d5898a1 100644
--- a/app/views/projects/mirrors/_show.html.haml
+++ b/app/views/projects/mirrors/_show.html.haml
@@ -1,3 +1 @@
-- if can?(current_user, :admin_remote_mirror, @project)
- = render 'projects/mirrors/push'
-
+= render 'projects/mirrors/mirror_repos'
diff --git a/app/views/shared/_remote_mirror_update_button.html.haml b/app/views/shared/_remote_mirror_update_button.html.haml
index 34de1c0695f..f32cff18fa8 100644
--- a/app/views/shared/_remote_mirror_update_button.html.haml
+++ b/app/views/shared/_remote_mirror_update_button.html.haml
@@ -1,13 +1,6 @@
-- if @project.has_remote_mirror?
- .append-bottom-default
- - if remote_mirror.update_in_progress?
- %span.btn.disabled
- = icon("refresh spin")
- Updating&hellip;
- - else
- = link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn" do
- = icon("refresh")
- Update Now
- - if @remote_mirror.last_successful_update_at
- %p.inline.prepend-left-10
- Successfully updated #{time_ago_with_tooltip(@remote_mirror.last_successful_update_at)}.
+- if remote_mirror.update_in_progress?
+ %button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body' }, title: _('Updating') }
+ = icon("refresh spin")
+- else
+ = link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn", data: { toggle: 'tooltip', container: 'body' }, title: _('Update now') do
+ = icon("refresh")
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a414f0a90cc..9b05dabdade 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -634,7 +634,7 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
-msgid "Authentication log"
+msgid "Authentication method"
msgstr ""
msgid "Author"
@@ -2169,6 +2169,9 @@ msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2388,6 +2391,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2511,6 +2517,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -3001,6 +3010,9 @@ msgstr ""
msgid "Inline"
msgstr ""
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3423,6 +3435,21 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -3480,7 +3507,7 @@ msgstr ""
msgid "Nav|Sign out and sign in with a different account"
msgstr ""
-msgid "Network"
+msgid "Never"
msgstr ""
msgid "New"
@@ -3740,6 +3767,9 @@ msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
@@ -4316,6 +4346,9 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Push"
+msgstr ""
+
msgid "Push events"
msgstr ""
@@ -4690,6 +4723,9 @@ msgstr ""
msgid "Set up Koding"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
@@ -5099,6 +5135,9 @@ msgstr ""
msgid "Test coverage parsing"
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -5156,6 +5195,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -5180,6 +5222,9 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after 10 minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -5318,6 +5363,9 @@ msgstr ""
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
msgid "Time before an issue gets scheduled"
msgstr ""
@@ -5631,9 +5679,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
msgstr ""
diff --git a/spec/features/projects/remote_mirror_spec.rb b/spec/features/projects/remote_mirror_spec.rb
index 5259a8942dc..33e9b73efe8 100644
--- a/spec/features/projects/remote_mirror_spec.rb
+++ b/spec/features/projects/remote_mirror_spec.rb
@@ -17,7 +17,7 @@ describe 'Project remote mirror', :feature do
visit project_mirror_path(project)
- expect(page).to have_content('The remote repository failed to update.')
+ expect_mirror_to_have_error_and_timeago('Never')
end
end
@@ -27,8 +27,14 @@ describe 'Project remote mirror', :feature do
visit project_mirror_path(project)
- expect(page).to have_content('The remote repository failed to update 5 minutes ago.')
+ expect_mirror_to_have_error_and_timeago('5 minutes ago')
end
end
+
+ def expect_mirror_to_have_error_and_timeago(timeago)
+ row = first('.js-mirrors-table-body tr')
+ expect(row).to have_content('Error')
+ expect(row).to have_content(timeago)
+ end
end
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index a0f5b234ebc..377a75cbcb3 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -129,9 +129,8 @@ describe 'Projects > Settings > Repository settings' do
visit project_settings_repository_path(project)
end
- it 'shows push mirror settings' do
- expect(page).to have_selector('#project_remote_mirrors_attributes_0_enabled')
- expect(page).to have_selector('#project_remote_mirrors_attributes_0_url')
+ it 'shows push mirror settings', :js do
+ expect(page).to have_selector('#mirror_direction')
end
end
end
diff --git a/spec/serializers/project_mirror_serializer_spec.rb b/spec/serializers/project_mirror_serializer_spec.rb
new file mode 100644
index 00000000000..5e47163532a
--- /dev/null
+++ b/spec/serializers/project_mirror_serializer_spec.rb
@@ -0,0 +1,7 @@
+require 'spec_helper'
+
+describe ProjectMirrorSerializer do
+ it 'represents ProjectMirror entities' do
+ expect(described_class.entity_class).to eq(ProjectMirrorEntity)
+ end
+end