Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-07-09 00:34:10 +00:00
parent 7f665f8757
commit 707b5c82a5
39 changed files with 247 additions and 1586 deletions

View File

@ -2,7 +2,7 @@
include:
- local: .gitlab/ci/cng/main.gitlab-ci.yml
- project: 'gitlab-org/quality/pipeline-common'
ref: '8.18.3'
ref: '8.18.4'
file: ci/base.gitlab-ci.yml
stages:

View File

@ -125,7 +125,6 @@ Performance/StringIdentifierArgument:
- 'lib/gitlab/utils/lazy_attributes.rb'
- 'lib/kramdown/parser/atlassian_document_format.rb'
- 'lib/sidebars/context.rb'
- 'qa/qa/flow/purchase.rb'
- 'qa/qa/page/component/ci_icon.rb'
- 'qa/qa/page/component/note.rb'
- 'qa/qa/resource/base.rb'

View File

@ -1 +1 @@
028b25396b2c27c0b994fed07e513e4eaa3f3c0c
3907ef1a1fc3655cc376db339ae19cb1f0f1ccf6

View File

@ -107,7 +107,9 @@ export default {
}
},
onInputValidationStateChange(inputId, value) {
this.$set(this.inputValidStates, inputId, value);
const copy = [...this.inputValidStates];
copy[inputId] = value;
this.inputValidStates = copy;
},
onHighlight(path) {
this.$emit('update:highlight', path);

View File

@ -75,6 +75,13 @@ class PoolRepository < ApplicationRecord
def create_object_pool
object_pool.create
rescue GRPC::AlreadyExists
# The object pool already exists. Nothing to do here.
rescue GRPC::FailedPrecondition => e
# This rescue is temporary until gitaly returns the correct error code for
# "repo exists already". Gitaly error messages are not guaranteed to match
# and so should not typically be used to determine error type.
raise unless e.message.include?('repository exists already')
end
# The members of the pool should have fetched the missing objects to their own

View File

@ -1,5 +1,5 @@
.gl-ml-auto.gl-mr-auto{ class: 'sm:gl-w-1/2' }
.gl-justify-content-left.gl-items-center
.gl-items-center
.gl-font-size-h1
= html_escape(_('%{client_name} is requesting access to your account on %{title}.')) % { title: brand_title.html_safe, client_name: "<strong>#{html_escape(@pre_auth.client.name)}</strong>".html_safe }
.gl-flex.gl-items-center.gl-gap-2.gl-py-5

View File

@ -7,7 +7,7 @@
= _("To continue, please update your password. After you update, you'll be directed to sign in again.")
.info-well
.well-segment
.gl-justify-content-left.gl-display-flex.gl-align-items-center
.gl-display-flex.gl-align-items-center
= render Pajamas::AvatarComponent.new(current_user, size: 24, avatar_options: { data: { qa_selector: 'user_avatar_content' }, title: current_user.username })
.gl-pl-4.gl-break-all
%span= _('Signed in as %{username}') % { username: '@' + current_user.username }

View File

@ -40,7 +40,22 @@ made to your repositories. There's two ways you can accomplish that:
Project.all.find_each { |project| project.update!(repository_read_only: true) }
```
When you're ready to revert this, you can do so with the following command:
To set only a subset of repositories to read-only, run the following:
```ruby
# List of project IDs of projects to set to read-only.
projects = [1,2,3]
projects.each do |p|
project = Project.find p
project.update!(repository_read_only: true)
rescue ActiveRecord::RecordNotFound
puts "Project ID #{p} not found"
end
```
When you're ready to revert this, change `repository_read_only` to `false` on the projects. For example, run the following:
```ruby
Project.all.find_each { |project| project.update!(repository_read_only: false) }

View File

@ -74,24 +74,29 @@ set of widgets for them.
|---|---|---|---|---|
| [WorkItemWidgetAssignees](../../../api/graphql/reference/index.md#workitemwidgetassignees) | List of work item assignees | |`Guest`|Yes|
| [WorkItemWidgetAwardEmoji](../../../api/graphql/reference/index.md#workitemwidgetawardemoji) | Emoji reactions added to work item, including support for upvote/downvote counts | |Anyone who can view|No|
| [WorkItemWidgetColor](../../../api/graphql/reference/index.md#workitemwidgetcolor) | Set color of a work item. **Note:** Color is available only for epics. | |`Reporter`|No|
| [WorkItemWidgetCurrentUserTodos](../../../api/graphql/reference/index.md#workitemwidgetcurrentusertodos) | User todo state of work item | |Anyone who can view|No|
| [WorkItemWidgetDescription](../../../api/graphql/reference/index.md#workitemwidgetdescription) | Description of work item, including support for edited state, timestamp, and author | |`Reporter`|No|
| [WorkItemWidgetDesigns](../../../api/graphql/reference/index.md#workitemwidgetdesigns) | Uploaded images and designs | |`Reporter`|No|
| [WorkItemWidgetDesigns](../../../api/graphql/reference/index.md#workitemwidgetdesigns) | Design attachments for work items | |`Reporter`|No|
| [WorkItemWidgetDevelopment](../../../api/graphql/reference/index.md#workitemwidgetdevelopment) | Show related branches and merge requests for work items | |`Reporter`|No|
| [WorkItemWidgetHealthStatus](../../../api/graphql/reference/index.md#workitemwidgethealthstatus) | Health status assignment support for work item | |`Reporter`|No|
| [WorkItemWidgetHierarchy](../../../api/graphql/reference/index.md#workitemwidgethierarchy) | Hierarchy of work items, including support for boolean representing presence of children. **Note:** Hierarchy is currently available only for OKRs. | `okrs_mvc` |`Guest`|No|
| [WorkItemWidgetHierarchy](../../../api/graphql/reference/index.md#workitemwidgethierarchy) | Hierarchy of work items, including support for boolean representing presence of children. | |`Guest`|No|
| [WorkItemWidgetIteration](../../../api/graphql/reference/index.md#workitemwidgetiteration) | Iteration assignment support for work item | |`Reporter`|No|
| [WorkItemWidgetLabels](../../../api/graphql/reference/index.md#workitemwidgetlabels) | List of labels added to work items, including support for checking whether scoped labels are supported | |`Reporter`|Yes|
| [WorkItemWidgetLinkedItems](../../../api/graphql/reference/index.md#workitemwidgetlinkeditems) | List of work items added as related to a given work item, with possible relationship types being `relates_to`, `blocks`, and `blocked_by`. Includes support for individual counts of blocked status, blocked by, blocking, and related to. | |`Guest`|No|
| [WorkItemWidgetMilestone](../../../api/graphql/reference/index.md#workitemwidgetmilestone) | Milestone assignment support for work item | |`Reporter`|No|
| [WorkItemWidgetNotes](../../../api/graphql/reference/index.md#workitemwidgetnotes) | List of discussions within a work item | |`Guest`|Yes|
| [WorkItemWidgetNotifications](../../../api/graphql/reference/index.md#workitemwidgetnotifications) | Notifications subscription status of a work item for current user | |Anyone who can view|No|
| [WorkItemWidgetParticipants](../../../api/graphql/reference/index.md#workitemwidgetparticipants) | Participants of a work item | |Anyone who can view|No|
| [WorkItemWidgetProgress](../../../api/graphql/reference/index.md#workitemwidgetprogress) | Progress value of a work item. **Note:** Progress is currently available only for OKRs. | `okrs_mvc` |`Reporter`|No|
| [WorkItemWidgetRequirementLegacy](../../../api/graphql/reference/index.md#workitemwidgetrequirementlegacy) | Legacy requirements | | |No|
| [WorkItemWidgetRolledupDates](../../../api/graphql/reference/index.md#workitemwidgetrolledupdates) | Set the start date and due date for epic work items, and roll up the start date and due date from child work items | |`Reporter`|No|
| [WorkItemWidgetStartAndDueDate](../../../api/graphql/reference/index.md#workitemwidgetstartandduedate) | Set start and due dates for a work item | |`Reporter`|No|
| [WorkItemWidgetStatus](../../../api/graphql/reference/index.md#workitemwidgetstatus) | Status of a work item when type is Requirement, with possible status types being `unverified`, `satisfied`, or `failed` | | |No|
| [WorkItemWidgetTestReports](../../../api/graphql/reference/index.md#workitemwidgettestreports) | Test reports associated with a work item | | | |
| [WorkItemWidgetTimeTracking](../../../api/graphql/reference/index.md#workitemwidgettimetracking) | Track total time spent on a work item | |`Reporter`|No|
| [WorkItemWidgetWeight](../../../api/graphql/reference/index.md#workitemwidgetweight) | Set weight of a work item | |`Reporter`|No|
| WorkItemWidgetLock | Lock/Unlock a work item | |`Reporter`|No|
| [WorkItemWidgetColor](../../../api/graphql/reference/index.md#workitemwidgetcolor) | Set color of a work item. **Note:** Color is currently available only for epics. | |`Reporter`|No|
#### Widget availability (updating)
@ -99,23 +104,28 @@ set of widgets for them.
|---|---|---|---|---|---|
| [WorkItemWidgetAssignees](../../../api/graphql/reference/index.md#workitemwidgetassignees) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetAwardEmoji](../../../api/graphql/reference/index.md#workitemwidgetawardemoji) | ✅ | ✔️ | ✅ | ✅ | ✅ |
| [WorkItemWidgetColor](../../../api/graphql/reference/index.md#workitemwidgetcolor) | ✅ | ❌ | ❌ | ❌ | ❌ |
| [WorkItemWidgetCurrentUserTodos](../../../api/graphql/reference/index.md#workitemwidgetcurrentusertodos) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetDescription](../../../api/graphql/reference/index.md#workitemwidgetdescription) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetDesigns](../../../api/graphql/reference/index.md#workitemwidgetdesigns) | ❌ | ✅ | ❌ | ❌ | ❌ |
| [WorkItemWidgetDesigns](../../../api/graphql/reference/index.md#workitemwidgetdesigns) | ✔️ | ✅ | ❌ | ❌ | ❌ |
| [WorkItemWidgetDevelopment](../../../api/graphql/reference/index.md#workitemwidgetdevelopment) | ❌ | ✅ | ❌ | ❌ | ❌ |
| [WorkItemWidgetHealthStatus](../../../api/graphql/reference/index.md#workitemwidgethealthstatus) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetHierarchy](../../../api/graphql/reference/index.md#workitemwidgethierarchy) | ✔ | ✔️ | ❌ | ✅ | ❌ |
| [WorkItemWidgetHierarchy](../../../api/graphql/reference/index.md#workitemwidgethierarchy) | ✅ | ✅ | ❌ | ✅ | ❌ |
| [WorkItemWidgetIteration](../../../api/graphql/reference/index.md#workitemwidgetiteration) | ❌ | ✅ | ✅ | ❌ | ❌ |
| [WorkItemWidgetLabels](../../../api/graphql/reference/index.md#workitemwidgetlabels) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetLinkedItems](../../../api/graphql/reference/index.md#workitemwidgetlinkeditems) | ✔️ | ✔️ | ✔️ | ✅ | ✅ |
| [WorkItemWidgetMilestone](../../../api/graphql/reference/index.md#workitemwidgetmilestone) | 🔍 | ✅ | ✅ | ✅ | ❌ |
| [WorkItemWidgetLinkedItems](../../../api/graphql/reference/index.md#workitemwidgetlinkeditems) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetMilestone](../../../api/graphql/reference/index.md#workitemwidgetmilestone) | | ✅ | ✅ | ✅ | ❌ |
| [WorkItemWidgetNotes](../../../api/graphql/reference/index.md#workitemwidgetnotes) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetNotifications](../../../api/graphql/reference/index.md#workitemwidgetnotifications) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetParticipants](../../../api/graphql/reference/index.md#workitemwidgetparticipants) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [WorkItemWidgetProgress](../../../api/graphql/reference/index.md#workitemwidgetprogress) | ❌ | ❌ | ❌ | ✅ | ✅ |
| [WorkItemWidgetStartAndDueDate](../../../api/graphql/reference/index.md#workitemwidgetstartandduedate) | 🔍 | ✅ | ✅ | ❌ | ✅ |
| [WorkItemWidgetRequirementLegacy](../../../api/graphql/reference/index.md#workitemwidgetrequirementlegacy) | ❌ | ❌ | ❌ | ❌ | ❌ |
| [WorkItemWidgetRolledupDates](../../../api/graphql/reference/index.md#workitemwidgetrolledupdates) | ✅ | ❌ | ❌ | ❌ | ❌ |
| [WorkItemWidgetStartAndDueDate](../../../api/graphql/reference/index.md#workitemwidgetstartandduedate) | ❌ | ✅ | ✅ | ❌ | ✅ |
| [WorkItemWidgetStatus](../../../api/graphql/reference/index.md#workitemwidgetstatus) | ❓ | ❓ | ❓ | ❓ | ❓ |
| [WorkItemWidgetTestReports](../../../api/graphql/reference/index.md#workitemwidgettestreports) | ❌ | ❌ | ❌ | ❌ | ❌ |
| [WorkItemWidgetWeight](../../../api/graphql/reference/index.md#workitemwidgetweight) | 🔍 | ✅ | ✅ | ❌ | ❌ |
| [WorkItemWidgetColor](../../../api/graphql/reference/index.md#workitemwidgettestreports) | ✅ | ❌ | ❌ | ❌ | ❌ |
| [WorkItemWidgetTimeTracking](../../../api/graphql/reference/index.md#workitemwidgettimetracking) | ✅ | ✅ | ✅ | ❌ | ❌ |
| [WorkItemWidgetWeight](../../../api/graphql/reference/index.md#workitemwidgetweight) | ❌ | ✅ | ✅ | ❌ | ❌ |
##### Legend

View File

@ -230,6 +230,14 @@ To resolve the issue:
1. If the error persists, try using a different web browser or an incognito/private browsing window.
#### Error: `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again`
You might get the error `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again.` when purchasing compute minutes.
This issue occurs when the credit card form is re-submitted too quickly within a specific timeframe (3 submissions in 1 minute or 6 submissions in 1 hour).
To resolve this issue, wait a few minutes and try the purchase process again.
## How compute usage is calculated
GitLab uses this formula to calculate the compute usage of a job:

View File

@ -147,13 +147,41 @@ Enable all feature flags maintained by "group::ai framework" by running this com
bundle exec rake gitlab:duo:enable_feature_flags
```
### Recommended: Run GDK in SaaS mode and enable AI features for a test group
### Recommended: Run `gitlab:duo:setup` Rake task to prepare the environment
**Why:** By default, GDK runs GitLab as a self-managed instance. But, the Duo
licensing setup for self-managed is more complex than it is for GitLab.com. If
the feature you are working on available on GitLab.com, you should
run GDK in "SaaS" mode so that you can skip the
[customersDot setup](#option-2-use-your-customersdot-instance-as-a-provider).
This Rake task ensures that the local environment is ready to run GitLab Duo.
The task can be run in either SaaS or Self-Managed modes, depending on which installation you currently imitate in GDK.
If you currently run you local GDK as SaaS (imitating GitLab.com), you need to provide the argument to the task:
`GITLAB_SIMULATE_SAAS=1 bundle exec 'rake gitlab:duo:setup[<test-group-name>]'`
Replace `<test-group-name>` with the name of any top-level group. Duo will be configured for that group. If the
group doesn't exist, it creates a new one. Make sure the script succeeded. It prints error messages with links
on how to resolve the error. You can re-run the script until it succeeds.
If you currently run you local GDK as Self-Managed (default for GDK), no arguments for Rake task are expected:
`GITLAB_SIMULATE_SAAS=0 bundle exec 'rake gitlab:duo:setup'`
It's recommended to run `gdk restart` after the task succeeded.
### Recommended: Set `CLOUD_CONNECTOR_SELF_SIGN_TOKENS` environment variable
If you plan to run you local GDK as Self-Managed (for GDK), it is recommended to set this environment variable.
It has no effect if you run you local GDK as SaaS, so you can always keep it set.
Setting this environment variable will allow the local GL instance to issue tokens itself, without syncing with CustomersDot first.
This is similar how GitLab.com operates, and we allow it for development purposes to simplify the setup.
With it you can skip the [CustomersDot setup](#option-2-use-your-customersdot-instance-as-a-provider).
This can done by either:
- setting it in the `env.runit` file in your GDK root
- executing `export CLOUD_CONNECTOR_SELF_SIGN_TOKENS=1` in your shell (but you need to repeat it for every new session)
You need to restart GDK to apply the change.
If you plan to use local CustomersDot or test cross-service integration, you may want to unset this variable.
### Option A: Run GDK in SaaS mode and enable AI features for a test group
**How:** the following should be set in the `env.runit` file in your GDK root:
@ -163,24 +191,22 @@ run GDK in "SaaS" mode so that you can skip the
export GITLAB_SIMULATE_SAAS=1
```
Run `gdk restart rails` after you update the file so that the value
is picked up by your local instance.
or, just for a current session:
If you are running GDK in SaaS mode, you also need to enable Duo
features for at least one group. To do this, run this command in your `/gitlab` directory:
```shell
export CLOUD_CONNECTOR_SELF_SIGN_TOKENS=1 && gdk restart
```
Make sure you run `gitlab:duo:setup` Rake task `/gitlab` directory:
```shell
GITLAB_SIMULATE_SAAS=1 RAILS_ENV=development bundle exec rake 'gitlab:duo:setup[<test-group-name>]'
```
Replace `<test-group-name>` with the name of any top-level group. If the
group doesn't exist, it creates a new one. You might need to
re-run the script multiple times; it prints error messages with links
on how to resolve the error.
Membership to a group with Duo features enabled is what enables many AI
features. To enable AI feature access locally, make sure that your test user is
a member of the group with Duo features enabled.
a member of the group with Duo features enabled (`<test-group-name>`)
and (for some features) have a [seat assigned](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/subscriptions/subscription-add-ons.md#for-gitlabcom).
Finally, you must clear the GitLab-Rails Redis cache. User access to GitLab Duo
features in SaaS mode is cached in Redis. This cache expires every 60 minutes.
@ -192,6 +218,37 @@ bundle exec rake cache:clear
**Troubleshooting:** If you have problems with your setup at this point, double-check your admin settings. When GDK is running, go to admin settings (Navigation -> Admin), then go to general settings (Settings -> General), and expand the "Account and limit" section. Scroll to the bottom of this section to make sure the setting "Allow use of licensed EE features" is toggled on.
### Option B: Run GDK in Self-Managed mode and enable AI features for the instance
**How:** This is the default for GDK. To set it explicitly, the following should be set in the `env.runit` file in your GDK root:
```shell
# <GDK-root>/env.runit
export GITLAB_SIMULATE_SAAS=0
```
or, just for a current session:
```shell
export CLOUD_CONNECTOR_SELF_SIGN_TOKENS=0 && gdk restart
```
Make sure you executed `gitlab:duo:setup` Rake task in `/gitlab` directory:
```shell
GITLAB_SIMULATE_SAAS=0 RAILS_ENV=development bundle exec rake 'gitlab:duo:setup'
```
and it succeeded.
Some AI features requires a [seat to be assigned](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/subscriptions/subscription-add-ons.md#for-self-managed) to a user to have access.
If you use `CLOUD_CONNECTOR_SELF_SIGN_TOKENS=1` you need to assign `root`/`admin` user to a seat,
to receive a "Code completion test was successful" notification from the Health Check
on the `http://localhost:3000/admin/code_suggestions` page.
Our customers (production environment) do not need to do that to run a Code Suggestions Health Check.
### Recommended: Test clients in Rails console
**Why:** you've completed all of the setup steps, now it's time to confirm that

View File

@ -591,3 +591,16 @@ Once Product Analytics services are running and are connected to your GDK, the t
```shell
bundle exec rspec qa/specs/features/ee/browser_ui/8_monitor/product_analytics/onboarding_spec.rb
```
## Tests that require a global server hook
The [`tag_revision_trigger_prereceive_hook_spec`](https://gitlab.com/gitlab-org/gitlab/-/blob/c3342dac0c6c8e9e11ec049b910eac832600b0bf/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb) requires a global server hook to be pre-configured in the target test environment. When running this tests against a local GDK, the server hook will need to be configured with:
```shell
# From the gdk directory
mkdir -p gitaly-custom-hooks/pre-receive.d
cp gitlab/qa/gdk/pre-receive gitaly-custom-hooks/pre-receive.d
chmod +x gitaly-custom-hooks/pre-receive.d/pre-receive
```
More information on global server hooks can be found in the [server hooks documentation](../../../administration/server_hooks.md#create-the-global-server-hook)

View File

@ -519,6 +519,14 @@ If your credit card is declined when purchasing a GitLab subscription, possible
Check with your financial institution to confirm if any of these reasons apply. If they don't
apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293).
### Error: `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again`
You might get the error `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again.` when purchasing a GitLab subscription.
This issue occurs when the credit card form is re-submitted too quickly within a specific timeframe (3 submissions in 1 minute or 6 submissions in 1 hour).
To resolve this issue, wait a few minutes and try the purchase process again.
### Unable to link subscription to namespace
If you cannot link a subscription to your namespace, ensure that you have the Owner role

View File

@ -500,6 +500,14 @@ If your credit card is declined when purchasing a GitLab subscription, possible
Check with your financial institution to confirm if any of these reasons apply. If they don't
apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293).
### Error: `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again`
You might get the error `Attempt_Exceed_Limitation - Attempt exceed the limitation, refresh page to try again.` when purchasing a GitLab subscription.
This issue occurs when the credit card form is re-submitted too quickly within a specific timeframe (3 submissions in 1 minute or 6 submissions in 1 hour).
To resolve this issue, wait a few minutes and try the purchase process again.
### Check daily and historical billable users
Administrators can get a list of daily and historical billable users in your GitLab instance.

View File

@ -58,7 +58,7 @@ GitLab self-managed administrators can reduce their attack surface by disabling
1. On the left sidebar, at the bottom, select **Admin Area**.
1. Select **Settings > General**.
1. Expand **Visibility and access controls**.
1. Expand **Import and export settings**.
1. Scroll to **Import sources**.
1. Clear checkboxes for importers that are not required.

View File

@ -19522,12 +19522,12 @@ msgstr ""
msgid "DuoProTrial|You have successfully created a trial subscription for GitLab Duo Pro. It will expire on %{exp_date}.%{new_line}To get started, enable the GitLab Duo Pro add-on for team members on this page by turning on the toggle for each team member. The subscription may take a minute to sync, so refresh the page if it's not visible yet."
msgstr ""
msgid "DuoProTrial|You've got %{daysRemaining} day remaining on your GitLab Duo Pro trial!"
msgid_plural "DuoProTrial|You've got %{daysRemaining} days remaining on your GitLab Duo Pro trial!"
msgid "DuoProTrial|You've got %{daysRemaining} day left in your GitLab Duo Pro trial"
msgid_plural "DuoProTrial|You've got %{daysRemaining} days left in your GitLab Duo Pro trial"
msgstr[0] ""
msgstr[1] ""
msgid "DuoProTrial|Your trial ends on %{strongStart}%{trialEndDate}%{strongEnd}. We hope youre enjoying the features of GitLab Duo Pro. To continue using your AI-powered assistant after you trial ends, you'll need to buy an add-on subscription."
msgid "DuoProTrial|Your trial ends on %{strongStart}%{trialEndDate}%{strongEnd}. To continue using features in GitLab Duo Pro, purchase a subscription add-on."
msgstr ""
msgid "Duplicate page: A page with that title already exists in the file %{file}"

View File

@ -35,12 +35,8 @@ module Gitlab
#
# @param plan [Hash] Name of the plan
# @option plan [Hash] Support::Helpers::FREE
# @option plan [Hash] Support::Helpers::PREMIUM
# @option plan [Hash] Support::Helpers::PREMIUM_SELF_MANAGED
# @option plan [Hash] Support::Helpers::ULTIMATE
# @option plan [Hash] Support::Helpers::ULTIMATE_SELF_MANAGED
# @option plan [Hash] Support::Helpers::COMPUTE_MINUTES
# @option plan [Hash] Support::Helpers::STORAGE
# @param users_in_license [Integer] Number of users in license
# @param license_type [Hash] Type of the license
# @option license_type [String] 'license file'

View File

@ -7,30 +7,6 @@ module Gitlab
class Billing < Chemlab::Page
h4 :billing_plan_header
link :start_your_free_trial
link :upgrade_to_premium
link :upgrade_to_ultimate
# Subscription details
button :refresh_seats
def refresh_subscription_seats
refresh_seats
::QA::Support::WaitForRequests.wait_for_requests
end
# Waits for subscription to be synced and UI to be updated
#
# @param subscription_plan [String]
def wait_for_subscription(subscription_plan)
::QA::Support::Waiter.wait_until(
max_duration: ::QA::Support::Helpers::Zuora::ZUORA_TIMEOUT,
sleep_interval: 2,
reload_page: Chemlab.configuration.browser.session,
message: "Subscription plan '#{subscription_plan}' failed to appear"
) do
billing_plan_header.match?(/currently using the #{subscription_plan} saas plan/i)
end
end
end
end
end

View File

@ -52,78 +52,6 @@ module Gitlab
def start_your_free_trial?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :upgrade_to_premium+
# Clicks +upgrade_to_premium+
def upgrade_to_premium
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
# expect(billing.upgrade_to_premium_element).to exist
# end
# @return [Watir::Link] The raw +Link+ element
def upgrade_to_premium_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
# expect(billing).to be_upgrade_to_premium
# end
# @return [Boolean] true if the +upgrade_to_premium+ element is present on the page
def upgrade_to_premium?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :upgrade_to_ultimate+
# Clicks +upgrade_to_ultimate+
def upgrade_to_ultimate
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
# expect(billing.upgrade_to_ultimate_element).to exist
# end
# @return [Watir::Link] The raw +Link+ element
def upgrade_to_ultimate_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
# expect(billing).to be_upgrade_to_ultimate
# end
# @return [Boolean] true if the +upgrade_to_ultimate+ element is present on the page
def upgrade_to_ultimate?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +button :refresh_seats+
# Clicks +refresh_seats+
def refresh_seats
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
# expect(billing.refresh_seats_element).to exist
# end
# @return [Watir::Button] The raw +Button+ element
def refresh_seats_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
# expect(billing).to be_refresh_seats
# end
# @return [Boolean] true if the +refresh_seats+ element is present on the page
def refresh_seats?
# This is a stub, used for indexing. The method is dynamically generated.
end
end
end
end

View File

@ -5,27 +5,11 @@ module Gitlab
module Group
module Settings
class UsageQuotas < Chemlab::Page
# Seats section
div :seats_in_use
p :seats_used
p :seats_owed
table :subscription_users
# Pipelines section
link :pipelines_tab
link :buy_compute_minutes
div :plan_compute_minutes
div :additional_compute_minutes
div :ci_purchase_successful_alert, text: /You have successfully purchased compute minutes/
# Storage section
link :storage_tab
link :purchase_more_storage
div :namespace_usage_total
span :group_usage_message
span :dependency_proxy_size
div :storage_purchased
div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/
span :project_repository_size
span :project_wiki_size
span :project_snippets_size
@ -35,57 +19,6 @@ module Gitlab
div :pending_members
button :approve_member
button :confirm_member_approval, text: /^OK$/
def plan_ci_limits
plan_compute_minutes[/(\d+){2}/]
end
def additional_ci_limits
additional_compute_minutes[/(\d+){2}/]
end
def additional_compute_minutes_added?
# When opening the Usage quotas page, Seats quota tab is opened briefly even when url is to a different tab
::QA::Support::WaitForRequests.wait_for_requests
additional_compute_minutes?
end
# Returns total purchased storage value once it's ready on page
#
# @return [Float] Total purchased storage value in GiB
def total_purchased_storage
::QA::Support::WaitForRequests.wait_for_requests
storage_purchased[/(\d+){2}.\d+/].to_f
end
# Waits for additional compute minutes to be available on the page
def wait_for_additional_compute_minutes_available
::QA::Support::Waiter.wait_until(
max_duration: ::QA::Support::Helpers::Zuora::ZUORA_TIMEOUT,
sleep_interval: 2,
reload_page: Chemlab.configuration.browser.session,
message: 'Expected additional compute minutes but they did not appear.'
) do
additional_compute_minutes_added?
end
end
# Waits for additional compute minutes amount to match the expected number of minutes
#
# @param [String] minutes
def wait_for_additional_compute_minute_limits(minutes)
wait_for_additional_compute_minutes_available
::QA::Support::Waiter.wait_until(
max_duration: ::QA::Support::Helpers::Zuora::ZUORA_TIMEOUT,
sleep_interval: 2,
reload_page: Chemlab.configuration.browser.session,
message: "Expected additional compute minutes to equal #{minutes}"
) do
additional_ci_limits == minutes
end
end
end
end
end

View File

@ -5,222 +5,6 @@ module Gitlab
module Group
module Settings
module UsageQuotas
# @note Defined as +div :seats_in_use+
# @return [String] The text content or value of +seats_in_use+
def seats_in_use
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.seats_in_use_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def seats_in_use_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_seats_in_use
# end
# @return [Boolean] true if the +seats_in_use+ element is present on the page
def seats_in_use?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +p :seats_used+
# @return [String] The text content or value of +seats_used+
def seats_used
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.seats_used_element).to exist
# end
# @return [Watir::P] The raw +P+ element
def seats_used_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_seats_used
# end
# @return [Boolean] true if the +seats_used+ element is present on the page
def seats_used?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +p :seats_owed+
# @return [String] The text content or value of +seats_owed+
def seats_owed
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.seats_owed_element).to exist
# end
# @return [Watir::P] The raw +P+ element
def seats_owed_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_seats_owed
# end
# @return [Boolean] true if the +seats_owed+ element is present on the page
def seats_owed?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +table :subscription_users+
# @return [String] The text content or value of +subscription_users+
def subscription_users
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.subscription_users_element).to exist
# end
# @return [Watir::Table] The raw +Table+ element
def subscription_users_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_subscription_users
# end
# @return [Boolean] true if the +subscription_users+ element is present on the page
def subscription_users?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :pipelines_tab+
# Clicks +pipelines_tab+
def pipelines_tab
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.pipelines_tab_element).to exist
# end
# @return [Watir::Link] The raw +Link+ element
def pipelines_tab_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_pipelines_tab
# end
# @return [Boolean] true if the +pipelines_tab+ element is present on the page
def pipelines_tab?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :buy_compute_minutes+
# Clicks +buy_compute_minutes+
def buy_compute_minutes
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.buy_compute_minutes_element).to exist
# end
# @return [Watir::Link] The raw +Link+ element
def buy_compute_minutes_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_buy_compute_minutes
# end
# @return [Boolean] true if the +buy_compute_minutes+ element is present on the page
def buy_compute_minutes?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :plan_compute_minutes+
# @return [String] The text content or value of +plan_compute_minutes+
def plan_compute_minutes
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.plan_compute_minutes_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def plan_compute_minutes_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_plan_compute_minutes
# end
# @return [Boolean] true if the +plan_compute_minutes+ element is present on the page
def plan_compute_minutes?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :additional_compute_minutes+
# @return [String] The text content or value of +additional_compute_minutes+
def additional_compute_minutes
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.additional_compute_minutes_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def additional_compute_minutes_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_additional_compute_minutes
# end
# @return [Boolean] true if the +additional_compute_minutes+ element is present on the page
def additional_compute_minutes?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :ci_purchase_successful_alert+
# @return [String] The text content or value of +ci_purchase_successful_alert+
def ci_purchase_successful_alert
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.ci_purchase_successful_alert_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def ci_purchase_successful_alert_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_ci_purchase_successful_alert
# end
# @return [Boolean] true if the +ci_purchase_successful_alert+ element is present on the page
def ci_purchase_successful_alert?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :storage_tab+
# Clicks +storage_tab+
def storage_tab
@ -245,54 +29,6 @@ module Gitlab
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :purchase_more_storage+
# Clicks +purchase_more_storage+
def purchase_more_storage
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.purchase_more_storage_element).to exist
# end
# @return [Watir::Link] The raw +Link+ element
def purchase_more_storage_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_purchase_more_storage
# end
# @return [Boolean] true if the +purchase_more_storage+ element is present on the page
def purchase_more_storage?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :used_storage_message+
# @return [String] The text content or value of +used_storage_message+
def used_storage_message
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.used_storage_message_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def used_storage_message_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_used_storage_message
# end
# @return [Boolean] true if the +used_storage_message+ element is present on the page
def used_storage_message?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +span :group_usage_message+
# @return [String] The text content or value of +group_usage_message+
def group_usage_message
@ -340,126 +76,6 @@ module Gitlab
def dependency_proxy_size?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :project_storage_used+
# @return [String] The text content or value of +project_storage_used+
def project_storage_used
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.project_storage_used_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def project_storage_used_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_project_storage_used
# end
# @return [Boolean] true if the +project_storage_used+ element is present on the page
def project_storage_used?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :project+
# @return [String] The text content or value of +project+
def project
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.project_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def project_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_project
# end
# @return [Boolean] true if the +project+ element is present on the page
def project?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :storage_purchased+
# @return [String] The text content or value of +storage_purchased+
def storage_purchased
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.storage_purchased_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def storage_purchased_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_storage_purchased
# end
# @return [Boolean] true if the +storage_purchased+ element is present on the page
def storage_purchased?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :storage_purchase_successful_alert+
# @return [String] The text content or value of +storage_purchase_successful_alert+
def storage_purchase_successful_alert
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.storage_purchase_successful_alert_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def storage_purchase_successful_alert_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_storage_purchase_successful_alert
# end
# @return [Boolean] true if the +storage_purchase_successful_alert+ element is present on the page
def storage_purchase_successful_alert?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +h2 :storage_available_alert+
# @return [String] The text content or value of +storage_available_alert+
def storage_available_alert
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.storage_available_alert_element).to exist
# end
# @return [Watir::H2] The raw +H2+ element
def storage_available_alert_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_storage_available_alert
# end
# @return [Boolean] true if the +storage_available_alert+ element is present on the page
def storage_available_alert?
# This is a stub, used for indexing. The method is dynamically generated.
end
end
end
end

View File

@ -1,67 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Page
module Subscriptions
class New < Chemlab::Page
path '/subscriptions/new'
# Purchase Details
select :plan_name
select :group_name
text_field :number_of_users
text_field :quantity
button :continue_to_billing, text: /Continue to billing/
# Billing address
select :country
text_field :street_address_1
text_field :street_address_2
text_field :city
select :state
text_field :zip_code
button :continue_to_payment, text: /Continue to payment/
# Payment method
# TODO: Revisit when https://gitlab.com/gitlab-org/quality/chemlab/-/issues/6 is closed
iframe :payment_form, id: 'z_hppm_iframe'
text_field(:name_on_card) { payment_form_element.text_field(id: 'input-creditCardHolderName') }
text_field(:card_number) { payment_form_element.text_field(id: 'input-creditCardNumber') }
select(:expiration_month) { payment_form_element.select(id: 'input-creditCardExpirationMonth') }
select(:expiration_year) { payment_form_element.select(id: 'input-creditCardExpirationYear') }
text_field(:cvv) { payment_form_element.text_field(id: 'input-cardSecurityCode') }
link(:review_your_order) { payment_form_element.link(text: /Review your order/) }
# ENDTODO
# ToS Acceptance
div :privacy_and_terms_confirm
# Confirmation
button :confirm_purchase, text: /Confirm purchase/
# Order Summary
div :selected_plan
div :total_amount
# Alerts
div :lock_competition_error, text: /Operation failed due to a lock competition, please retry later./
def purchase
::QA::Support::Retrier.retry_until(
max_duration: 80,
sleep_interval: 10,
message: 'Expected no Zuora lock competition error'
) do
::QA::Runtime::Logger.debug('Attempting to purchase subscription')
privacy_and_terms_confirm_element.click
confirm_purchase
::QA::Support::WaitForRequests.wait_for_requests
!lock_competition_error?
end
end
end
end
end
end

View File

@ -1,651 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Page
module Subscriptions
module New
# @note Defined as +select :plan_name+
# @return [String] The text content or value of +plan_name+
def plan_name
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.plan_name_element).to exist
# end
# @return [Watir::Select] The raw +Select+ element
def plan_name_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_plan_name
# end
# @return [Boolean] true if the +plan_name+ element is present on the page
def plan_name?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +select :group_name+
# @return [String] The text content or value of +group_name+
def group_name
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.group_name_element).to exist
# end
# @return [Watir::Select] The raw +Select+ element
def group_name_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_group_name
# end
# @return [Boolean] true if the +group_name+ element is present on the page
def group_name?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :number_of_users+
# @return [String] The text content or value of +number_of_users+
def number_of_users
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of number_of_users
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.number_of_users = 'value'
# end
# @param value [String] The value to set.
def number_of_users=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.number_of_users_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def number_of_users_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_number_of_users
# end
# @return [Boolean] true if the +number_of_users+ element is present on the page
def number_of_users?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :quantity+
# @return [String] The text content or value of +quantity+
def quantity
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of quantity
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.quantity = 'value'
# end
# @param value [String] The value to set.
def quantity=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.quantity_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def quantity_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_quantity
# end
# @return [Boolean] true if the +quantity+ element is present on the page
def quantity?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +button :continue_to_billing+
# Clicks +continue_to_billing+
def continue_to_billing
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.continue_to_billing_element).to exist
# end
# @return [Watir::Button] The raw +Button+ element
def continue_to_billing_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_continue_to_billing
# end
# @return [Boolean] true if the +continue_to_billing+ element is present on the page
def continue_to_billing?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +select :country+
# @return [String] The text content or value of +country+
def country
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.country_element).to exist
# end
# @return [Watir::Select] The raw +Select+ element
def country_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_country
# end
# @return [Boolean] true if the +country+ element is present on the page
def country?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :street_address_1+
# @return [String] The text content or value of +street_address_1+
def street_address_1
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of street_address_1
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.street_address_1 = 'value'
# end
# @param value [String] The value to set.
def street_address_1=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.street_address_1_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def street_address_1_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_street_address_1
# end
# @return [Boolean] true if the +street_address_1+ element is present on the page
def street_address_1?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :street_address_2+
# @return [String] The text content or value of +street_address_2+
def street_address_2
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of street_address_2
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.street_address_2 = 'value'
# end
# @param value [String] The value to set.
def street_address_2=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.street_address_2_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def street_address_2_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_street_address_2
# end
# @return [Boolean] true if the +street_address_2+ element is present on the page
def street_address_2?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :city+
# @return [String] The text content or value of +city+
def city
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of city
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.city = 'value'
# end
# @param value [String] The value to set.
def city=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.city_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def city_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_city
# end
# @return [Boolean] true if the +city+ element is present on the page
def city?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +select :state+
# @return [String] The text content or value of +state+
def state
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.state_element).to exist
# end
# @return [Watir::Select] The raw +Select+ element
def state_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_state
# end
# @return [Boolean] true if the +state+ element is present on the page
def state?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :zip_code+
# @return [String] The text content or value of +zip_code+
def zip_code
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of zip_code
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.zip_code = 'value'
# end
# @param value [String] The value to set.
def zip_code=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.zip_code_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def zip_code_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_zip_code
# end
# @return [Boolean] true if the +zip_code+ element is present on the page
def zip_code?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +button :continue_to_payment+
# Clicks +continue_to_payment+
def continue_to_payment
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.continue_to_payment_element).to exist
# end
# @return [Watir::Button] The raw +Button+ element
def continue_to_payment_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_continue_to_payment
# end
# @return [Boolean] true if the +continue_to_payment+ element is present on the page
def continue_to_payment?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +iframe :payment_form+
# @return [String] The text content or value of +payment_form+
def payment_form
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.payment_form_element).to exist
# end
# @return [Watir::Iframe] The raw +Iframe+ element
def payment_form_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_payment_form
# end
# @return [Boolean] true if the +payment_form+ element is present on the page
def payment_form?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :name_on_card+
# @return [String] The text content or value of +name_on_card+
def name_on_card
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of name_on_card
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.name_on_card = 'value'
# end
# @param value [String] The value to set.
def name_on_card=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.name_on_card_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def name_on_card_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_name_on_card
# end
# @return [Boolean] true if the +name_on_card+ element is present on the page
def name_on_card?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :card_number+
# @return [String] The text content or value of +card_number+
def card_number
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of card_number
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.card_number = 'value'
# end
# @param value [String] The value to set.
def card_number=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.card_number_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def card_number_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_card_number
# end
# @return [Boolean] true if the +card_number+ element is present on the page
def card_number?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +select :expiration_month+
# @return [String] The text content or value of +expiration_month+
def expiration_month
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.expiration_month_element).to exist
# end
# @return [Watir::Select] The raw +Select+ element
def expiration_month_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_expiration_month
# end
# @return [Boolean] true if the +expiration_month+ element is present on the page
def expiration_month?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +select :expiration_year+
# @return [String] The text content or value of +expiration_year+
def expiration_year
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.expiration_year_element).to exist
# end
# @return [Watir::Select] The raw +Select+ element
def expiration_year_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_expiration_year
# end
# @return [Boolean] true if the +expiration_year+ element is present on the page
def expiration_year?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +text_field :cvv+
# @return [String] The text content or value of +cvv+
def cvv
# This is a stub, used for indexing. The method is dynamically generated.
end
# Set the value of cvv
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# new.cvv = 'value'
# end
# @param value [String] The value to set.
def cvv=(value)
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.cvv_element).to exist
# end
# @return [Watir::TextField] The raw +TextField+ element
def cvv_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_cvv
# end
# @return [Boolean] true if the +cvv+ element is present on the page
def cvv?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :review_your_order+
# Clicks +review_your_order+
def review_your_order
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.review_your_order_element).to exist
# end
# @return [Watir::Link] The raw +Link+ element
def review_your_order_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_review_your_order
# end
# @return [Boolean] true if the +review_your_order+ element is present on the page
def review_your_order?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +button :confirm_purchase+
# Clicks +confirm_purchase+
def confirm_purchase
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.confirm_purchase_element).to exist
# end
# @return [Watir::Button] The raw +Button+ element
def confirm_purchase_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_confirm_purchase
# end
# @return [Boolean] true if the +confirm_purchase+ element is present on the page
def confirm_purchase?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :selected_plan+
# @return [String] The text content or value of +selected_plan+
def selected_plan
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.selected_plan_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def selected_plan_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_selected_plan
# end
# @return [Boolean] true if the +selected_plan+ element is present on the page
def selected_plan?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :total_amount+
# @return [String] The text content or value of +total_amount+
def total_amount
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.total_amount_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def total_amount_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_total_amount
# end
# @return [Boolean] true if the +total_amount+ element is present on the page
def total_amount?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :lock_competition_error+
# @return [String] The text content or value of +lock_competition_error+
def lock_competition_error
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new.lock_competition_error_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def lock_competition_error_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Subscriptions::New.perform do |new|
# expect(new).to be_lock_competition_error
# end
# @return [Boolean] true if the +lock_competition_error+ element is present on the page
def lock_competition_error?
# This is a stub, used for indexing. The method is dynamically generated.
end
end
end
end
end

View File

@ -1,115 +0,0 @@
# frozen_string_literal: true
module QA
module Flow
module Purchase
include Support::Helpers::Plan
extend self
def upgrade_subscription(plan: PREMIUM, skip_contact: false)
Page::Group::Menu.perform(&:go_to_billing)
Gitlab::Page::Group::Settings::Billing.perform do |billing|
billing.send("upgrade_to_#{plan[:name].downcase}")
end
Gitlab::Page::Subscriptions::New.perform do |new_subscription|
new_subscription.continue_to_billing
fill_in_default_info(skip_contact)
new_subscription.purchase
end
end
def purchase_compute_minutes(quantity: 1, skip_contact: false)
Page::Group::Menu.perform(&:go_to_usage_quotas)
Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quota|
usage_quota.pipelines_tab
usage_quota.buy_compute_minutes
end
Gitlab::Page::Subscriptions::New.perform do |compute_minutes|
compute_minutes.quantity = quantity
compute_minutes.continue_to_billing
fill_in_default_info(skip_contact)
compute_minutes.purchase
end
end
def purchase_storage(quantity: 1, skip_contact: false)
Page::Group::Menu.perform(&:go_to_usage_quotas)
Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quota|
usage_quota.storage_tab
usage_quota.purchase_more_storage
end
# Purchase checkout opens a new tab but buying additional storage does not
session = Chemlab.configuration.browser.session.engine
session.switch_window if session.windows.size == 2
Gitlab::Page::Subscriptions::New.perform do |storage|
storage.quantity = quantity
storage.continue_to_billing
fill_in_default_info(skip_contact)
storage.purchase
end
end
def fill_in_customer_info
Gitlab::Page::Subscriptions::New.perform do |subscription|
subscription.country = user_billing_info[:country]
subscription.street_address_1 = user_billing_info[:address_1]
subscription.street_address_2 = user_billing_info[:address_2]
subscription.city = user_billing_info[:city]
subscription.state = user_billing_info[:state]
subscription.zip_code = user_billing_info[:zip]
end
end
def fill_in_payment_info
Gitlab::Page::Subscriptions::New.perform do |subscription|
subscription.name_on_card = credit_card_info[:name]
subscription.card_number = credit_card_info[:number]
subscription.expiration_month = credit_card_info[:month]
subscription.expiration_year = credit_card_info[:year]
subscription.cvv = credit_card_info[:cvv]
subscription.review_your_order
end
end
def fill_in_default_info(skip_contact)
Gitlab::Page::Subscriptions::New.perform do |subscription|
fill_in_customer_info unless skip_contact
subscription.continue_to_payment
fill_in_payment_info
end
end
def credit_card_info
{
name: 'QA Test',
number: '4111111111111111',
month: '01',
year: '2025',
cvv: '232'
}.freeze
end
def user_billing_info
{
country: 'United States of America',
address_1: 'Address 1',
address_2: 'Address 2',
city: 'San Francisco',
state: 'California',
zip: '94102'
}.freeze
end
end
end
end
QA::Flow::Purchase.prepend_mod_with('Flow::Purchase', namespace: QA)

View File

@ -16,8 +16,7 @@ module QA
:name,
:full_path,
# Add visibility to enable create private group
:visibility,
:shared_with_groups
:visibility
# Get group projects
#
@ -181,7 +180,6 @@ module QA
:require_two_factor_authentication,
:share_with_group_lock,
:subgroup_creation_level,
:shared_with_groups,
:two_factor_grace_period
# TODO: Add back visibility comparison once https://gitlab.com/gitlab-org/gitlab/-/issues/331252 is fixed
# :visibility

View File

@ -3,6 +3,11 @@
module QA
RSpec.describe 'Create' do
describe 'Prereceive hook', product_group: :source_code do
# NOTE: this test requires a global server hook to be configured in the target test environment.
# If running this test against a local GDK installation, please follow the instructions in the
# following guide to set up the hook:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md#tests-that-require-a-global-server-hook
let(:project) { create(:project, :with_readme) }
context 'when creating a tag for a ref' do

View File

@ -16,7 +16,7 @@ module QA
Flow::Login.sign_in
end
it 'by adding a home page to the wiki',
it 'by adding a home page to the wiki', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do
project.visit!
@ -37,7 +37,7 @@ module QA
end
end
it 'by adding a second page to the wiki',
it 'by adding a second page to the wiki', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347808' do
wiki.visit!
@ -91,7 +91,7 @@ module QA
end
end
it 'by adding a wiki page with spaces in the path using git push',
it 'by adding a wiki page with spaces in the path using git push', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/442387' do
Resource::Repository::WikiPush.fabricate! do |push|
push.file_name = "#{new_wiki_page_with_spaces_in_the_path}.md"

View File

@ -16,7 +16,7 @@ module QA
Flow::Login.sign_in
end
it 'by manipulating content on the page',
it 'by manipulating content on the page', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347810' do
wiki.visit!

View File

@ -15,7 +15,7 @@ module QA
end
context 'with Wiki Sidebar' do
it 'has all expected links that work',
it 'has all expected links that work', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347814' do
small_wiki.visit!
@ -36,7 +36,7 @@ module QA
end
context 'with Wiki Page List' do
it 'has all expected links that work',
it 'has all expected links that work', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347813' do
large_wiki.visit!

View File

@ -19,7 +19,7 @@ module QA
context 'when a file with the same name already exists' do
let(:file_name) { 'README.md' }
it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/390005' do
it 'throws an error', :blocking, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/390005' do
Page::Project::WebIDE::VSCode.perform do |ide|
ide.upload_file(file_path)
@ -57,7 +57,7 @@ module QA
it_behaves_like 'upload a file'
end
context 'when the file is an image',
context 'when the file is an image', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/390007' do
let(:file_name) { 'dk.png' }

View File

@ -6,15 +6,6 @@ module QA
module Plan
FREE = { name: 'free', price: 0, yearly_price: 0, compute_minutes: 400 }.freeze
PREMIUM = {
plan_id: '2c92a00d76f0d5060176f2fb0a5029ff',
rate_charge_id: '2c92a00d76f0d5060176f2fb0a672a02',
name: 'premium',
price: 19,
yearly_price: 228,
compute_minutes: 10000
}.freeze
PREMIUM_SELF_MANAGED = {
plan_id: '2c92a01176f0d50a0176f3043c4d4a53',
rate_charge_id: '2c92a01176f0d50a0176f3043c6a4a58',
@ -23,15 +14,6 @@ module QA
yearly_price: 228
}.freeze
ULTIMATE = {
plan_id: '2c92a0ff76f0d5250176f2f8c86f305a',
rate_charge_id: '2c92a0ff76f0d5250176f2f8c896305c',
name: 'ultimate',
price: 99,
yearly_price: 1188,
compute_minutes: 50000
}.freeze
ULTIMATE_SELF_MANAGED = {
plan_id: '2c92a00c76f0c6c20176f2f9328b33c9',
rate_charge_id: '2c92a00c76f0c6c20176f2fcbb645b5f',
@ -40,22 +22,6 @@ module QA
yearly_price: 1188
}.freeze
COMPUTE_MINUTES = {
plan_id: '2c92a0086a07f4a8016a2c0a1f7b4b4c',
rate_charge_id: '2c92a0fd6a07f4c6016a2c0af07c3f21',
name: 'compute_minutes',
price: 10,
compute_minutes: 1000
}.freeze
STORAGE = {
plan_id: '2c92a00f7279a6f5017279d299d01cf9',
rate_charge_id: '2c92a0ff7279a74f017279d5bea71fc5',
name: 'storage',
price: 60,
storage: 10
}.freeze
LICENSE_TYPE = {
legacy_license: 'legacy license',
online_cloud: 'online license',

View File

@ -1,11 +0,0 @@
# frozen_string_literal: true
module QA
module Support
module Helpers
module Zuora
ZUORA_TIMEOUT = 60
end
end
end
end

View File

@ -44,7 +44,8 @@ module QA
quarantine: example.metadata[:quarantine],
screenshot: example.metadata[:screenshot],
product_group: example.metadata[:product_group],
ci_job_url: QA::Runtime::Env.ci_job_url
ci_job_url: QA::Runtime::Env.ci_job_url,
level: 'E2E'
}
e = example.exception

View File

@ -74,21 +74,17 @@ describe('Pipelines stage component', () => {
createComponent({ updateDropdown: true });
mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, legacyStageReply);
await openStageDropdown();
await findDropdownToggle().trigger('click');
});
it('displays loading state while jobs are being fetched', async () => {
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({ isLoading: true });
await waitForPromises();
it('displays loading state while jobs are being fetched', () => {
expect(findLoadingState().exists()).toBe(true);
expect(findLoadingState().text()).toBe(LegacyPipelineStage.i18n.loadingText);
});
it('does not display loading state after jobs have been fetched', async () => {
await waitForPromises();
await nextTick();
expect(findLoadingState().exists()).toBe(false);
});

View File

@ -125,9 +125,9 @@ describe('IDE Terminal', () => {
canScrollDown: false,
});
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({ canScrollUp: true, canScrollDown: true });
const terminalInstance = GLTerminal.mock.instances[0];
const scrollHandler = terminalInstance.addScrollListener.mock.calls[0][0];
scrollHandler({ canScrollUp: true, canScrollDown: true });
return nextTick().then(() => {
expect(wrapper.findComponent(TerminalControls).props()).toEqual({

View File

@ -1,5 +1,6 @@
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { trimText } from 'helpers/text_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
@ -66,11 +67,14 @@ const defaultMrProps = () => ({
});
const getStatusText = () => wrapper.findByTestId('statusText').text();
const findCancelAutoMergeButton = () => wrapper.find('[data-testid="cancelAutomaticMergeButton"]');
describe('MRWidgetAutoMergeEnabled', () => {
let oldWindowGl;
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
oldWindowGl = window.gl;
@ -82,6 +86,7 @@ describe('MRWidgetAutoMergeEnabled', () => {
});
afterEach(() => {
mock.restore();
window.gl = oldWindowGl;
});
@ -130,13 +135,8 @@ describe('MRWidgetAutoMergeEnabled', () => {
factory({
...defaultMrProps(),
});
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
isCancellingAutoMerge: true,
});
await nextTick();
await findCancelAutoMergeButton().trigger('click');
expect(wrapper.find('.js-cancel-auto-merge').props('loading')).toBe(true);
});

View File

@ -5,7 +5,6 @@ import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import waitForPromises from 'helpers/wait_for_promises';
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesStore from '~/filtered_search/stores/recent_searches_store';
import {
FILTERED_SEARCH_TERM,
TOKEN_TYPE_AUTHOR,
@ -37,6 +36,27 @@ jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils', (
).filterEmptySearchTerm,
}));
const mockFetch = jest.fn().mockResolvedValue([]);
const mockServiceResults = (results) => {
mockFetch.mockResolvedValueOnce(results);
};
jest.mock('~/filtered_search/services/recent_searches_service', () => {
const ServiceMock = jest.fn(function ServiceMock() {
Object.assign(this, {
fetch: mockFetch,
save: jest.fn(),
isAvailable: () => true,
});
});
ServiceMock.isAvailable = () => true;
return {
__esModule: true,
default: ServiceMock,
};
});
const defaultProps = {
namespace: 'gitlab-org/gitlab-test',
recentSearchesStorageKey: 'issues',
@ -140,17 +160,9 @@ describe('FilteredSearchBarRoot', () => {
});
describe('filteredRecentSearches', () => {
beforeEach(() => {
createComponent();
});
it('returns array of recent searches filtering out any string type (unsupported) items', async () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
recentSearches: [{ foo: 'bar' }, 'foo'],
});
mockServiceResults([{ foo: 'bar' }, 'foo']);
createComponent();
await nextTick();
expect(wrapper.vm.filteredRecentSearches).toHaveLength(1);
@ -158,15 +170,11 @@ describe('FilteredSearchBarRoot', () => {
});
it('returns array of recent searches sanitizing any duplicate token values', async () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
recentSearches: [
[tokenValueAuthor, tokenValueLabel, tokenValueMilestone, tokenValueLabel],
[tokenValueAuthor, tokenValueMilestone],
],
});
mockServiceResults([
[tokenValueAuthor, tokenValueLabel, tokenValueMilestone, tokenValueLabel],
[tokenValueAuthor, tokenValueMilestone],
]);
createComponent();
await nextTick();
expect(wrapper.vm.filteredRecentSearches).toHaveLength(2);
@ -174,6 +182,7 @@ describe('FilteredSearchBarRoot', () => {
});
it('returns undefined when recentSearchesStorageKey prop is not set on component', async () => {
createComponent();
wrapper.setProps({
recentSearchesStorageKey: '',
});
@ -209,31 +218,16 @@ describe('FilteredSearchBarRoot', () => {
describe('methods', () => {
describe('setupRecentSearch', () => {
it('initializes `recentSearchesService` and `recentSearchesStore` props when `recentSearchesStorageKey` is available', () => {
createComponent();
expect(wrapper.vm.recentSearchesService instanceof RecentSearchesService).toBe(true);
expect(wrapper.vm.recentSearchesStore instanceof RecentSearchesStore).toBe(true);
});
it('initializes `recentSearchesPromise` prop with a promise by using `recentSearchesService.fetch()`', () => {
expect(localStorage.setItem).not.toHaveBeenCalled();
createComponent();
expect(localStorage.setItem).toHaveBeenCalledWith('canUseLocalStorage', 'true');
expect(wrapper.vm.recentSearchesPromise instanceof Promise).toBe(true);
});
describe('when `recentSearchesStorageKey` is changed', () => {
it('gets new items from local storage', async () => {
it('reinitializes storage service', async () => {
createComponent();
expect(localStorage.getItem).toHaveBeenCalledWith(
expect(RecentSearchesService).toHaveBeenLastCalledWith(
'gitlab-org/gitlab-test-issue-recent-searches',
);
await wrapper.setProps({ recentSearchesStorageKey: RECENT_SEARCHES_STORAGE_KEY_GROUPS });
expect(localStorage.getItem).toHaveBeenCalledWith(
expect(RecentSearchesService).toHaveBeenLastCalledWith(
'gitlab-org/gitlab-test-groups-recent-searches',
);
});
@ -305,16 +299,9 @@ describe('FilteredSearchBarRoot', () => {
it('clears search history from recent searches store', () => {
createComponent();
jest.spyOn(wrapper.vm.recentSearchesStore, 'setRecentSearches').mockReturnValue([]);
expect(localStorage.setItem).toHaveBeenCalledTimes(2);
findGlFilteredSearch().vm.$emit('clear-history');
expect(wrapper.vm.recentSearchesStore.setRecentSearches).toHaveBeenCalledWith([]);
expect(localStorage.setItem).toHaveBeenCalledTimes(4);
expect(localStorage.setItem).toHaveBeenLastCalledWith(
'gitlab-org/gitlab-test-issue-recent-searches',
'[]',
);
expect(wrapper.vm.recentSearches).toEqual([]);
});
});
@ -323,14 +310,7 @@ describe('FilteredSearchBarRoot', () => {
const mockFilters = [tokenValueAuthor, 'foo'];
beforeEach(async () => {
createComponent();
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
filterValue: mockFilters,
});
createComponent({ propsData: { initialFilterValue: mockFilters } });
await nextTick();
});
@ -353,27 +333,11 @@ describe('FilteredSearchBarRoot', () => {
});
it('calls `recentSearchesService.save` with array of searches', async () => {
expect(localStorage.setItem).toHaveBeenCalledTimes(4);
findGlFilteredSearch().vm.$emit('submit');
await waitForPromises();
expect(localStorage.setItem).toHaveBeenCalledTimes(6);
expect(localStorage.setItem).toHaveBeenLastCalledWith(
'gitlab-org/gitlab-test-issue-recent-searches',
JSON.stringify([mockFilters]),
);
});
it('sets `recentSearches` data prop with array of searches', async () => {
expect(localStorage.setItem).toHaveBeenCalledTimes(4);
findGlFilteredSearch().vm.$emit('submit');
await waitForPromises();
expect(localStorage.setItem).toHaveBeenCalledTimes(6);
expect(localStorage.setItem).toHaveBeenLastCalledWith(
'gitlab-org/gitlab-test-issue-recent-searches',
JSON.stringify([mockFilters]),
);
const { save } = RecentSearchesService.mock.instances.at(-1);
expect(save).toHaveBeenLastCalledWith([mockFilters]);
});
it('calls `blurSearchInput` method to remove focus from filter input field', () => {
@ -396,12 +360,10 @@ describe('FilteredSearchBarRoot', () => {
describe('template', () => {
it('renders gl-filtered-search component', async () => {
mockServiceResults(mockHistoryItems);
createComponent();
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
await wrapper.setData({
recentSearches: mockHistoryItems,
});
await nextTick();
await nextTick();
const glFilteredSearchEl = wrapper.findComponent(GlFilteredSearch);

View File

@ -11,20 +11,23 @@ RSpec.describe Gitlab::GitalyClient::ObjectPoolService, feature_category: :sourc
subject { described_class.new(object_pool) }
before do
subject.create(raw_repository) # rubocop:disable Rails/SaveBang
subject.create(raw_repository) # rubocop:disable Rails/SaveBang -- This is a gitaly call
::Gitlab::GitalyClient.clear_stubs!
end
describe '#create' do
it 'exists on disk' do
expect(object_pool.repository.exists?).to be(true)
end
it 'sends a create_object_pool message' do
expected_request = Gitaly::CreateObjectPoolRequest.new(
object_pool: object_pool.gitaly_object_pool,
origin: raw_repository.gitaly_repository)
context 'when the pool already exists' do
it 'returns an error' do
expect do
subject.create(raw_repository) # rubocop:disable Rails/SaveBang
end.to raise_error(GRPC::FailedPrecondition)
expect_next_instance_of(Gitaly::ObjectPoolService::Stub) do |stub|
expect(stub)
.to receive(:create_object_pool)
.with(expected_request, kind_of(Hash))
end
subject.create(raw_repository) # rubocop:disable Rails/SaveBang -- This is a gitaly call
end
end

View File

@ -21,12 +21,10 @@ RSpec.describe ObjectPool::CreateWorker, feature_category: :shared do
pool.create_object_pool
end
it 'cleans up the pool' do
expect do
subject.perform(pool.id)
end.to raise_error(GRPC::FailedPrecondition)
it 'marks the pool as ready' do
subject.perform(pool.id)
expect(pool.reload.failed?).to be(true)
expect(pool.reload).to be_ready
end
end
@ -40,7 +38,7 @@ RSpec.describe ObjectPool::CreateWorker, feature_category: :shared do
subject.perform(pool.id)
end.to raise_error(GRPC::Internal)
expect(pool.reload.failed?).to be(true)
expect(pool.reload).to be_failed
end
end