preview-logs-pane.test.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import React from 'react'
  2. import { screen, fireEvent } from '@testing-library/react'
  3. import PreviewLogsPane from '../../../../../frontend/js/features/preview/components/preview-logs-pane'
  4. import sinon from 'sinon'
  5. import { renderWithEditorContext } from '../../../helpers/render-with-context'
  6. const { expect } = require('chai')
  7. describe('<PreviewLogsPane />', function() {
  8. const sampleError1 = {
  9. content: 'error 1 content',
  10. file: 'main.tex',
  11. level: 'error',
  12. line: 17,
  13. message: 'Misplaced alignment tab character &.'
  14. }
  15. const sampleError2 = {
  16. content: 'error 1 content',
  17. file: 'main.tex',
  18. level: 'error',
  19. line: 22,
  20. message: 'Extra alignment tab has been changed to cr.'
  21. }
  22. const sampleWarning = {
  23. file: 'main.tex',
  24. level: 'warning',
  25. line: 30,
  26. message: "Reference `idontexist' on page 1 undefined on input line 30."
  27. }
  28. const sampleTypesettingIssue = {
  29. file: 'main.tex',
  30. level: 'typesetting',
  31. line: 12,
  32. message: "Reference `idontexist' on page 1 undefined on input line 30."
  33. }
  34. const sampleRawLog = `
  35. This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex 2020.9.10) 6 NOV 2020 15:23
  36. entering extended mode
  37. \\write18 enabled.
  38. %&-line parsing enabled.
  39. **main.tex
  40. (/compile/main.tex
  41. LaTeX2e <2020-02-02> patch level 5
  42. L3 programming layer <2020-07-17> (/usr/local/texlive/2020/texmf-dist/tex/latex
  43. /base/article.cls
  44. Document Class: article 2019/12/20 v1.4l Standard LaTeX document class
  45. (/usr/local/texlive/2020/texmf-dist/tex/latex/base/size10.clo
  46. File: size10.clo 2019/12/20 v1.4l Standard LaTeX file (size option)
  47. )`
  48. const errors = [sampleError1, sampleError2]
  49. const warnings = [sampleWarning]
  50. const typesetting = [sampleTypesettingIssue]
  51. const logEntries = {
  52. all: [...errors, ...warnings, ...typesetting],
  53. errors,
  54. warnings,
  55. typesetting
  56. }
  57. const noOp = () => {}
  58. const onLogEntryLocationClick = sinon.stub()
  59. describe('with logs', function() {
  60. beforeEach(function() {
  61. renderWithEditorContext(
  62. <PreviewLogsPane
  63. logEntries={logEntries}
  64. rawLog={sampleRawLog}
  65. onLogEntryLocationClick={onLogEntryLocationClick}
  66. onClearCache={noOp}
  67. />
  68. )
  69. })
  70. it('renders all log entries with appropriate labels', function() {
  71. const errorEntries = screen.getAllByLabelText(
  72. `Log entry with level: error`
  73. )
  74. const warningEntries = screen.getAllByLabelText(
  75. `Log entry with level: warning`
  76. )
  77. const typesettingEntries = screen.getAllByLabelText(
  78. `Log entry with level: typesetting`
  79. )
  80. expect(errorEntries).to.have.lengthOf(errors.length)
  81. expect(warningEntries).to.have.lengthOf(warnings.length)
  82. expect(typesettingEntries).to.have.lengthOf(typesetting.length)
  83. })
  84. it('renders the raw log', function() {
  85. screen.getByLabelText('Raw logs from the LaTeX compiler')
  86. })
  87. it('renders a link to location button for every error and warning log entry', function() {
  88. logEntries.all.forEach((entry, index) => {
  89. const linkToSourceButton = screen.getByRole('button', {
  90. name: `Navigate to log position in source code: ${entry.file}, ${entry.line}`
  91. })
  92. fireEvent.click(linkToSourceButton)
  93. expect(onLogEntryLocationClick).to.have.callCount(index + 1)
  94. const call = onLogEntryLocationClick.getCall(index)
  95. expect(
  96. call.calledWith({
  97. file: entry.file,
  98. line: entry.line,
  99. column: entry.column
  100. })
  101. ).to.be.true
  102. })
  103. })
  104. it(' does not render a link to location button for the raw log entry', function() {
  105. const rawLogEntry = screen.getByLabelText(
  106. 'Raw logs from the LaTeX compiler'
  107. )
  108. expect(rawLogEntry.querySelector('.log-entry-header-link')).to.not.exist
  109. })
  110. })
  111. describe('with validation issues', function() {
  112. const sampleValidationIssues = {
  113. sizeCheck: {
  114. resources: [
  115. { path: 'foo/bar', kbSize: 76221 },
  116. { path: 'bar/baz', kbSize: 2342 }
  117. ]
  118. },
  119. mainFile: true
  120. }
  121. it('renders a validation entry for known issues', function() {
  122. renderWithEditorContext(
  123. <PreviewLogsPane
  124. validationIssues={sampleValidationIssues}
  125. onLogEntryLocationClick={onLogEntryLocationClick}
  126. onClearCache={noOp}
  127. />
  128. )
  129. const validationEntries = screen.getAllByLabelText(
  130. 'A validation issue which prevented this project from compiling'
  131. )
  132. expect(validationEntries).to.have.lengthOf(
  133. Object.keys(sampleValidationIssues).length
  134. )
  135. })
  136. it('ignores unknown issues', function() {
  137. renderWithEditorContext(
  138. <PreviewLogsPane
  139. validationIssues={{ unknownIssue: true }}
  140. onLogEntryLocationClick={onLogEntryLocationClick}
  141. onClearCache={noOp}
  142. />
  143. )
  144. const validationEntries = screen.queryAllByLabelText(
  145. 'A validation issue prevented this project from compiling'
  146. )
  147. expect(validationEntries).to.have.lengthOf(0)
  148. })
  149. })
  150. describe('with compilation errors', function() {
  151. const sampleErrors = {
  152. clsiMaintenance: true,
  153. tooRecentlyCompiled: true,
  154. compileTerminated: true
  155. }
  156. it('renders an error entry for known errors', function() {
  157. renderWithEditorContext(
  158. <PreviewLogsPane
  159. errors={sampleErrors}
  160. onLogEntryLocationClick={onLogEntryLocationClick}
  161. onClearCache={noOp}
  162. />
  163. )
  164. const errorEntries = screen.getAllByLabelText(
  165. 'An error which prevented this project from compiling'
  166. )
  167. expect(errorEntries).to.have.lengthOf(Object.keys(sampleErrors).length)
  168. })
  169. it('ignores unknown errors', function() {
  170. renderWithEditorContext(
  171. <PreviewLogsPane
  172. errors={{ unknownIssue: true }}
  173. onLogEntryLocationClick={onLogEntryLocationClick}
  174. onClearCache={noOp}
  175. />
  176. )
  177. const errorEntries = screen.queryAllByLabelText(
  178. 'There was an error compiling this project'
  179. )
  180. expect(errorEntries).to.have.lengthOf(0)
  181. })
  182. })
  183. describe('with failing code check', function() {
  184. beforeEach(function() {
  185. renderWithEditorContext(
  186. <PreviewLogsPane
  187. logEntries={logEntries}
  188. rawLog={sampleRawLog}
  189. onLogEntryLocationClick={onLogEntryLocationClick}
  190. onClearCache={noOp}
  191. autoCompileHasLintingError
  192. />
  193. )
  194. })
  195. it('renders a code check failed entry', function() {
  196. screen.getByText(
  197. 'Your code has errors that need to be fixed before the auto-compile can run'
  198. )
  199. })
  200. })
  201. })