Skip to content

Commit 25a47aa

Browse files
fix: only adjust autoscrolling in interactive mode (#23053)
Co-authored-by: Lachlan Miller <[email protected]>
1 parent 566a1a2 commit 25a47aa

File tree

5 files changed

+74
-23
lines changed

5 files changed

+74
-23
lines changed

packages/app/src/main.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ import Toast, { POSITION } from 'vue-toastification'
1212
import 'vue-toastification/dist/index.css'
1313
import { createWebsocket, getRunnerConfigFromWindow } from './runner'
1414

15-
// set a global so we can run
16-
// conditional code in the vite branch
17-
// so that the existing runner code
18-
// @ts-ignore
19-
window.__vite__ = true
20-
2115
const app = createApp(App)
2216

2317
const config = getRunnerConfigFromWindow()

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

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { RootRunnable } from '../../src/runnables/runnables-store'
33
import { itHandlesFileOpening } from '../support/utils'
44
import type { BaseReporterProps } from '../../src/main'
55
import type { RunnablesErrorModel } from '../../src/runnables/runnable-error'
6+
import appState from '../../src/lib/app-state'
67
import { MobxRunnerStore } from '@packages/app/src/store'
8+
import scroller from '../../src/lib/scroller'
79

810
const runnerStore = new MobxRunnerStore('e2e')
911

@@ -16,7 +18,7 @@ runnerStore.setSpec({
1618
describe('runnables', () => {
1719
let runner: EventEmitter
1820
let runnables: RootRunnable
19-
let render: (renderProps?: Partial<BaseReporterProps>) => void
21+
let render: (renderProps?: Partial<BaseReporterProps>, cypressMode?: 'run' | 'open') => void
2022
let start: (renderProps?: Partial<BaseReporterProps>) => Cypress.Chainable
2123

2224
beforeEach(() => {
@@ -26,12 +28,15 @@ describe('runnables', () => {
2628

2729
runner = new EventEmitter()
2830

29-
render = (renderProps: Partial<BaseReporterProps> = {}) => {
31+
render = (renderProps: Partial<BaseReporterProps> = {}, cypressMode = 'open') => {
3032
cy.visit('/').then((win: Cypress.AUTWindow) => {
33+
win.__CYPRESS_MODE__ = cypressMode
3134
win.render({
3235
runner,
3336
studioEnabled: renderProps.studioEnabled || false,
3437
runnerStore,
38+
scroller,
39+
appState,
3540
...renderProps,
3641
})
3742
})
@@ -183,14 +188,21 @@ describe('runnables', () => {
183188
})
184189

185190
describe('runnable-header (unified)', () => {
191+
let spy: Cypress.Agent<sinon.SinonSpy>
192+
186193
beforeEach(() => {
187-
cy.window().then((win) => win.__vite__ = true)
194+
scroller.__setScrollThreholdMs(500)
195+
spy = cy.spy(appState, 'temporarilySetAutoScrolling')
188196

189197
start({
190198
runnerStore,
191199
})
192200
})
193201

202+
afterEach(() => {
203+
scroller.__reset()
204+
})
205+
194206
it('contains name of spec and emits when clicked', () => {
195207
const selector = '.runnable-header a'
196208

@@ -201,5 +213,26 @@ describe('runnables', () => {
201213
expect(runner.emit).to.be.calledWith('open:file:unified')
202214
})
203215
})
216+
217+
it('adds a scroll listener in open mode', () => {
218+
appState.startRunning()
219+
cy.get('.container')
220+
.trigger('scroll')
221+
.trigger('scroll')
222+
.trigger('scroll').then(() => {
223+
expect(spy).to.have.been.calledWith(false)
224+
})
225+
})
226+
227+
it('does not add a scroll listener in run mode', () => {
228+
render({}, 'run')
229+
appState.startRunning()
230+
cy.get('.container')
231+
.trigger('scroll')
232+
.trigger('scroll')
233+
.trigger('scroll').then(() => {
234+
expect(spy).not.to.have.been.calledWith(false)
235+
})
236+
})
204237
})
205238
})

packages/reporter/src/lib/scroller.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
- element distance from top of container
1414
*/
1515

16-
import { TimeoutID } from './types'
17-
18-
type UserScrollCallback = () => void
16+
export type UserScrollCallback = () => void
1917

2018
const PADDING = 100
19+
const SCROLL_THRESHOLD_MS = 50
2120

2221
export class Scroller {
2322
private _container: Element | null = null
2423
private _userScrollCount = 0
2524
private _userScroll = true
26-
private _countUserScrollsTimeout?: TimeoutID
25+
private _countUserScrollsTimeout?: number
26+
private _userScrollThresholdMs = SCROLL_THRESHOLD_MS
2727

2828
setContainer (container: Element, onUserScroll?: UserScrollCallback) {
2929
this._container = container
@@ -53,7 +53,7 @@ export class Scroller {
5353
onUserScroll()
5454
}
5555

56-
clearTimeout(this._countUserScrollsTimeout as TimeoutID)
56+
clearTimeout(this._countUserScrollsTimeout)
5757
this._countUserScrollsTimeout = undefined
5858
this._userScrollCount = 0
5959

@@ -62,10 +62,10 @@ export class Scroller {
6262

6363
if (this._countUserScrollsTimeout) return
6464

65-
this._countUserScrollsTimeout = setTimeout(() => {
65+
this._countUserScrollsTimeout = window.setTimeout(() => {
6666
this._countUserScrollsTimeout = undefined
6767
this._userScrollCount = 0
68-
}, 50)
68+
}, this._userScrollThresholdMs)
6969
})
7070
}
7171

@@ -129,8 +129,20 @@ export class Scroller {
129129
this._container = null
130130
this._userScroll = true
131131
this._userScrollCount = 0
132-
clearTimeout(this._countUserScrollsTimeout as TimeoutID)
132+
clearTimeout(this._countUserScrollsTimeout)
133133
this._countUserScrollsTimeout = undefined
134+
this._userScrollThresholdMs = SCROLL_THRESHOLD_MS
135+
}
136+
137+
__setScrollThreholdMs (ms: number) {
138+
const isCypressInCypress = document.defaultView !== top
139+
140+
// only allow this to be set in testing
141+
if (!isCypressInCypress) {
142+
return
143+
}
144+
145+
this._userScrollThresholdMs = ms
134146
}
135147
}
136148

packages/reporter/src/main.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ declare global {
144144
Cypress: any
145145
state: AppState
146146
render: ((props: Partial<BaseReporterProps>) => void)
147+
__CYPRESS_MODE__: 'run' | 'open'
147148
}
148149
}
149150

packages/reporter/src/runnables/runnables.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Runnable from './runnable-and-suite'
99
import RunnableHeader from './runnable-header'
1010
import { RunnablesStore, RunnableArray } from './runnables-store'
1111
import statsStore, { StatsStore } from '../header/stats-store'
12-
import { Scroller } from '../lib/scroller'
12+
import { Scroller, UserScrollCallback } from '../lib/scroller'
1313
import type { AppState } from '../lib/app-state'
1414
import OpenFileInIDE from '../lib/open-file-in-ide'
1515

@@ -176,11 +176,22 @@ class Runnables extends Component<RunnablesProps> {
176176
componentDidMount () {
177177
const { scroller, appState } = this.props
178178

179-
scroller.setContainer(this.refs.container as Element, action('user:scroll:detected', () => {
180-
if (appState && appState.isRunning) {
181-
appState.temporarilySetAutoScrolling(false)
182-
}
183-
}))
179+
let maybeHandleScroll: UserScrollCallback | undefined = undefined
180+
181+
if (window.__CYPRESS_MODE__ === 'open') {
182+
// in open mode, listen for scroll events so that users can pause the command log auto-scroll
183+
// by manually scrolling the command log
184+
maybeHandleScroll = action('user:scroll:detected', () => {
185+
if (appState && appState.isRunning) {
186+
appState.temporarilySetAutoScrolling(false)
187+
}
188+
})
189+
}
190+
191+
// we need to always call scroller.setContainer, but the callback can be undefined
192+
// so we pass maybeHandleScroll. If we don't, Cypress blows up with an error like
193+
// `A container must be set on the scroller with scroller.setContainer(container)`
194+
scroller.setContainer(this.refs.container as Element, maybeHandleScroll)
184195
}
185196
}
186197

0 commit comments

Comments
 (0)