preview-pane.test.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import { screen, render, fireEvent } from '@testing-library/react'
  2. import PreviewPane from '../../../../../frontend/js/features/preview/components/preview-pane'
  3. const { expect } = require('chai')
  4. describe('<PreviewPane />', function () {
  5. const sampleError1 = {
  6. content: 'error 1 content',
  7. file: 'main.tex',
  8. level: 'error',
  9. line: 17,
  10. message: 'Misplaced alignment tab character &.',
  11. }
  12. const sampleError2 = {
  13. content: 'error 1 content',
  14. file: 'main.tex',
  15. level: 'error',
  16. line: 22,
  17. message: 'Extra alignment tab has been changed to cr.',
  18. }
  19. const sampleWarning = {
  20. file: 'main.tex',
  21. level: 'warning',
  22. line: 30,
  23. message: "Reference `idontexist' on page 1 undefined on input line 30.",
  24. }
  25. describe('first error pop-up', function () {
  26. it('renders a first error pop-up with the first error', function () {
  27. const propsAfterCompileWithErrors = getProps(false, {
  28. errors: [sampleError1, sampleError2],
  29. warnings: [sampleWarning],
  30. })
  31. render(<PreviewPane {...propsAfterCompileWithErrors} />)
  32. screen.getByRole('alertdialog', {
  33. name: 'This project has errors. This is the first one.',
  34. })
  35. screen.getByText(sampleError1.message)
  36. })
  37. it('does not render a first error pop-up when there are only warnings', function () {
  38. const propsAfterCompileWithWarningsOnly = getProps(false, {
  39. errors: [],
  40. warnings: [sampleWarning],
  41. })
  42. render(<PreviewPane {...propsAfterCompileWithWarningsOnly} />)
  43. expect(
  44. screen.queryByRole('alertdialog', {
  45. name: 'This project has errors. This is the first one.',
  46. })
  47. ).to.not.exist
  48. })
  49. it('does not render a first error pop-up when a compile is ongoing', function () {
  50. const propsWhileCompiling = getProps(true, {
  51. errors: [sampleError1, sampleError2],
  52. warnings: [sampleWarning],
  53. })
  54. render(<PreviewPane {...propsWhileCompiling} />)
  55. expect(
  56. screen.queryByRole('alertdialog', {
  57. name: 'This project has errors. This is the first one.',
  58. })
  59. ).to.not.exist
  60. })
  61. it('does not render a first error pop-up when viewing logs', function () {
  62. const propsWithErrorsViewingLogs = getProps(
  63. false,
  64. {
  65. errors: [sampleError1, sampleError2],
  66. warnings: [sampleWarning],
  67. },
  68. Date.now(),
  69. true
  70. )
  71. render(<PreviewPane {...propsWithErrorsViewingLogs} />)
  72. expect(
  73. screen.queryByRole('alertdialog', {
  74. name: 'This project has errors. This is the first one.',
  75. })
  76. ).to.not.exist
  77. })
  78. it('does not render a first error pop-up when going back to the PDF view after viewing logs', function () {
  79. const nowTimestamp = Date.now()
  80. const propsWithErrorsViewingLogs = getProps(
  81. false,
  82. {
  83. errors: [sampleError1, sampleError2],
  84. warnings: [sampleWarning],
  85. },
  86. nowTimestamp,
  87. true
  88. )
  89. const propsWithErrorsAfterViewingLogs = getProps(
  90. false,
  91. {
  92. errors: [sampleError1, sampleError2],
  93. warnings: [sampleWarning],
  94. },
  95. nowTimestamp,
  96. false
  97. )
  98. const { rerender } = render(
  99. <PreviewPane {...propsWithErrorsViewingLogs} />
  100. )
  101. rerender(<PreviewPane {...propsWithErrorsAfterViewingLogs} />)
  102. expect(
  103. screen.queryByRole('alertdialog', {
  104. name: 'This project has errors. This is the first one.',
  105. })
  106. ).to.not.exist
  107. })
  108. it('renders a first error pop-up with updated errors after recompiling', function () {
  109. const nowTimestamp = Date.now()
  110. const laterTimestamp = Date.now() + 1000
  111. const propsWithErrorsAfterFirstCompile = getProps(
  112. false,
  113. {
  114. errors: [sampleError1, sampleError2],
  115. warnings: [sampleWarning],
  116. },
  117. nowTimestamp,
  118. true
  119. )
  120. const propsWithErrorsAfterSecondCompile = getProps(
  121. false,
  122. {
  123. errors: [sampleError2],
  124. warnings: [sampleWarning],
  125. },
  126. laterTimestamp,
  127. false
  128. )
  129. const { rerender } = render(
  130. <PreviewPane {...propsWithErrorsAfterFirstCompile} />
  131. )
  132. rerender(<PreviewPane {...propsWithErrorsAfterSecondCompile} />)
  133. screen.getByRole('alertdialog', {
  134. name: 'This project has errors. This is the first one.',
  135. })
  136. screen.getByText(sampleError2.message)
  137. })
  138. it('allows dismissing the first error pop-up', function () {
  139. const propsWithErrors = getProps(false, {
  140. errors: [sampleError1, sampleError2],
  141. warnings: [sampleWarning],
  142. })
  143. render(<PreviewPane {...propsWithErrors} />)
  144. const dismissPopUpButton = screen.getByRole('button', {
  145. name: 'Dismiss first error alert',
  146. })
  147. fireEvent.click(dismissPopUpButton)
  148. expect(
  149. screen.queryByRole('alertdialog', {
  150. name: 'This project has errors. This is the first one.',
  151. })
  152. ).to.not.exist
  153. })
  154. it('does not render the first error pop-up with new recompiles after it being dismissed once', function () {
  155. const nowTimestamp = Date.now()
  156. const laterTimestamp = Date.now() + 1000
  157. const propsWithErrorsForFirstCompile = getProps(
  158. false,
  159. {
  160. errors: [sampleError1, sampleError2],
  161. warnings: [sampleWarning],
  162. },
  163. nowTimestamp
  164. )
  165. const propsWithErrorsForSecondCompile = getProps(
  166. false,
  167. {
  168. errors: [sampleError2],
  169. warnings: [sampleWarning],
  170. },
  171. laterTimestamp
  172. )
  173. const { rerender } = render(
  174. <PreviewPane {...propsWithErrorsForFirstCompile} />
  175. )
  176. const dismissPopUpButton = screen.getByRole('button', {
  177. name: 'Dismiss first error alert',
  178. })
  179. fireEvent.click(dismissPopUpButton)
  180. rerender(<PreviewPane {...propsWithErrorsForSecondCompile} />)
  181. expect(
  182. screen.queryByRole('alertdialog', {
  183. name: 'This project has errors. This is the first one.',
  184. })
  185. ).to.not.exist
  186. })
  187. })
  188. describe('accessible description of the compile result', function () {
  189. it('renders an accessible description with the errors and warnings count', function () {
  190. const errors = [sampleError1, sampleError2]
  191. const warnings = [sampleWarning]
  192. const propsWithErrorsAndWarnings = getProps(false, {
  193. errors,
  194. warnings,
  195. })
  196. render(<PreviewPane {...propsWithErrorsAndWarnings} />)
  197. screen.getByText(`${errors.length} error${errors.length > 1 ? 's' : ''}`)
  198. screen.getByText(
  199. `${warnings.length} warning${warnings.length > 1 ? 's' : ''}`
  200. )
  201. })
  202. it('renders an accessible description for failed compiles with CLSI errors', function () {
  203. const sampleCLSIError = {
  204. clsiMaintenance: true,
  205. }
  206. const propsWithCLSIError = getProps(
  207. false,
  208. {},
  209. Date.now(),
  210. false,
  211. true,
  212. {},
  213. sampleCLSIError
  214. )
  215. render(<PreviewPane {...propsWithCLSIError} />)
  216. screen.getByText('This project did not compile because of an error')
  217. })
  218. it('renders an accessible description for failed compiles with validation issues', function () {
  219. const sampleValidationIssue = {
  220. clsiMaintenance: true,
  221. }
  222. const propsWithValidationIssue = getProps(
  223. false,
  224. {},
  225. Date.now(),
  226. false,
  227. true,
  228. sampleValidationIssue,
  229. {}
  230. )
  231. render(<PreviewPane {...propsWithValidationIssue} />)
  232. screen.getByText(
  233. 'This project did not compile because of a validation issue'
  234. )
  235. })
  236. })
  237. function getProps(
  238. isCompiling = false,
  239. logEntries = {},
  240. lastCompileTimestamp = Date.now(),
  241. isShowingLogs = false,
  242. compileFailed = false,
  243. validationIssues = {},
  244. errors = {}
  245. ) {
  246. const logEntriesWithDefaults = {
  247. errors: [],
  248. warnings: [],
  249. typesetting: [],
  250. ...logEntries,
  251. }
  252. logEntriesWithDefaults.all = [
  253. ...logEntriesWithDefaults.errors,
  254. ...logEntriesWithDefaults.warnings,
  255. ...logEntriesWithDefaults.typesetting,
  256. ]
  257. return {
  258. compilerState: {
  259. autoCompileHasChanges: false,
  260. isAutoCompileOn: false,
  261. isCompiling: isCompiling,
  262. isClearingCache: false,
  263. isDraftModeOn: false,
  264. isSyntaxCheckOn: false,
  265. lastCompileTimestamp,
  266. logEntries: logEntriesWithDefaults,
  267. compileFailed,
  268. validationIssues,
  269. errors,
  270. },
  271. onClearCache: () => {},
  272. onLogEntryLocationClick: () => {},
  273. onRecompile: () => {},
  274. onRecompileFromScratch: () => {},
  275. onRunSyntaxCheckNow: () => {},
  276. onSetAutoCompile: () => {},
  277. onSetDraftMode: () => {},
  278. onSetSyntaxCheck: () => {},
  279. onToggleLogs: () => {},
  280. onSetSplitLayout: () => {},
  281. onSetFullLayout: () => {},
  282. onStopCompilation: () => {},
  283. showLogs: isShowingLogs,
  284. splitLayout: true,
  285. }
  286. }
  287. })