Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: only adjust autoscrolling in interactive mode #23053

Merged
merged 17 commits into from Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 0 additions & 6 deletions packages/app/src/main.ts
Expand Up @@ -12,12 +12,6 @@ import Toast, { POSITION } from 'vue-toastification'
import 'vue-toastification/dist/index.css'
import { createWebsocket, getRunnerConfigFromWindow } from './runner'

// set a global so we can run
// conditional code in the vite branch
// so that the existing runner code
// @ts-ignore
window.__vite__ = true

const app = createApp(App)

const config = getRunnerConfigFromWindow()
Expand Down
39 changes: 36 additions & 3 deletions packages/reporter/cypress/e2e/runnables.cy.ts
Expand Up @@ -3,7 +3,9 @@ import { RootRunnable } from '../../src/runnables/runnables-store'
import { itHandlesFileOpening } from '../support/utils'
import type { BaseReporterProps } from '../../src/main'
import type { RunnablesErrorModel } from '../../src/runnables/runnable-error'
import appState from '../../src/lib/app-state'
import { MobxRunnerStore } from '@packages/app/src/store'
import scroller from '../../src/lib/scroller'

const runnerStore = new MobxRunnerStore('e2e')

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

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

runner = new EventEmitter()

render = (renderProps: Partial<BaseReporterProps> = {}) => {
render = (renderProps: Partial<BaseReporterProps> = {}, cypressMode = 'open') => {
marktnoonan marked this conversation as resolved.
Show resolved Hide resolved
cy.visit('/').then((win: Cypress.AUTWindow) => {
win.__CYPRESS_MODE__ = cypressMode
win.render({
runner,
studioEnabled: renderProps.studioEnabled || false,
runnerStore,
scroller,
appState,
...renderProps,
})
})
Expand Down Expand Up @@ -183,14 +188,21 @@ describe('runnables', () => {
})

describe('runnable-header (unified)', () => {
let spy: Cypress.Agent<sinon.SinonSpy>

beforeEach(() => {
cy.window().then((win) => win.__vite__ = true)
scroller.__setScrollThreholdMs(500)
spy = cy.spy(appState, 'temporarilySetAutoScrolling')

start({
runnerStore,
})
})

afterEach(() => {
scroller.__reset()
})

it('contains name of spec and emits when clicked', () => {
const selector = '.runnable-header a'

Expand All @@ -201,5 +213,26 @@ describe('runnables', () => {
expect(runner.emit).to.be.calledWith('open:file:unified')
})
})

it('adds a scroll listener in open mode', () => {
appState.startRunning()
cy.get('.container')
.trigger('scroll')
.trigger('scroll')
.trigger('scroll').then(() => {
expect(spy).to.have.been.calledWith(false)
})
})

it('does not add a scroll listener in run mode', () => {
render({}, 'run')
appState.startRunning()
cy.get('.container')
.trigger('scroll')
.trigger('scroll')
.trigger('scroll').then(() => {
expect(spy).not.to.have.been.calledWith(false)
})
})
})
})
28 changes: 20 additions & 8 deletions packages/reporter/src/lib/scroller.ts
Expand Up @@ -13,17 +13,17 @@
- element distance from top of container
*/

import { TimeoutID } from './types'

type UserScrollCallback = () => void
export type UserScrollCallback = () => void

const PADDING = 100
const SCROLL_THRESHOLD_MS = 50

export class Scroller {
private _container: Element | null = null
private _userScrollCount = 0
private _userScroll = true
private _countUserScrollsTimeout?: TimeoutID
private _countUserScrollsTimeout?: number
private _userScrollThresholdMs = SCROLL_THRESHOLD_MS

setContainer (container: Element, onUserScroll?: UserScrollCallback) {
this._container = container
Expand Down Expand Up @@ -53,7 +53,7 @@ export class Scroller {
onUserScroll()
}

clearTimeout(this._countUserScrollsTimeout as TimeoutID)
clearTimeout(this._countUserScrollsTimeout)
this._countUserScrollsTimeout = undefined
this._userScrollCount = 0

Expand All @@ -62,10 +62,10 @@ export class Scroller {

if (this._countUserScrollsTimeout) return

this._countUserScrollsTimeout = setTimeout(() => {
this._countUserScrollsTimeout = window.setTimeout(() => {
this._countUserScrollsTimeout = undefined
this._userScrollCount = 0
}, 50)
}, this._userScrollThresholdMs)
})
}

Expand Down Expand Up @@ -129,8 +129,20 @@ export class Scroller {
this._container = null
this._userScroll = true
this._userScrollCount = 0
clearTimeout(this._countUserScrollsTimeout as TimeoutID)
clearTimeout(this._countUserScrollsTimeout)
this._countUserScrollsTimeout = undefined
this._userScrollThresholdMs = SCROLL_THRESHOLD_MS
}

__setScrollThreholdMs (ms: number) {
const isCypressInCypress = document.defaultView !== top

// only allow this to be set in testing
if (!isCypressInCypress) {
return
}

this._userScrollThresholdMs = ms
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/reporter/src/main.tsx
Expand Up @@ -144,6 +144,7 @@ declare global {
Cypress: any
state: AppState
render: ((props: Partial<BaseReporterProps>) => void)
__CYPRESS_MODE__: 'run' | 'open'
}
}

Expand Down
23 changes: 17 additions & 6 deletions packages/reporter/src/runnables/runnables.tsx
Expand Up @@ -9,7 +9,7 @@ import Runnable from './runnable-and-suite'
import RunnableHeader from './runnable-header'
import { RunnablesStore, RunnableArray } from './runnables-store'
import statsStore, { StatsStore } from '../header/stats-store'
import { Scroller } from '../lib/scroller'
import { Scroller, UserScrollCallback } from '../lib/scroller'
import type { AppState } from '../lib/app-state'
import OpenFileInIDE from '../lib/open-file-in-ide'

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

scroller.setContainer(this.refs.container as Element, action('user:scroll:detected', () => {
if (appState && appState.isRunning) {
appState.temporarilySetAutoScrolling(false)
}
}))
let maybeHandleScroll: UserScrollCallback | undefined = undefined

if (window.__CYPRESS_MODE__ === 'open') {
// in open mode, listen for scroll events so that users can pause the command log auto-scroll
// by manually scrolling the command log
maybeHandleScroll = action('user:scroll:detected', () => {
marktnoonan marked this conversation as resolved.
Show resolved Hide resolved
if (appState && appState.isRunning) {
appState.temporarilySetAutoScrolling(false)
}
})
}

// we need to always call scroller.setContainer, but the callback can be undefined
// so we pass maybeHandleScroll. If we don't, Cypress blows up with an error like
// `A container must be set on the scroller with scroller.setContainer(container)`
scroller.setContainer(this.refs.container as Element, maybeHandleScroll)
}
}

Expand Down