Prevent premature Merge button before auto-merge loads

What does this MR do and why?

Closes #593704 (closed)

When a project uses merge trains and the GitLab API responds slowly on page load, the merge request widget's primary button briefly renders as a clickable Merge button before switching to Set to auto-merge. Clicking it during that window bypasses the merge train and merges immediately.

The ready-to-merge component renders as soon as the fast GraphQL merge status resolves, but availableAutoMergeStrategies arrives later from a separate REST widget poll. Until it does, the value is undefined, and the button logic falls through to a clickable "Merge" state instead of waiting.

This MR adds a computed autoMergeStrategiesPending (true when availableAutoMergeStrategies === undefined) to the CE and EE ready-to-merge mixins. While pending, the primary button shows a spinner with the label "Checking merge options…" and stays disabled. Once the poll resolves, the button behaves as before. A genuinely empty array ([], auto-merge unavailable) is unaffected and still renders an enabled "Merge" button.

This is a frontend-only change that consumes an existing API field — no backend/API changes, and it's independently deployable.

Note on validation: the production flash is timing-dependent (the widget awaits Promise.all on both polls) and isn't reliably reproducible in a local GDK. This fix and its tests are validated against the store state sequence described in the issue. The reporter may want to confirm the fix in their own environment.

Note — relation to backend enforcement: this change is the frontend safeguard against an accidental click during the load window. Hard, server-side prevention of a direct merge that bypasses the train already exists as an opt-in per-project setting (merge_train_enforcement, see #597962 (closed)); this MR complements it for projects that have not enabled enforcement.

Screenshots or screen recordings

Both captured on a slow-loading merge-train MR (auto-merge strategies delayed to expose the window).

Before — the primary button shows a clickable Merge during the load window (an immediate merge that bypasses the merge train):

image.png

After — the button shows a spinner with "Checking merge options…" and stays disabled until auto-merge availability loads (then becomes "Set to auto-merge"):

image.png

How to set up and validate locally

The flash is timing-dependent — the widget awaits Promise.all on both the (fast) GraphQL merge-status query and the (slower) REST widget poll that carries availableAutoMergeStrategies — so the reliable way to reproduce it is to simulate the late arrival of availableAutoMergeStrategies in the store.

1. Use a merge-train-capable MR

Open an MR on a project with merge trains enabled (Premium/Ultimate) and a pipeline, so the widget offers a merge-train auto-merge strategy (i.e. it ultimately shows Set to auto-merge).

2. Temporarily delay the strategies in the store (repro snippet — do NOT commit)

In app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js, inside setData, replace the two lines that set availableAutoMergeStrategies / preferredAutoMergeStrategy with a cookie-gated delay:

this.autoMergeStrategy = data.auto_merge_strategy;

// TEMP repro #593704 — simulate slow auto-merge-strategy arrival.


// Toggle in the browser console: document.cookie='slow_strategies=1'


const __delayStrategies =
  typeof document !== 'undefined' && document.cookie.includes('slow_strategies=1');
this.availableAutoMergeStrategies = __delayStrategies
  ? undefined
  : data.available_auto_merge_strategies;
this.preferredAutoMergeStrategy = MergeRequestStore.getPreferredAutoMergeStrategy(
  this.availableAutoMergeStrategies,
);
if (__delayStrategies) {
  setTimeout(() => {
    this.availableAutoMergeStrategies = data.available_auto_merge_strategies;
    this.preferredAutoMergeStrategy = MergeRequestStore.getPreferredAutoMergeStrategy(
      this.availableAutoMergeStrategies,
    );
  }, 12000);
}

3. Enable the delay and reload

  1. In the browser DevTools console: document.cookie='slow_strategies=1'
  2. Reload the MR page and watch the primary merge button for ~12s. (The window repeats on each widget poll, ~10–15s, so you get several chances.)

4. What you should see

  • With this fix: the button shows a spinner + "Checking merge options…" and stays disabled during the window, then switches to "Set to auto-merge".
  • Without the fix (before): view the same MR on master (or temporarily revert the three fix files) and repeat — the button is a clickable blue "Merge" during the window, which performs an immediate merge and bypasses the train.

5. Clean up

Remove the temp snippet and clear the cookie: document.cookie='slow_strategies=;max-age=0'.

6. Run the Jest specs

yarn jest spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js

Covering:

  • spinner + disabled state + "Checking merge options…" while availableAutoMergeStrategies is undefined
  • enabled "Merge" button when the array is empty ([]) — genuinely no auto-merge is unaffected (no false-positive disable)
  • transition to "Set to auto-merge" once strategies load
Edited by Daniel Prause

Merge request reports

Loading
Loading