Cypress previewReady() flakes on opacity assertion against .editorFrame after #3574994
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3586806. -->
Reported by: [penyaskito](https://www.drupal.org/user/959536)
Related to !984
>>>
<h3 id="overview">Overview</h3>
<p>Cypress E2E specs that call <code>cy.previewReady()</code> intermittently fail with:</p>
<p><code>expected <div._editorFrame_…> to have CSS property opacity with the value '1', but the value was '0'</code></p>
<p>Example failure: <a href="https://git.drupalcode.org/project/canvas/-/jobs/9512425">job 9512425</a>, where all 30 tests in <code>multivalue-form-design-number-integer.cy.js</code> failed on this assertion. 16 specs use <code>previewReady</code>, so the flake surfaces unevenly across runs.</p>
<p>The flake was introduced by <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3574994" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3574994</a></span> (specifically MR !638), which changed the assertion target in <code>ui/tests/support/commands.js</code>:</p>
<ul>
<li>The previous selector <code>.canvasEditorFrameScalingContainer</code> has no <code>opacity</code> rule in the editor frame stylesheet, so its computed opacity is always <code>1</code> — the assertion was effectively a no-op presence check.
</li>
<li>The new selector <code>[data-testid="canvas-editor-frame"]</code> targets the parent <code>.editorFrame</code>, which starts at <code>opacity: 0</code> and transitions to <code>1</code> over <code>0.3s</code> only when the local <code>isVisible</code> state flips. <code>isVisible</code> depends on the Redux <code>firstLoadComplete</code> flag, which depends in turn on the preview iframe load completing.
</li>
</ul>
<p>With Cypress's default 4s <code>defaultCommandTimeout</code>, that chain can exceed the budget on loaded CI runners, and a strict <code>'1'</code> string match can also miss mid-transition reads of values like <code>0.97</code>.</p>
<h3 id="steps-to-reproduce">Steps to reproduce</h3>
<ol>
<li>Run any spec that calls <code>cy.previewReady()</code> on a CI runner under load (e.g. <code>multivalue-form-design-number-integer.cy.js</code>).
</li>
<li>Observe intermittent failures of the form <code>expected <div._editorFrame_> to have CSS property opacity with the value '1', but the value was '0'</code>.
</li>
</ol>
<h3 id="proposed-resolution">Proposed resolution</h3>
<p>Drop the editor-frame opacity assertion from <code>previewReady</code> entirely. The iframe selector that ran on the next line — <code>[data-test-canvas-content-initialized="true"][data-canvas-swap-active="true"]</code> with a 10s timeout — is the real "preview is ready" contract. That attribute is set in <code>Viewport.tsx</code> after <code>IframeSwapper</code>'s load-event-driven swap completes, so it already encodes "the iframe finished loading and is the active one." Any wrapper-level state (including the editor-frame opacity transition) is downstream of this signal and strictly racier.</p>
<p>Harden the helper while we're there:</p>
<ul>
<li>Assert <code>contentDocument.readyState === 'complete'</code> independent of the data-attribute bookkeeping, so the test stays correct if the Viewport-side bookkeeping is ever refactored.
</li>
<li>Assert the iframe body has at least one child element, so we catch the "iframe loaded but document is empty" failure mode.
</li>
</ul>
<p>No production-side changes are needed; this is a test-only fix in <code>ui/tests/support/commands.js</code>.</p>
<h3 id="remaining-tasks">Remaining tasks</h3>
<ul>
<li>Review and merge the MR.</li>
<li>Watch CI on subsequent runs to confirm the flake is gone.</li>
</ul>
issue