diff options
Diffstat (limited to 'doc/development/testing_guide')
8 files changed, 179 insertions, 57 deletions
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md index eda1c8c3d10..ea36214f6b7 100644 --- a/doc/development/testing_guide/best_practices.md +++ b/doc/development/testing_guide/best_practices.md @@ -155,16 +155,6 @@ FPROF=1 bin/rspec spec/[path]/[to]/[spec].rb FPROF=flamegraph bin/rspec spec/[path]/[to]/[spec].rb ``` -A common change is to use [`let_it_be`](#common-test-setup): - -```ruby -# Old -let(:project) { create(:project) } - -# New -let_it_be(:project) { create(:project) } -``` - A common cause of a large number of created factories is [factory cascades](https://github.com/test-prof/test-prof/blob/master/docs/profilers/factory_prof.md#factory-flamegraph), which result when factories create and recreate associations. They can be identified by a noticeable difference between `total time` and `top-level time` numbers: @@ -215,6 +205,56 @@ In this case, the `total time` and `top-level time` numbers match more closely: 8 8 0.0477s 0.0477s 0.0477s namespace ``` +##### Let's talk about `let` + +There are various ways to create objects and store them in variables in your tests. They are, from least efficient to most efficient: + +- `let!` creates the object before each example runs. It also creates a new object for every example. You should only use this option if you need to create a clean object before each example without explicitly referring to it. +- `let` lazily creates the object. It isn't created until the object is called. `let` is generally inefficient as it creates a new object for every example. `let` is fine for simple values. However, more efficient variants of `let` are best when dealing with database models such as factories. +- `let_it_be_with_refind` works similar to `let_it_be_with_reload`, but the [former calls `ActiveRecord::Base#find`](https://github.com/test-prof/test-prof/blob/936b29f87b36f88a134e064aa6d8ade143ae7a13/lib/test_prof/ext/active_record_refind.rb#L15) instead of `ActiveRecord::Base#reload`. `reload` is usually faster than `refind`. +- `let_it_be_with_reload` creates an object one time for all examples in the same context, but after each example, the database changes are rolled back, and `object.reload` will be called to restore the object to its original state. This means you can make changes to the object before or during an example. However, there are cases where [state leaks across other models](https://github.com/test-prof/test-prof/blob/master/docs/recipes/let_it_be.md#state-leakage-detection) can occur. In these cases, `let` may be an easier option, especially if only a few examples exist. +- `let_it_be` creates an immutable object one time for all of the examples in the same context. This is a great alternative to `let` and `let!` for objects that do not need to change from one example to another. Using `let_it_be` can dramatically speed up tests that create database models. See <https://github.com/test-prof/test-prof/blob/master/docs/recipes/let_it_be.md#let-it-be> for more details and examples. + +`let_it_be` is the most optimized option since it instantiates an object once and does not change it. If you find yourself needing `let` instead of `let_it_be`, try `let_it_be_with_reload`. + +```ruby +# Old +let(:project) { create(:project) } + +# New +let_it_be(:project) { create(:project) } + +# If you need to expect changes to the object in the test +let_it_be_with_reload(:project) { create(:project) } +``` + +Here is an example of when `let_it_be` cannot be used, but `let_it_be_with_reload` allows for more efficiency than `let`: + +```ruby +let_it_be(:user) { create(:user) } +let_it_be_with_reload(:project) { create(:project) } # The test will fail if `let_it_be` is used + +context 'with a developer' do + before do + project.add_developer(user) + end + + it 'project has an owner and a developer' do + expect(project.members.map(&:access_level)).to match_array([Gitlab::Access::OWNER, Gitlab::Access::DEVELOPER]) + end +end + +context 'with a maintainer' do + before do + project.add_maintainer(user) + end + + it 'project has an owner and a maintainer' do + expect(project.members.map(&:access_level)).to match_array([Gitlab::Access::OWNER, Gitlab::Access::MAINTAINER]) + end +end +``` + #### Stubbing methods within factories You should avoid using `allow(object).to receive(:method)` in factories, as this makes the factory unable to be used with `let_it_be`. diff --git a/doc/development/testing_guide/contract/consumer_tests.md b/doc/development/testing_guide/contract/consumer_tests.md index b4d6882a655..df7c9ee0abd 100644 --- a/doc/development/testing_guide/contract/consumer_tests.md +++ b/doc/development/testing_guide/contract/consumer_tests.md @@ -10,13 +10,15 @@ This tutorial guides you through writing a consumer test from scratch. To start, ## Create the skeleton -Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs` called `discussions.spec.js`. +Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs/project/merge_request` called `discussions.spec.js`. Then, populate it with the following function and parameters: - [`pactWith`](#the-pactwith-function) - [`PactOptions`](#the-pactoptions-parameter) - [`PactFn`](#the-pactfn-parameter) +To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure). + ### The `pactWith` function The Pact consumer test is defined through the `pactWith` function that takes `PactOptions` and the `PactFn`. @@ -36,15 +38,17 @@ const { pactWith } = require('jest-pact'); pactWith( { - consumer: 'Merge Request Page', + consumer: 'MergeRequest#show', provider: 'Merge Request Discussions Endpoint', log: '../logs/consumer.log', - dir: '../contracts', + dir: '../contracts/project/merge_request/show', }, PactFn ); ``` +To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions). + ### The `PactFn` parameter The `PactFn` is where your tests are defined. This is where you set up the mock provider and where you can use the standard Jest methods like [`Jest.describe`](https://jestjs.io/docs/api#describename-fn), [`Jest.beforeEach`](https://jestjs.io/docs/api#beforeeachfn-timeout), and [`Jest.it`](https://jestjs.io/docs/api#testname-fn-timeout). For more information, see [https://jestjs.io/docs/api](https://jestjs.io/docs/api). @@ -54,20 +58,20 @@ const { pactWith } = require('jest-pact'); pactWith( { - consumer: 'Merge Request Page', + consumer: 'MergeRequest#show', provider: 'Merge Request Discussions Endpoint', log: '../logs/consumer.log', dir: '../contracts', }, (provider) => { - describe('Discussions Endpoint', () => { + describe('Merge Request Discussions Endpoint', () => { beforeEach(() => { - + }); it('return a successful body', () => { - + }); }); }, @@ -93,14 +97,14 @@ const { Matchers } = require('@pact-foundation/pact'); pactWith( { - consumer: 'Merge Request Page', + consumer: 'MergeRequest#show', provider: 'Merge Request Discussions Endpoint', log: '../logs/consumer.log', - dir: '../contracts', + dir: '../contracts/project/merge_request/show', }, (provider) => { - describe('Discussions Endpoint', () => { + describe('Merge Request Discussions Endpoint', () => { beforeEach(() => { const interaction = { state: 'a merge request with discussions exists', @@ -144,7 +148,7 @@ Notice how we use `Matchers` in the `body` of the expected response. This allows After the mock provider is set up, you can write the test. For this test, you make a request and expect a particular response. -First, set up the client that makes the API request. To do that, either create or find an existing file under `spec/contracts/consumer/endpoints` and add the following API request. +First, set up the client that makes the API request. To do that, create `spec/contracts/consumer/endpoints/project/merge_requests.js` and add the following API request. ```javascript const axios = require('axios'); @@ -169,18 +173,18 @@ After that's set up, import it to the test file and call it to make the request. const { pactWith } = require('jest-pact'); const { Matchers } = require('@pact-foundation/pact'); -const { getDiscussions } = require('../endpoints/merge_requests'); +const { getDiscussions } = require('../endpoints/project/merge_requests'); pactWith( { - consumer: 'Merge Request Page', + consumer: 'MergeRequest#show', provider: 'Merge Request Discussions Endpoint', log: '../logs/consumer.log', - dir: '../contracts', + dir: '../contracts/project/merge_request/show', }, (provider) => { - describe('Discussions Endpoint', () => { + describe('Merge Request Discussions Endpoint', () => { beforeEach(() => { const interaction = { state: 'a merge request with discussions exists', @@ -230,7 +234,7 @@ There we have it! The consumer test is now set up. You can now try [running this As you may have noticed, the request and response definitions can get large. This results in the test being difficult to read, with a lot of scrolling to find what you want. You can make the test easier to read by extracting these out to a `fixture`. -Create a file under `spec/contracts/consumer/fixtures` called `discussions.fixture.js`. You place the `request` and `response` definitions here. +Create a file under `spec/contracts/consumer/fixtures/project/merge_request` called `discussions.fixture.js` where you will place the `request` and `response` definitions. ```javascript const { Matchers } = require('@pact-foundation/pact'); @@ -274,18 +278,18 @@ With all of that moved to the `fixture`, you can simplify the test to the follow const { pactWith } = require('jest-pact'); const { Discussions } = require('../fixtures/discussions.fixture'); -const { getDiscussions } = require('../endpoints/merge_requests'); +const { getDiscussions } = require('../endpoints/project/merge_requests'); pactWith( { - consumer: 'Merge Request Page', + consumer: 'MergeRequest#show', provider: 'Merge Request Discussions Endpoint', log: '../logs/consumer.log', - dir: '../contracts', + dir: '../contracts/project/merge_request/show', }, (provider) => { - describe('Discussions Endpoint', () => { + describe('Merge Request Discussions Endpoint', () => { beforeEach(() => { const interaction = { state: 'a merge request with discussions exists', diff --git a/doc/development/testing_guide/contract/index.md b/doc/development/testing_guide/contract/index.md index 6556bd85624..8e12eea2874 100644 --- a/doc/development/testing_guide/contract/index.md +++ b/doc/development/testing_guide/contract/index.md @@ -37,3 +37,42 @@ rake contracts:mr:pact:verify:discussions # Verify provider against the rake contracts:mr:pact:verify:metadata # Verify provider against the consumer pacts for metadata rake contracts:mr:test:merge_request[contract_mr] # Run all merge request contract tests ``` + +## Test suite folder structure and naming conventions + +To keep the consumer and provider test suite organized and maintainable, it's important that tests are organized, also that consumers and providers are named consistently. Therefore, it's important to adhere to the following conventions. + +### Test suite folder structure + +Having an organized and sensible folder structure for the test suite makes it easier to find relevant files when reviewing, debugging, or introducing tests. + +#### Consumer tests + +The consumer tests are grouped according to the different pages in the application. Each file contains various types of requests found in a page. As such, the consumer test files are named using the Rails standards of how pages are referenced. For example, the project pipelines page would be the `Project::Pipeline#index` page so the equivalent consumer test would be located in `consumer/specs/project/pipelines/index.spec.js`. + +When defining the location to output the contract generated by the test, we want to follow the same file structure which would be `contracts/project/pipelines/` for this example. This is the structure in `consumer/endpoints` and `consumer/fixtures` as well. + +#### Provider tests + +The provider tests are grouped similarly to our controllers. Each of these tests contains various tests for an API endpoint. For example, the API endpoint to get a list of pipelines for a project would be located in `provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb`. The provider states are structured the same way. + +### Naming conventions + +When writing the consumer and provider tests, there are parts where a name is required for the consumer and provider. Since there are no restrictions imposed by Pact on how these should be named, a naming convention is important to keep it easy for us to figure out which consumer and provider tests are involved during debugging. Pact also uses the consumer and provider names to generate the generated contracts in the `#{consumer_name}-#{provider_name}` format. + +#### Consumer naming + +As mentioned in the [folder structure section](#consumer-tests), consumer tests are grouped according to the different pages in the application. As such, consumer names should follow the same naming format using the Rails standard. For example, the consumer test for `Project::Pipeline#index` would be `ProjectPipeline#index` as the consumer name. Since Pact uses this name to name the contracts it generates, the colons (`::`) are dropped as colons are not valid characters in file names. + +#### Provider naming + +These are the API endpoints that provides the data to the consumer so they are simply named according to the API endpoint they pertain to. Be mindful that this name is as descriptive as possible. For example, if we're writing a test for the `GET /groups/:id/projects` endpoint, we don't want to simply name it "Projects endpoint" as there is a `GET /projects` endpoint as well that also fetches a list of projects the user has access to across all of GitLab. An easy way to name them is by checking out our [API documentation](../../../api/api_resources.md) and naming it the same way it is named in there. So the [`GET /groups/:id/projects`](../../../api/groups.md#list-a-groups-projects) would be called `List a group’s projects` and [`GET /projects`](../../../api/projects.md#list-all-projects) would be called `List all projects`. Subsequently, the test files are named `list_a_groups_projects_helper.rb` and `list_all_projects_helper.rb` respectively. + +There are some cases where the provider being tested may not be documented so, in those cases, fall back to choosing a name that is as descriptive as possible to ensure it's easy to tell what the provider is for. + +#### Conventions summary + +| Tests | Folder structure | Naming convention | +| ----- | ---------------- | ----------------- | +| Consumer Test | Follows the Rails reference standards. For example, `Project::Pipeline#index` would be `consumer/specs/project/pipelines/index.spec.js` | Follows the Rails naming standard. For example, `Project::Pipeline#index` would be `ProjectPipeline#index` | +| Provider Test | Grouped like the Rails controllers. For example, [`List project pipelines` API endpoint](../../../api/pipelines.md#list-project-pipelines) would be `provider/pact_helpers/project/pipelines/provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb` | Follows the API documentation naming scheme. For example, [`GET /projects/:id/pipelines`](../../../api/pipelines.md#list-project-pipelines) would be called `List project pipelines`. | diff --git a/doc/development/testing_guide/contract/provider_tests.md b/doc/development/testing_guide/contract/provider_tests.md index 0da5bcb4aef..92ac4c4ed71 100644 --- a/doc/development/testing_guide/contract/provider_tests.md +++ b/doc/development/testing_guide/contract/provider_tests.md @@ -6,23 +6,25 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Writing provider tests -This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`. +This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`. As Pact is a consumer-driven testing tool, this tutorial assumes that there is an existing consumer test that had already generated a contract for us to work with. ## Create the skeleton -Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/specs`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial. +Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/pact_helpers/project/merge_request`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial. + +To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure). ### The `service_provider` block The `service_provider` block is where the provider test is defined. For this block, put in a description of the service provider. Name it exactly as it is called in the contracts that are derived from the consumer tests. ```ruby -require_relative '../spec_helper' +require_relative '../../../spec_helper' module Provider module DiscussionsHelper Pact.service_provider 'Merge Request Discussions Endpoint' do - + end end end @@ -33,33 +35,35 @@ end The `honours_pact_with` block describes which consumer this provider test is addressing. Similar to the `service_provider` block, name this exactly the same as it's called in the contracts that are derived from the consumer tests. ```ruby -require_relative '../spec_helper' +require_relative '../../../spec_helper' module Provider module DiscussionsHelper Pact.service_provider 'Merge Request Discussions Endpoint' do - honours_pact_with 'Merge Request Page' do - + honours_pact_with 'MergeRequest#show' do + end end end end ``` +To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions). + ## Configure the test app For the provider tests to verify the contracts, you must hook it up to a test app that makes the actual request and return a response to verify against the contract. To do this, configure the `app` the test uses as `Environment::Test.app`, which is defined in [`spec/contracts/provider/environments/test.rb`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/environments/test.rb). ```ruby -require_relative '../spec_helper' +require_relative '../../../spec_helper' module Provider module DiscussionsHelper Pact.service_provider 'Merge Request Discussions Endpoint' do app { Environment::Test.app } - - honours_pact_with 'Merge Request Page' do - + + honours_pact_with 'MergeRequest#show' do + end end end @@ -71,15 +75,15 @@ end Now that the test app is configured, all that is left is to define which contract this provider test is verifying. To do this, set the `pact_uri`. ```ruby -require_relative '../spec_helper' +require_relative '../../../spec_helper' module Provider module DiscussionsHelper Pact.service_provider 'Merge Request Discussions Endpoint' do app { Environment::Test.app } - - honours_pact_with 'Merge Request Page' do - pact_uri '../contracts/merge_request_page-merge_request_discussions_endpoint.json' + + honours_pact_with 'MergeRequest#show' do + pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json' end end end @@ -95,8 +99,8 @@ Under the `contracts:mr` namespace, introduce the Rake task to run this new test ```ruby Pact::VerificationTask.new(:discussions) do |pact| pact.uri( - "#{contracts}/contracts/merge_request_page-merge_request_discussions_endpoint.json", - pact_helper: "#{provider}/specs/discussions_helper.rb" + "#{contracts}/contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json", + pact_helper: "#{provider}/pact_helpers/project/merge_request/discussions_helper.rb" ) end ``` @@ -109,7 +113,7 @@ As the last step, create the test data that allows the provider test to return t You can read more about [provider states](https://docs.pact.io/implementation_guides/ruby/provider_states). We can do global provider states but for this tutorial, the provider state is for one specific `state`. -To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states`. As a quick aside, make sure to also import this state file in the `discussions_helper.rb` file. +To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states/project/merge_request`. Be sure to also import this state file in the `discussions_helper.rb` file. ### Default user in `spec/contracts/provider/spec_helper.rb` @@ -118,10 +122,13 @@ Before you create the test data, note that a default user is created in the [`sp ```ruby RSpec.configure do |config| config.include Devise::Test::IntegrationHelpers + config.include FactoryBot::Syntax::Methods + config.before do - user = FactoryBot.create(:user, name: "Contract Test").tap do |user| + user = create(:user, name: Provider::UsersHelper::CONTRACT_USER_NAME).tap do |user| user.current_sign_in_at = Time.current end + sign_in user end end @@ -134,7 +141,7 @@ Any further modifications to the user that's needed can be done through the indi In the state file, you must define which consumer this provider state is for. You can do that with `provider_states_for`. Make sure that the `name` provided matches the name defined for the consumer. ```ruby -Pact.provider_states_for 'Merge Request Page' do +Pact.provider_states_for 'MergeRequest#show' do end ``` @@ -143,7 +150,7 @@ end In the `provider_states_for` block, you then define the state the test data is for. These states are also defined in the consumer test. In this case, there is a `'a merge request with discussions exists'` state. ```ruby -Pact.provider_states_for "Merge Request Page" do +Pact.provider_states_for "MergeRequest#show" do provider_state "a merge request with discussions exists" do end @@ -155,7 +162,7 @@ end This is where you define the test data creation steps. Use `FactoryBot` to create the data. As you create the test data, you can keep [running the provider test](index.md#run-the-provider-tests) to check on the status of the test and figure out what else is missing in your data setup. ```ruby -Pact.provider_states_for "Merge Request Page" do +Pact.provider_states_for "MergeRequest#show" do provider_state "a merge request with discussions exists" do set_up do user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME) @@ -172,6 +179,28 @@ Pact.provider_states_for "Merge Request Page" do end ``` -Note the `Provider::UsersHelper::CONTRACT_USER_NAME` here to fetch a user is a user that is from the [`spec_helper`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/spec_helper.rb) that sets up a user before any of these tests run. +## Using the test data + +Now that the provider state file is created, you need to import the state file to the provider test. + +```ruby +# frozen_string_literal: true + +require_relative '../../../spec_helper' +require_relative '../../../states/project/merge_request/discussions_state' + +module Provider + module DiscussionsHelper + Pact.service_provider "/merge_request/discussions" do + app { Environments::Test.app } + + honours_pact_with 'Merge Request#show' do + pact_uri '../contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json' + end + end + end +end + +``` -And with that, the provider tests for `discussion_helper.rb` should now pass with this. +And there we have it. The provider test for `discussions_helper.rb` should now pass with this. diff --git a/doc/development/testing_guide/end_to_end/best_practices.md b/doc/development/testing_guide/end_to_end/best_practices.md index 85f8beeacad..00b843ffdbe 100644 --- a/doc/development/testing_guide/end_to_end/best_practices.md +++ b/doc/development/testing_guide/end_to_end/best_practices.md @@ -220,7 +220,7 @@ For example, if you encapsulate some actions and expectations in a private metho it "has Owner role with Owner permissions" do Page::Dashboard::Projects.perform do |projects| projects.filter_by_name(project.name) - + expect(projects).to have_project_with_access_role(project.name, 'Owner') end diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md index 9730115fd9f..06359d612ad 100644 --- a/doc/development/testing_guide/end_to_end/index.md +++ b/doc/development/testing_guide/end_to_end/index.md @@ -174,7 +174,7 @@ See [Review Apps](../review_apps.md) for more details about Review Apps. To run tests in parallel on CI, the [Knapsack](https://github.com/KnapsackPro/knapsack) gem is used. Knapsack reports are generated automatically and stored in the `GCS` bucket -`knapsack-reports` in the `gitlab-qa-resources` project. The [`KnapsackReport`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/tools/knapsack_report.rb) +`knapsack-reports` in the `gitlab-qa-resources` project. The [`KnapsackReport`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/support/knapsack_report.rb) helper handles automated report generation and upload. ## Test metrics diff --git a/doc/development/testing_guide/end_to_end/resources.md b/doc/development/testing_guide/end_to_end/resources.md index dacc428aec6..a519d1ecb47 100644 --- a/doc/development/testing_guide/end_to_end/resources.md +++ b/doc/development/testing_guide/end_to_end/resources.md @@ -568,6 +568,16 @@ def unique_identifiers end ``` +### Resources cleanup + +We have a mechanism to [collect](https://gitlab.com/gitlab-org/gitlab/-/blob/44345381e89d6bbd440f7b4c680d03e8b75b86de/qa/qa/tools/test_resource_data_processor.rb#L32) +all resources created during test executions, and another to [handle](https://gitlab.com/gitlab-org/gitlab/-/blob/44345381e89d6bbd440f7b4c680d03e8b75b86de/qa/qa/tools/test_resources_handler.rb#L44) +these resources. On [dotcom environments](https://about.gitlab.com/handbook/engineering/infrastructure/environments/#environments), after a test suite finishes in the [QA pipelines](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#scheduled-qa-test-pipelines), resources from all passing test are +automatically deleted in the same pipeline run. Resources from all failed tests are reserved for investigation, +and won't be deleted until the following Saturday by a scheduled pipeline. When introducing new resources, please +also make sure to add any resource that cannot be deleted to the [IGNORED_RESOURCES](https://gitlab.com/gitlab-org/gitlab/-/blob/44345381e89d6bbd440f7b4c680d03e8b75b86de/qa/qa/tools/test_resources_handler.rb#L29) +list. + ## Where to ask for help? If you need more information, ask for help on `#quality` channel on Slack diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md index f1083c23406..532bb9fcdef 100644 --- a/doc/development/testing_guide/review_apps.md +++ b/doc/development/testing_guide/review_apps.md @@ -45,7 +45,7 @@ Maintainers can elect to use the [process for merging during broken `master`](ht On every [pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/125315730) in the `qa` stage, the `review-performance` job is automatically started: this job does basic browser performance testing using a -[Sitespeed.io Container](../../user/project/merge_requests/browser_performance_testing.md). +[Sitespeed.io Container](../../ci/testing/browser_performance_testing.md). ## Sample Data for Review Apps |