settings-modal.test.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { screen, render, waitFor } from '@testing-library/react'
  2. import { expect } from 'chai'
  3. import fetchMock from 'fetch-mock'
  4. import { EditorProviders } from '../../helpers/editor-providers'
  5. import SettingsModal from '@/features/settings/components/settings-modal'
  6. import { Folder } from '@ol-types/folder'
  7. import { ImageName, OverallThemeMeta } from '@ol-types/project-settings'
  8. const selectTab = async (tabName: string) => {
  9. const tab = screen.getByRole('tab', { name: tabName })
  10. expect(tab).to.exist
  11. tab.click()
  12. }
  13. const assertSettingIsVisible = (settingName: string) => {
  14. expect(screen.getByLabelText(settingName)).to.exist
  15. }
  16. const TAB_SETTINGS = {
  17. Editor: [
  18. 'Auto-complete',
  19. 'Auto-close brackets',
  20. 'Code check',
  21. 'Keybindings',
  22. 'PDF Viewer',
  23. 'Reference search',
  24. 'Spellcheck language',
  25. 'Dictionary',
  26. 'Breadcrumbs',
  27. 'Equation preview',
  28. ],
  29. Compiler: [
  30. 'Main document',
  31. 'Compiler',
  32. 'TeX Live version',
  33. 'Compile mode',
  34. 'Stop on first error',
  35. 'Autocompile',
  36. ],
  37. Appearance: [
  38. 'Overall theme',
  39. 'Editor theme',
  40. 'Editor font size',
  41. 'Editor font family',
  42. 'Editor line height',
  43. ],
  44. }
  45. describe('<SettingsModal />', function () {
  46. const overallThemes: OverallThemeMeta[] = [
  47. {
  48. name: 'Overall Theme 1',
  49. val: '',
  50. },
  51. {
  52. name: 'Overall Theme 2',
  53. val: 'light-',
  54. },
  55. ]
  56. const editorThemes = [
  57. { name: 'editortheme-1', dark: false },
  58. { name: 'editortheme-2', dark: false },
  59. { name: 'editortheme-3', dark: false },
  60. ]
  61. const legacyEditorThemes = [
  62. { name: 'legacytheme-1', dark: false },
  63. { name: 'legacytheme-2', dark: false },
  64. { name: 'legacytheme-3', dark: false },
  65. ]
  66. const imageNames: ImageName[] = [
  67. {
  68. imageDesc: 'Image 1',
  69. imageName: 'img-1',
  70. allowed: true,
  71. },
  72. {
  73. imageDesc: 'Image 2',
  74. imageName: 'img-2',
  75. allowed: true,
  76. },
  77. ]
  78. const rootFolder: Folder = {
  79. _id: 'root-folder-id',
  80. name: 'rootFolder',
  81. docs: [
  82. {
  83. _id: '123abc',
  84. name: 'main.tex',
  85. },
  86. ],
  87. fileRefs: [],
  88. folders: [],
  89. }
  90. let originalSettings: typeof window.metaAttributesCache
  91. beforeEach(function () {
  92. originalSettings = window.metaAttributesCache.get('ol-ExposedSettings')
  93. window.metaAttributesCache.set('ol-ExposedSettings', {
  94. validRootDocExtensions: ['tex'],
  95. ieeeBrandId: 1234,
  96. })
  97. window.metaAttributesCache.set('ol-imageNames', imageNames)
  98. window.metaAttributesCache.set('ol-overallThemes', overallThemes)
  99. window.metaAttributesCache.set('ol-editorThemes', editorThemes)
  100. window.metaAttributesCache.set('ol-legacyEditorThemes', legacyEditorThemes)
  101. })
  102. afterEach(function () {
  103. fetchMock.removeRoutes().clearHistory()
  104. window.metaAttributesCache.set('ol-ExposedSettings', originalSettings)
  105. })
  106. it('Shows all settings options', async function () {
  107. render(
  108. <EditorProviders
  109. rootFolder={[rootFolder as any]}
  110. layoutContext={{ leftMenuShown: true }}
  111. >
  112. <SettingsModal />
  113. </EditorProviders>
  114. )
  115. Object.entries(TAB_SETTINGS).forEach(([tabName, settings]) => {
  116. selectTab(tabName)
  117. settings.forEach(setting => assertSettingIsVisible(setting))
  118. })
  119. })
  120. describe('when a user has Writefull enabled', function () {
  121. beforeEach(function () {
  122. window.metaAttributesCache.set('ol-writefullEnabled', true)
  123. window.metaAttributesCache.set('ol-showAiFeatures', true)
  124. })
  125. afterEach(function () {
  126. window.metaAttributesCache.delete('ol-writefullEnabled')
  127. window.metaAttributesCache.delete('ol-showAiFeatures')
  128. })
  129. it('shows the AI assistance section in the Editor tab', async function () {
  130. render(
  131. <EditorProviders
  132. rootFolder={[rootFolder as any]}
  133. layoutContext={{ leftMenuShown: true }}
  134. >
  135. <SettingsModal />
  136. </EditorProviders>
  137. )
  138. selectTab('Editor')
  139. await waitFor(() => expect(screen.getByText('AI assistance')).to.exist)
  140. })
  141. })
  142. describe('when a user does not have Writefull enabled', function () {
  143. afterEach(function () {
  144. window.metaAttributesCache.delete('ol-writefullEnabled')
  145. window.metaAttributesCache.delete('ol-showAiFeatures')
  146. window.metaAttributesCache.delete('ol-cannot-use-ai')
  147. })
  148. it('does not show the AI assistance section when ol-writefullEnabled is false', async function () {
  149. window.metaAttributesCache.set('ol-writefullEnabled', false)
  150. window.metaAttributesCache.set('ol-showAiFeatures', true)
  151. render(
  152. <EditorProviders
  153. rootFolder={[rootFolder as any]}
  154. layoutContext={{ leftMenuShown: true }}
  155. >
  156. <SettingsModal />
  157. </EditorProviders>
  158. )
  159. selectTab('Editor')
  160. await waitFor(
  161. () => expect(screen.getByLabelText('Auto-complete')).to.exist
  162. )
  163. expect(screen.queryByText('AI assistance')).to.be.null
  164. })
  165. it('does not show the AI assistance section when ol-showAiFeatures is false', async function () {
  166. window.metaAttributesCache.set('ol-writefullEnabled', true)
  167. window.metaAttributesCache.set('ol-showAiFeatures', false)
  168. render(
  169. <EditorProviders
  170. rootFolder={[rootFolder as any]}
  171. layoutContext={{ leftMenuShown: true }}
  172. >
  173. <SettingsModal />
  174. </EditorProviders>
  175. )
  176. selectTab('Editor')
  177. await waitFor(
  178. () => expect(screen.getByLabelText('Auto-complete')).to.exist
  179. )
  180. expect(screen.queryByText('AI assistance')).to.be.null
  181. })
  182. it('does not show the AI assistance section when ol-cannot-use-ai is true', async function () {
  183. window.metaAttributesCache.set('ol-writefullEnabled', true)
  184. window.metaAttributesCache.set('ol-showAiFeatures', true)
  185. window.metaAttributesCache.set('ol-cannot-use-ai', true)
  186. render(
  187. <EditorProviders
  188. rootFolder={[rootFolder as any]}
  189. layoutContext={{ leftMenuShown: true }}
  190. >
  191. <SettingsModal />
  192. </EditorProviders>
  193. )
  194. selectTab('Editor')
  195. await waitFor(
  196. () => expect(screen.getByLabelText('Auto-complete')).to.exist
  197. )
  198. expect(screen.queryByText('AI assistance')).to.be.null
  199. })
  200. })
  201. describe('when open=project-notifications query param is present', function () {
  202. beforeEach(function () {
  203. window.metaAttributesCache.set('ol-splitTestVariants', {
  204. 'email-notifications': 'enabled',
  205. })
  206. fetchMock.get(/\/notifications\/preferences\/project\//, {
  207. trackedChangesOnOwnProject: false,
  208. trackedChangesOnInvitedProject: false,
  209. commentOnOwnProject: false,
  210. commentOnInvitedProject: false,
  211. repliesOnOwnProject: false,
  212. repliesOnInvitedProject: false,
  213. repliesOnAuthoredThread: false,
  214. repliesOnParticipatingThread: false,
  215. })
  216. window.history.pushState({}, '', '?open=project-notifications')
  217. })
  218. afterEach(function () {
  219. window.history.pushState({}, '', window.location.pathname)
  220. window.metaAttributesCache.delete('ol-splitTestVariants')
  221. })
  222. it('opens the modal and selects the Project notifications tab', async function () {
  223. render(
  224. <EditorProviders rootFolder={[rootFolder as any]}>
  225. <SettingsModal />
  226. </EditorProviders>
  227. )
  228. await waitFor(
  229. () =>
  230. expect(
  231. screen.getByRole('tab', {
  232. name: /Project notifications/,
  233. selected: true,
  234. })
  235. ).to.exist
  236. )
  237. })
  238. it('removes the open param from the URL', async function () {
  239. render(
  240. <EditorProviders rootFolder={[rootFolder as any]}>
  241. <SettingsModal />
  242. </EditorProviders>
  243. )
  244. await waitFor(() => {
  245. expect(window.location.search).to.not.include(
  246. 'open=project-notifications'
  247. )
  248. })
  249. })
  250. })
  251. })