preview-logs-pane-entry.test.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import { screen, render, fireEvent } from '@testing-library/react'
  4. import PreviewLogsPaneEntry from '../../../../../frontend/js/features/preview/components/preview-logs-pane-entry.js'
  5. describe('<PreviewLogsPaneEntry />', function () {
  6. const level = 'error'
  7. it('renders a configurable aria-label', function () {
  8. const sampleAriaLabel = 'lorem ipsum dolor sit amet'
  9. render(
  10. <PreviewLogsPaneEntry entryAriaLabel={sampleAriaLabel} level={level} />
  11. )
  12. screen.getByLabelText(sampleAriaLabel)
  13. })
  14. describe('logs pane source location link', function () {
  15. const file = 'foo.tex'
  16. const line = 42
  17. const column = 21
  18. const onSourceLocationClick = sinon.stub()
  19. afterEach(function () {
  20. onSourceLocationClick.reset()
  21. })
  22. it('renders both file and line', function () {
  23. render(
  24. <PreviewLogsPaneEntry sourceLocation={{ file, line }} level={level} />
  25. )
  26. screen.getByRole('button', {
  27. name: `Navigate to log position in source code: ${file}, ${line}`,
  28. })
  29. })
  30. it('renders only file when line information is not available', function () {
  31. render(<PreviewLogsPaneEntry sourceLocation={{ file }} level={level} />)
  32. screen.getByRole('button', {
  33. name: `Navigate to log position in source code: ${file}`,
  34. })
  35. })
  36. it('does not render when file information is not available', function () {
  37. render(<PreviewLogsPaneEntry level={level} />)
  38. expect(
  39. screen.queryByRole('button', {
  40. name: `Navigate to log position in source code: `,
  41. })
  42. ).to.not.exist
  43. })
  44. it('calls the callback with file, line and column on click', function () {
  45. render(
  46. <PreviewLogsPaneEntry
  47. sourceLocation={{ file, line, column }}
  48. level={level}
  49. onSourceLocationClick={onSourceLocationClick}
  50. />
  51. )
  52. const linkToSourceButton = screen.getByRole('button', {
  53. name: `Navigate to log position in source code: ${file}, ${line}`,
  54. })
  55. fireEvent.click(linkToSourceButton)
  56. expect(onSourceLocationClick).to.be.calledOnce
  57. expect(onSourceLocationClick).to.be.calledWith({
  58. file,
  59. line,
  60. column,
  61. })
  62. })
  63. })
  64. describe('logs pane entry raw contents', function () {
  65. const rawContent = 'foo bar latex error stuff baz'
  66. // JSDom doesn't compute layout/sizing, so we need to simulate sizing for the elements
  67. // Here we are simulating that the content is bigger than the `collapsedSize`, so
  68. // the expand-collapse widget is used
  69. const originalScrollHeight = Object.getOwnPropertyDescriptor(
  70. HTMLElement.prototype,
  71. 'offsetHeight'
  72. )
  73. const originalScrollWidth = Object.getOwnPropertyDescriptor(
  74. HTMLElement.prototype,
  75. 'offsetWidth'
  76. )
  77. beforeEach(function () {
  78. Object.defineProperty(HTMLElement.prototype, 'scrollHeight', {
  79. configurable: true,
  80. value: 500,
  81. })
  82. Object.defineProperty(HTMLElement.prototype, 'scrollWidth', {
  83. configurable: true,
  84. value: 500,
  85. })
  86. })
  87. afterEach(function () {
  88. Object.defineProperty(
  89. HTMLElement.prototype,
  90. 'scrollHeight',
  91. originalScrollHeight
  92. )
  93. Object.defineProperty(
  94. HTMLElement.prototype,
  95. 'scrollWidth',
  96. originalScrollWidth
  97. )
  98. })
  99. it('renders collapsed contents by default', function () {
  100. render(<PreviewLogsPaneEntry rawContent={rawContent} level={level} />)
  101. screen.getByText(rawContent)
  102. screen.getByRole('button', {
  103. name: 'Expand',
  104. })
  105. })
  106. it('supports expanding contents', function () {
  107. render(<PreviewLogsPaneEntry rawContent={rawContent} level={level} />)
  108. screen.getByText(rawContent)
  109. const expandCollapseBtn = screen.getByRole('button', {
  110. name: 'Expand',
  111. })
  112. fireEvent.click(expandCollapseBtn)
  113. screen.getByRole('button', {
  114. name: 'Collapse',
  115. })
  116. })
  117. it('should not render at all when there are no log contents', function () {
  118. const { container } = render(<PreviewLogsPaneEntry level={level} />)
  119. expect(container.querySelector('.log-entry-content')).to.not.exist
  120. })
  121. })
  122. describe('formatted content', function () {
  123. const rawContent = 'foo bar latex error stuff baz'
  124. const formattedContentText = 'foo bar baz'
  125. const formattedContent = <>{formattedContentText}</>
  126. const infoURL = 'www.overleaf.com/learn/latex'
  127. it('renders the hint', function () {
  128. render(
  129. <PreviewLogsPaneEntry
  130. rawContent={rawContent}
  131. formattedContent={formattedContent}
  132. extraInfoURL={infoURL}
  133. level={level}
  134. />
  135. )
  136. screen.getByText(formattedContentText)
  137. })
  138. it('renders the link to learn more', function () {
  139. render(
  140. <PreviewLogsPaneEntry
  141. rawContent={rawContent}
  142. formattedContent={formattedContent}
  143. extraInfoURL={infoURL}
  144. level={level}
  145. />
  146. )
  147. screen.getByRole('link', { name: 'Learn more' })
  148. })
  149. it('does not render the link when it is not available', function () {
  150. render(
  151. <PreviewLogsPaneEntry
  152. rawContent={rawContent}
  153. formattedContent={formattedContent}
  154. level={level}
  155. />
  156. )
  157. expect(screen.queryByRole('link', { name: 'Learn more' })).to.not.exist
  158. })
  159. })
  160. })