Commit 10d0e569 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent a19a376b
...@@ -196,6 +196,11 @@ ul.content-list { ...@@ -196,6 +196,11 @@ ul.content-list {
display: flex; display: flex;
align-items: center; align-items: center;
white-space: nowrap; white-space: nowrap;
// Override style that allows the flex-row text to wrap.
&.allow-wrap {
white-space: normal;
}
} }
.row-main-content { .row-main-content {
......
...@@ -94,6 +94,25 @@ module ApplicationHelper ...@@ -94,6 +94,25 @@ module ApplicationHelper
sanitize(str, tags: %w(a span)) sanitize(str, tags: %w(a span))
end end
def body_data
{
page: body_data_page,
page_type_id: controller.params[:id],
find_file: find_file_path,
group: "#{@group&.path}"
}.merge(project_data)
end
def project_data
return {} unless @project
{
project_id: @project.id,
project: @project.path,
namespace_id: @project.namespace&.id
}
end
def body_data_page def body_data_page
[*controller.controller_path.split('/'), controller.action_name].compact.join(':') [*controller.controller_path.split('/'), controller.action_name].compact.join(':')
end end
......
- managed_namespace_help_text = s_('ClusterIntegration|Choose a prefix to be used for your namespaces. Defaults to your project path.') - managed_namespace_help_text = s_('ClusterIntegration|Set a prefix for your namespaces. If not set, defaults to your project path. If modified, existing environments will use their current namespaces until the cluster cache is cleared.')
- non_managed_namespace_help_text = s_('ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, pod logs, and Web terminals.') - non_managed_namespace_help_text = s_('ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, pod logs, and Web terminals.')
- managed_namespace_help_link = link_to _('More information'), help_page_path('user/project/clusters/index.md', - managed_namespace_help_link = link_to _('More information'), help_page_path('user/project/clusters/index.md',
anchor: 'gitlab-managed-clusters'), target: '_blank' anchor: 'gitlab-managed-clusters'), target: '_blank'
......
!!! 5 !!! 5
%html{ lang: I18n.locale, class: page_class } %html{ lang: I18n.locale, class: page_class }
= render "layouts/head" = render "layouts/head"
%body{ class: "#{user_application_theme} #{@body_class} #{client_class_list}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } } %body{ class: "#{user_application_theme} #{@body_class} #{client_class_list}", data: body_data }
= render "layouts/init_auto_complete" if @gfm_form = render "layouts/init_auto_complete" if @gfm_form
= render "layouts/init_client_detection_flags" = render "layouts/init_client_detection_flags"
= render 'peek/bar' = render 'peek/bar'
......
- commit = @repository.commit(tag.dereferenced_target) - commit = @repository.commit(tag.dereferenced_target)
- release = @releases.find { |release| release.tag == tag.name } - release = @releases.find { |release| release.tag == tag.name }
%li.flex-row %li.flex-row.allow-wrap
.row-main-content.str-truncated .row-main-content
= icon('tag') = icon('tag')
= link_to tag.name, project_tag_path(@project, tag.name), class: 'item-title ref-name prepend-left-4' = link_to tag.name, project_tag_path(@project, tag.name), class: 'item-title ref-name prepend-left-4'
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
= _("Release") = _("Release")
= link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'tag-release-link' = link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'tag-release-link'
- if release.description.present? - if release.description.present?
.description.md.prepend-top-default .md.prepend-top-default
= markdown_field(release, :description) = markdown_field(release, :description)
.row-fixed-content.controls.flex-row .row-fixed-content.controls.flex-row
......
---
title: Allow patch notes on repo tags page to word wrap
merge_request: 20135
author:
type: fixed
---
title: Add body data elements for pageview context
merge_request: 18450
author:
type: added
---
title: Update copy on managed namespace prefixes
merge_request: 20935
author:
type: fixed
...@@ -30,6 +30,23 @@ The source of the documentation exists within the codebase of each GitLab applic ...@@ -30,6 +30,23 @@ The source of the documentation exists within the codebase of each GitLab applic
Documentation issues and merge requests are part of their respective repositories and all have the label `Documentation`. Documentation issues and merge requests are part of their respective repositories and all have the label `Documentation`.
### Branch naming
The [CI pipeline for the main GitLab project](../pipelines.md) is configured to automatically
run only the jobs that match the type of contribution. If your contribution contains
**only** documentation changes, then only documentation-related jobs will be run, and
the pipeline will complete much faster than a code contribution.
If you are submitting documentation-only changes to Runner, Omnibus, or Charts,
the fast pipeline is not determined automatically. Instead, create branches for
docs-only merge requests using the following guide:
| Branch name | Valid example |
|:----------------------|:-----------------------------|
| Starting with `docs/` | `docs/update-api-issues` |
| Starting with `docs-` | `docs-update-api-issues` |
| Ending in `-docs` | `123-update-api-issues-docs` |
## Contributing to docs ## Contributing to docs
[Contributions to GitLab docs](workflow.md) are welcome from the entire GitLab community. [Contributions to GitLab docs](workflow.md) are welcome from the entire GitLab community.
......
...@@ -54,9 +54,9 @@ In summary: ...@@ -54,9 +54,9 @@ In summary:
- **Do**: Split tests across separate files, unless the tests share expensive setup. - **Do**: Split tests across separate files, unless the tests share expensive setup.
- **Don't**: Put new tests in an existing file without considering the impact on parallelization. - **Don't**: Put new tests in an existing file without considering the impact on parallelization.
## Limit the use of `before(:all)` hook ## Limit the use of `before(:all)` and `after` hooks
Limit the use of `before(:all)` to perform setup tasks with only API calls, non UI operations Limit the use of `before(:all)` hook to perform setup tasks with only API calls, non UI operations
or basic UI operations such as login. or basic UI operations such as login.
We use [`capybara-screenshot`](https://github.com/mattheworiordan/capybara-screenshot) library to automatically save screenshots on failures. We use [`capybara-screenshot`](https://github.com/mattheworiordan/capybara-screenshot) library to automatically save screenshots on failures.
...@@ -66,6 +66,10 @@ This library [saves the screenshots in the RSpec's `after` hook](https://github. ...@@ -66,6 +66,10 @@ This library [saves the screenshots in the RSpec's `after` hook](https://github.
Given this fact, we should limit the use of `before(:all)` to only those operations where a screenshot is not Given this fact, we should limit the use of `before(:all)` to only those operations where a screenshot is not
necessary in case of failure and QA logs would be enough for debugging. necessary in case of failure and QA logs would be enough for debugging.
Similarly, the `after` hook should only be used for non-UI operations. Any UI operations in `after` hook in a test file
would execute before the `after` hook that takes the screenshot. This would result in moving the UI status away from the
point of failure and so the screenshot would not be captured at the right moment.
## Ensure tests do not leave the browser logged in ## Ensure tests do not leave the browser logged in
All QA tests expect to be able to log in at the start of the test. All QA tests expect to be able to log in at the start of the test.
...@@ -74,7 +78,7 @@ That's not possible if a test leaves the browser logged in when it finishes. Nor ...@@ -74,7 +78,7 @@ That's not possible if a test leaves the browser logged in when it finishes. Nor
For an example see: <https://gitlab.com/gitlab-org/gitlab/issues/34736> For an example see: <https://gitlab.com/gitlab-org/gitlab/issues/34736>
Ideally, any actions peformed in an `after(:context)` (or [`before(:context)`](#limit-the-use-of-beforeall-hook)) block would be performed via the API. But if it's necessary to do so via the UI (e.g., if API functionality doesn't exist), make sure to log out at the end of the block. Ideally, any actions peformed in an `after(:context)` (or [`before(:context)`](#limit-the-use-of-beforeall-and-after-hooks)) block would be performed via the API. But if it's necessary to do so via the UI (e.g., if API functionality doesn't exist), make sure to log out at the end of the block.
```ruby ```ruby
after(:all) do after(:all) do
......
...@@ -14,8 +14,11 @@ To access the visibility and access control options: ...@@ -14,8 +14,11 @@ To access the visibility and access control options:
## Default branch protection ## Default branch protection
Branch protection specifies which roles can push to branches and which roles can delete This global option defines the branch protection that applies to every repository's default branch. [Branch protection](../../project/protected_branches.md) specifies which roles can push to branches and which roles can delete
branches. branches. In this case _Default_ refers to a repository's default branch, which in most cases is _master_.
branches. "Default" in this case refers to a repository's default branch, which in most cases would be "master".
This setting applies only to each repositories' default branch. To protect other branches, you must configure branch protection in repository. For details, see [Protected Branches](../../project/protected_branches.md).
To change the default branch protection: To change the default branch protection:
......
...@@ -33,6 +33,9 @@ with `repo:status` access granted: ...@@ -33,6 +33,9 @@ with `repo:status` access granted:
1. Optionally uncheck **Static status check names** checkbox to disable static status check names. 1. Optionally uncheck **Static status check names** checkbox to disable static status check names.
1. Save or optionally click "Test Settings". 1. Save or optionally click "Test Settings".
Once the integration is configured, see [Pipelines for external pull requests](../../../ci/ci_cd_for_external_repos/#pipelines-for-external-pull-requests)
to configure pipelines to run for open pull requests.
#### Static / dynamic status check names #### Static / dynamic status check names
> - Introduced in GitLab 11.5: using static status check names as opt-in option. > - Introduced in GitLab 11.5: using static status check names as opt-in option.
......
...@@ -3595,9 +3595,6 @@ msgstr "" ...@@ -3595,9 +3595,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)" msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "" msgstr ""
msgid "ClusterIntegration|Choose a prefix to be used for your namespaces. Defaults to your project path."
msgstr ""
msgid "ClusterIntegration|Choose the %{startLink}security group %{externalLinkIcon} %{endLink} to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets." msgid "ClusterIntegration|Choose the %{startLink}security group %{externalLinkIcon} %{endLink} to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets."
msgstr "" msgstr ""
...@@ -4132,6 +4129,9 @@ msgstr "" ...@@ -4132,6 +4129,9 @@ msgstr ""
msgid "ClusterIntegration|Service token is required." msgid "ClusterIntegration|Service token is required."
msgstr "" msgstr ""
msgid "ClusterIntegration|Set a prefix for your namespaces. If not set, defaults to your project path. If modified, existing environments will use their current namespaces until the cluster cache is cleared."
msgstr ""
msgid "ClusterIntegration|Show" msgid "ClusterIntegration|Show"
msgstr "" msgstr ""
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Configure', :orchestrated, :mattermost do context 'Configure', :orchestrated, :mattermost do
describe 'Mattermost support' do describe 'Mattermost support' do
it 'user creates a group with a mattermost team' do it 'user creates a group with a mattermost team' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform(&:go_to_groups) Page::Main::Menu.perform(&:go_to_groups)
Page::Dashboard::Groups.perform do |groups| Page::Dashboard::Groups.perform do |groups|
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Manage' do context 'Manage' do
describe 'Project transfer between groups' do describe 'Project transfer between groups' do
it 'user transfers a project between groups' do it 'user transfers a project between groups' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
source_group = Resource::Group.fabricate_via_api! do |group| source_group = Resource::Group.fabricate_via_api! do |group|
group.path = 'source-group' group.path = 'source-group'
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Manage', :smoke do context 'Manage', :smoke do
describe 'basic user login' do describe 'basic user login' do
it 'user logs in using basic credentials and logs out' do it 'user logs in using basic credentials and logs out' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform do |menu| Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area expect(menu).to have_personal_area
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Manage', :orchestrated, :ldap_no_tls, :ldap_tls do context 'Manage', :orchestrated, :ldap_no_tls, :ldap_tls do
describe 'LDAP login' do describe 'LDAP login' do
it 'user logs into GitLab using LDAP credentials' do it 'user logs into GitLab using LDAP credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform do |menu| Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area expect(menu).to have_personal_area
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Manage', :orchestrated, :mattermost do context 'Manage', :orchestrated, :mattermost do
describe 'Mattermost login' do describe 'Mattermost login' do
it 'user logs into Mattermost using GitLab OAuth' do it 'user logs into Mattermost using GitLab OAuth' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
Support::Retrier.retry_on_exception do Support::Retrier.retry_on_exception do
Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) Runtime::Browser.visit(:mattermost, Page::Mattermost::Login)
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Manage' do context 'Manage' do
describe 'Add project member' do describe 'Add project member' do
it 'user adds project member' do it 'user adds project member' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Manage', :smoke do context 'Manage', :smoke do
describe 'Project creation' do describe 'Project creation' do
it 'user creates a new project' do it 'user creates a new project' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
created_project = Resource::Project.fabricate_via_browser_ui! do |project| created_project = Resource::Project.fabricate_via_browser_ui! do |project|
project.name = 'awesome-project' project.name = 'awesome-project'
......
...@@ -23,8 +23,7 @@ module QA ...@@ -23,8 +23,7 @@ module QA
end end
it 'user imports a GitHub repo' do it 'user imports a GitHub repo' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
imported_project # import the project imported_project # import the project
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Manage' do context 'Manage' do
describe 'Project activity' do describe 'Project activity' do
it 'user creates an event in the activity page upon Git push' do it 'user creates an event in the activity page upon Git push' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
project_push = Resource::Repository::ProjectPush.fabricate! do |push| project_push = Resource::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md' push.file_name = 'README.md'
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Create' do context 'Create' do
describe 'Create a new merge request' do describe 'Create a new merge request' do
before do before do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
@project = Resource::Project.fabricate_via_api! do |project| @project = Resource::Project.fabricate_via_api! do |project|
project.name = 'project' project.name = 'project'
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Create' do context 'Create' do
describe 'Merge request creation from fork' do describe 'Merge request creation from fork' do
it 'user forks a project, submits a merge request and maintainer merges it' do it 'user forks a project, submits a merge request and maintainer merges it' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
merge_request = Resource::MergeRequestFromFork.fabricate! do |merge_request| merge_request = Resource::MergeRequestFromFork.fabricate! do |merge_request|
merge_request.fork_branch = 'feature-branch' merge_request.fork_branch = 'feature-branch'
......
...@@ -5,8 +5,7 @@ module QA ...@@ -5,8 +5,7 @@ module QA
context 'Create', :quarantine do context 'Create', :quarantine do
describe 'Merge request rebasing' do describe 'Merge request rebasing' do
it 'user rebases source branch of merge request' do it 'user rebases source branch of merge request' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
project = Resource::Project.fabricate! do |project| project = Resource::Project.fabricate! do |project|
project.name = "only-fast-forward" project.name = "only-fast-forward"
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Create' do context 'Create' do
describe 'Merge request squashing' do describe 'Merge request squashing' do
it 'user squashes commits while merging' do it 'user squashes commits while merging' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
project = Resource::Project.fabricate! do |project| project = Resource::Project.fabricate! do |project|
project.name = "squash-before-merge" project.name = "squash-before-merge"
......
...@@ -16,8 +16,7 @@ module QA ...@@ -16,8 +16,7 @@ module QA
commit_message_of_third_branch = "Add #{file_third_branch}" commit_message_of_third_branch = "Add #{file_third_branch}"
before do before do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
project = Resource::Project.fabricate! do |proj| project = Resource::Project.fabricate! do |proj|
proj.name = 'project-qa-test' proj.name = 'project-qa-test'
......
...@@ -6,8 +6,7 @@ module QA ...@@ -6,8 +6,7 @@ module QA
let(:key_title) { "key for ssh tests #{Time.now.to_f}" } let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
it 'user adds and then removes an SSH key', :smoke do it 'user adds and then removes an SSH key', :smoke do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
key = Resource::SSHKey.fabricate! do |resource| key = Resource::SSHKey.fabricate! do |resource|
resource.title = key_title resource.title = key_title
......
...@@ -6,8 +6,7 @@ module QA ...@@ -6,8 +6,7 @@ module QA
context 'Create', :quarantine do context 'Create', :quarantine do
describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2 do describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2 do
it 'user pushes to the repository' do it 'user pushes to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
# Create a project to push to # Create a project to push to
project = Resource::Project.fabricate! do |project| project = Resource::Project.fabricate! do |project|
......
...@@ -17,20 +17,15 @@ module QA ...@@ -17,20 +17,15 @@ module QA
end end
end end
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
end
around do |example| around do |example|
# Create an SSH key to be used with Git # Create an SSH key to be used with Git
login Flow::Login.sign_in
ssh_key ssh_key
example.run example.run
# Remove the SSH key # Remove the SSH key
login Flow::Login.sign_in
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_settings_link)
Page::Profile::Menu.perform(&:click_ssh_keys) Page::Profile::Menu.perform(&:click_ssh_keys)
Page::Profile::SSHKeys.perform do |ssh_keys| Page::Profile::SSHKeys.perform do |ssh_keys|
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Create' do context 'Create' do
describe 'Git push over HTTP', :ldap_no_tls do describe 'Git push over HTTP', :ldap_no_tls do
it 'user using a personal access token pushes code to the repository' do it 'user using a personal access token pushes code to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
access_token = Resource::PersonalAccessToken.fabricate!.access_token access_token = Resource::PersonalAccessToken.fabricate!.access_token
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Create' do context 'Create' do
describe 'Git push over HTTP', :ldap_no_tls do describe 'Git push over HTTP', :ldap_no_tls do
it 'user pushes code to the repository' do it 'user pushes code to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
project_push = Resource::Repository::ProjectPush.fabricate! do |push| project_push = Resource::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md' push.file_name = 'README.md'
......
...@@ -9,8 +9,7 @@ module QA ...@@ -9,8 +9,7 @@ module QA
let(:key_title) { "key for ssh tests #{Time.now.to_f}" } let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
it 'user adds an ssh key and pushes code to the repository' do it 'user adds an ssh key and pushes code to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
key = Resource::SSHKey.fabricate! do |resource| key = Resource::SSHKey.fabricate! do |resource|
resource.title = key_title resource.title = key_title
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Create', :smoke do context 'Create', :smoke do
describe 'Snippet creation' do describe 'Snippet creation' do
it 'User creates a snippet' do it 'User creates a snippet' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform(&:go_to_snippets) Page::Main::Menu.perform(&:go_to_snippets)
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Create' do context 'Create' do
describe 'Wiki management' do describe 'Wiki management' do
it 'user creates, edits, clones, and pushes to the wiki' do it 'user creates, edits, clones, and pushes to the wiki' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
wiki = Resource::Wiki.fabricate! do |resource| wiki = Resource::Wiki.fabricate! do |resource|
resource.title = 'Home' resource.title = 'Home'
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Release' do context 'Release' do
describe 'Deploy key creation' do describe 'Deploy key creation' do
it 'user adds a deploy key' do it 'user adds a deploy key' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
key = Runtime::Key::RSA.new key = Runtime::Key::RSA.new
deploy_key_title = 'deploy key title' deploy_key_title = 'deploy key title'
......
...@@ -10,8 +10,7 @@ module QA ...@@ -10,8 +10,7 @@ module QA
@job_log_json_flag_enabled = Runtime::Feature.enabled?('job_log_json') @job_log_json_flag_enabled = Runtime::Feature.enabled?('job_log_json')
Runtime::Feature.disable('job_log_json') if @job_log_json_flag_enabled Runtime::Feature.disable('job_log_json') if @job_log_json_flag_enabled
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
@runner_name = "qa-runner-#{Time.now.to_i}" @runner_name = "qa-runner-#{Time.now.to_i}"
......
...@@ -4,8 +4,7 @@ module QA ...@@ -4,8 +4,7 @@ module QA
context 'Release' do context 'Release' do
describe 'Deploy token creation' do describe 'Deploy token creation' do
it 'user adds a deploy token' do it 'user adds a deploy token' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Flow::Login.sign_in
Page::Main::Login.perform(&:sign_in_using_credentials)
deploy_token_name = 'deploy token name' deploy_token_name = 'deploy token name'
one_week_from_now = Date.today + 7 one_week_from_now = Date.today + 7
......
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import AccessorUtilities from '~/lib/utils/accessor'; import AccessorUtilities from '~/lib/utils/accessor';
describe('AccessorUtilities', () => { describe('AccessorUtilities', () => {
useLocalStorageSpy();
const testError = new Error('test error'); const testError = new Error('test error');
describe('isPropertyAccessSafe', () => { describe('isPropertyAccessSafe', () => {
let base; let base;
it('should return `true` if access is safe', () => { it('should return `true` if access is safe', () => {
base = { testProp: 'testProp' }; base = {
testProp: 'testProp',
};
expect(AccessorUtilities.isPropertyAccessSafe(base, 'testProp')).toBe(true); expect(AccessorUtilities.isPropertyAccessSafe(base, 'testProp')).toBe(true);
}); });
...@@ -54,17 +58,12 @@ describe('AccessorUtilities', () => { ...@@ -54,17 +58,12 @@ describe('AccessorUtilities', () => {
}); });
describe('isLocalStorageAccessSafe', () => { describe('isLocalStorageAccessSafe', () => {
beforeEach(() => {
spyOn(window.localStorage, 'setItem');
spyOn(window.localStorage, 'removeItem');
});
it('should return `true` if access is safe', () => { it('should return `true` if access is safe', () => {
expect(AccessorUtilities.isLocalStorageAccessSafe()).toBe(true); expect(AccessorUtilities.isLocalStorageAccessSafe()).toBe(true);
}); });
it('should return `false` if access to .setItem isnt safe', () => { it('should return `false` if access to .setItem isnt safe', () => {
window.localStorage.setItem.and.callFake(() => { window.localStorage.setItem.mockImplementation(() => {
throw testError; throw testError;
}); });
......
...@@ -25,7 +25,7 @@ describe('DOM Utils', () => { ...@@ -25,7 +25,7 @@ describe('DOM Utils', () => {
addClassIfElementExists(childElement, className); addClassIfElementExists(childElement, className);
expect(childElement.classList).toContain(className); expect(childElement.classList).toContainEqual(className);
}); });
it('does not throw if element does not exist', () => { it('does not throw if element does not exist', () => {
...@@ -40,22 +40,44 @@ describe('DOM Utils', () => { ...@@ -40,22 +40,44 @@ describe('DOM Utils', () => {
describe('canScrollUp', () => { describe('canScrollUp', () => {
[1, 100].forEach(scrollTop => { [1, 100].forEach(scrollTop => {
it(`is true if scrollTop is > 0 (${scrollTop})`, () => { it(`is true if scrollTop is > 0 (${scrollTop})`, () => {
expect(canScrollUp({ scrollTop })).toBe(true); expect(
canScrollUp({
scrollTop,
}),
).toBe(true);
}); });
}); });
[0, -10].forEach(scrollTop => { [0, -10].forEach(scrollTop => {
it(`is false if scrollTop is <= 0 (${scrollTop})`, () => { it(`is false if scrollTop is <= 0 (${scrollTop})`, () => {
expect(canScrollUp({ scrollTop })).toBe(false); expect(
canScrollUp({
scrollTop,
}),
).toBe(false);
}); });
}); });
it('is true if scrollTop is > margin', () => { it('is true if scrollTop is > margin', () => {
expect(canScrollUp({ scrollTop: TEST_MARGIN + 1 }, TEST_MARGIN)).toBe(true); expect(
canScrollUp(
{
scrollTop: TEST_MARGIN + 1,
},
TEST_MARGIN,
),
).toBe(true);
}); });
it('is false if scrollTop is <= margin', () => { it('is false if scrollTop is <= margin', () => {
expect(canScrollUp({ scrollTop: TEST_MARGIN }, TEST_MARGIN)).toBe(false); expect(
canScrollUp(
{
scrollTop: TEST_MARGIN,
},
TEST_MARGIN,
),
).toBe(false);
}); });
}); });
...@@ -63,7 +85,11 @@ describe('DOM Utils', () => { ...@@ -63,7 +85,11 @@ describe('DOM Utils', () => {
let element; let element;
beforeEach(() => { beforeEach(() => {
element = { scrollTop: 7, offsetHeight: 22, scrollHeight: 30 }; element = {
scrollTop: 7,
offsetHeight: 22,
scrollHeight: 30,
};
}); });
it('is true if element can be scrolled down', () => { it('is true if element can be scrolled down', () => {
......
...@@ -20,7 +20,7 @@ describe('File upload', () => { ...@@ -20,7 +20,7 @@ describe('File upload', () => {
const btn = document.querySelector('.js-button'); const btn = document.querySelector('.js-button');
const input = document.querySelector('.js-input'); const input = document.querySelector('.js-input');
spyOn(input, 'click'); jest.spyOn(input, 'click').mockReturnValue();
btn.click(); btn.click();
...@@ -43,7 +43,7 @@ describe('File upload', () => { ...@@ -43,7 +43,7 @@ describe('File upload', () => {
const btn = document.querySelector('.js-button'); const btn = document.querySelector('.js-button');
fileUpload('.js-not-button', '.js-input'); fileUpload('.js-not-button', '.js-input');
spyOn(input, 'click'); jest.spyOn(input, 'click').mockReturnValue();
btn.click(); btn.click();
...@@ -55,7 +55,7 @@ describe('File upload', () => { ...@@ -55,7 +55,7 @@ describe('File upload', () => {
const btn = document.querySelector('.js-button'); const btn = document.querySelector('.js-button');
fileUpload('.js-button', '.js-not-input'); fileUpload('.js-button', '.js-not-input');
spyOn(input, 'click'); jest.spyOn(input, 'click').mockReturnValue();
btn.click(); btn.click();
......
...@@ -17,51 +17,44 @@ describe('Icon utils', () => { ...@@ -17,51 +17,44 @@ describe('Icon utils', () => {
let axiosMock; let axiosMock;
let mockEndpoint; let mockEndpoint;
let getIcon;
const mockName = 'mockIconName'; const mockName = 'mockIconName';
const mockPath = 'mockPath'; const mockPath = 'mockPath';
const getIcon = () => iconUtils.getSvgIconPathContent(mockName);
beforeEach(() => { beforeEach(() => {
axiosMock = new MockAdapter(axios); axiosMock = new MockAdapter(axios);
mockEndpoint = axiosMock.onGet(gon.sprite_icons); mockEndpoint = axiosMock.onGet(gon.sprite_icons);
getIcon = iconUtils.getSvgIconPathContent(mockName);
}); });
afterEach(() => { afterEach(() => {
axiosMock.restore(); axiosMock.restore();
}); });
it('extracts svg icon path content from sprite icons', done => { it('extracts svg icon path content from sprite icons', () => {
mockEndpoint.replyOnce( mockEndpoint.replyOnce(
200, 200,
`<svg><symbol id="${mockName}"><path d="${mockPath}"/></symbol></svg>`, `<svg><symbol id="${mockName}"><path d="${mockPath}"/></symbol></svg>`,
); );
getIcon
.then(path => { return getIcon().then(path => {
expect(path).toBe(mockPath); expect(path).toBe(mockPath);
done(); });
})
.catch(done.fail);
}); });
it('returns null if icon path content does not exist', done => { it('returns null if icon path content does not exist', () => {
mockEndpoint.replyOnce(200, ``); mockEndpoint.replyOnce(200, ``);
getIcon
.then(path => { return getIcon().then(path => {
expect(path).toBe(null); expect(path).toBe(null);
done(); });
})
.catch(done.fail);
}); });
it('returns null if an http error occurs', done => { it('returns null if an http error occurs', () => {
mockEndpoint.replyOnce(500); mockEndpoint.replyOnce(500);
getIcon
.then(path => { return getIcon().then(path => {
expect(path).toBe(null); expect(path).toBe(null);
done(); });
})
.catch(done.fail);
}); });
}); });
}); });
...@@ -243,7 +243,7 @@ describe('init markdown', () => { ...@@ -243,7 +243,7 @@ describe('init markdown', () => {
}); });
it('uses ace editor insert text when editor is passed in', () => { it('uses ace editor insert text when editor is passed in', () => {
spyOn(editor, 'insert'); jest.spyOn(editor, 'insert').mockReturnValue();
insertMarkdownText({ insertMarkdownText({
text: editor.getValue, text: editor.getValue,
...@@ -258,7 +258,7 @@ describe('init markdown', () => { ...@@ -258,7 +258,7 @@ describe('init markdown', () => {
}); });
it('adds block tags on line above and below selection', () => { it('adds block tags on line above and below selection', () => {
spyOn(editor, 'insert'); jest.spyOn(editor, 'insert').mockReturnValue();
const selected = 'this text \n is multiple \n lines'; const selected = 'this text \n is multiple \n lines';
const text = `before \n ${selected} \n after`; const text = `before \n ${selected} \n after`;
...@@ -276,7 +276,7 @@ describe('init markdown', () => { ...@@ -276,7 +276,7 @@ describe('init markdown', () => {
}); });
it('uses ace editor to navigate back tag length when nothing is selected', () => { it('uses ace editor to navigate back tag length when nothing is selected', () => {
spyOn(editor, 'navigateLeft'); jest.spyOn(editor, 'navigateLeft').mockReturnValue();
insertMarkdownText({ insertMarkdownText({
text: editor.getValue, text: editor.getValue,
...@@ -291,7 +291,7 @@ describe('init markdown', () => { ...@@ -291,7 +291,7 @@ describe('init markdown', () => {
}); });
it('ace editor does not navigate back when there is selected text', () => { it('ace editor does not navigate back when there is selected text', () => {
spyOn(editor, 'navigateLeft'); jest.spyOn(editor, 'navigateLeft').mockReturnValue();
insertMarkdownText({ insertMarkdownText({
text: editor.getValue, text: editor.getValue,
......
...@@ -4,7 +4,10 @@ import UsersCache from '~/lib/utils/users_cache'; ...@@ -4,7 +4,10 @@ import UsersCache from '~/lib/utils/users_cache';
describe('UsersCache', () => { describe('UsersCache', () => {
const dummyUsername = 'win'; const dummyUsername = 'win';
const dummyUserId = 123; const dummyUserId = 123;
const dummyUser = { name: 'has a farm', username: 'farmer' }; const dummyUser = {
name: 'has a farm',
username: 'farmer',
};
const dummyUserStatus = 'my status'; const dummyUserStatus = 'my status';
beforeEach(() => { beforeEach(() => {
...@@ -68,7 +71,6 @@ describe('UsersCache', () => { ...@@ -68,7 +71,6 @@ describe('UsersCache', () => {
it('does nothing if cache contains no matching data', () => { it('does nothing if cache contains no matching data', () => {
UsersCache.internalStorage['no body'] = 'no data'; UsersCache.internalStorage['no body'] = 'no data';
UsersCache.remove(dummyUsername); UsersCache.remove(dummyUsername);
expect(UsersCache.internalStorage['no body']).toBe('no data'); expect(UsersCache.internalStorage['no body']).toBe('no data');
...@@ -76,7 +78,6 @@ describe('UsersCache', () => { ...@@ -76,7 +78,6 @@ describe('UsersCache', () => {
it('removes matching data', () => { it('removes matching data', () => {
UsersCache.internalStorage[dummyUsername] = dummyUser; UsersCache.internalStorage[dummyUsername] = dummyUser;
UsersCache.remove(dummyUsername); UsersCache.remove(dummyUsername);
expect(UsersCache.internalStorage).toEqual({}); expect(UsersCache.internalStorage).toEqual({});
...@@ -87,13 +88,16 @@ describe('UsersCache', () => { ...@@ -87,13 +88,16 @@ describe('UsersCache', () => {
let apiSpy; let apiSpy;
beforeEach(() => { beforeEach(() => {
spyOn(Api, 'users').and.callFake((query, options) => apiSpy(query, options)); jest.spyOn(Api, 'users').mockImplementation((query, options) => apiSpy(query, options));
}); });
it('stores and returns data from API call if cache is empty', done => { it('stores and returns data from API call if cache is empty', done => {
apiSpy = (query, options) => { apiSpy = (query, options) => {
expect(query).toBe(''); expect(query).toBe('');
expect(options).toEqual({ username: dummyUsername }); expect(options).toEqual({
username: dummyUsername,
});
return Promise.resolve({ return Promise.resolve({
data: [dummyUser], data: [dummyUser],
}); });
...@@ -110,14 +114,18 @@ describe('UsersCache', () => { ...@@ -110,14 +114,18 @@ describe('UsersCache', () => {
it('returns undefined if Ajax call fails and cache is empty', done => { it('returns undefined if Ajax call fails and cache is empty', done => {
const dummyError = new Error('server exploded'); const dummyError = new Error('server exploded');
apiSpy = (query, options) => { apiSpy = (query, options) => {
expect(query).toBe(''); expect(query).toBe('');
expect(options).toEqual({ username: dummyUsername }); expect(options).toEqual({
username: dummyUsername,
});
return Promise.reject(dummyError); return Promise.reject(dummyError);
}; };
UsersCache.retrieve(dummyUsername) UsersCache.retrieve(dummyUsername)
.then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`)) .then(user => done.fail(`Received unexpected user: ${JSON.stringify(user)}`))
.catch(error => { .catch(error => {
expect(error).toBe(dummyError); expect(error).toBe(dummyError);
}) })
...@@ -127,7 +135,8 @@ describe('UsersCache', () => { ...@@ -127,7 +135,8 @@ describe('UsersCache', () => {
it('makes no Ajax call if matching data exists', done => { it('makes no Ajax call if matching data exists', done => {
UsersCache.internalStorage[dummyUsername] = dummyUser; UsersCache.internalStorage[dummyUsername] = dummyUser;
apiSpy = () => fail(new Error('expected no Ajax call!'));
apiSpy = () => done.fail(new Error('expected no Ajax call!'));
UsersCache.retrieve(dummyUsername) UsersCache.retrieve(dummyUsername)
.then(user => { .then(user => {
...@@ -142,12 +151,13 @@ describe('UsersCache', () => { ...@@ -142,12 +151,13 @@ describe('UsersCache', () => {
let apiSpy; let apiSpy;
beforeEach(() => { beforeEach(() => {
spyOn(Api, 'user').and.callFake(id => apiSpy(id)); jest.spyOn(Api, 'user').mockImplementation(id => apiSpy(id));
}); });
it('stores and returns data from API call if cache is empty', done => { it('stores and returns data from API call if cache is empty', done => {
apiSpy = id => { apiSpy = id => {
expect(id).toBe(dummyUserId); expect(id).toBe(dummyUserId);
return Promise.resolve({ return Promise.resolve({
data: dummyUser, data: dummyUser,
}); });
...@@ -164,13 +174,15 @@ describe('UsersCache', () => { ...@@ -164,13 +174,15 @@ describe('UsersCache', () => {
it('returns undefined if Ajax call fails and cache is empty', done => { it('returns undefined if Ajax call fails and cache is empty', done => {
const dummyError = new Error('server exploded'); const dummyError = new Error('server exploded');
apiSpy = id => { apiSpy = id => {
expect(id).toBe(dummyUserId); expect(id).toBe(dummyUserId);
return Promise.reject(dummyError); return Promise.reject(dummyError);
}; };
UsersCache.retrieveById(dummyUserId) UsersCache.retrieveById(dummyUserId)
.then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`)) .then(user => done.fail(`Received unexpected user: ${JSON.stringify(user)}`))
.catch(error => { .catch(error => {
expect(error).toBe(dummyError); expect(error).toBe(dummyError);
}) })
...@@ -180,7 +192,8 @@ describe('UsersCache', () => { ...@@ -180,7 +192,8 @@ describe('UsersCache', () => {
it('makes no Ajax call if matching data exists', done => { it('makes no Ajax call if matching data exists', done => {
UsersCache.internalStorage[dummyUserId] = dummyUser; UsersCache.internalStorage[dummyUserId] = dummyUser;
apiSpy = () => fail(new Error('expected no Ajax call!'));
apiSpy = () => done.fail(new Error('expected no Ajax call!'));
UsersCache.retrieveById(dummyUserId) UsersCache.retrieveById(dummyUserId)
.then(user => { .then(user => {
...@@ -195,12 +208,13 @@ describe('UsersCache', () => { ...@@ -195,12 +208,13 @@ describe('UsersCache', () => {
let apiSpy; let apiSpy;
beforeEach(() => { beforeEach(() => {
spyOn(Api, 'userStatus').and.callFake(id => apiSpy(id)); jest.spyOn(Api, 'userStatus').mockImplementation(id => apiSpy(id));
}); });
it('stores and returns data from API call if cache is empty', done => { it('stores and returns data from API call if cache is empty', done => {
apiSpy = id => { apiSpy = id => {
expect(id).toBe(dummyUserId); expect(id).toBe(dummyUserId);
return Promise.resolve({ return Promise.resolve({
data: dummyUserStatus, data: dummyUserStatus,
}); });
...@@ -217,13 +231,15 @@ describe('UsersCache', () => { ...@@ -217,13 +231,15 @@ describe('UsersCache', () => {
it('returns undefined if Ajax call fails and cache is empty', done => { it('returns undefined if Ajax call fails and cache is empty', done => {
const dummyError = new Error('server exploded'); const dummyError = new Error('server exploded');
apiSpy = id => { apiSpy = id => {
expect(id).toBe(dummyUserId); expect(id).toBe(dummyUserId);
return Promise.reject(dummyError); return Promise.reject(dummyError);
}; };
UsersCache.retrieveStatusById(dummyUserId) UsersCache.retrieveStatusById(dummyUserId)
.then(userStatus => fail(`Received unexpected user: ${JSON.stringify(userStatus)}`)) .then(userStatus => done.fail(`Received unexpected user: ${JSON.stringify(userStatus)}`))
.catch(error => { .catch(error => {
expect(error).toBe(dummyError); expect(error).toBe(dummyError);
}) })
...@@ -232,8 +248,11 @@ describe('UsersCache', () => { ...@@ -232,8 +248,11 @@ describe('UsersCache', () => {
}); });
it('makes no Ajax call if matching data exists', done => { it('makes no Ajax call if matching data exists', done => {
UsersCache.internalStorage[dummyUserId] = { status: dummyUserStatus }; UsersCache.internalStorage[dummyUserId] = {
apiSpy = () => fail(new Error('expected no Ajax call!')); status: dummyUserStatus,
};
apiSpy = () => done.fail(new Error('expected no Ajax call!'));
UsersCache.retrieveStatusById(dummyUserId) UsersCache.retrieveStatusById(dummyUserId)
.then(userStatus => { .then(userStatus => {
......
...@@ -235,4 +235,88 @@ describe ApplicationHelper do ...@@ -235,4 +235,88 @@ describe ApplicationHelper do
end end
end end
end end
describe '#body_data' do
context 'when @project is not set' do
it 'does not include project data in the body data elements' do
expect(helper.body_data).to eq(
{
page: 'application',
page_type_id: nil,
find_file: nil,
group: ''
}
)
end
context 'when @group is set' do
it 'sets group in the body data elements' do
group = create(:group)
assign(:group, group)
expect(helper.body_data).to eq(
{
page: 'application',
page_type_id: nil,
find_file: nil,
group: group.path
}
)
end
end
end
context 'when @project is set' do
it 'includes all possible body data elements and associates the project elements with project' do
project = create(:project)
assign(:project, project)
expect(helper.body_data).to eq(
{
page: 'application',
page_type_id: nil,
find_file: nil,
group: '',
project_id: project.id,
project: project.name,
namespace_id: project.namespace.id
}
)
end
context 'when controller is issues' do
before do
stub_controller_method(:controller_path, 'projects:issues')
end
context 'when params[:id] is present and the issue exsits and action_name is show' do
it 'sets all project and id elements correctly related to the issue' do
issue = create(:issue)
stub_controller_method(:action_name, 'show')
stub_controller_method(:params, { id: issue.id })
assign(:project, issue.project)
expect(helper.body_data).to eq(
{
page: 'projects:issues:show',
page_type_id: issue.id,
find_file: nil,
group: '',
project_id: issue.project.id,
project: issue.project.name,
namespace_id: issue.project.namespace.id
}
)
end
end
end
end
def stub_controller_method(method_name, value)
allow(helper.controller).to receive(method_name).and_return(value)
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
describe 'layouts/application' do
let(:user) { create(:user) }
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
allow(view).to receive(:experiment_enabled?).and_return(false)
allow(view).to receive(:session).and_return({})
allow(view).to receive(:user_signed_in?).and_return(true)
allow(view).to receive(:current_user).and_return(user)
end
context 'body data elements for pageview context' do
let(:body_data) do
{
body_data_page: 'projects:issues:show',
body_data_page_type_id: '1',
body_data_project_id: '2',
body_data_namespace_id: '3'
}
end
before do
allow(view).to receive(:body_data).and_return(body_data)
render
end
it 'includes the body element page' do
expect(rendered).to include('data-page="projects:issues:show"')
end
it 'includes the body element page_type_id' do
expect(rendered).to include('data-page-type-id="1"')
end
it 'includes the body element project_id' do
expect(rendered).to include('data-project-id="2"')
end
it 'includes the body element namespace_id' do
expect(rendered).to include('data-namespace-id="3"')
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment