.ai/principles/distilled/testing-e2e.md
RSpec.describe for the DevOps stage name, describe for the feature under test, context for conditions, and it for the expected result, so that the full test name reads as a sentence.context block descriptions with when, with, without, for, and, on, in, as, or if (enforced by the RuboCop/RSpec/ContextWording cop).testcase: RSpec metadata tag linking every test to its corresponding test case URL in the GitLab project test cases (https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/:id).testcase URL as an argument to it_behaves_like rather than using the testcase: tag directly.element DSL method and a corresponding data-testid attribute in the view; DO NOT use string/regexp pattern matching in element declarations (forbidden by the QA/ElementWithPattern RuboCop cop).<descriptor>-<type> kebab-case formula (e.g., username-field, edit-button, clone-dropdown); use only the approved type suffixes (-button, -checkbox, -container, -content, -dropdown, -field, -link, -modal, -placeholder, -radio, -tab, -menu_item).required: true to enable dynamic element validation on navigation and click.data-qa-selector attributes in new page objects; use data-testid exclusively (data-qa-selector is deprecated).data-qa-* extensible attributes (e.g., qa_issue_title: issue.title) to select a specific item from a list rather than relying on text matching.bin/qa Test::Sanity::Selectors locally to validate page object selectors; the qa:selectors CI job enforces this on every push.QA::Page::PageConcern, overriding self.prepended, calling super first, and wrapping include/prepend and view/element definitions inside base.class_eval.click_ prefix for page object methods that select a single link or button; use go_to_ prefix for methods that interact with multiple elements to navigate to a page..perform block arguments using the snake_case page object name (e.g., members, merge_request); DO NOT use page (shadows Capybara DSL) or overly long names; use new_page when new would be ambiguous.Resource::Base and implement #fabricate! using only page objects for browser UI interactions.#api_get_path, #api_post_path, and #api_post_body to enable API fabrication.fabricate_via_api! (or the FactoryBot create helper) over fabricate_via_browser_ui! to save time and cost.attribute method with a block to declare resource attributes that depend on other resources or must be populated from the page; remember that all attributes are lazily constructed — call an attribute method before navigating away from the page where it is populated.populate(:attr) inside #fabricate! to eagerly construct attributes that must be captured immediately after creation.IGNORED_RESOURCES list in qa/qa/tools/test_resources_handler.rb.Commit resource (HTTP API) instead of ProjectPush (Git CLI) for creating repository commits; Exception: when the test specifically covers SSH integration or Git CLI behavior.Flow::Login.sign_in (and Flow::Login.while_signed_in) for authentication steps rather than duplicating login page interactions across tests.QA::Flow rather than repeating them inline.before(:all) / before(:context) blocks (not let) for resources that can be shared across multiple examples, to avoid redundant creation; use let when the resource cannot be shared.before(:context) hooks to API calls, non-UI operations, or basic UI operations such as login — complex UI setup in before(:context) prevents screenshot capture on failure.after hooks; UI operations in after move the browser state away from the failure point and prevent accurate screenshots.after(:context) / before(:context) blocks that perform UI actions, so subsequent tests can sign in cleanly.expect() statements unrelated to what the test is verifying — keep tests lean.aggregate_failures (inline block or :aggregate_failures metadata) when a test must contain multiple expectations, so all failures are reported together.expect { ... }.not_to raise_error block; keep actions and assertions separate for clearer failure logs.match_when_negated with has_no_* predicate methods) for page object predicates used with not_to; DO NOT rely on not_to have_* without a negatable matcher, as it waits the full timeout before failing.eventually_ matchers (e.g., expect { value }.to eventually_eq(x).within(max_duration: 120)) for expectations that require waiting on asynchronous state.sleep calls to wait for readiness; use framework helpers (Support::Retrier, Support::Waiter) or resource readiness checks (e.g., runner.wait_until_online) that poll for the correct signal.issue.visit!) rather than using page.go_back or browser history, which can land on unexpected pages.scroll_to_element before interacting with controls that may be below the fold, to avoid element click intercepted or stale element failures.click_element_coordinates when a mask or overlay blocks the page; DO NOT click body to blur, as it can unintentionally trigger other elements.only: metadata to restrict a test to specific environments, pipelines, or jobs (e.g., only: :production, only: { pipeline: :nightly }, only: { job: 'ee:instance' }).except: metadata to exclude a test from specific environments, pipelines, or jobs.:production and a { <switch>: 'value' } hash in the same only:/except: — they are mutually exclusive; control production matching via tld and domain independently.before or after blocks, apply only:/except: metadata to the outer RSpec.describe block, not the it block.quarantine: { only: { subdomain: :staging } } syntax to quarantine a test only for a specific environment.feature_flag: { name: 'flag_name' } RSpec tag to every test that enables a feature flag, so it is skipped on environments where it should not run.scope: :global in the feature_flag metadata when the flag is enabled instance-wide, to skip the test on all live .com environments; omit scope or use a scoped value (:project, :group, :user) to skip only on canary, production, and pre-production.Runtime::Feature.enable(:flag, project: project)); DO NOT enable flags globally unless necessary.:requires_admin when the test performs other admin actions unrelated to the feature flag toggle.activated boolean variable (defaulting to false in initialize) toggled at fabrication time; clean up the variable and conditions after the flag is removed.cng-instance and cng-instance-ff-inverse e2e:test-on-cng jobs run automatically to verify both states.:requires_admin RSpec metadata tag to every test that requires administrator access, to prevent it from running against production and other restricted environments.loader.inflector.inflect block in qa/qa.rb.logger instead of puts for all log output in QA code, to enable log-level control, tagging, and auto-formatting.For the full picture, see: