Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ed01bf9b25
commit
e9626c2383
|
@ -93,7 +93,12 @@ export default {
|
||||||
<div>
|
<div>
|
||||||
<editor-state-observer @docUpdate="notifyChange" @focus="focus" @blur="blur" />
|
<editor-state-observer @docUpdate="notifyChange" @focus="focus" @blur="blur" />
|
||||||
<content-editor-error />
|
<content-editor-error />
|
||||||
<div data-testid="content-editor" class="md-area" :class="{ 'is-focused': focused }">
|
<div
|
||||||
|
data-testid="content-editor"
|
||||||
|
data-qa-selector="content_editor_container"
|
||||||
|
class="md-area"
|
||||||
|
:class="{ 'is-focused': focused }"
|
||||||
|
>
|
||||||
<top-toolbar ref="toolbar" class="gl-mb-4" />
|
<top-toolbar ref="toolbar" class="gl-mb-4" />
|
||||||
<formatting-bubble-menu />
|
<formatting-bubble-menu />
|
||||||
<div v-if="isLoadingContent" class="gl-w-full gl-display-flex gl-justify-content-center">
|
<div v-if="isLoadingContent" class="gl-w-full gl-display-flex gl-justify-content-center">
|
||||||
|
|
|
@ -98,6 +98,7 @@ export default {
|
||||||
name="content_editor_image"
|
name="content_editor_image"
|
||||||
:accept="$options.acceptedMimes"
|
:accept="$options.acceptedMimes"
|
||||||
class="gl-display-none"
|
class="gl-display-none"
|
||||||
|
data-qa-selector="file_upload_field"
|
||||||
@change="onFileSelect"
|
@change="onFileSelect"
|
||||||
/>
|
/>
|
||||||
</gl-dropdown>
|
</gl-dropdown>
|
||||||
|
|
|
@ -61,6 +61,7 @@ export default {
|
||||||
<gl-dropdown
|
<gl-dropdown
|
||||||
v-gl-tooltip="$options.i18n.placeholder"
|
v-gl-tooltip="$options.i18n.placeholder"
|
||||||
size="small"
|
size="small"
|
||||||
|
data-qa-selector="text_style_dropdown"
|
||||||
:disabled="!activeItem"
|
:disabled="!activeItem"
|
||||||
:text="activeItemLabel"
|
:text="activeItemLabel"
|
||||||
>
|
>
|
||||||
|
@ -69,6 +70,8 @@ export default {
|
||||||
:key="index"
|
:key="index"
|
||||||
is-check-item
|
is-check-item
|
||||||
:is-checked="isActive(item)"
|
:is-checked="isActive(item)"
|
||||||
|
data-qa-selector="text_style_menu_item"
|
||||||
|
:data-qa-text-style="item.label"
|
||||||
@click="execute(item)"
|
@click="execute(item)"
|
||||||
>
|
>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
|
|
|
@ -397,6 +397,7 @@ export default {
|
||||||
v-if="showContentEditorAlert"
|
v-if="showContentEditorAlert"
|
||||||
class="gl-mb-6"
|
class="gl-mb-6"
|
||||||
variant="info"
|
variant="info"
|
||||||
|
data-qa-selector="try_new_editor_container"
|
||||||
:primary-button-text="$options.i18n.contentEditor.useNewEditor.primaryLabel"
|
:primary-button-text="$options.i18n.contentEditor.useNewEditor.primaryLabel"
|
||||||
:secondary-button-text="$options.i18n.contentEditor.useNewEditor.secondaryLabel"
|
:secondary-button-text="$options.i18n.contentEditor.useNewEditor.secondaryLabel"
|
||||||
:dismiss-label="$options.i18n.contentEditor.useNewEditor.secondaryLabel"
|
:dismiss-label="$options.i18n.contentEditor.useNewEditor.secondaryLabel"
|
||||||
|
|
|
@ -18,7 +18,7 @@ module SpammableActions::CaptchaCheck::JsonFormatActionsSupport
|
||||||
def with_captcha_check_json_format(&block)
|
def with_captcha_check_json_format(&block)
|
||||||
# NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response
|
# NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response
|
||||||
# which requires a CAPTCHA to be solved in order for the request to be resubmitted.
|
# which requires a CAPTCHA to be solved in order for the request to be resubmitted.
|
||||||
# See https://stackoverflow.com/q/26547466/25192
|
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10
|
||||||
captcha_render_lambda = -> { render json: spam_action_response_fields(spammable), status: :conflict }
|
captcha_render_lambda = -> { render json: spam_action_response_fields(spammable), status: :conflict }
|
||||||
with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block)
|
with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
---
|
---
|
||||||
data_category: Optional
|
data_category: Optional
|
||||||
key_path: usage_activity_by_stage_monthly.plan.epics
|
key_path: usage_activity_by_stage_monthly.plan.epics
|
||||||
description:
|
description: Count distinct author ids from epics
|
||||||
product_section: dev
|
product_section: dev
|
||||||
product_stage:
|
product_stage: plan
|
||||||
product_group: group::plan
|
product_group: group::plan
|
||||||
product_category:
|
product_category: epics
|
||||||
value_type: number
|
value_type: number
|
||||||
status: data_available
|
status: data_available
|
||||||
time_frame: 28d
|
time_frame: 28d
|
||||||
data_source:
|
data_source: database
|
||||||
distribution:
|
distribution:
|
||||||
- ce
|
- ee
|
||||||
tier:
|
tier:
|
||||||
- free
|
- premium
|
||||||
skip_validation: true
|
|
||||||
performance_indicator_type:
|
performance_indicator_type:
|
||||||
- gmau
|
- gmau
|
||||||
- paid_gmau
|
- paid_gmau
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
---
|
---
|
||||||
data_category: Operational
|
data_category: Operational
|
||||||
key_path: usage_activity_by_stage.plan.epics
|
key_path: usage_activity_by_stage.plan.epics
|
||||||
description:
|
description: Count distinct author ids from epics
|
||||||
product_section: dev
|
product_section: dev
|
||||||
product_stage:
|
product_stage: plan
|
||||||
product_group: group::plan
|
product_group: group::plan
|
||||||
product_category:
|
product_category: epics
|
||||||
value_type: number
|
value_type: number
|
||||||
status: data_available
|
status: data_available
|
||||||
time_frame: all
|
time_frame: all
|
||||||
data_source:
|
data_source: database
|
||||||
distribution:
|
distribution:
|
||||||
- ce
|
- ee
|
||||||
tier:
|
tier:
|
||||||
- free
|
- premium
|
||||||
skip_validation: true
|
|
||||||
|
|
|
@ -167,7 +167,11 @@ curl --header "Authorization: Bearer OAUTH-TOKEN" "https://gitlab.example.com/ap
|
||||||
Read more about [GitLab as an OAuth2 provider](oauth2.md).
|
Read more about [GitLab as an OAuth2 provider](oauth2.md).
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
We recommend that OAuth access tokens have an expiration. You can use a `refresh_token` to refresh tokens. Integrations may need to be updated to refresh tokens prior to expiration, which is based on the [expires_in](https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.14) property in the token endpoint response.
|
We recommend OAuth access tokens have an expiration. You can use the `refresh_token` parameter
|
||||||
|
to refresh tokens. Integrations may need to be updated to use refresh tokens prior to
|
||||||
|
expiration, which is based on the [expires_in](https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.14)
|
||||||
|
property in the token endpoint response. See [OAuth2 token](oauth2.md) documentation
|
||||||
|
for examples requesting a new access token using a refresh token.
|
||||||
|
|
||||||
A default refresh setting of two hours is tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/336598).
|
A default refresh setting of two hours is tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/336598).
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,28 @@ Before starting the flow, generate the `STATE`, the `CODE_VERIFIER` and the `COD
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
1. To retrieve a new `access_token`, use the `refresh_token` parameter. Refresh tokens may
|
||||||
|
be used even after the `access_token` itself expires. This request:
|
||||||
|
- Invalidates the existing `access_token` and `refresh_token`.
|
||||||
|
- Sends new tokens in the response.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
parameters = 'client_id=APP_ID&client_secret=APP_SECRET&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI&code_verifier=CODE_VERIFIER'
|
||||||
|
RestClient.post 'https://gitlab.example.com/oauth/token', parameters
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"expires_in": 7200,
|
||||||
|
"refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f",
|
||||||
|
"created_at": 1628711391
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
The `redirect_uri` must match the `redirect_uri` used in the original
|
The `redirect_uri` must match the `redirect_uri` used in the original
|
||||||
authorization request.
|
authorization request.
|
||||||
|
@ -182,6 +204,28 @@ be used as a CSRF token.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
1. To retrieve a new `access_token`, use the `refresh_token` parameter. Refresh tokens may
|
||||||
|
be used even after the `access_token` itself expires. This request:
|
||||||
|
- Invalidates the existing `access_token` and `refresh_token`.
|
||||||
|
- Sends new tokens in the response.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
parameters = 'client_id=APP_ID&client_secret=APP_SECRET&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI'
|
||||||
|
RestClient.post 'https://gitlab.example.com/oauth/token', parameters
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"expires_in": 7200,
|
||||||
|
"refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f",
|
||||||
|
"created_at": 1628711391
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
The `redirect_uri` must match the `redirect_uri` used in the original
|
The `redirect_uri` must match the `redirect_uri` used in the original
|
||||||
authorization request.
|
authorization request.
|
||||||
|
|
|
@ -1546,7 +1546,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
- `201 OK` on success.
|
- `201 Created` on success.
|
||||||
- `404 User Not Found` if user cannot be found.
|
- `404 User Not Found` if user cannot be found.
|
||||||
- `403 Forbidden` if the user cannot be approved because they are blocked by an administrator or by LDAP synchronization.
|
- `403 Forbidden` if the user cannot be approved because they are blocked by an administrator or by LDAP synchronization.
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ more complicated issues.
|
||||||
To validate the configuration by running a pipeline simulation:
|
To validate the configuration by running a pipeline simulation:
|
||||||
|
|
||||||
1. Paste the GitLab CI configuration to verify into the text box.
|
1. Paste the GitLab CI configuration to verify into the text box.
|
||||||
1. Click the **Simulate pipeline creation for the default branch** checkbox.
|
1. Select the **Simulate pipeline creation for the default branch** checkbox.
|
||||||
1. Click **Validate**.
|
1. Select **Validate**.
|
||||||
|
|
||||||
![Dry run](img/ci_lint_dry_run.png)
|
![Dry run](img/ci_lint_dry_run.png)
|
||||||
|
|
||||||
|
|
|
@ -135,13 +135,13 @@ operation of the pipeline.
|
||||||
|
|
||||||
To execute a pipeline manually:
|
To execute a pipeline manually:
|
||||||
|
|
||||||
1. Navigate to your project's **CI/CD > Pipelines**.
|
1. On the top bar, select **Menu > Projects** and find your project.
|
||||||
1. Select the **Run pipeline** button.
|
1. On the left sidebar, select **CI/CD > Pipelines**.
|
||||||
1. On the **Run pipeline** page:
|
1. Select **Run pipeline**.
|
||||||
1. Select the branch or tag to run the pipeline for in the **Run for branch name or tag** field.
|
1. In the **Run for branch name or tag** field, select the branch or tag to run the pipeline for.
|
||||||
1. Enter any [environment variables](../variables/index.md) required for the pipeline run.
|
1. Enter any [environment variables](../variables/index.md) required for the pipeline to run.
|
||||||
You can set specific variables to have their [values prefilled in the form](#prefill-variables-in-manual-pipelines).
|
You can set specific variables to have their [values prefilled in the form](#prefill-variables-in-manual-pipelines).
|
||||||
1. Click the **Run pipeline** button.
|
1. Select **Run pipeline**.
|
||||||
|
|
||||||
The pipeline now executes the jobs as configured.
|
The pipeline now executes the jobs as configured.
|
||||||
|
|
||||||
|
@ -413,7 +413,7 @@ Feature.disable(:pipeline_graph_layers_view)
|
||||||
|
|
||||||
Pipeline mini graphs take less space and can tell you at a
|
Pipeline mini graphs take less space and can tell you at a
|
||||||
quick glance if all jobs passed or something failed. The pipeline mini graph can
|
quick glance if all jobs passed or something failed. The pipeline mini graph can
|
||||||
be found when you navigate to:
|
be found when you go to:
|
||||||
|
|
||||||
- The pipelines index page.
|
- The pipelines index page.
|
||||||
- A single commit page.
|
- A single commit page.
|
||||||
|
|
|
@ -911,14 +911,16 @@ describe '#show', :snowplow do
|
||||||
expect_snowplow_event(
|
expect_snowplow_event(
|
||||||
category: 'Experiment',
|
category: 'Experiment',
|
||||||
action: 'start',
|
action: 'start',
|
||||||
standard_context: { namespace: group, project: project }
|
namespace: group,
|
||||||
|
project: project
|
||||||
)
|
)
|
||||||
expect_snowplow_event(
|
expect_snowplow_event(
|
||||||
category: 'Experiment',
|
category: 'Experiment',
|
||||||
action: 'sent',
|
action: 'sent',
|
||||||
property: 'property',
|
property: 'property',
|
||||||
label: 'label',
|
label: 'label',
|
||||||
standard_context: { namespace: group, project: project }
|
namespace: group,
|
||||||
|
project: project
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
1
qa/qa.rb
1
qa/qa.rb
|
@ -544,6 +544,7 @@ module QA
|
||||||
autoload :AccessTokens, 'qa/page/component/access_tokens'
|
autoload :AccessTokens, 'qa/page/component/access_tokens'
|
||||||
autoload :CommitModal, 'qa/page/component/commit_modal'
|
autoload :CommitModal, 'qa/page/component/commit_modal'
|
||||||
autoload :VisibilitySetting, 'qa/page/component/visibility_setting'
|
autoload :VisibilitySetting, 'qa/page/component/visibility_setting'
|
||||||
|
autoload :ContentEditor, 'qa/page/component/content_editor'
|
||||||
|
|
||||||
module Import
|
module Import
|
||||||
autoload :Gitlab, 'qa/page/component/import/gitlab'
|
autoload :Gitlab, 'qa/page/component/import/gitlab'
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
module Page
|
||||||
|
module Component
|
||||||
|
module ContentEditor
|
||||||
|
extend QA::Page::PageConcern
|
||||||
|
|
||||||
|
def self.included(base)
|
||||||
|
super
|
||||||
|
|
||||||
|
base.view 'app/assets/javascripts/content_editor/components/content_editor.vue' do
|
||||||
|
element :content_editor_container
|
||||||
|
end
|
||||||
|
|
||||||
|
base.view 'app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue' do
|
||||||
|
element :text_style_dropdown
|
||||||
|
element :text_style_menu_item
|
||||||
|
end
|
||||||
|
|
||||||
|
base.view 'app/assets/javascripts/content_editor/components/toolbar_image_button.vue' do
|
||||||
|
element :file_upload_field
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_heading(heading, text)
|
||||||
|
within_element(:content_editor_container) do
|
||||||
|
text_area.set(text)
|
||||||
|
# wait for text style option to become active after typing
|
||||||
|
has_active_element?(:text_style_dropdown, wait: 1)
|
||||||
|
click_element(:text_style_dropdown)
|
||||||
|
within_element(:text_style_dropdown) do
|
||||||
|
click_element(:text_style_menu_item, text_style: heading)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload_image(image_path)
|
||||||
|
within_element(:content_editor_container) do
|
||||||
|
# add image on a new line
|
||||||
|
text_area.send_keys(:return)
|
||||||
|
find_element(:file_upload_field, visible: false).send_keys(image_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def text_area
|
||||||
|
find('[contenteditable="true"]', visible: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -68,6 +68,18 @@ module QA
|
||||||
def has_no_page?
|
def has_no_page?
|
||||||
has_element?(:create_first_page_link)
|
has_element?(:create_first_page_link)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_heading?(heading_type, text)
|
||||||
|
within_element(:wiki_page_content) do
|
||||||
|
has_css?(heading_type, text: text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_image?(image_file_name)
|
||||||
|
within_element(:wiki_page_content) do
|
||||||
|
has_css?("img[src$='#{image_file_name}']")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,7 @@ module QA
|
||||||
element :wiki_content_textarea
|
element :wiki_content_textarea
|
||||||
element :wiki_message_textbox
|
element :wiki_message_textbox
|
||||||
element :wiki_submit_button
|
element :wiki_submit_button
|
||||||
|
element :try_new_editor_container
|
||||||
end
|
end
|
||||||
|
|
||||||
base.view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
|
base.view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
|
||||||
|
@ -41,6 +42,12 @@ module QA
|
||||||
click_element(:delete_button, Page::Modal::DeleteWiki)
|
click_element(:delete_button, Page::Modal::DeleteWiki)
|
||||||
Page::Modal::DeleteWiki.perform(&:confirm_deletion)
|
Page::Modal::DeleteWiki.perform(&:confirm_deletion)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def use_new_editor
|
||||||
|
within_element(:try_new_editor_container) do
|
||||||
|
click_button('Use the new editor')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ module QA
|
||||||
class Edit < Base
|
class Edit < Base
|
||||||
include Page::Component::WikiPageForm
|
include Page::Component::WikiPageForm
|
||||||
include Page::Component::WikiSidebar
|
include Page::Component::WikiSidebar
|
||||||
|
include Page::Component::ContentEditor
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
RSpec.describe 'Create' do
|
||||||
|
context 'Content Editor' do
|
||||||
|
let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
|
||||||
|
let(:page_title) { 'Content Editor Page' }
|
||||||
|
let(:heading_text) { 'My New Heading' }
|
||||||
|
let(:image_file_name) { 'testfile.png' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Flow::Login.sign_in
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
initial_wiki.project.remove_via_api!
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a formatted Wiki page with an image uploaded', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1861' do
|
||||||
|
initial_wiki.visit!
|
||||||
|
|
||||||
|
Page::Project::Wiki::Show.perform(&:click_new_page)
|
||||||
|
|
||||||
|
Page::Project::Wiki::Edit.perform do |edit|
|
||||||
|
edit.set_title(page_title)
|
||||||
|
edit.use_new_editor
|
||||||
|
edit.add_heading('Heading 1', heading_text)
|
||||||
|
edit.upload_image(File.absolute_path(File.join('qa', 'fixtures', 'designs', image_file_name)))
|
||||||
|
end
|
||||||
|
|
||||||
|
Page::Project::Wiki::Edit.perform(&:click_submit)
|
||||||
|
|
||||||
|
Page::Project::Wiki::Show.perform do |wiki|
|
||||||
|
aggregate_failures 'page shows expected content' do
|
||||||
|
expect(wiki).to have_title(page_title)
|
||||||
|
expect(wiki).to have_heading('h1', heading_text)
|
||||||
|
expect(wiki).to have_image(image_file_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue