mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-22 07:00:22 -05:00
Add spec header with link to open file in editor (#7515)
Co-authored-by: Chris Breiding <chrisbreiding@gmail.com> Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
describe('special characters', () => {
|
||||
it('displays file name with decoded special characters', () => {
|
||||
cy.wrap(Cypress.$(window.top.document.body))
|
||||
.find('.reporter .runnable-header a')
|
||||
.should('have.text', 'cypress/integration/meta_&%_spec.ts')
|
||||
})
|
||||
})
|
||||
@@ -1,131 +1,5 @@
|
||||
const { EventEmitter } = require('events')
|
||||
const _ = Cypress._
|
||||
|
||||
const itHandlesFileOpening = (containerSelector) => {
|
||||
beforeEach(function () {
|
||||
cy.stub(this.runner, 'emit').callThrough()
|
||||
this.setError(this.commandErr)
|
||||
})
|
||||
|
||||
describe('when user has already set opener and opens file', function () {
|
||||
beforeEach(function () {
|
||||
this.editor = {}
|
||||
|
||||
this.runner.emit.withArgs('get:user:editor').yields({
|
||||
preferredOpener: this.editor,
|
||||
})
|
||||
|
||||
cy.contains('View stack trace').click()
|
||||
})
|
||||
|
||||
it('opens in preferred opener', function () {
|
||||
cy.get(`${containerSelector} a`).first().click().then(() => {
|
||||
expect(this.runner.emit).to.be.calledWith('open:file', {
|
||||
where: this.editor,
|
||||
file: '/me/dev/my/app.js',
|
||||
line: 2,
|
||||
column: 7,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when user has not already set opener and opens file', function () {
|
||||
const availableEditors = [
|
||||
{ id: 'computer', name: 'On Computer', isOther: false, openerId: 'computer' },
|
||||
{ id: 'atom', name: 'Atom', isOther: false, openerId: 'atom' },
|
||||
{ id: 'vim', name: 'Vim', isOther: false, openerId: 'vim' },
|
||||
{ id: 'sublime', name: 'Sublime Text', isOther: false, openerId: 'sublime' },
|
||||
{ id: 'vscode', name: 'Visual Studio Code', isOther: false, openerId: 'vscode' },
|
||||
{ id: 'other', name: 'Other', isOther: true, openerId: '' },
|
||||
]
|
||||
|
||||
beforeEach(function () {
|
||||
this.runner.emit.withArgs('get:user:editor').yields({ availableEditors })
|
||||
// usual viewport of only reporter is a bit cramped for the modal
|
||||
cy.viewport(600, 600)
|
||||
cy.contains('View stack trace').click()
|
||||
cy.get(`${containerSelector} a`).first().click()
|
||||
})
|
||||
|
||||
it('opens modal with available editors', function () {
|
||||
_.each(availableEditors, ({ name }) => {
|
||||
cy.contains(name)
|
||||
})
|
||||
|
||||
cy.contains('Other')
|
||||
cy.contains('Set preference and open file')
|
||||
})
|
||||
|
||||
// NOTE: this fails because mobx doesn't make the editors observable, so
|
||||
// the changes to the path don't bubble up correctly. this only happens
|
||||
// in the Cypress test and not when running the actual app
|
||||
it.skip('updates "Other" path when typed into', function () {
|
||||
cy.contains('Other').find('input[type="text"]').type('/path/to/editor')
|
||||
.should('have.value', '/path/to/editor')
|
||||
})
|
||||
|
||||
describe('when editor is not selected', function () {
|
||||
it('disables submit button', function () {
|
||||
cy.contains('Set preference and open file')
|
||||
.should('have.class', 'is-disabled')
|
||||
.click()
|
||||
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'set:user:editor')
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'open:file')
|
||||
})
|
||||
|
||||
it('shows validation message when hovering over submit button', function () {
|
||||
cy.get('.editor-picker-modal .submit').trigger('mouseover')
|
||||
cy.get('.cy-tooltip').should('have.text', 'Please select a preference')
|
||||
})
|
||||
})
|
||||
|
||||
describe('when Other is selected but path is not entered', function () {
|
||||
beforeEach(function () {
|
||||
cy.contains('Other').click()
|
||||
})
|
||||
|
||||
it('disables submit button', function () {
|
||||
cy.contains('Set preference and open file')
|
||||
.should('have.class', 'is-disabled')
|
||||
.click()
|
||||
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'set:user:editor')
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'open:file')
|
||||
})
|
||||
|
||||
it('shows validation message when hovering over submit button', function () {
|
||||
cy.get('.editor-picker-modal .submit').trigger('mouseover')
|
||||
cy.get('.cy-tooltip').should('have.text', 'Please enter the path for the "Other" editor')
|
||||
})
|
||||
})
|
||||
|
||||
describe('when editor is set', function () {
|
||||
beforeEach(function () {
|
||||
cy.contains('Visual Studio Code').click()
|
||||
cy.contains('Set preference and open file').click()
|
||||
})
|
||||
|
||||
it('closes modal', function () {
|
||||
cy.contains('Set preference and open file').should('not.be.visible')
|
||||
})
|
||||
|
||||
it('emits set:user:editor', function () {
|
||||
expect(this.runner.emit).to.be.calledWith('set:user:editor', availableEditors[4])
|
||||
})
|
||||
|
||||
it('opens file in selected editor', function () {
|
||||
expect(this.runner.emit).to.be.calledWith('open:file', {
|
||||
where: availableEditors[4],
|
||||
file: '/me/dev/my/app.js',
|
||||
line: 2,
|
||||
column: 7,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const { itHandlesFileOpening } = require('../support/utils')
|
||||
|
||||
describe('test errors', function () {
|
||||
beforeEach(function () {
|
||||
@@ -356,7 +230,11 @@ describe('test errors', function () {
|
||||
cy.get('.command-wrapper').should('be.visible')
|
||||
})
|
||||
|
||||
itHandlesFileOpening('.runnable-err-stack-trace')
|
||||
itHandlesFileOpening('.runnable-err-stack-trace', {
|
||||
file: '/me/dev/my/app.js',
|
||||
line: 2,
|
||||
column: 7,
|
||||
}, true)
|
||||
})
|
||||
|
||||
describe('command error', function () {
|
||||
@@ -408,9 +286,11 @@ describe('test errors', function () {
|
||||
})
|
||||
|
||||
describe('code frames', function () {
|
||||
it('shows code frame when included on error', function () {
|
||||
beforeEach(function () {
|
||||
this.setError(this.commandErr)
|
||||
})
|
||||
|
||||
it('shows code frame when included on error', function () {
|
||||
cy
|
||||
.get('.test-err-code-frame')
|
||||
.should('be.visible')
|
||||
@@ -418,7 +298,6 @@ describe('test errors', function () {
|
||||
|
||||
it('does not show code frame when not included on error', function () {
|
||||
this.commandErr.codeFrame = undefined
|
||||
this.setError(this.commandErr)
|
||||
|
||||
cy
|
||||
.get('.test-err-code-frame')
|
||||
@@ -426,8 +305,6 @@ describe('test errors', function () {
|
||||
})
|
||||
|
||||
it('use correct language class', function () {
|
||||
this.setError(this.commandErr)
|
||||
|
||||
cy
|
||||
.get('.test-err-code-frame pre')
|
||||
.should('have.class', 'language-javascript')
|
||||
@@ -435,13 +312,15 @@ describe('test errors', function () {
|
||||
|
||||
it('falls back to text language class', function () {
|
||||
this.commandErr.codeFrame.language = null
|
||||
this.setError(this.commandErr)
|
||||
|
||||
cy
|
||||
.get('.test-err-code-frame pre')
|
||||
.should('have.class', 'language-text')
|
||||
})
|
||||
|
||||
itHandlesFileOpening('.test-err-code-frame')
|
||||
itHandlesFileOpening('.test-err-code-frame', {
|
||||
file: '/me/dev/my/app.js',
|
||||
line: 2,
|
||||
column: 7,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { itHandlesFileOpening } from '../support/utils'
|
||||
|
||||
describe('controls', function () {
|
||||
beforeEach(function () {
|
||||
@@ -103,5 +104,17 @@ describe('controls', function () {
|
||||
.should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
describe('header', function () {
|
||||
it('displays', function () {
|
||||
cy.get('.runnable-header').find('a').should('have.text', 'cypress/integration/tests_spec.ts')
|
||||
})
|
||||
|
||||
itHandlesFileOpening('.runnable-header', {
|
||||
file: '/foo/bar',
|
||||
line: 0,
|
||||
column: 0,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
const _ = Cypress._
|
||||
|
||||
export const itHandlesFileOpening = (containerSelector, file, stackTrace = false) => {
|
||||
beforeEach(function () {
|
||||
cy.stub(this.runner, 'emit').callThrough()
|
||||
})
|
||||
|
||||
describe('when user has already set opener and opens file', function () {
|
||||
beforeEach(function () {
|
||||
this.editor = {}
|
||||
|
||||
this.runner.emit.withArgs('get:user:editor').yields({
|
||||
preferredOpener: this.editor,
|
||||
})
|
||||
|
||||
if (stackTrace) {
|
||||
cy.contains('View stack trace').click()
|
||||
}
|
||||
})
|
||||
|
||||
it('opens in preferred opener', function () {
|
||||
cy.get(`${containerSelector} a`).first().click().then(() => {
|
||||
expect(this.runner.emit).to.be.calledWith('open:file', {
|
||||
where: this.editor,
|
||||
...file,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when user has not already set opener and opens file', function () {
|
||||
const availableEditors = [
|
||||
{ id: 'computer', name: 'On Computer', isOther: false, openerId: 'computer' },
|
||||
{ id: 'atom', name: 'Atom', isOther: false, openerId: 'atom' },
|
||||
{ id: 'vim', name: 'Vim', isOther: false, openerId: 'vim' },
|
||||
{ id: 'sublime', name: 'Sublime Text', isOther: false, openerId: 'sublime' },
|
||||
{ id: 'vscode', name: 'Visual Studio Code', isOther: false, openerId: 'vscode' },
|
||||
{ id: 'other', name: 'Other', isOther: true, openerId: '' },
|
||||
]
|
||||
|
||||
beforeEach(function () {
|
||||
this.runner.emit.withArgs('get:user:editor').yields({ availableEditors })
|
||||
// usual viewport of only reporter is a bit cramped for the modal
|
||||
cy.viewport(600, 600)
|
||||
|
||||
if (stackTrace) {
|
||||
cy.contains('View stack trace').click()
|
||||
}
|
||||
|
||||
cy.get(`${containerSelector} a`).first().click()
|
||||
})
|
||||
|
||||
it('opens modal with available editors', function () {
|
||||
_.each(availableEditors, ({ name }) => {
|
||||
cy.contains(name)
|
||||
})
|
||||
|
||||
cy.contains('Other')
|
||||
cy.contains('Set preference and open file')
|
||||
})
|
||||
|
||||
// NOTE: this fails because mobx doesn't make the editors observable, so
|
||||
// the changes to the path don't bubble up correctly. this only happens
|
||||
// in the Cypress test and not when running the actual app
|
||||
it.skip('updates "Other" path when typed into', function () {
|
||||
cy.contains('Other').find('input[type="text"]').type('/path/to/editor')
|
||||
.should('have.value', '/path/to/editor')
|
||||
})
|
||||
|
||||
describe('when editor is not selected', function () {
|
||||
it('disables submit button', function () {
|
||||
cy.contains('Set preference and open file')
|
||||
.should('have.class', 'is-disabled')
|
||||
.click()
|
||||
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'set:user:editor')
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'open:file')
|
||||
})
|
||||
|
||||
it('shows validation message when hovering over submit button', function () {
|
||||
cy.get('.editor-picker-modal .submit').trigger('mouseover')
|
||||
cy.get('.cy-tooltip').should('have.text', 'Please select a preference')
|
||||
})
|
||||
})
|
||||
|
||||
describe('when Other is selected but path is not entered', function () {
|
||||
beforeEach(function () {
|
||||
cy.contains('Other').click()
|
||||
})
|
||||
|
||||
it('disables submit button', function () {
|
||||
cy.contains('Set preference and open file')
|
||||
.should('have.class', 'is-disabled')
|
||||
.click()
|
||||
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'set:user:editor')
|
||||
cy.wrap(this.runner.emit).should('not.to.be.calledWith', 'open:file')
|
||||
})
|
||||
|
||||
it('shows validation message when hovering over submit button', function () {
|
||||
cy.get('.editor-picker-modal .submit').trigger('mouseover')
|
||||
cy.get('.cy-tooltip').should('have.text', 'Please enter the path for the "Other" editor')
|
||||
})
|
||||
})
|
||||
|
||||
describe('when editor is set', function () {
|
||||
beforeEach(function () {
|
||||
cy.contains('Visual Studio Code').click()
|
||||
cy.contains('Set preference and open file').click()
|
||||
})
|
||||
|
||||
it('closes modal', function () {
|
||||
cy.contains('Set preference and open file').should('not.be.visible')
|
||||
})
|
||||
|
||||
it('emits set:user:editor', function () {
|
||||
expect(this.runner.emit).to.be.calledWith('set:user:editor', availableEditors[4])
|
||||
})
|
||||
|
||||
it('opens file in selected editor', function () {
|
||||
expect(this.runner.emit).to.be.calledWith('open:file', {
|
||||
where: availableEditors[4],
|
||||
...file,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -18,10 +18,10 @@ export interface AnErrorProps {
|
||||
const AnError = observer(({ error }: AnErrorProps) => (
|
||||
<div className='error'>
|
||||
<h2>
|
||||
<i className='fas fa-exclamation-triangle'></i> {error.title}
|
||||
<i className='fas fa-exclamation-triangle' /> {error.title}
|
||||
{error.link &&
|
||||
<a href={error.link} target='_blank' rel='noopener noreferrer'>
|
||||
<i className='fas fa-question-circle'></i>
|
||||
<i className='fas fa-question-circle' />
|
||||
</a>
|
||||
}
|
||||
</h2>
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
import _ from 'lodash'
|
||||
import { computed, observable } from 'mobx'
|
||||
|
||||
export interface FileDetails {
|
||||
absoluteFile: string
|
||||
column: number
|
||||
line: number
|
||||
originalFile: string
|
||||
relativeFile: string
|
||||
}
|
||||
import { FileDetails } from '../opener/file-model'
|
||||
|
||||
interface ParsedStackMessageLine {
|
||||
message: string
|
||||
|
||||
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react'
|
||||
import Prism from 'prismjs'
|
||||
|
||||
import { CodeFrame } from './err-model'
|
||||
import ErrorFilePath from './error-file-path'
|
||||
import FileOpener from '../opener/file-opener'
|
||||
|
||||
interface Props {
|
||||
codeFrame: CodeFrame
|
||||
@@ -24,7 +24,7 @@ class ErrorCodeFrame extends Component<Props> {
|
||||
|
||||
return (
|
||||
<div className='test-err-code-frame'>
|
||||
<ErrorFilePath fileDetails={this.props.codeFrame} />
|
||||
<FileOpener className="runnable-err-file-path" fileDetails={this.props.codeFrame} />
|
||||
<pre ref='codeFrame' data-line={highlightLine}>
|
||||
<code className={`language-${language || 'text'}`}>{frame}</code>
|
||||
</pre>
|
||||
|
||||
@@ -2,7 +2,7 @@ import _ from 'lodash'
|
||||
import { observer } from 'mobx-react'
|
||||
import React, { ReactElement } from 'react'
|
||||
|
||||
import ErrorFilePath from './error-file-path'
|
||||
import FileOpener from '../opener/file-opener'
|
||||
import Err from './err-model'
|
||||
|
||||
const cypressLineRegex = /(cypress:\/\/|cypress_runner\.js)/
|
||||
@@ -62,7 +62,7 @@ const ErrorStack = observer(({ err }: Props) => {
|
||||
}
|
||||
|
||||
const link = (
|
||||
<ErrorFilePath key={key} fileDetails={stackLine} />
|
||||
<FileOpener key={key} className="runnable-err-file-path" fileDetails={stackLine} />
|
||||
)
|
||||
|
||||
return makeLine(key, [whitespace, `at ${fn} (`, link, ')'])
|
||||
|
||||
@@ -222,27 +222,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editor-picker-modal {
|
||||
max-width: 40em;
|
||||
|
||||
.editor-picker {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
> span:first-child {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
button.is-disabled,
|
||||
button.is-disabled:hover,
|
||||
button.is-disabled:focus {
|
||||
background: $pass !important;
|
||||
cursor: default !important;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
padding: 1em 1em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +62,12 @@ const TestError = observer((props: TestErrorProps) => {
|
||||
<div className='runnable-err'>
|
||||
<div className='runnable-err-header'>
|
||||
<div className='runnable-err-name'>
|
||||
<i className='fas fa-exclamation-circle'></i>
|
||||
<i className='fas fa-exclamation-circle' />
|
||||
{err.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className='runnable-err-message'>
|
||||
<span dangerouslySetInnerHTML={{ __html: formattedMessage(err.message) }}></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: formattedMessage(err.message) }} />
|
||||
<DocsUrl url={err.docsUrl} />
|
||||
</div>
|
||||
{codeFrame && <ErrorCodeFrame codeFrame={codeFrame} />}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface FileDetails {
|
||||
absoluteFile: string
|
||||
column: number
|
||||
line: number
|
||||
originalFile: string
|
||||
relativeFile: string
|
||||
}
|
||||
+7
-10
@@ -2,13 +2,9 @@ import _ from 'lodash'
|
||||
import { action } from 'mobx'
|
||||
import { observer, useLocalStore } from 'mobx-react'
|
||||
import React, { MouseEvent } from 'react'
|
||||
// @ts-ignore
|
||||
import Tooltip from '@cypress/react-tooltip'
|
||||
// @ts-ignore
|
||||
import { EditorPicker } from '@packages/ui-components'
|
||||
|
||||
import EditorPickerModal, { Editor } from './editor-picker-modal'
|
||||
import { FileDetails } from './err-model'
|
||||
import { FileDetails } from './file-model'
|
||||
import events from '../lib/events'
|
||||
|
||||
interface GetUserEditorResult {
|
||||
@@ -17,7 +13,8 @@ interface GetUserEditorResult {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
fileDetails: FileDetails
|
||||
fileDetails: FileDetails,
|
||||
className?: string
|
||||
}
|
||||
|
||||
const openFile = (where: Editor, { absoluteFile: file, line, column }: FileDetails) => {
|
||||
@@ -29,7 +26,7 @@ const openFile = (where: Editor, { absoluteFile: file, line, column }: FileDetai
|
||||
})
|
||||
}
|
||||
|
||||
const ErrorFilePath = observer(({ fileDetails }: Props) => {
|
||||
const FileOpener = observer(({ fileDetails, className }: Props) => {
|
||||
const state = useLocalStore(() => ({
|
||||
editors: [] as Editor[],
|
||||
chosenEditor: {} as Editor,
|
||||
@@ -80,8 +77,8 @@ const ErrorFilePath = observer(({ fileDetails }: Props) => {
|
||||
const { originalFile, line, column } = fileDetails
|
||||
|
||||
return (
|
||||
<a className='runnable-err-file-path' onClick={attemptOpenFile} href='#'>
|
||||
{originalFile}:{line}:{column}
|
||||
<a className={className} onClick={attemptOpenFile} href='#'>
|
||||
{originalFile}{!!line && `:${line}`}{!!column && `:${column}`}
|
||||
<EditorPickerModal
|
||||
chosenEditor={state.chosenEditor}
|
||||
editors={state.editors}
|
||||
@@ -94,4 +91,4 @@ const ErrorFilePath = observer(({ fileDetails }: Props) => {
|
||||
)
|
||||
})
|
||||
|
||||
export default ErrorFilePath
|
||||
export default FileOpener
|
||||
@@ -0,0 +1,23 @@
|
||||
.editor-picker-modal {
|
||||
max-width: 40em;
|
||||
|
||||
.editor-picker {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
> span:first-child {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
button.is-disabled,
|
||||
button.is-disabled:hover,
|
||||
button.is-disabled:focus {
|
||||
background: $pass !important;
|
||||
cursor: default !important;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
padding: 1em 1em 1em;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import React, { Component } from 'react'
|
||||
|
||||
import FileOpener from '../opener/file-opener'
|
||||
|
||||
interface RunnableHeaderProps {
|
||||
specPath: string
|
||||
}
|
||||
|
||||
class RunnableHeader extends Component<RunnableHeaderProps> {
|
||||
render () {
|
||||
const { specPath } = this.props
|
||||
const relativeSpecPath = window.Cypress?.spec.relative
|
||||
const fileDetails = {
|
||||
absoluteFile: specPath,
|
||||
column: 0,
|
||||
line: 0,
|
||||
originalFile: relativeSpecPath,
|
||||
relativeFile: relativeSpecPath,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="runnable-header">
|
||||
{ relativeSpecPath === '__all' ? <span>All Specs</span> : <FileOpener fileDetails={fileDetails} /> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default RunnableHeader
|
||||
@@ -239,4 +239,28 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.runnable-header {
|
||||
background: #f2f2f2;
|
||||
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.25);
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
overflow-wrap: break-word;
|
||||
padding: 5px 10px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
a:before,
|
||||
span:before {
|
||||
@extend .#{$fa-css-prefix};
|
||||
@extend .#{$fa-css-prefix}-file;
|
||||
color: #bdbdbd;
|
||||
display: inline;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { Component } from 'react'
|
||||
|
||||
import AnError, { Error } from '../errors/an-error'
|
||||
import Runnable from './runnable-and-suite'
|
||||
import RunnableHeader from './runnable-header'
|
||||
import { RunnablesStore, RunnableArray } from './runnables-store'
|
||||
import { Scroller } from '../lib/scroller'
|
||||
import { AppState } from '../lib/app-state'
|
||||
@@ -55,6 +56,7 @@ class Runnables extends Component<RunnablesProps> {
|
||||
|
||||
return (
|
||||
<div ref='container' className='container'>
|
||||
<RunnableHeader specPath={specPath} />
|
||||
{content(runnablesStore, specPath, error)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -78,7 +78,7 @@ class Test extends Component<Props> {
|
||||
style={{ paddingLeft: indent(model.level) }}
|
||||
>
|
||||
<div className='runnable-content-region'>
|
||||
<i aria-hidden="true" className='runnable-state fas'></i>
|
||||
<i aria-hidden="true" className='runnable-state fas' />
|
||||
<div
|
||||
aria-expanded={this._shouldBeOpen() === true}
|
||||
className='runnable-title'
|
||||
@@ -91,7 +91,7 @@ class Test extends Component<Props> {
|
||||
</div>
|
||||
<div className='runnable-controls'>
|
||||
<Tooltip placement='top' title='One or more commands failed' className='cy-tooltip'>
|
||||
<i className='fas fa-exclamation-triangle'></i>
|
||||
<i className='fas fa-exclamation-triangle' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,6 @@ export default {
|
||||
return !!location.hash
|
||||
},
|
||||
|
||||
specFile () {
|
||||
return this.specPath().replace(/(.*)\//, '')
|
||||
},
|
||||
|
||||
specPath () {
|
||||
if (location.hash) {
|
||||
const match = location.hash.match(/tests\/(.*)$/)
|
||||
|
||||
Reference in New Issue
Block a user