mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3242 from overleaf/jel-add-recompile-from-scratch
Add recompile from scratch option GitOrigin-RevId: 59836df9049e307acb11824058024919409ea4a4
This commit is contained in:
parent
4690b2ec86
commit
f8a8c9bbd6
8 changed files with 191 additions and 10 deletions
|
@ -3,11 +3,13 @@ div.full-size.pdf(ng-controller="PdfController")
|
||||||
preview-pane(
|
preview-pane(
|
||||||
compiler-state=`{
|
compiler-state=`{
|
||||||
isAutoCompileOn: autocompile_enabled,
|
isAutoCompileOn: autocompile_enabled,
|
||||||
|
isClearingCache: pdf.clearingCache,
|
||||||
isCompiling: pdf.compiling,
|
isCompiling: pdf.compiling,
|
||||||
isDraftModeOn: draft,
|
isDraftModeOn: draft,
|
||||||
isSyntaxCheckOn: stop_on_validation_error,
|
isSyntaxCheckOn: stop_on_validation_error,
|
||||||
logEntries: pdf.logEntries ? pdf.logEntries : {}
|
logEntries: pdf.logEntries ? pdf.logEntries : {}
|
||||||
}`
|
}`
|
||||||
|
on-clear-cache="clearCache"
|
||||||
on-recompile="recompile"
|
on-recompile="recompile"
|
||||||
on-run-syntax-check-now="runSyntaxCheckNow"
|
on-run-syntax-check-now="runSyntaxCheckNow"
|
||||||
on-set-auto-compile="setAutoCompile"
|
on-set-auto-compile="setAutoCompile"
|
||||||
|
@ -379,6 +381,7 @@ script(type='text/ng-template', id='clearCacheModalTemplate')
|
||||||
.modal-body
|
.modal-body
|
||||||
p #{translate("clear_cache_explanation")}
|
p #{translate("clear_cache_explanation")}
|
||||||
p #{translate("clear_cache_is_safe")}
|
p #{translate("clear_cache_is_safe")}
|
||||||
|
.alert.alert-danger(ng-if="state.error") #{translate("generic_something_went_wrong")}.
|
||||||
.modal-footer
|
.modal-footer
|
||||||
button.btn.btn-default(
|
button.btn.btn-default(
|
||||||
ng-click="cancel()"
|
ng-click="cancel()"
|
||||||
|
|
|
@ -29,5 +29,8 @@
|
||||||
"your_project_has_errors",
|
"your_project_has_errors",
|
||||||
"view_warnings",
|
"view_warnings",
|
||||||
"view_logs",
|
"view_logs",
|
||||||
"view_pdf"
|
"view_pdf",
|
||||||
|
"recompile_from_scratch",
|
||||||
|
"run_syntax_check_now",
|
||||||
|
"toggle_compile_options_menu"
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
function PreviewPane({
|
function PreviewPane({
|
||||||
compilerState,
|
compilerState,
|
||||||
|
onClearCache,
|
||||||
onRecompile,
|
onRecompile,
|
||||||
onRunSyntaxCheckNow,
|
onRunSyntaxCheckNow,
|
||||||
onSetAutoCompile,
|
onSetAutoCompile,
|
||||||
|
@ -35,6 +36,7 @@ function PreviewPane({
|
||||||
compilerState={compilerState}
|
compilerState={compilerState}
|
||||||
logsState={{ nErrors, nWarnings, nLogEntries }}
|
logsState={{ nErrors, nWarnings, nLogEntries }}
|
||||||
showLogs={showLogs}
|
showLogs={showLogs}
|
||||||
|
onClearCache={onClearCache}
|
||||||
onRecompile={onRecompile}
|
onRecompile={onRecompile}
|
||||||
onRunSyntaxCheckNow={onRunSyntaxCheckNow}
|
onRunSyntaxCheckNow={onRunSyntaxCheckNow}
|
||||||
onSetAutoCompile={onSetAutoCompile}
|
onSetAutoCompile={onSetAutoCompile}
|
||||||
|
@ -67,6 +69,7 @@ PreviewPane.propTypes = {
|
||||||
isSyntaxCheckOn: PropTypes.bool.isRequired,
|
isSyntaxCheckOn: PropTypes.bool.isRequired,
|
||||||
logEntries: PropTypes.object.isRequired
|
logEntries: PropTypes.object.isRequired
|
||||||
}),
|
}),
|
||||||
|
onClearCache: PropTypes.func.isRequired,
|
||||||
onRecompile: PropTypes.func.isRequired,
|
onRecompile: PropTypes.func.isRequired,
|
||||||
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
||||||
onSetAutoCompile: PropTypes.func.isRequired,
|
onSetAutoCompile: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -7,10 +7,12 @@ import Icon from '../../../shared/components/icon'
|
||||||
function PreviewRecompileButton({
|
function PreviewRecompileButton({
|
||||||
compilerState: {
|
compilerState: {
|
||||||
isAutoCompileOn,
|
isAutoCompileOn,
|
||||||
|
isClearingCache,
|
||||||
isCompiling,
|
isCompiling,
|
||||||
isDraftModeOn,
|
isDraftModeOn,
|
||||||
isSyntaxCheckOn
|
isSyntaxCheckOn
|
||||||
},
|
},
|
||||||
|
onClearCache,
|
||||||
onRecompile,
|
onRecompile,
|
||||||
onRunSyntaxCheckNow,
|
onRunSyntaxCheckNow,
|
||||||
onSetAutoCompile,
|
onSetAutoCompile,
|
||||||
|
@ -19,6 +21,16 @@ function PreviewRecompileButton({
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
function handleRecompileFromScratch() {
|
||||||
|
onClearCache()
|
||||||
|
.then(() => {
|
||||||
|
onRecompile()
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function handleSelectAutoCompileOn() {
|
function handleSelectAutoCompileOn() {
|
||||||
onSetAutoCompile(true)
|
onSetAutoCompile(true)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +59,7 @@ function PreviewRecompileButton({
|
||||||
<Dropdown id="pdf-recompile-dropdown" className="btn-recompile-group">
|
<Dropdown id="pdf-recompile-dropdown" className="btn-recompile-group">
|
||||||
<button className="btn btn-recompile" onClick={onRecompile}>
|
<button className="btn btn-recompile" onClick={onRecompile}>
|
||||||
<Icon type="refresh" spin={isCompiling} />
|
<Icon type="refresh" spin={isCompiling} />
|
||||||
{isCompiling ? (
|
{isCompiling || isClearingCache ? (
|
||||||
<span className="btn-recompile-label">
|
<span className="btn-recompile-label">
|
||||||
{t('compiling')}
|
{t('compiling')}
|
||||||
…
|
…
|
||||||
|
@ -56,7 +68,10 @@ function PreviewRecompileButton({
|
||||||
<span className="btn-recompile-label">{t('recompile')}</span>
|
<span className="btn-recompile-label">{t('recompile')}</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<Dropdown.Toggle className="btn btn-recompile" />
|
<Dropdown.Toggle
|
||||||
|
aria-label={t('toggle_compile_options_menu')}
|
||||||
|
className="btn btn-recompile"
|
||||||
|
/>
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
<MenuItem header>{t('auto_compile')}</MenuItem>
|
<MenuItem header>{t('auto_compile')}</MenuItem>
|
||||||
<MenuItem onSelect={handleSelectAutoCompileOn}>
|
<MenuItem onSelect={handleSelectAutoCompileOn}>
|
||||||
|
@ -89,6 +104,14 @@ function PreviewRecompileButton({
|
||||||
<Icon type="" modifier="fw" />
|
<Icon type="" modifier="fw" />
|
||||||
{t('run_syntax_check_now')}
|
{t('run_syntax_check_now')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem divider />
|
||||||
|
<MenuItem
|
||||||
|
onSelect={handleRecompileFromScratch}
|
||||||
|
disabled={isCompiling || isClearingCache}
|
||||||
|
aria-disabled={!!(isCompiling || isClearingCache)}
|
||||||
|
>
|
||||||
|
{t('recompile_from_scratch')}
|
||||||
|
</MenuItem>
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
)
|
)
|
||||||
|
@ -97,11 +120,13 @@ function PreviewRecompileButton({
|
||||||
PreviewRecompileButton.propTypes = {
|
PreviewRecompileButton.propTypes = {
|
||||||
compilerState: PropTypes.shape({
|
compilerState: PropTypes.shape({
|
||||||
isAutoCompileOn: PropTypes.bool.isRequired,
|
isAutoCompileOn: PropTypes.bool.isRequired,
|
||||||
|
isClearingCache: PropTypes.bool.isRequired,
|
||||||
isCompiling: PropTypes.bool.isRequired,
|
isCompiling: PropTypes.bool.isRequired,
|
||||||
isDraftModeOn: PropTypes.bool.isRequired,
|
isDraftModeOn: PropTypes.bool.isRequired,
|
||||||
isSyntaxCheckOn: PropTypes.bool.isRequired,
|
isSyntaxCheckOn: PropTypes.bool.isRequired,
|
||||||
logEntries: PropTypes.object.isRequired
|
logEntries: PropTypes.object.isRequired
|
||||||
}),
|
}),
|
||||||
|
onClearCache: PropTypes.func.isRequired,
|
||||||
onRecompile: PropTypes.func.isRequired,
|
onRecompile: PropTypes.func.isRequired,
|
||||||
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
||||||
onSetAutoCompile: PropTypes.func.isRequired,
|
onSetAutoCompile: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import PreviewLogsToggleButton from './preview-logs-toggle-button'
|
||||||
function PreviewToolbar({
|
function PreviewToolbar({
|
||||||
compilerState,
|
compilerState,
|
||||||
logsState,
|
logsState,
|
||||||
|
onClearCache,
|
||||||
onRecompile,
|
onRecompile,
|
||||||
onRunSyntaxCheckNow,
|
onRunSyntaxCheckNow,
|
||||||
onSetAutoCompile,
|
onSetAutoCompile,
|
||||||
|
@ -24,6 +25,7 @@ function PreviewToolbar({
|
||||||
onSetAutoCompile={onSetAutoCompile}
|
onSetAutoCompile={onSetAutoCompile}
|
||||||
onSetDraftMode={onSetDraftMode}
|
onSetDraftMode={onSetDraftMode}
|
||||||
onSetSyntaxCheck={onSetSyntaxCheck}
|
onSetSyntaxCheck={onSetSyntaxCheck}
|
||||||
|
onClearCache={onClearCache}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="toolbar-pdf-right">
|
<div className="toolbar-pdf-right">
|
||||||
|
@ -51,6 +53,7 @@ PreviewToolbar.propTypes = {
|
||||||
nLogEntries: PropTypes.number.isRequired
|
nLogEntries: PropTypes.number.isRequired
|
||||||
}),
|
}),
|
||||||
showLogs: PropTypes.bool.isRequired,
|
showLogs: PropTypes.bool.isRequired,
|
||||||
|
onClearCache: PropTypes.func.isRequired,
|
||||||
onRecompile: PropTypes.func.isRequired,
|
onRecompile: PropTypes.func.isRequired,
|
||||||
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
||||||
onSetAutoCompile: PropTypes.func.isRequired,
|
onSetAutoCompile: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -20,12 +20,14 @@ App.controller('PdfController', function(
|
||||||
$modal,
|
$modal,
|
||||||
synctex,
|
synctex,
|
||||||
eventTracking,
|
eventTracking,
|
||||||
localStorage
|
localStorage,
|
||||||
|
$q
|
||||||
) {
|
) {
|
||||||
let autoCompile = true
|
let autoCompile = true
|
||||||
|
|
||||||
// pdf.view = uncompiled | pdf | errors
|
// pdf.view = uncompiled | pdf | errors
|
||||||
$scope.pdf.view = $scope.pdf.url ? 'pdf' : 'uncompiled'
|
$scope.pdf.view = $scope.pdf.url ? 'pdf' : 'uncompiled'
|
||||||
|
$scope.pdf.clearingCache = false
|
||||||
$scope.shouldShowLogs = false
|
$scope.shouldShowLogs = false
|
||||||
$scope.wikiEnabled = window.wikiEnabled
|
$scope.wikiEnabled = window.wikiEnabled
|
||||||
|
|
||||||
|
@ -796,7 +798,10 @@ App.controller('PdfController', function(
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.clearCache = function() {
|
$scope.clearCache = function() {
|
||||||
return $http({
|
$scope.pdf.clearingCache = true
|
||||||
|
const deferred = $q.defer()
|
||||||
|
|
||||||
|
$http({
|
||||||
url: `/project/${$scope.project_id}/output`,
|
url: `/project/${$scope.project_id}/output`,
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
params: {
|
params: {
|
||||||
|
@ -806,6 +811,20 @@ App.controller('PdfController', function(
|
||||||
'X-Csrf-Token': window.csrfToken
|
'X-Csrf-Token': window.csrfToken
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.then(function(response) {
|
||||||
|
$scope.pdf.clearingCache = false
|
||||||
|
return deferred.resolve()
|
||||||
|
})
|
||||||
|
.catch(function(response) {
|
||||||
|
console.error(response)
|
||||||
|
const error = response.data
|
||||||
|
$scope.pdf.clearingCache = false
|
||||||
|
$scope.pdf.renderingError = false
|
||||||
|
$scope.pdf.error = true
|
||||||
|
$scope.pdf.view = 'errors'
|
||||||
|
return deferred.reject(error)
|
||||||
|
})
|
||||||
|
return deferred.promise
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.toggleLogs = function() {
|
$scope.toggleLogs = function() {
|
||||||
|
@ -1056,14 +1075,20 @@ App.controller('PdfLogEntryController', function($scope, ide, eventTracking) {
|
||||||
})
|
})
|
||||||
|
|
||||||
App.controller('ClearCacheModalController', function($scope, $modalInstance) {
|
App.controller('ClearCacheModalController', function($scope, $modalInstance) {
|
||||||
$scope.state = { inflight: false }
|
$scope.state = { error: false, inflight: false }
|
||||||
|
|
||||||
$scope.clear = function() {
|
$scope.clear = function() {
|
||||||
$scope.state.inflight = true
|
$scope.state.inflight = true
|
||||||
$scope.clearCache().then(function() {
|
$scope
|
||||||
$scope.state.inflight = false
|
.clearCache()
|
||||||
$modalInstance.close()
|
.then(function() {
|
||||||
})
|
$scope.state.inflight = false
|
||||||
|
$modalInstance.close()
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
$scope.state.error = true
|
||||||
|
$scope.state.inflight = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.cancel = () => $modalInstance.dismiss('cancel')
|
$scope.cancel = () => $modalInstance.dismiss('cancel')
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"n_warnings_plural": "__count__ warnings",
|
"n_warnings_plural": "__count__ warnings",
|
||||||
"n_errors": "__count__ error",
|
"n_errors": "__count__ error",
|
||||||
"n_errors_plural": "__count__ errors",
|
"n_errors_plural": "__count__ errors",
|
||||||
|
"toggle_compile_options_menu": "Toggle compile options menu",
|
||||||
"view_pdf": "View PDF",
|
"view_pdf": "View PDF",
|
||||||
"your_project_has_errors": "Your project has errors",
|
"your_project_has_errors": "Your project has errors",
|
||||||
"view_warnings": "View warnings",
|
"view_warnings": "View warnings",
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
import React from 'react'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import { screen, render, fireEvent } from '@testing-library/react'
|
||||||
|
import PreviewRecompileButton from '../../../../../frontend/js/features/preview/components/preview-recompile-button'
|
||||||
|
const { expect } = require('chai')
|
||||||
|
|
||||||
|
describe('<PreviewRecompileButton />', function() {
|
||||||
|
let onRecompile, onClearCache
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
onRecompile = sinon.stub().resolves()
|
||||||
|
onClearCache = sinon.stub().resolves()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders all items', function() {
|
||||||
|
renderPreviewRecompileButton()
|
||||||
|
|
||||||
|
const menuItems = screen.getAllByRole('menuitem')
|
||||||
|
expect(menuItems.length).to.equal(8)
|
||||||
|
expect(menuItems.map(item => item.textContent)).to.deep.equal([
|
||||||
|
'On',
|
||||||
|
'Off',
|
||||||
|
'Normal',
|
||||||
|
'Fast [draft]',
|
||||||
|
'Check syntax before compile',
|
||||||
|
"Don't check syntax",
|
||||||
|
'Run syntax check now',
|
||||||
|
'Recompile from scratch'
|
||||||
|
])
|
||||||
|
|
||||||
|
const menuHeadingItems = screen.getAllByRole('heading')
|
||||||
|
expect(menuHeadingItems.length).to.equal(3)
|
||||||
|
expect(menuHeadingItems.map(item => item.textContent)).to.deep.equal([
|
||||||
|
'Auto Compile',
|
||||||
|
'Compile Mode',
|
||||||
|
'Syntax Checks'
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Recompile from scratch', function() {
|
||||||
|
describe('click', function() {
|
||||||
|
it('should call onClearCache and onRecompile', async function() {
|
||||||
|
renderPreviewRecompileButton()
|
||||||
|
|
||||||
|
const button = screen.getByRole('menuitem', {
|
||||||
|
name: 'Recompile from scratch'
|
||||||
|
})
|
||||||
|
await fireEvent.click(button)
|
||||||
|
expect(onClearCache).to.have.been.calledOnce
|
||||||
|
expect(onRecompile).to.have.been.calledOnce
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('processing', function() {
|
||||||
|
it('shows processing view and disable menuItem when clearing cache', function() {
|
||||||
|
renderPreviewRecompileButton({ isClearingCache: true })
|
||||||
|
|
||||||
|
screen.getByRole('button', { name: 'Compiling …' })
|
||||||
|
expect(
|
||||||
|
screen
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: 'Recompile from scratch'
|
||||||
|
})
|
||||||
|
.getAttribute('aria-disabled')
|
||||||
|
).to.equal('true')
|
||||||
|
expect(
|
||||||
|
screen
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: 'Recompile from scratch'
|
||||||
|
})
|
||||||
|
.closest('li')
|
||||||
|
.getAttribute('class')
|
||||||
|
).to.equal('disabled')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows processing view and disable menuItem when recompiling', function() {
|
||||||
|
renderPreviewRecompileButton({ isCompiling: true })
|
||||||
|
|
||||||
|
screen.getByRole('button', { name: 'Compiling …' })
|
||||||
|
expect(
|
||||||
|
screen
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: 'Recompile from scratch'
|
||||||
|
})
|
||||||
|
.getAttribute('aria-disabled')
|
||||||
|
).to.equal('true')
|
||||||
|
expect(
|
||||||
|
screen
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: 'Recompile from scratch'
|
||||||
|
})
|
||||||
|
.closest('li')
|
||||||
|
.getAttribute('class')
|
||||||
|
).to.equal('disabled')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
function renderPreviewRecompileButton(compilerState = {}) {
|
||||||
|
render(
|
||||||
|
<PreviewRecompileButton
|
||||||
|
compilerState={{
|
||||||
|
isAutoCompileOn: true,
|
||||||
|
isClearingCache: false,
|
||||||
|
isCompiling: false,
|
||||||
|
isDraftModeOn: false,
|
||||||
|
isSyntaxCheckOn: false,
|
||||||
|
...compilerState
|
||||||
|
}}
|
||||||
|
onRecompile={onRecompile}
|
||||||
|
onRunSyntaxCheckNow={() => {}}
|
||||||
|
onSetAutoCompile={() => {}}
|
||||||
|
onSetDraftMode={() => {}}
|
||||||
|
onSetSyntaxCheck={() => {}}
|
||||||
|
onClearCache={onClearCache}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in a new issue