{
+ showAlertFromLocalStorage();
+
const el = document.querySelector(selector);
if (!el) {
diff --git a/app/assets/javascripts/runner/admin_runner_edit/index.js b/app/assets/javascripts/runner/runner_edit/index.js
similarity index 74%
rename from app/assets/javascripts/runner/admin_runner_edit/index.js
rename to app/assets/javascripts/runner/runner_edit/index.js
index a2ac5731a62..5b2ddb8f68e 100644
--- a/app/assets/javascripts/runner/admin_runner_edit/index.js
+++ b/app/assets/javascripts/runner/runner_edit/index.js
@@ -1,11 +1,11 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
-import AdminRunnerEditApp from './admin_runner_edit_app.vue';
+import RunnerEditApp from './runner_edit_app.vue';
Vue.use(VueApollo);
-export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
+export const initRunnerEdit = (selector) => {
const el = document.querySelector(selector);
if (!el) {
@@ -22,7 +22,7 @@ export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
el,
apolloProvider,
render(h) {
- return h(AdminRunnerEditApp, {
+ return h(RunnerEditApp, {
props: {
runnerId,
runnerPath,
diff --git a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue b/app/assets/javascripts/runner/runner_edit/runner_edit_app.vue
similarity index 98%
rename from app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue
rename to app/assets/javascripts/runner/runner_edit/runner_edit_app.vue
index 40787cf72da..879162916a9 100644
--- a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue
+++ b/app/assets/javascripts/runner/runner_edit/runner_edit_app.vue
@@ -9,7 +9,7 @@ import runnerFormQuery from '../graphql/edit/runner_form.query.graphql';
import { captureException } from '../sentry_utils';
export default {
- name: 'AdminRunnerEditApp',
+ name: 'RunnerEditApp',
components: {
RunnerHeader,
RunnerUpdateForm,
diff --git a/app/views/groups/runners/edit.html.haml b/app/views/groups/runners/edit.html.haml
index c5999317597..d315e623ae2 100644
--- a/app/views/groups/runners/edit.html.haml
+++ b/app/views/groups/runners/edit.html.haml
@@ -1,14 +1,16 @@
+- runner_name = "##{@runner.id} (#{@runner.short_sha})"
- breadcrumb_title _('Edit')
-- page_title _('Edit'), "##{@runner.id} (#{@runner.short_sha})"
-
+- page_title _('Edit'), runner_name
- add_to_breadcrumbs _('Runners'), group_runners_path(@group)
-- add_to_breadcrumbs "#{@runner.short_sha}", group_runner_path(@group, @runner)
+- add_to_breadcrumbs runner_name, group_runner_path(@group, @runner)
+- if Feature.enabled?(:group_runner_edit_vue_ui, @group)
+ #js-group-runner-edit{ data: {runner_id: @runner.id, runner_path: group_runner_path(@group, @runner) } }
+- else
+ %h1.page-title.gl-font-size-h-display
+ = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id })
+ = render 'shared/runners/runner_type_badge', runner: @runner
-%h1.page-title.gl-font-size-h-display
- = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id })
- = render 'shared/runners/runner_type_badge', runner: @runner
+ = render 'shared/runners/runner_type_alert', runner: @runner
-= render 'shared/runners/runner_type_alert', runner: @runner
-
-= render 'shared/runners/form', runner: @runner, runner_form_url: group_runner_path(@group, @runner)
+ = render 'shared/runners/form', runner: @runner, runner_form_url: group_runner_path(@group, @runner)
diff --git a/config/feature_flags/development/group_runner_edit_vue_ui.yml b/config/feature_flags/development/group_runner_edit_vue_ui.yml
new file mode 100644
index 00000000000..e707edc2256
--- /dev/null
+++ b/config/feature_flags/development/group_runner_edit_vue_ui.yml
@@ -0,0 +1,8 @@
+---
+name: group_runner_edit_vue_ui
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372055
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96451
+milestone: '15.4'
+type: development
+group: group::runner
+default_enabled: false
diff --git a/doc/administration/application_settings_cache.md b/doc/administration/application_settings_cache.md
index 30fd9ab85a8..88e39a50b6c 100644
--- a/doc/administration/application_settings_cache.md
+++ b/doc/administration/application_settings_cache.md
@@ -18,7 +18,9 @@ extra load for Redis and PostgreSQL.
To change the expiry value:
-**For Omnibus installations**
+::Tabs
+
+:::TabTitle Omnibus package
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -34,7 +36,7 @@ To change the expiry value:
gitlab-ctl restart
```
-**For installations from source**
+:::TabTitle Source
1. Edit `config/gitlab.yml`:
@@ -45,3 +47,5 @@ To change the expiry value:
1. Save the file, and then [restart](restart_gitlab.md#installations-from-source)
GitLab for the changes to take effect.
+
+::EndTabs
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index f6fab6b7acc..648a2aac339 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -341,13 +341,20 @@ We also run our test suite against PG11 upon specific database library changes i
### Current versions testing
-| Where? | PostgreSQL version | Ruby version |
-| ------ | ------------------ | ------------ |
-| Merge requests | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `master` branch commits | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `maintenance` scheduled pipelines (every 2 hours at even hour) | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `maintenance` scheduled pipelines (every 2 hours at odd hour) | 12 (default version), 11 for DB library changes | 3.0 (set in the schedule variables) |
-| `nightly` scheduled pipelines | 12 (default version), 11, 13 | 2.7 (default version) |
+| Where? | PostgreSQL version | Ruby version |
+|------------------------------------------------------------------------------------------------|-------------------------------------------------|--------------|
+| Merge requests | 12 (default version), 11 for DB library changes | 2.7 (default version) |
+| `master` branch commits | 12 (default version), 11 for DB library changes | 2.7 (default version) |
+| `maintenance` scheduled pipelines for the `master` branch (every even-numbered hour) | 12 (default version), 11 for DB library changes | 2.7 (default version) |
+| `maintenance` scheduled pipelines for the `ruby3` branch (every odd-numbered hour), see below. | 12 (default version), 11 for DB library changes | 3.0 (coded in the branch) |
+| `nightly` scheduled pipelines for the `master` branch | 12 (default version), 11, 13 | 2.7 (default version) |
+
+The pipeline configuration for the scheduled pipeline testing Ruby 3 is
+stored in the `ruby3-sync` branch. The pipeline updates the `ruby3` branch
+with latest `master`, and then it triggers a regular branch pipeline for
+`ruby3`. Any changes in `ruby3` are only for running the pipeline. It should
+never be merged back to `master`. Any other Ruby 3 changes should go into
+`master` directly, which should be compatible with Ruby 2.7.
### Long-term plan
diff --git a/doc/user/clusters/agent/gitops/helm.md b/doc/user/clusters/agent/gitops/helm.md
index 8626ddeb45f..bdc2664e7ba 100644
--- a/doc/user/clusters/agent/gitops/helm.md
+++ b/doc/user/clusters/agent/gitops/helm.md
@@ -75,20 +75,6 @@ In GitLab, the agent for Kubernetes regularly compares the desired state from th
the actual state from the Kubernetes cluster. Deviations from the desired state are fixed at every check. These checks
happen automatically every 5 minutes. They are not configurable.
-## Known issues
-
-The following are known issues:
-
-- Your chart must be in a GitLab project. The project must be an agent configuration project or a public
- project. This known issue also exists for manifest-based GitOps and is tracked in
- [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7704).
-- Values for the chart must be in a `values.yaml` file. This file must be with the chart,
- in the same project and path.
-- Because of drift detection and remediation, release history, stored in the cluster, is not useful.
- A new release is created every five minutes and the oldest release is discarded.
- Eventually history consists only of the same information.
- View [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/372023) for details.
-
## Example repository layout
```plaintext
@@ -104,6 +90,20 @@ The following are known issues:
└── some-file-used-in-chart.txt
```
+## Known issues
+
+The following are known issues:
+
+- Your chart must be in a GitLab project. The project must be an agent configuration project or a public
+ project. This known issue also exists for manifest-based GitOps and is tracked in
+ [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7704).
+- Values for the chart must be in a `values.yaml` file. This file must be with the chart,
+ in the same project and path.
+- Because of drift detection and remediation, release history, stored in the cluster, is not useful.
+ A new release is created every five minutes and the oldest release is discarded.
+ Eventually history consists only of the same information.
+ View [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/372023) for details.
+
## Troubleshooting
### Agent cannot find values for the chart
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 01a583dadb5..d946108264e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6240,12 +6240,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6261,12 +6255,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -20067,9 +20055,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-msgstr ""
-
msgid "In case of pull mirroring, your 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 ""
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index e50674228f6..d88c016dd3a 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -520,44 +520,19 @@ RSpec.describe "Admin Runners" do
end
describe "Runner edit page" do
- let(:runner) { create(:ci_runner, :project) }
+ let(:project_runner) { create(:ci_runner, :project) }
let!(:project1) { create(:project) }
let!(:project2) { create(:project) }
before do
- visit edit_admin_runner_path(runner)
+ visit edit_admin_runner_path(project_runner)
wait_for_requests
end
- describe 'breadcrumbs' do
- it 'contains the current runner id and token' do
- page.within '[data-testid="breadcrumb-links"]' do
- expect(page).to have_link("##{runner.id} (#{runner.short_sha})")
- expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_content("Edit")
- end
- end
- end
-
- describe 'runner header', :js do
- it 'contains the runner status, type and id' do
- expect(page).to have_content("never contacted specific Runner ##{runner.id} created")
- end
- end
-
- context 'when a runner is updated', :js do
- before do
- click_on _('Save changes')
- wait_for_requests
- end
-
- it 'show success alert' do
- expect(page.find('[data-testid="alert-success"]')).to have_content('saved')
- end
-
- it 'redirects to runner page' do
- expect(current_url).to match(admin_runner_path(runner))
- end
+ it_behaves_like 'submits edit runner form' do
+ let(:runner) { project_runner }
+ let(:runner_page_path) { admin_runner_path(project_runner) }
end
describe 'projects' do
@@ -583,7 +558,7 @@ RSpec.describe "Admin Runners" do
describe 'enable/create' do
shared_examples 'assignable runner' do
it 'enables a runner for a project' do
- within '[data-testid="unassigned-projects"]' do
+ within find('[data-testid="unassigned-projects"] tr', text: project2.full_name) do
click_on 'Enable'
end
@@ -594,21 +569,21 @@ RSpec.describe "Admin Runners" do
end
end
- context 'with specific runner' do
- let(:runner) { create(:ci_runner, :project, projects: [project1]) }
+ context 'with project runner' do
+ let(:project_runner) { create(:ci_runner, :project, projects: [project1]) }
before do
- visit edit_admin_runner_path(runner)
+ visit edit_admin_runner_path(project_runner)
end
it_behaves_like 'assignable runner'
end
context 'with locked runner' do
- let(:runner) { create(:ci_runner, :project, projects: [project1], locked: true) }
+ let(:locked_runner) { create(:ci_runner, :project, projects: [project1], locked: true) }
before do
- visit edit_admin_runner_path(runner)
+ visit edit_admin_runner_path(locked_runner)
end
it_behaves_like 'assignable runner'
diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb
index b98c94b030d..a0251c7d47c 100644
--- a/spec/features/groups/group_runners_spec.rb
+++ b/spec/features/groups/group_runners_spec.rb
@@ -144,44 +144,70 @@ RSpec.describe "Group Runners" do
end
end
- describe "Group runner edit page", :js do
- let!(:runner) do
- create(:ci_runner, :group, groups: [group], description: 'runner-foo', contacted_at: Time.zone.now)
+ describe "Group runner show page", :js do
+ let!(:group_runner) do
+ create(:ci_runner, :group, groups: [group], description: 'runner-foo')
end
it 'user views runner details' do
- visit group_runner_path(group, runner)
+ visit group_runner_path(group, group_runner)
expect(page).to have_content "#{s_('Runners|Description')} runner-foo"
end
+ end
- it 'user edits the runner to be protected' do
- visit edit_group_runner_path(group, runner)
-
- expect(page.find_field('runner[access_level]')).not_to be_checked
-
- check 'runner_access_level'
- click_button _('Save changes')
-
- expect(page).to have_content "#{s_('Runners|Configuration')} #{s_('Runners|Protected')}"
+ describe "Group runner edit page", :js do
+ let!(:group_runner) do
+ create(:ci_runner, :group, groups: [group])
end
- context 'when a runner has a tag' do
+ context 'when group_runner_edit_vue_ui is disabled' do
before do
- runner.update!(tag_list: ['tag1'])
+ stub_feature_flags(group_runner_edit_vue_ui: false)
end
- it 'user edits runner not to run untagged jobs' do
- visit edit_group_runner_path(group, runner)
+ it 'user edits the runner to be protected' do
+ visit edit_group_runner_path(group, group_runner)
- page.find_field('runner[tag_list]').set('tag1, tag2')
+ expect(page.find_field('runner[access_level]')).not_to be_checked
- uncheck 'runner_run_untagged'
+ check 'runner_access_level'
click_button _('Save changes')
- # Tags can be in any order
- expect(page).to have_content /#{s_('Runners|Tags')}.*tag1/
- expect(page).to have_content /#{s_('Runners|Tags')}.*tag2/
+ expect(page).to have_content "#{s_('Runners|Configuration')} #{s_('Runners|Protected')}"
+ end
+
+ context 'when a runner has a tag' do
+ before do
+ group_runner.update!(tag_list: ['tag1'])
+ end
+
+ it 'user edits runner not to run untagged jobs' do
+ visit edit_group_runner_path(group, group_runner)
+
+ page.find_field('runner[tag_list]').set('tag1, tag2')
+
+ uncheck 'runner_run_untagged'
+ click_button _('Save changes')
+
+ # Tags can be in any order
+ expect(page).to have_content /#{s_('Runners|Tags')}.*tag1/
+ expect(page).to have_content /#{s_('Runners|Tags')}.*tag2/
+ end
+ end
+ end
+
+ context 'when group_runner_edit_vue_ui is enabled' do
+ before do
+ stub_feature_flags(group_runner_edit_vue_ui: true)
+
+ visit edit_group_runner_path(group, group_runner)
+ wait_for_requests
+ end
+
+ it_behaves_like 'submits edit runner form' do
+ let(:runner) { group_runner }
+ let(:runner_page_path) { group_runner_path(group, group_runner) }
end
end
end
diff --git a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
index cccb4e7836b..6ad8a9de8d3 100644
--- a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
+++ b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
@@ -1,49 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`content_editor/components/toolbar_link_button renders dropdown component 1`] = `
-"
-
-
-
-
-
-
+"
+
+
+
+
+
+
+
-
+
"
`;
diff --git a/spec/frontend/content_editor/components/toolbar_image_button_spec.js b/spec/frontend/content_editor/components/toolbar_image_button_spec.js
index 1c2e58c6cfb..5a9d16e7a87 100644
--- a/spec/frontend/content_editor/components/toolbar_image_button_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_image_button_spec.js
@@ -3,6 +3,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarImageButton from '~/content_editor/components/toolbar_image_button.vue';
import Attachment from '~/content_editor/extensions/attachment';
import Image from '~/content_editor/extensions/image';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands } from '../test_utils';
describe('content_editor/components/toolbar_image_button', () => {
@@ -14,6 +15,9 @@ describe('content_editor/components/toolbar_image_button', () => {
provide: {
tiptapEditor: editor,
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
});
};
diff --git a/spec/frontend/content_editor/components/toolbar_link_button_spec.js b/spec/frontend/content_editor/components/toolbar_link_button_spec.js
index 544f260e749..295b41528fe 100644
--- a/spec/frontend/content_editor/components/toolbar_link_button_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_link_button_spec.js
@@ -4,6 +4,7 @@ import ToolbarLinkButton from '~/content_editor/components/toolbar_link_button.v
import eventHubFactory from '~/helpers/event_hub_factory';
import Link from '~/content_editor/extensions/link';
import { hasSelection } from '~/content_editor/services/utils';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils';
jest.mock('~/content_editor/services/utils');
@@ -18,6 +19,9 @@ describe('content_editor/components/toolbar_link_button', () => {
tiptapEditor: editor,
eventHub: eventHubFactory(),
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
});
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
diff --git a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
index 874d659dde7..a23f8370adf 100644
--- a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
@@ -4,6 +4,7 @@ import ToolbarMoreDropdown from '~/content_editor/components/toolbar_more_dropdo
import Diagram from '~/content_editor/extensions/diagram';
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
import eventHubFactory from '~/helpers/event_hub_factory';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils';
describe('content_editor/components/toolbar_more_dropdown', () => {
@@ -24,6 +25,9 @@ describe('content_editor/components/toolbar_more_dropdown', () => {
tiptapEditor,
eventHub,
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
propsData,
});
};
diff --git a/spec/frontend/content_editor/components/toolbar_table_button_spec.js b/spec/frontend/content_editor/components/toolbar_table_button_spec.js
index a35f15594fe..aa4604661e5 100644
--- a/spec/frontend/content_editor/components/toolbar_table_button_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_table_button_spec.js
@@ -1,6 +1,7 @@
import { GlDropdown, GlButton } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarTableButton from '~/content_editor/components/toolbar_table_button.vue';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands } from '../test_utils';
describe('content_editor/components/toolbar_table_button', () => {
@@ -12,6 +13,9 @@ describe('content_editor/components/toolbar_table_button', () => {
provide: {
tiptapEditor: editor,
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
});
};
diff --git a/spec/frontend/runner/components/runner_update_form_spec.js b/spec/frontend/runner/components/runner_update_form_spec.js
index 3037364d941..7b67a89f989 100644
--- a/spec/frontend/runner/components/runner_update_form_spec.js
+++ b/spec/frontend/runner/components/runner_update_form_spec.js
@@ -1,6 +1,7 @@
import Vue, { nextTick } from 'vue';
import { GlForm, GlSkeletonLoader } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
+import { __ } from '~/locale';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -47,6 +48,7 @@ describe('RunnerUpdateForm', () => {
const findSubmit = () => wrapper.find('[type="submit"]');
const findSubmitDisabledAttr = () => findSubmit().attributes('disabled');
+ const findCancelBtn = () => wrapper.findByRole('link', { name: __('Cancel') });
const submitForm = () => findForm().trigger('submit');
const submitFormAndWait = () => submitForm().then(waitForPromises);
@@ -117,6 +119,11 @@ describe('RunnerUpdateForm', () => {
expect(mockRunner).toMatchObject(getFieldsModel());
});
+ it('Form shows a cancel button', () => {
+ expect(runnerUpdateHandler).not.toHaveBeenCalled();
+ expect(findCancelBtn().attributes('href')).toBe(mockRunnerPath);
+ });
+
it('Form prevent multiple submissions', async () => {
await submitForm();
diff --git a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js b/spec/frontend/runner/runner_edit/runner_edit_app_spec.js
similarity index 94%
rename from spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js
rename to spec/frontend/runner/runner_edit/runner_edit_app_spec.js
index ffe3599ac64..f47c22c9e19 100644
--- a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js
+++ b/spec/frontend/runner/runner_edit/runner_edit_app_spec.js
@@ -9,7 +9,7 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/runner/components/runner_header.vue';
import RunnerUpdateForm from '~/runner/components/runner_update_form.vue';
import runnerFormQuery from '~/runner/graphql/edit/runner_form.query.graphql';
-import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue';
+import RunnerEditApp from '~//runner/runner_edit/runner_edit_app.vue';
import { captureException } from '~/runner/sentry_utils';
import { runnerFormData } from '../mock_data';
@@ -24,7 +24,7 @@ const mockRunnerPath = `/admin/runners/${mockRunnerId}`;
Vue.use(VueApollo);
-describe('AdminRunnerEditApp', () => {
+describe('RunnerEditApp', () => {
let wrapper;
let mockRunnerQuery;
@@ -32,7 +32,7 @@ describe('AdminRunnerEditApp', () => {
const findRunnerUpdateForm = () => wrapper.findComponent(RunnerUpdateForm);
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
- wrapper = mountFn(AdminRunnerEditApp, {
+ wrapper = mountFn(RunnerEditApp, {
apolloProvider: createMockApollo([[runnerFormQuery, mockRunnerQuery]]),
propsData: {
runnerId: mockRunnerId,
@@ -102,7 +102,7 @@ describe('AdminRunnerEditApp', () => {
it('error is reported to sentry', () => {
expect(captureException).toHaveBeenCalledWith({
error: new Error('Error!'),
- component: 'AdminRunnerEditApp',
+ component: 'RunnerEditApp',
});
});
diff --git a/spec/services/protected_branches/cache_service_spec.rb b/spec/services/protected_branches/cache_service_spec.rb
index 4fa7553c23d..00d1e8b5457 100644
--- a/spec/services/protected_branches/cache_service_spec.rb
+++ b/spec/services/protected_branches/cache_service_spec.rb
@@ -75,10 +75,12 @@ RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache do
expect(service.fetch('main', dry_run: true) { true }).to eq(true)
expect(Gitlab::AppLogger).to receive(:error).with(
- 'class' => described_class.name,
- 'message' => /Cache mismatch/,
- 'project_id' => project.id,
- 'project_path' => project.full_path
+ {
+ 'class' => described_class.name,
+ 'message' => /Cache mismatch/,
+ 'project_id' => project.id,
+ 'project_path' => project.full_path
+ }
)
expect(service.fetch('main', dry_run: true) { false }).to eq(false)
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
index e5043e7b28f..fb24f83bf3e 100644
--- a/spec/support/shared_examples/features/runners_shared_examples.rb
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -145,3 +145,39 @@ RSpec.shared_examples 'pauses, resumes and deletes a runner' do
end
end
end
+
+RSpec.shared_examples 'submits edit runner form' do
+ it 'breadcrumb contains runner id and token' do
+ page.within '[data-testid="breadcrumb-links"]' do
+ expect(page).to have_link("##{runner.id} (#{runner.short_sha})")
+ expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_content("Edit")
+ end
+ end
+
+ describe 'runner header', :js do
+ it 'contains the runner id' do
+ expect(page).to have_content("Runner ##{runner.id} created")
+ end
+ end
+
+ context 'when a runner is updated', :js do
+ before do
+ find('[data-testid="runner-field-description"] input').set('new-runner-description')
+
+ click_on _('Save changes')
+ wait_for_requests
+ end
+
+ it 'redirects to runner page' do
+ expect(current_url).to match(runner_page_path)
+ end
+
+ it 'show success alert' do
+ expect(page.find('[data-testid="alert-success"]')).to have_content('saved')
+ end
+
+ it 'shows updated information' do
+ expect(page).to have_content("#{s_('Runners|Description')} new-runner-description")
+ end
+ end
+end
diff --git a/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb b/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb
index 7e3829b9b95..7126d8328e0 100755
--- a/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb
+++ b/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb
@@ -17,15 +17,7 @@ module OmniAuth
protected
def request_phase
- if env['REQUEST_METHOD'] == 'GET'
-
- if @configuration.use_sessions? && request.cookies[@configuration.session_cookie]
- redirect callback_url
- else
- get_credentials
- end
-
- elsif (env['REQUEST_METHOD'] == 'POST') && (not request.params['username'])
+ if (env['REQUEST_METHOD'] == 'POST') && (not request.params['username'])
get_credentials
else
session['omniauth.crowd'] = {'username' => request['username'], 'password' => request['password']}
diff --git a/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb b/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb
index f234ef82e76..000b3901f86 100755
--- a/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb
+++ b/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb
@@ -20,9 +20,21 @@ describe OmniAuth::Strategies::Crowd, :type=>:strategy do
@sso_url_image = nil
let(:config) { OmniAuth::Strategies::Crowd::Configuration.new(strategy[1]) }
let(:validator) { OmniAuth::Strategies::Crowd::CrowdValidator.new(config, 'foo', 'bar', nil, nil) }
+ let(:csrf_token) { SecureRandom.base64(32) }
+ let(:base_env) { { 'rack.session' => { csrf: csrf_token }, 'rack.input' => StringIO.new("authenticity_token=#{escaped_token}") } }
+ let(:post_env) { make_env('/auth/crowd', base_env) }
+ let(:escaped_token) { URI.encode_www_form_component(csrf_token, Encoding::UTF_8) }
+
+ def make_env(path = '/auth/crowd', props = {})
+ {
+ 'REQUEST_METHOD' => 'POST',
+ 'PATH_INFO' => path,
+ 'rack.session' => {},
+ 'rack.input' => StringIO.new('test=true')
+ }.merge(props)
+ end
describe 'Authentication Request Body' do
-
it 'should send password in session request' do
body = <<-BODY.strip
@@ -42,18 +54,10 @@ BODY
end
end
- describe 'GET /auth/crowd' do
- it 'should show the login form' do
- get '/auth/crowd'
- expect(last_response).to be_ok
- end
- end
-
describe 'POST /auth/crowd' do
- it 'should redirect to callback' do
- post '/auth/crowd', :username=>'foo', :password=>'bar'
- expect(last_response).to be_redirect
- expect(last_response.headers['Location']).to eq('http://example.org/auth/crowd/callback')
+ it 'should show the login form' do
+ post '/auth/crowd', nil, post_env
+ expect(last_response).to be_ok
end
end
@@ -79,13 +83,16 @@ BODY
to_return(:status => [415, "Unsupported Media Type"])
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
end
+
it 'should call through to the master app' do
expect(last_response.body).to eq('true')
end
+
it 'should have an auth hash' do
auth = last_request.env['omniauth.auth']
expect(auth).to be_kind_of(Hash)
end
+
it 'should have good data' do
auth = last_request.env['omniauth.auth']
expect(auth['provider']).to eq(:crowd)
@@ -142,8 +149,7 @@ BODY
end
end
- describe 'GET /auth/crowd without credentials will redirect to login form' do
-
+ describe 'POST /auth/crowd without credentials will redirect to login form' do
sso_url = 'https://foo.bar'
before do
@@ -152,10 +158,9 @@ BODY
end
it 'should have the SSO button in the response body' do
-
found_legend = found_anchor = nil
- get '/auth/crowd'
+ post '/auth/crowd', nil, post_env
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
@@ -163,26 +168,23 @@ BODY
found_legend = true
elsif element.name === 'a' && element.attr('href') === "#{sso_url}/users/auth/crowd/callback"
found_anchor = true
- end
+ end
end
expect(found_legend).to(be(true))
expect(found_anchor).to(be(true))
-
end
after do
@using_sessions = false
@sso_url = nil
end
-
end
-
- describe 'GET /auth/crowd without credentials will redirect to login form which has custom image in the SSO link' do
-
+
+ describe 'POST /auth/crowd without credentials will redirect to login form which has custom image in the SSO link' do
sso_url = 'https://foo.bar'
sso_url_image = 'https://foo.bar/image.png'
-
+
before do
@using_sessions = true
@sso_url = sso_url
@@ -190,10 +192,9 @@ BODY
end
it 'should have the SSO button with a custom image in the response body' do
-
found_legend = found_anchor = found_image = false
- get '/auth/crowd'
+ post '/auth/crowd', nil, post_env
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
@@ -206,14 +207,12 @@ BODY
if element.children.length === 1 && element.children.first.name === 'img' && element.children.first.attr('src') === sso_url_image
found_image = true
end
-
end
end
expect(found_legend).to(be(true))
expect(found_anchor).to(be(true))
expect(found_image).to(be(true))
-
end
after do
@@ -221,46 +220,13 @@ BODY
@sso_url = nil
@sso_url_image = nil
end
-
end
- describe 'GET /auth/crowd without credentials but with SSO cookie will redirect to callback' do
-
- sso_url = 'https://foo.bar'
-
- before do
-
- @using_sessions = true
- @sso_url = sso_url
-
- set_cookie('crowd.token_key=foobar')
-
- end
-
- it 'should redirect to callback' do
- get '/auth/crowd'
- expect(last_response).to be_redirect
- expect(last_response.headers['Location']).to eq('http://example.org/auth/crowd/callback')
- end
-
- after do
-
- @using_sessions = false
- @sso_url = nil
-
- clear_cookies()
-
- end
-
- end
-
describe 'POST /auth/crowd/callback without credentials but with SSO cookie will redirect to login form because session is invalid' do
-
sso_url = 'https://foo.bar'
token = 'foobar'
-
+
before do
-
@using_sessions = true
@sso_url = sso_url
@@ -268,7 +234,6 @@ BODY
to_return(:status => [404])
set_cookie("crowd.token_key=#{token}")
-
end
it 'should redirect to login form' do
@@ -360,7 +325,6 @@ BODY
end
it 'should return user data' do
-
auth = nil
get '/auth/crowd/callback'