mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-17 13:39:05 +00:00
New compile UI autocompile (#3762)
* Animate recompile button when autocompile is waiting * Add code-check failed notice to the new compile UI GitOrigin-RevId: 83b62f41438e8e5b94bd893c222bec37745c0f57
This commit is contained in:
parent
ba4300d9e1
commit
15f49994bd
13 changed files with 290 additions and 139 deletions
services/web
app/views/project/editor
frontend
extracted-translations.json
js/features/preview/components
preview-logs-pane.jspreview-logs-toggle-button.jspreview-pane.jspreview-recompile-button.jspreview-toolbar.js
stylesheets/app/editor
test/frontend/features/preview/components
|
@ -2,6 +2,8 @@ div.full-size.pdf(ng-controller="PdfController")
|
|||
if showNewLogsUI
|
||||
preview-pane(
|
||||
compiler-state=`{
|
||||
autoCompileHasChanges: changesToAutoCompile,
|
||||
autoCompileHasLintingError: autoCompileLintingError,
|
||||
isAutoCompileOn: autocompile_enabled,
|
||||
isClearingCache: pdf.clearingCache,
|
||||
isCompiling: pdf.compiling,
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
"close": "",
|
||||
"clsi_maintenance": "",
|
||||
"clsi_unavailable": "",
|
||||
"code_check_failed": "",
|
||||
"code_check_failed_explanation": "",
|
||||
"collabs_per_proj": "",
|
||||
"collapse": "",
|
||||
"common": "",
|
||||
|
@ -199,4 +201,4 @@
|
|||
"zotero_reference_loading_error_expired": "",
|
||||
"zotero_reference_loading_error_forbidden": "",
|
||||
"zotero_sync_description": ""
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ function PreviewLogsPane({
|
|||
outputFiles = [],
|
||||
isClearingCache,
|
||||
isCompiling = false,
|
||||
autoCompileHasLintingError = false,
|
||||
onLogEntryLocationClick,
|
||||
onClearCache
|
||||
}) {
|
||||
|
@ -113,6 +114,7 @@ function PreviewLogsPane({
|
|||
<div className="logs-pane">
|
||||
<div className="logs-pane-content">
|
||||
<LogsPaneBetaNotice />
|
||||
{autoCompileHasLintingError ? <AutoCompileLintingErrorEntry /> : null}
|
||||
{errors ? errorsUI : null}
|
||||
{validationIssues ? validationIssuesUI : null}
|
||||
{allCompilerIssues.length > 0 ? logEntriesUI : null}
|
||||
|
@ -123,6 +125,22 @@ function PreviewLogsPane({
|
|||
)
|
||||
}
|
||||
|
||||
function AutoCompileLintingErrorEntry() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="log-entry">
|
||||
<div className="log-entry-header log-entry-header-error">
|
||||
<div className="log-entry-header-icon-container">
|
||||
<Icon type="exclamation-triangle" modifier="fw" />
|
||||
</div>
|
||||
<h3 className="log-entry-header-title">
|
||||
{t('code_check_failed_explanation')}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function LogsPaneBetaNotice() {
|
||||
const { t } = useTranslation()
|
||||
const [dismissedBetaNotice, setDismissedBetaNotice] = usePersistedState(
|
||||
|
@ -173,6 +191,7 @@ PreviewLogsPane.propTypes = {
|
|||
warning: PropTypes.array,
|
||||
typesetting: PropTypes.array
|
||||
}),
|
||||
autoCompileHasLintingError: PropTypes.bool,
|
||||
rawLog: PropTypes.string,
|
||||
outputFiles: PropTypes.array,
|
||||
isClearingCache: PropTypes.bool,
|
||||
|
|
|
@ -10,24 +10,16 @@ const MAX_ERRORS_COUNT = 99
|
|||
function PreviewLogsToggleButton({
|
||||
onToggle,
|
||||
showLogs,
|
||||
autoCompileLintingError = false,
|
||||
compileFailed = false,
|
||||
logsState: { nErrors, nWarnings },
|
||||
showText
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const toggleButtonClasses = classNames(
|
||||
'btn',
|
||||
'btn-xs',
|
||||
'btn-toggle-logs',
|
||||
'toolbar-item',
|
||||
{
|
||||
'btn-danger': !showLogs && nErrors,
|
||||
'btn-warning': !showLogs && !nErrors && nWarnings,
|
||||
'btn-default': showLogs || (!nErrors && !nWarnings)
|
||||
}
|
||||
)
|
||||
|
||||
let textStyle = {}
|
||||
let btnColorCssClass = 'btn-default'
|
||||
let buttonContents
|
||||
|
||||
if (!showText) {
|
||||
textStyle = {
|
||||
position: 'absolute',
|
||||
|
@ -40,23 +32,40 @@ function PreviewLogsToggleButton({
|
|||
onToggle()
|
||||
}
|
||||
|
||||
if (showLogs) {
|
||||
buttonContents = <ViewPdf textStyle={textStyle} />
|
||||
} else {
|
||||
buttonContents = (
|
||||
<CompilationResult
|
||||
textStyle={textStyle}
|
||||
autoCompileLintingError={autoCompileLintingError}
|
||||
nErrors={nErrors}
|
||||
nWarnings={nWarnings}
|
||||
/>
|
||||
)
|
||||
if (autoCompileLintingError || nErrors > 0) {
|
||||
btnColorCssClass = 'btn-danger'
|
||||
} else if (nWarnings > 0) {
|
||||
btnColorCssClass = 'btn-warning'
|
||||
}
|
||||
}
|
||||
const buttonClasses = classNames(
|
||||
'btn',
|
||||
'btn-xs',
|
||||
'btn-toggle-logs',
|
||||
'toolbar-item',
|
||||
btnColorCssClass
|
||||
)
|
||||
|
||||
const buttonElement = (
|
||||
<button
|
||||
id="logs-toggle"
|
||||
type="button"
|
||||
disabled={compileFailed}
|
||||
className={toggleButtonClasses}
|
||||
className={buttonClasses}
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
{showLogs ? (
|
||||
<ViewPdfButton textStyle={textStyle} />
|
||||
) : (
|
||||
<CompilationResultIndicator
|
||||
textStyle={textStyle}
|
||||
nErrors={nErrors}
|
||||
nWarnings={nWarnings}
|
||||
/>
|
||||
)}
|
||||
{buttonContents}
|
||||
</button>
|
||||
)
|
||||
|
||||
|
@ -76,21 +85,40 @@ function PreviewLogsToggleButton({
|
|||
)
|
||||
}
|
||||
|
||||
function CompilationResultIndicator({ textStyle, nErrors, nWarnings }) {
|
||||
if (nErrors || nWarnings) {
|
||||
function CompilationResult({
|
||||
textStyle,
|
||||
autoCompileLintingError,
|
||||
nErrors,
|
||||
nWarnings
|
||||
}) {
|
||||
if (autoCompileLintingError) {
|
||||
return <AutoCompileLintingError textStyle={textStyle} />
|
||||
} else if (nErrors || nWarnings) {
|
||||
return (
|
||||
<LogsCompilationResultIndicator
|
||||
<LogsCompilationResult
|
||||
logType={nErrors ? 'errors' : 'warnings'}
|
||||
nLogs={nErrors || nWarnings}
|
||||
textStyle={textStyle}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return <ViewLogsButton textStyle={textStyle} />
|
||||
return <ViewLogs textStyle={textStyle} />
|
||||
}
|
||||
}
|
||||
|
||||
function LogsCompilationResultIndicator({ textStyle, logType, nLogs }) {
|
||||
function ViewPdf({ textStyle }) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
<Icon type="file-pdf-o" />
|
||||
<span className="toolbar-text" style={textStyle}>
|
||||
{t('view_pdf')}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function LogsCompilationResult({ textStyle, logType, nLogs }) {
|
||||
const { t } = useTranslation()
|
||||
const label =
|
||||
logType === 'errors' ? t('your_project_has_errors') : t('view_warnings')
|
||||
|
@ -110,7 +138,19 @@ function LogsCompilationResultIndicator({ textStyle, logType, nLogs }) {
|
|||
)
|
||||
}
|
||||
|
||||
function ViewLogsButton({ textStyle }) {
|
||||
function AutoCompileLintingError({ textStyle }) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
<Icon type="exclamation-triangle" />
|
||||
<span className="toolbar-text" style={textStyle}>
|
||||
{t('code_check_failed')}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function ViewLogs({ textStyle }) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
|
@ -122,41 +162,40 @@ function ViewLogsButton({ textStyle }) {
|
|||
)
|
||||
}
|
||||
|
||||
function ViewPdfButton({ textStyle }) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
<Icon type="file-pdf-o" />
|
||||
<span className="toolbar-text" style={textStyle}>
|
||||
{t('view_pdf')}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
PreviewLogsToggleButton.propTypes = {
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
logsState: PropTypes.shape({
|
||||
nErrors: PropTypes.number.isRequired,
|
||||
nWarnings: PropTypes.number.isRequired,
|
||||
nLogEntries: PropTypes.number.isRequired
|
||||
nWarnings: PropTypes.number.isRequired
|
||||
}),
|
||||
showLogs: PropTypes.bool.isRequired,
|
||||
showText: PropTypes.bool.isRequired,
|
||||
compileFailed: PropTypes.bool
|
||||
compileFailed: PropTypes.bool,
|
||||
autoCompileLintingError: PropTypes.bool
|
||||
}
|
||||
|
||||
LogsCompilationResultIndicator.propTypes = {
|
||||
CompilationResult.propTypes = {
|
||||
textStyle: PropTypes.object.isRequired,
|
||||
autoCompileLintingError: PropTypes.bool,
|
||||
nErrors: PropTypes.number.isRequired,
|
||||
nWarnings: PropTypes.number.isRequired
|
||||
}
|
||||
|
||||
LogsCompilationResult.propTypes = {
|
||||
logType: PropTypes.string.isRequired,
|
||||
nLogs: PropTypes.number.isRequired,
|
||||
textStyle: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
ViewLogsButton.propTypes = {
|
||||
AutoCompileLintingError.propTypes = {
|
||||
textStyle: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
ViewPdfButton.propTypes = {
|
||||
ViewLogs.propTypes = {
|
||||
textStyle: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
ViewPdf.propTypes = {
|
||||
textStyle: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
|
|
|
@ -53,10 +53,6 @@ function PreviewPane({
|
|||
compilerState.logEntries && compilerState.logEntries.warnings
|
||||
? compilerState.logEntries.warnings.length
|
||||
: 0
|
||||
const nLogEntries =
|
||||
compilerState.logEntries && compilerState.logEntries.all
|
||||
? compilerState.logEntries.all.length
|
||||
: 0
|
||||
|
||||
const hasCLSIErrors =
|
||||
compilerState.errors &&
|
||||
|
@ -84,7 +80,7 @@ function PreviewPane({
|
|||
<>
|
||||
<PreviewToolbar
|
||||
compilerState={compilerState}
|
||||
logsState={{ nErrors, nWarnings, nLogEntries }}
|
||||
logsState={{ nErrors, nWarnings }}
|
||||
showLogs={showLogs}
|
||||
onRecompile={onRecompile}
|
||||
onRecompileFromScratch={onRecompileFromScratch}
|
||||
|
@ -130,6 +126,7 @@ function PreviewPane({
|
|||
rawLog={compilerState.rawLog}
|
||||
validationIssues={compilerState.validationIssues}
|
||||
errors={compilerState.errors}
|
||||
autoCompileHasLintingError={compilerState.autoCompileHasLintingError}
|
||||
outputFiles={outputFiles}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
isClearingCache={compilerState.isClearingCache}
|
||||
|
@ -143,6 +140,7 @@ function PreviewPane({
|
|||
|
||||
PreviewPane.propTypes = {
|
||||
compilerState: PropTypes.shape({
|
||||
autoCompileHasLintingError: PropTypes.bool,
|
||||
isAutoCompileOn: PropTypes.bool.isRequired,
|
||||
isCompiling: PropTypes.bool.isRequired,
|
||||
isDraftModeOn: PropTypes.bool.isRequired,
|
||||
|
|
|
@ -2,10 +2,13 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import { Dropdown, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import classNames from 'classnames'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
|
||||
function PreviewRecompileButton({
|
||||
compilerState: {
|
||||
autoCompileHasChanges,
|
||||
autoCompileHasLintingError,
|
||||
isAutoCompileOn,
|
||||
isCompiling,
|
||||
isDraftModeOn,
|
||||
|
@ -67,10 +70,19 @@ function PreviewRecompileButton({
|
|||
compilingProps = _hideText()
|
||||
}
|
||||
|
||||
const recompileButtonGroupClasses = classNames(
|
||||
'btn-recompile-group',
|
||||
'toolbar-item',
|
||||
{
|
||||
'btn-recompile-group-has-changes':
|
||||
autoCompileHasChanges && !autoCompileHasLintingError
|
||||
}
|
||||
)
|
||||
|
||||
const buttonElement = (
|
||||
<Dropdown
|
||||
id="pdf-recompile-dropdown"
|
||||
className="btn-recompile-group toolbar-item"
|
||||
className={recompileButtonGroupClasses}
|
||||
>
|
||||
<button className="btn btn-recompile" onClick={onRecompile}>
|
||||
<Icon type="refresh" spin={isCompiling} />
|
||||
|
@ -160,6 +172,8 @@ function PreviewRecompileButton({
|
|||
|
||||
PreviewRecompileButton.propTypes = {
|
||||
compilerState: PropTypes.shape({
|
||||
autoCompileHasChanges: PropTypes.bool.isRequired,
|
||||
autoCompileHasLintingError: PropTypes.bool,
|
||||
isAutoCompileOn: PropTypes.bool.isRequired,
|
||||
isCompiling: PropTypes.bool.isRequired,
|
||||
isDraftModeOn: PropTypes.bool.isRequired,
|
||||
|
|
|
@ -236,6 +236,10 @@ function PreviewToolbar({
|
|||
<PreviewLogsToggleButton
|
||||
logsState={logsState}
|
||||
showLogs={showLogs}
|
||||
autoCompileLintingError={
|
||||
compilerState.isAutoCompileOn &&
|
||||
compilerState.autoCompileHasLintingError
|
||||
}
|
||||
compileFailed={compilerState.compileFailed}
|
||||
onToggle={onToggleLogs}
|
||||
showText={showToggleText}
|
||||
|
@ -256,6 +260,7 @@ function PreviewToolbar({
|
|||
|
||||
PreviewToolbar.propTypes = {
|
||||
compilerState: PropTypes.shape({
|
||||
autoCompileHasLintingError: PropTypes.bool,
|
||||
isAutoCompileOn: PropTypes.bool.isRequired,
|
||||
isCompiling: PropTypes.bool.isRequired,
|
||||
isDraftModeOn: PropTypes.bool.isRequired,
|
||||
|
@ -265,8 +270,7 @@ PreviewToolbar.propTypes = {
|
|||
}),
|
||||
logsState: PropTypes.shape({
|
||||
nErrors: PropTypes.number.isRequired,
|
||||
nWarnings: PropTypes.number.isRequired,
|
||||
nLogEntries: PropTypes.number.isRequired
|
||||
nWarnings: PropTypes.number.isRequired
|
||||
}),
|
||||
showLogs: PropTypes.bool.isRequired,
|
||||
splitLayout: PropTypes.bool.isRequired,
|
||||
|
|
|
@ -83,19 +83,29 @@
|
|||
.btn-recompile-group {
|
||||
align-self: stretch;
|
||||
margin-right: 6px;
|
||||
.btn-recompile {
|
||||
height: 100%;
|
||||
.btn-primary;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
&:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
&[disabled] {
|
||||
background-color: mix(@btn-primary-bg, @toolbar-alt-bg-color, 65%);
|
||||
.opacity(1);
|
||||
}
|
||||
border-radius: 0 @btn-border-radius-base @btn-border-radius-base 0;
|
||||
background-color: @btn-primary-bg;
|
||||
&.btn-recompile-group-has-changes {
|
||||
#gradient > .striped(@color: rgba(255, 255, 255, 0.2), @angle: -45deg);
|
||||
background-size: @stripe-width @stripe-width;
|
||||
.animation(pdf-toolbar-stripes 2s linear infinite);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-recompile {
|
||||
height: 100%;
|
||||
// .btn-primary;
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
&:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
&[disabled] {
|
||||
background-color: mix(@btn-primary-bg, @toolbar-alt-bg-color, 65%);
|
||||
.opacity(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,63 +56,63 @@ entering extended mode
|
|||
warnings,
|
||||
typesetting
|
||||
}
|
||||
|
||||
const noOp = () => {}
|
||||
const onLogEntryLocationClick = sinon.stub()
|
||||
const noOp = () =>
|
||||
describe('with logs', function() {
|
||||
beforeEach(function() {
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
logEntries={logEntries}
|
||||
rawLog={sampleRawLog}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
onClearCache={noOp}
|
||||
/>
|
||||
)
|
||||
})
|
||||
it('renders all log entries with appropriate labels', function() {
|
||||
const errorEntries = screen.getAllByLabelText(
|
||||
`Log entry with level: error`
|
||||
)
|
||||
const warningEntries = screen.getAllByLabelText(
|
||||
`Log entry with level: warning`
|
||||
)
|
||||
const typesettingEntries = screen.getAllByLabelText(
|
||||
`Log entry with level: typesetting`
|
||||
)
|
||||
expect(errorEntries).to.have.lengthOf(errors.length)
|
||||
expect(warningEntries).to.have.lengthOf(warnings.length)
|
||||
expect(typesettingEntries).to.have.lengthOf(typesetting.length)
|
||||
})
|
||||
|
||||
it('renders the raw log', function() {
|
||||
screen.getByLabelText('Raw logs from the LaTeX compiler')
|
||||
})
|
||||
describe('with logs', function() {
|
||||
beforeEach(function() {
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
logEntries={logEntries}
|
||||
rawLog={sampleRawLog}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
onClearCache={noOp}
|
||||
/>
|
||||
)
|
||||
})
|
||||
it('renders all log entries with appropriate labels', function() {
|
||||
const errorEntries = screen.getAllByLabelText(
|
||||
`Log entry with level: error`
|
||||
)
|
||||
const warningEntries = screen.getAllByLabelText(
|
||||
`Log entry with level: warning`
|
||||
)
|
||||
const typesettingEntries = screen.getAllByLabelText(
|
||||
`Log entry with level: typesetting`
|
||||
)
|
||||
expect(errorEntries).to.have.lengthOf(errors.length)
|
||||
expect(warningEntries).to.have.lengthOf(warnings.length)
|
||||
expect(typesettingEntries).to.have.lengthOf(typesetting.length)
|
||||
})
|
||||
|
||||
it('renders a link to location button for every error and warning log entry', function() {
|
||||
logEntries.all.forEach((entry, index) => {
|
||||
const linkToSourceButton = screen.getByRole('button', {
|
||||
name: `Navigate to log position in source code: ${entry.file}, ${entry.line}`
|
||||
})
|
||||
fireEvent.click(linkToSourceButton)
|
||||
expect(onLogEntryLocationClick).to.have.callCount(index + 1)
|
||||
const call = onLogEntryLocationClick.getCall(index)
|
||||
expect(
|
||||
call.calledWith({
|
||||
file: entry.file,
|
||||
line: entry.line,
|
||||
column: entry.column
|
||||
})
|
||||
).to.be.true
|
||||
it('renders the raw log', function() {
|
||||
screen.getByLabelText('Raw logs from the LaTeX compiler')
|
||||
})
|
||||
|
||||
it('renders a link to location button for every error and warning log entry', function() {
|
||||
logEntries.all.forEach((entry, index) => {
|
||||
const linkToSourceButton = screen.getByRole('button', {
|
||||
name: `Navigate to log position in source code: ${entry.file}, ${entry.line}`
|
||||
})
|
||||
})
|
||||
it(' does not render a link to location button for the raw log entry', function() {
|
||||
const rawLogEntry = screen.getByLabelText(
|
||||
'Raw logs from the LaTeX compiler'
|
||||
)
|
||||
expect(rawLogEntry.querySelector('.log-entry-header-link')).to.not.exist
|
||||
fireEvent.click(linkToSourceButton)
|
||||
expect(onLogEntryLocationClick).to.have.callCount(index + 1)
|
||||
const call = onLogEntryLocationClick.getCall(index)
|
||||
expect(
|
||||
call.calledWith({
|
||||
file: entry.file,
|
||||
line: entry.line,
|
||||
column: entry.column
|
||||
})
|
||||
).to.be.true
|
||||
})
|
||||
})
|
||||
it(' does not render a link to location button for the raw log entry', function() {
|
||||
const rawLogEntry = screen.getByLabelText(
|
||||
'Raw logs from the LaTeX compiler'
|
||||
)
|
||||
expect(rawLogEntry.querySelector('.log-entry-header-link')).to.not.exist
|
||||
})
|
||||
})
|
||||
|
||||
describe('with validation issues', function() {
|
||||
const sampleValidationIssues = {
|
||||
|
@ -191,4 +191,23 @@ entering extended mode
|
|||
expect(errorEntries).to.have.lengthOf(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with failing code check', function() {
|
||||
beforeEach(function() {
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
logEntries={logEntries}
|
||||
rawLog={sampleRawLog}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
onClearCache={noOp}
|
||||
autoCompileHasLintingError
|
||||
/>
|
||||
)
|
||||
})
|
||||
it('renders a code check failed entry', function() {
|
||||
screen.getByText(
|
||||
'Your code has errors that need to be fixed before the auto-compile can run'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -9,15 +9,18 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
logsState,
|
||||
onToggleLogs,
|
||||
showLogs,
|
||||
showText
|
||||
showText = false,
|
||||
autoCompileLintingError = false,
|
||||
compileFailed = false
|
||||
) {
|
||||
if (showText === undefined) showText = true
|
||||
render(
|
||||
<PreviewLogsToggleButton
|
||||
logsState={logsState}
|
||||
onToggle={onToggleLogs}
|
||||
showLogs={showLogs}
|
||||
showText={showText}
|
||||
autoCompileLintingError={autoCompileLintingError}
|
||||
compileFailed={compileFailed}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -25,8 +28,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
describe('basic toggle functionality', function() {
|
||||
const logsState = {
|
||||
nErrors: 0,
|
||||
nWarnings: 0,
|
||||
nLogEntries: 0
|
||||
nWarnings: 0
|
||||
}
|
||||
const onToggleLogs = () => {}
|
||||
it('should render a view logs button when previewing the PDF', function() {
|
||||
|
@ -46,18 +48,31 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
it('should render a view logs button by default', function() {
|
||||
const logsState = {
|
||||
nErrors: 0,
|
||||
nWarnings: 0,
|
||||
nLogEntries: 0
|
||||
nWarnings: 0
|
||||
}
|
||||
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||
screen.getByText('View logs')
|
||||
})
|
||||
|
||||
it('should render the code check failed notice', function() {
|
||||
const logsState = {
|
||||
nErrors: 1,
|
||||
nWarnings: 0
|
||||
}
|
||||
renderPreviewLogsToggleButton(
|
||||
logsState,
|
||||
onToggleLogs,
|
||||
showLogs,
|
||||
false,
|
||||
true
|
||||
)
|
||||
screen.getByText('Code check failed')
|
||||
})
|
||||
|
||||
it('should render an error status message when there are errors', function() {
|
||||
const logsState = {
|
||||
nErrors: 1,
|
||||
nWarnings: 0,
|
||||
nLogEntries: 0
|
||||
nWarnings: 0
|
||||
}
|
||||
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||
screen.getByText(`This project has errors (${logsState.nErrors})`)
|
||||
|
@ -66,8 +81,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
it('should render an error status message when there are both errors and warnings', function() {
|
||||
const logsState = {
|
||||
nErrors: 1,
|
||||
nWarnings: 1,
|
||||
nLogEntries: 0
|
||||
nWarnings: 1
|
||||
}
|
||||
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||
screen.getByText(`This project has errors (${logsState.nErrors})`)
|
||||
|
@ -76,8 +90,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
it('should render a warning status message when there are warnings but no errors', function() {
|
||||
const logsState = {
|
||||
nErrors: 0,
|
||||
nWarnings: 1,
|
||||
nLogEntries: 0
|
||||
nWarnings: 1
|
||||
}
|
||||
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||
screen.getByText(`View warnings (${logsState.nWarnings})`)
|
||||
|
@ -86,8 +99,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
it('should render 99+ errors when there are more than 99 errors', function() {
|
||||
const logsState = {
|
||||
nErrors: 100,
|
||||
nWarnings: 0,
|
||||
nLogEntries: 0
|
||||
nWarnings: 0
|
||||
}
|
||||
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||
screen.getByText('This project has errors (99+)')
|
||||
|
@ -95,8 +107,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
it('should show the button text when prop showText=true', function() {
|
||||
const logsState = {
|
||||
nErrors: 0,
|
||||
nWarnings: 0,
|
||||
nLogEntries: 0
|
||||
nWarnings: 0
|
||||
}
|
||||
const showText = true
|
||||
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs, showText)
|
||||
|
@ -105,8 +116,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
|||
it('should not show the button text when prop showText=false', function() {
|
||||
const logsState = {
|
||||
nErrors: 0,
|
||||
nWarnings: 0,
|
||||
nLogEntries: 0
|
||||
nWarnings: 0
|
||||
}
|
||||
const showText = false
|
||||
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs, showText)
|
||||
|
|
|
@ -275,6 +275,7 @@ describe('<PreviewPane />', function() {
|
|||
]
|
||||
return {
|
||||
compilerState: {
|
||||
autoCompileHasChanges: false,
|
||||
isAutoCompileOn: false,
|
||||
isCompiling: isCompiling,
|
||||
isClearingCache: false,
|
||||
|
|
|
@ -88,14 +88,46 @@ describe('<PreviewRecompileButton />', function() {
|
|||
)
|
||||
})
|
||||
|
||||
describe('Autocompile feedback', function() {
|
||||
it('shows animated visual feedback via CSS class when there are uncompiled changes', function() {
|
||||
const { container } = renderPreviewRecompileButton({
|
||||
autoCompileHasChanges: true,
|
||||
autoCompileHasLintingError: false
|
||||
})
|
||||
const recompileBtnGroupEl = container.querySelector(
|
||||
'.btn-recompile-group'
|
||||
)
|
||||
expect(
|
||||
recompileBtnGroupEl.classList.contains(
|
||||
'btn-recompile-group-has-changes'
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
it('does not show animated visual feedback via CSS class when there are no uncompiled changes', function() {
|
||||
const { container } = renderPreviewRecompileButton({
|
||||
autoCompileHasChanges: false,
|
||||
autoCompileHasLintingError: false
|
||||
})
|
||||
const recompileBtnGroupEl = container.querySelector(
|
||||
'.btn-recompile-group'
|
||||
)
|
||||
expect(
|
||||
recompileBtnGroupEl.classList.contains(
|
||||
'btn-recompile-group-has-changes'
|
||||
)
|
||||
).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
function renderPreviewRecompileButton(compilerState = {}, showText) {
|
||||
if (!compilerState.logEntries) {
|
||||
compilerState.logEntries = {}
|
||||
}
|
||||
if (showText === undefined) showText = true
|
||||
render(
|
||||
return render(
|
||||
<PreviewRecompileButton
|
||||
compilerState={{
|
||||
autoCompileHasChanges: false,
|
||||
isAutoCompileOn: true,
|
||||
isClearingCache: false,
|
||||
isCompiling: false,
|
||||
|
|
|
@ -25,6 +25,7 @@ describe('<PreviewToolbar />', function() {
|
|||
render(
|
||||
<PreviewToolbar
|
||||
compilerState={{
|
||||
autoCompileHasChanges: true,
|
||||
isAutoCompileOn: true,
|
||||
isClearingCache: false,
|
||||
isCompiling: false,
|
||||
|
@ -33,7 +34,7 @@ describe('<PreviewToolbar />', function() {
|
|||
logEntries: {},
|
||||
...compilerState
|
||||
}}
|
||||
logsState={{ nErrors: 0, nWarnings: 0, nLogEntries: 0, ...logState }}
|
||||
logsState={{ nErrors: 0, nWarnings: 0, ...logState }}
|
||||
onRecompile={onRecompile}
|
||||
onRecompileFromScratch={onRecompileFromScratch}
|
||||
onRunSyntaxCheckNow={onRunSyntaxCheckNow}
|
||||
|
|
Loading…
Add table
Reference in a new issue