Skip to content

Commit 482cd41

Browse files
chore: reporter support for test replay embedding (#34031)
Two reporter changes consumed by the test-replay viewer, which bundles the reporter as a git submodule and renders it in a read-only context: - Re-export the singular `Attempt` component. It is imported by the test-replay submodule barrel to render one attempt at a time from a single AttemptModel. It was dropped as an unused export in #33212, which is true within the reporter but not for the external consumer. - Add `appState.cyPromptActionsEnabled` (default true) gating the cy.prompt Feedback / Get Code buttons. Read-only hosts such as test replay have no live backend to service these actions, so they disable the flag. It is intentionally excluded from `appState` reset defaults so it persists for the host's lifetime. Default behavior in the Cypress app is unchanged. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f3de1b2 commit 482cd41

5 files changed

Lines changed: 98 additions & 1 deletion

File tree

packages/reporter/cypress/e2e/commands.cy.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,3 +1313,65 @@ describe('commands', { viewportHeight: 1000 }, () => {
13131313
})
13141314
})
13151315
})
1316+
1317+
describe('cy.prompt command buttons', { viewportHeight: 1000 }, () => {
1318+
let runner: EventEmitter
1319+
let runnables: RootRunnable
1320+
1321+
const addPromptCommand = () => {
1322+
addCommand(runner, {
1323+
id: 200,
1324+
name: 'prompt',
1325+
message: 'cy.prompt instructions',
1326+
state: 'passed',
1327+
})
1328+
}
1329+
1330+
beforeEach(() => {
1331+
cy.fixture('runnables_commands').then((_runnables) => {
1332+
runnables = _runnables
1333+
})
1334+
1335+
runner = new EventEmitter()
1336+
1337+
cy.visit('/').then((win) => {
1338+
win.render({
1339+
runner,
1340+
runnerStore: {
1341+
spec: {
1342+
name: 'foo',
1343+
absolute: '/foo/bar',
1344+
relative: 'foo/bar',
1345+
},
1346+
},
1347+
})
1348+
})
1349+
1350+
cy.get('.reporter.mounted').then(() => {
1351+
runner.emit('runnables:ready', runnables)
1352+
runner.emit('reporter:start', {})
1353+
})
1354+
})
1355+
1356+
it('shows the Feedback and Get Code buttons by default', () => {
1357+
addPromptCommand()
1358+
1359+
cy.get('.command-name-prompt').should('exist')
1360+
cy.get('.command-prompt-get-feedback').should('be.visible')
1361+
cy.get('.command-prompt-get-code').should('be.visible')
1362+
})
1363+
1364+
it('hides the buttons when cy.prompt actions are disabled (e.g. test replay)', () => {
1365+
// command.tsx reads the reporter's appState singleton, exposed as window.state
1366+
cy.window().then((win) => {
1367+
win.state.setCyPromptActionsEnabled(false)
1368+
addPromptCommand()
1369+
})
1370+
1371+
// command still renders; only the buttons are gated
1372+
cy.get('.command-name-prompt').should('exist')
1373+
cy.get('.command-prompt-get-code-feedback-container').should('not.exist')
1374+
cy.get('.command-prompt-get-feedback').should('not.exist')
1375+
cy.get('.command-prompt-get-code').should('not.exist')
1376+
})
1377+
})

packages/reporter/cypress/e2e/unit/app_state.cy.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,5 +239,29 @@ describe('app state', () => {
239239
instance.reset()
240240
expect(instance.studioActive).to.be.false
241241
})
242+
243+
it('preserves cyPromptActionsEnabled', () => {
244+
const instance = new AppState()
245+
246+
// intentionally excluded from reset defaults
247+
instance.setCyPromptActionsEnabled(false)
248+
instance.reset()
249+
expect(instance.cyPromptActionsEnabled).to.be.false
250+
})
251+
})
252+
253+
context('#setCyPromptActionsEnabled', () => {
254+
it('defaults to true', () => {
255+
const instance = new AppState()
256+
257+
expect(instance.cyPromptActionsEnabled).to.be.true
258+
})
259+
260+
it('sets cyPromptActionsEnabled', () => {
261+
const instance = new AppState()
262+
263+
instance.setCyPromptActionsEnabled(false)
264+
expect(instance.cyPromptActionsEnabled).to.be.false
265+
})
242266
})
243267
})

packages/reporter/src/attempts/attempts.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,7 @@ const Attempts: React.FC<AttemptsProps> = observer(({ test, isSingleStudioTest,
111111

112112
Attempts.displayName = 'Attempts'
113113

114+
/** @public - consumed by the external test-replay viewer */
115+
export { Attempt }
116+
114117
export default Attempts

packages/reporter/src/commands/command.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ const Command: React.FC<CommandProps> = observer(({ model, aliasesWithDuplicates
567567
<CommandDetails model={model} groupId={groupId} aliasesWithDuplicates={aliasesWithDuplicates} />
568568
<CommandControls model={model} commandName={commandName} />
569569
</div>
570-
{model.isCyPrompt && (
570+
{model.isCyPrompt && appState.cyPromptActionsEnabled && (
571571
<div
572572
className='command-prompt-get-code-feedback-container'
573573
onClick={(e) => e.stopPropagation()}

packages/reporter/src/lib/app-state.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class AppState {
3434
studioActive = defaults.studioActive
3535
studioSingleTestActive = defaults.studioSingleTestActive
3636
showFetchRequests = true
37+
// Gates the cy.prompt Feedback / Get Code buttons; read-only hosts disable it.
38+
// Omitted from `defaults` so it survives `reset()`.
39+
cyPromptActionsEnabled = true
3740
isStopped = false
3841
hasBeenPaused = defaults.hasBeenPaused
3942
_resetAutoScrollingEnabledTo = true
@@ -52,6 +55,7 @@ class AppState {
5255
studioActive: observable,
5356
studioSingleTestActive: observable,
5457
showFetchRequests: observable,
58+
cyPromptActionsEnabled: observable,
5559
hasBeenPaused: observable,
5660
codeEditorLineWrap: observable,
5761
})
@@ -152,6 +156,10 @@ class AppState {
152156
this.showFetchRequests = showFetchRequests
153157
}
154158

159+
setCyPromptActionsEnabled (cyPromptActionsEnabled: boolean) {
160+
this.cyPromptActionsEnabled = cyPromptActionsEnabled
161+
}
162+
155163
reset () {
156164
_.each(defaults, (value: any, key: string) => {
157165
this[key] = value

0 commit comments

Comments
 (0)