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

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