Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0a70b104d0
commit
a37d7647a5
30 changed files with 304 additions and 283 deletions
|
@ -71,29 +71,31 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
:title="__('Insert image')"
|
||||
:text="__('Insert image')"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
icon="media"
|
||||
text-sr-only
|
||||
data-testid="insert-image-toolbar-button"
|
||||
@hidden="resetFields()"
|
||||
>
|
||||
<gl-dropdown-form class="gl-px-3!">
|
||||
<gl-form-input-group v-model="imgSrc" :placeholder="__('Image URL')">
|
||||
<template #append>
|
||||
<gl-button variant="confirm" @click="insertImage">{{ __('Insert') }}</gl-button>
|
||||
</template>
|
||||
</gl-form-input-group>
|
||||
</gl-dropdown-form>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-item @click="openFileUpload">
|
||||
{{ __('Upload image') }}
|
||||
</gl-dropdown-item>
|
||||
|
||||
<span class="gl-display-inline-flex">
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
:text="__('Insert image')"
|
||||
:title="__('Insert image')"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
icon="media"
|
||||
lazy
|
||||
text-sr-only
|
||||
data-testid="insert-image-toolbar-button"
|
||||
@hidden="resetFields()"
|
||||
>
|
||||
<gl-dropdown-form class="gl-px-3!">
|
||||
<gl-form-input-group v-model="imgSrc" :placeholder="__('Image URL')">
|
||||
<template #append>
|
||||
<gl-button variant="confirm" @click="insertImage">{{ __('Insert') }}</gl-button>
|
||||
</template>
|
||||
</gl-form-input-group>
|
||||
</gl-dropdown-form>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-item @click="openFileUpload">
|
||||
{{ __('Upload image') }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
<input
|
||||
ref="fileSelector"
|
||||
type="file"
|
||||
|
@ -103,5 +105,5 @@ export default {
|
|||
data-qa-selector="file_upload_field"
|
||||
@change="onFileSelect"
|
||||
/>
|
||||
</gl-dropdown>
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
@ -89,32 +89,34 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<editor-state-observer @transaction="updateLinkState">
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
:title="__('Insert link')"
|
||||
:text="__('Insert link')"
|
||||
:toggle-class="{ active: isActive }"
|
||||
text-sr-only
|
||||
size="small"
|
||||
category="tertiary"
|
||||
icon="link"
|
||||
@show="selectLink()"
|
||||
>
|
||||
<gl-dropdown-form class="gl-px-3!">
|
||||
<gl-form-input-group v-model="linkHref" :placeholder="__('Link URL')">
|
||||
<template #append>
|
||||
<gl-button variant="confirm" @click="updateLink">{{ __('Apply') }}</gl-button>
|
||||
</template>
|
||||
</gl-form-input-group>
|
||||
</gl-dropdown-form>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-item v-if="isActive" @click="removeLink">
|
||||
{{ __('Remove link') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item v-else @click="openFileUpload">
|
||||
{{ __('Upload file') }}
|
||||
</gl-dropdown-item>
|
||||
|
||||
<span class="gl-display-inline-flex">
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
:title="__('Insert link')"
|
||||
:text="__('Insert link')"
|
||||
:toggle-class="{ active: isActive }"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
icon="link"
|
||||
text-sr-only
|
||||
lazy
|
||||
@show="selectLink()"
|
||||
>
|
||||
<gl-dropdown-form class="gl-px-3!">
|
||||
<gl-form-input-group v-model="linkHref" :placeholder="__('Link URL')">
|
||||
<template #append>
|
||||
<gl-button variant="confirm" @click="updateLink">{{ __('Apply') }}</gl-button>
|
||||
</template>
|
||||
</gl-form-input-group>
|
||||
</gl-dropdown-form>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-item v-if="isActive" @click="removeLink">
|
||||
{{ __('Remove link') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item v-else @click="openFileUpload">
|
||||
{{ __('Upload file') }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
<input
|
||||
ref="fileSelector"
|
||||
type="file"
|
||||
|
@ -122,6 +124,6 @@ export default {
|
|||
class="gl-display-none"
|
||||
@change="onFileSelect"
|
||||
/>
|
||||
</gl-dropdown>
|
||||
</span>
|
||||
</editor-state-observer>
|
||||
</template>
|
||||
|
|
|
@ -56,6 +56,7 @@ export default {
|
|||
text-sr-only
|
||||
class="content-editor-dropdown"
|
||||
right
|
||||
lazy
|
||||
>
|
||||
<gl-dropdown-item @click="insert('codeBlock')">
|
||||
{{ __('Code block') }}
|
||||
|
|
|
@ -81,6 +81,7 @@ export default {
|
|||
class="content-editor-dropdown"
|
||||
right
|
||||
text-sr-only
|
||||
lazy
|
||||
>
|
||||
<gl-dropdown-form class="gl-px-3!">
|
||||
<div v-for="r of list(maxRows)" :key="r" class="gl-display-flex">
|
||||
|
|
|
@ -64,6 +64,7 @@ export default {
|
|||
data-qa-selector="text_style_dropdown"
|
||||
:disabled="!activeItem"
|
||||
:text="activeItemLabel"
|
||||
lazy
|
||||
>
|
||||
<gl-dropdown-item
|
||||
v-for="(item, index) in $options.items"
|
||||
|
|
|
@ -25,7 +25,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
|
||||
class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-3 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
|
||||
>
|
||||
<toolbar-text-style-dropdown
|
||||
data-testid="text-styles"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { initAdminRunnerEdit } from '~/runner/admin_runner_edit';
|
||||
import { initRunnerEdit } from '~/runner/runner_edit';
|
||||
|
||||
initAdminRunnerEdit();
|
||||
initRunnerEdit('#js-admin-runner-edit');
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import { initRunnerEdit } from '~/runner/runner_edit';
|
||||
|
||||
initRunnerEdit('#js-group-runner-edit');
|
|
@ -1,11 +1,14 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage';
|
||||
import GroupRunnerShowApp from './group_runner_show_app.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
export const initGroupRunnerShow = (selector = '#js-group-runner-show') => {
|
||||
showAlertFromLocalStorage();
|
||||
|
||||
const el = document.querySelector(selector);
|
||||
|
||||
if (!el) {
|
||||
|
|
|
@ -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,
|
|
@ -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,
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,49 +1,33 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`content_editor/components/toolbar_link_button renders dropdown component 1`] = `
|
||||
"<div class=\\"dropdown b-dropdown gl-new-dropdown btn-group\\" title=\\"Insert link\\">
|
||||
<!----><button aria-haspopup=\\"true\\" aria-expanded=\\"false\\" type=\\"button\\" class=\\"btn dropdown-toggle btn-default btn-sm gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only\\">
|
||||
<!----> <svg data-testid=\\"link-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"dropdown-icon gl-icon s16\\">
|
||||
<use href=\\"#link\\"></use>
|
||||
</svg> <span class=\\"gl-new-dropdown-button-text gl-sr-only\\">Insert link</span> <svg data-testid=\\"chevron-down-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"gl-button-icon dropdown-chevron gl-icon s16\\">
|
||||
<use href=\\"#chevron-down\\"></use>
|
||||
</svg></button>
|
||||
<ul role=\\"menu\\" tabindex=\\"-1\\" class=\\"dropdown-menu\\">
|
||||
<div class=\\"gl-new-dropdown-inner\\">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class=\\"gl-new-dropdown-contents\\">
|
||||
"<div title=\\"Insert link\\" lazy=\\"\\">
|
||||
<li role=\\"presentation\\" class=\\"gl-px-3!\\">
|
||||
<form tabindex=\\"-1\\" class=\\"b-dropdown-form gl-p-0\\">
|
||||
<div role=\\"group\\" class=\\"input-group\\" placeholder=\\"Link URL\\">
|
||||
<!---->
|
||||
<li role=\\"presentation\\" class=\\"gl-px-3!\\">
|
||||
<form tabindex=\\"-1\\" class=\\"b-dropdown-form gl-p-0\\">
|
||||
<div role=\\"group\\" class=\\"input-group\\" placeholder=\\"Link URL\\">
|
||||
<!---->
|
||||
<!----> <input type=\\"text\\" placeholder=\\"Link URL\\" class=\\"form-control gl-form-input\\">
|
||||
<div class=\\"input-group-append\\"><button type=\\"button\\" class=\\"btn btn-confirm btn-md gl-button\\">
|
||||
<!---->
|
||||
<!----> <span class=\\"gl-button-text\\">Apply</span></button></div>
|
||||
<!---->
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
<li role=\\"presentation\\" class=\\"gl-new-dropdown-divider\\">
|
||||
<hr role=\\"separator\\" aria-orientation=\\"horizontal\\" class=\\"dropdown-divider\\">
|
||||
</li>
|
||||
<li role=\\"presentation\\" class=\\"gl-new-dropdown-item\\"><button role=\\"menuitem\\" type=\\"button\\" class=\\"dropdown-item\\">
|
||||
<!----> <input type=\\"text\\" placeholder=\\"Link URL\\" class=\\"form-control gl-form-input\\">
|
||||
<div class=\\"input-group-append\\"><button type=\\"button\\" class=\\"btn btn-confirm btn-md gl-button\\">
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<div class=\\"gl-new-dropdown-item-text-wrapper\\">
|
||||
<p class=\\"gl-new-dropdown-item-text-primary\\">
|
||||
Upload file
|
||||
</p>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
</button></li> <input type=\\"file\\" name=\\"content_editor_attachment\\" class=\\"gl-display-none\\">
|
||||
<!----> <span class=\\"gl-button-text\\">Apply</span></button></div>
|
||||
<!---->
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
<li role=\\"presentation\\" class=\\"gl-new-dropdown-divider\\">
|
||||
<hr role=\\"separator\\" aria-orientation=\\"horizontal\\" class=\\"dropdown-divider\\">
|
||||
</li>
|
||||
<li role=\\"presentation\\" class=\\"gl-new-dropdown-item\\"><button role=\\"menuitem\\" type=\\"button\\" class=\\"dropdown-item\\">
|
||||
<!---->
|
||||
<!---->
|
||||
<!---->
|
||||
<div class=\\"gl-new-dropdown-item-text-wrapper\\">
|
||||
<p class=\\"gl-new-dropdown-item-text-primary\\">
|
||||
Upload file
|
||||
</p>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</ul>
|
||||
</button></li>
|
||||
</div>"
|
||||
`;
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
});
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']}
|
||||
|
|
|
@ -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
|
||||
<password>
|
||||
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue