settings-modal.test.tsx 9.0 KB

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