mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-25 08:29:06 -06:00
feat: persist pane sizes and fix resizing bug (#15480)
This commit is contained in:
@@ -7,15 +7,27 @@ import '@packages/runner/src/main.scss'
|
||||
|
||||
const selectors = {
|
||||
reporter: '[data-cy=reporter]',
|
||||
noSpecSelectedReporter: '[data-cy=no-spec-selected-reporter]',
|
||||
specsList: '[data-cy=specs-list]',
|
||||
searchInput: 'input[placeholder="Find spec..."]',
|
||||
}
|
||||
|
||||
interface Overrides {
|
||||
saveState?: Function
|
||||
}
|
||||
|
||||
const noop = () => {}
|
||||
|
||||
class FakeEventManager {
|
||||
start = () => { }
|
||||
on = () => { }
|
||||
stop = () => {}
|
||||
notifyRunningSpec = () => { }
|
||||
constructor (overrides: Overrides = {}) {
|
||||
this.saveState = overrides.saveState || noop
|
||||
}
|
||||
|
||||
start = noop
|
||||
on = noop
|
||||
stop = noop
|
||||
notifyRunningSpec = noop
|
||||
saveState: Function = () => { }
|
||||
}
|
||||
|
||||
const fakeConfig = { projectName: 'Project', env: {}, isTextTerminal: false } as any as Cypress.RuntimeConfigOptions
|
||||
@@ -58,7 +70,34 @@ describe('RunnerCt', () => {
|
||||
})
|
||||
|
||||
context('keyboard shortcuts', () => {
|
||||
beforeEach(() => {
|
||||
it('toggles specs list drawer using shortcut', () => {
|
||||
const saveState = cy.stub()
|
||||
|
||||
mount(
|
||||
<RunnerCt
|
||||
state={makeState()}
|
||||
// @ts-ignore - this is difficult to stub. Real one breaks things.
|
||||
eventManager={new FakeEventManager({ saveState })}
|
||||
config={fakeConfig}
|
||||
/>,
|
||||
)
|
||||
|
||||
cy.window().then((win) => win.focus())
|
||||
cy.get(selectors.specsList).should('be.visible')
|
||||
|
||||
cy.realPress(['Meta', 'B'])
|
||||
cy.get(selectors.specsList).should('not.be.visible').then(() => {
|
||||
expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: false })
|
||||
})
|
||||
|
||||
cy.realPress(['Meta', 'B'])
|
||||
cy.get(selectors.specsList).should('be.visible').then(() => {
|
||||
expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: false }),
|
||||
expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: true })
|
||||
})
|
||||
})
|
||||
|
||||
it('focuses the search field on "/"', () => {
|
||||
mount(
|
||||
<RunnerCt
|
||||
state={makeState()}
|
||||
@@ -68,20 +107,6 @@ describe('RunnerCt', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
cy.window().then((win) => win.focus())
|
||||
})
|
||||
|
||||
it('toggles specs list drawer using shortcut', () => {
|
||||
cy.get(selectors.specsList).should('be.visible')
|
||||
|
||||
cy.realPress(['Meta', 'B'])
|
||||
cy.get(selectors.specsList).should('not.be.visible')
|
||||
|
||||
cy.realPress(['Meta', 'B'])
|
||||
cy.get(selectors.specsList).should('be.visible')
|
||||
})
|
||||
|
||||
it('focuses the search field on "/"', () => {
|
||||
cy.realPress('/')
|
||||
cy.get(selectors.searchInput).should('be.focused')
|
||||
})
|
||||
@@ -95,7 +120,7 @@ describe('RunnerCt', () => {
|
||||
eventManager={new FakeEventManager()}
|
||||
config={fakeConfig} />)
|
||||
|
||||
cy.get(selectors.reporter).should('not.be.visible')
|
||||
cy.get(selectors.noSpecSelectedReporter).should('exist')
|
||||
cy.percySnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ export const ReporterContainer = observer(
|
||||
function ReporterContainer (props: ReporterContainerProps) {
|
||||
if (!props.state.spec) {
|
||||
return (
|
||||
<div className='no-spec' data-cy="reporter">
|
||||
<div className='no-spec' data-cy="no-spec-selected-reporter">
|
||||
<NoSpecSelected />
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -95,9 +95,9 @@ const App: React.FC<AppProps> = observer(
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
const isOpenMode = !config.isTextTerminal
|
||||
|
||||
state.setIsSpecsListOpen(isOpenMode)
|
||||
if (config.isTextTerminal) {
|
||||
state.setIsSpecsListOpen(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useScreenshotHandler({
|
||||
@@ -124,7 +124,10 @@ const App: React.FC<AppProps> = observer(
|
||||
type: 'js',
|
||||
onClick: ({ index }) => {
|
||||
onNavItemClick(index)
|
||||
state.setIsSpecsListOpen(!props.state.isSpecsListOpen)
|
||||
const isOpen = !props.state.isSpecsListOpen
|
||||
|
||||
state.setIsSpecsListOpen(isOpen)
|
||||
props.eventManager.saveState({ ctIsSpecsListOpen: isOpen })
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -150,7 +153,10 @@ const App: React.FC<AppProps> = observer(
|
||||
|
||||
function toggleSpecsList () {
|
||||
setActiveIndex((val) => val === 0 ? undefined : 0)
|
||||
state.setIsSpecsListOpen(!props.state.isSpecsListOpen)
|
||||
const newVal = !props.state.isSpecsListOpen
|
||||
|
||||
state.setIsSpecsListOpen(newVal)
|
||||
props.eventManager.saveState({ ctIsSpecsListOpen: newVal })
|
||||
}
|
||||
|
||||
function focusSpecsList () {
|
||||
@@ -179,6 +185,12 @@ const App: React.FC<AppProps> = observer(
|
||||
state.updateSpecListWidth(newWidth)
|
||||
}
|
||||
|
||||
function persistWidth (prop: 'ctReporterWidth' | 'ctSpecListWidth') {
|
||||
return (newWidth: number) => {
|
||||
props.eventManager.saveState({ [prop]: newWidth })
|
||||
}
|
||||
}
|
||||
|
||||
function hideIfScreenshotting (callback: () => number) {
|
||||
if (state.screenshotting) {
|
||||
return 0
|
||||
@@ -218,6 +230,21 @@ const App: React.FC<AppProps> = observer(
|
||||
<KeyboardHelper />
|
||||
)
|
||||
|
||||
const MainAreaComponent: React.FC | typeof SplitPane = props.state.spec
|
||||
? SplitPane
|
||||
: (props) => <div>{props.children}</div>
|
||||
|
||||
const mainAreaProps = props.state.spec
|
||||
? {
|
||||
split: 'vertical',
|
||||
minSize: hideReporterIfNecessary(() => 100),
|
||||
maxSize: hideReporterIfNecessary(() => 600),
|
||||
defaultSize: hideReporterIfNecessary(() => state.reporterWidth),
|
||||
className: 'primary',
|
||||
onChange: debounce(onReporterSplitPaneChange),
|
||||
}
|
||||
: {}
|
||||
|
||||
return (
|
||||
<SplitPane
|
||||
split="vertical"
|
||||
@@ -229,10 +256,10 @@ const App: React.FC<AppProps> = observer(
|
||||
{leftNav}
|
||||
<SplitPane
|
||||
split="vertical"
|
||||
// do not allow resizing of this for now, simplifes calculation for scale of AUT.
|
||||
minSize={hideIfScreenshotting(() => state.isSpecsListOpen ? 30 : 0)}
|
||||
maxSize={hideIfScreenshotting(() => state.isSpecsListOpen ? 600 : 0)}
|
||||
defaultSize={hideIfScreenshotting(() => state.isSpecsListOpen ? DEFAULT_LIST_WIDTH : 0)}
|
||||
defaultSize={hideIfScreenshotting(() => state.isSpecsListOpen ? state.specListWidth : 0)}
|
||||
onDragFinished={persistWidth('ctSpecListWidth')}
|
||||
className="primary"
|
||||
// @ts-expect-error split-pane ref types are weak so we are using our custom type for ref
|
||||
ref={splitPaneRef}
|
||||
@@ -250,15 +277,7 @@ const App: React.FC<AppProps> = observer(
|
||||
}
|
||||
onSelectSpec={runSpec}
|
||||
/>
|
||||
|
||||
<SplitPane
|
||||
split="vertical"
|
||||
minSize={hideReporterIfNecessary(() => 100)}
|
||||
maxSize={hideReporterIfNecessary(() => 600)}
|
||||
defaultSize={hideReporterIfNecessary(() => DEFAULT_REPORTER_WIDTH)}
|
||||
className="primary"
|
||||
onChange={debounce(onReporterSplitPaneChange)}
|
||||
>
|
||||
<MainAreaComponent {...mainAreaProps}>
|
||||
<ReporterContainer
|
||||
state={props.state}
|
||||
config={props.config}
|
||||
@@ -271,7 +290,7 @@ const App: React.FC<AppProps> = observer(
|
||||
allowResize={props.state.isAnyDevtoolsPluginOpen}
|
||||
size={hideIfScreenshotting(() =>
|
||||
state.isAnyDevtoolsPluginOpen
|
||||
? DEFAULT_PLUGINS_HEIGHT
|
||||
? state.pluginsHeight
|
||||
// show the small not resize-able panel with buttons or nothing
|
||||
: state.isAnyPluginToShow ? PLUGIN_BAR_HEIGHT : 0)}
|
||||
onChange={debounce(onPluginsSplitPaneChange)}
|
||||
@@ -296,9 +315,8 @@ const App: React.FC<AppProps> = observer(
|
||||
pluginRootContainer={pluginRootContainer}
|
||||
/>
|
||||
</SplitPane>
|
||||
</SplitPane>
|
||||
</MainAreaComponent>
|
||||
</SplitPane>
|
||||
|
||||
</SplitPane>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -136,9 +136,10 @@ export default class State {
|
||||
multiSpecs = [],
|
||||
reporterWidth = DEFAULT_REPORTER_WIDTH,
|
||||
specListWidth = DEFAULT_LIST_WIDTH,
|
||||
isSpecsListOpen = true,
|
||||
}) {
|
||||
this.reporterWidth = reporterWidth
|
||||
this.pluginsHeight = PLUGIN_BAR_HEIGHT
|
||||
this.isSpecsListOpen = isSpecsListOpen
|
||||
this.spec = spec
|
||||
this.specs = specs
|
||||
this.specListWidth = specListWidth
|
||||
|
||||
@@ -31,7 +31,12 @@ const Runner = {
|
||||
|
||||
configState.specs = config.specs
|
||||
|
||||
const state = new State(configState)
|
||||
const ctRunnerSpecificDefaults = {
|
||||
reporterWidth: config.state.ctReporterWidth,
|
||||
isSpecsListOpen: config.state.ctIsSpecsListOpen,
|
||||
specListWidth: config.state.ctSpecListWidth,
|
||||
}
|
||||
const state = new State({ ...configState, ...ctRunnerSpecificDefaults })
|
||||
|
||||
const setSpecByUrlHash = () => {
|
||||
const specPath = util.specPath()
|
||||
|
||||
@@ -25,6 +25,9 @@ reporterWidth
|
||||
showedOnBoardingModal
|
||||
showedStudioModal
|
||||
preferredOpener
|
||||
ctReporterWidth
|
||||
ctIsSpecsListOpen
|
||||
ctSpecListWidth
|
||||
`.trim().split(/\s+/)
|
||||
|
||||
const formStatePath = (projectRoot) => {
|
||||
|
||||
Reference in New Issue
Block a user