Pārlūkot izejas kodu

Merge pull request #29113 from overleaf/dp-settings-modal-tests

Add frontend tests for new editor settings modal

GitOrigin-RevId: a7142b5b45e9484126d159445f2dddd7d3c86584
David 9 mēneši atpakaļ
vecāks
revīzija
1720ef19f4
17 mainītis faili ar 855 papildinājumiem un 0 dzēšanām
  1. 126 0
      services/web/test/frontend/features/settings-modal/settings-modal.test.tsx
  2. 51 0
      services/web/test/frontend/features/settings-modal/settings/auto-close-brackets-setting.test.tsx
  3. 51 0
      services/web/test/frontend/features/settings-modal/settings/auto-complete-setting.test.tsx
  4. 51 0
      services/web/test/frontend/features/settings-modal/settings/code-check-setting.test.tsx
  5. 36 0
      services/web/test/frontend/features/settings-modal/settings/compiler-setting.test.tsx
  6. 35 0
      services/web/test/frontend/features/settings-modal/settings/dictionary-setting.test.tsx
  7. 45 0
      services/web/test/frontend/features/settings-modal/settings/editor-theme-setting.test.tsx
  8. 35 0
      services/web/test/frontend/features/settings-modal/settings/font-family-setting.test.tsx
  9. 31 0
      services/web/test/frontend/features/settings-modal/settings/font-size-setting.test.tsx
  10. 47 0
      services/web/test/frontend/features/settings-modal/settings/image-name-setting.test.tsx
  11. 33 0
      services/web/test/frontend/features/settings-modal/settings/keybinding-setting.test.tsx
  12. 33 0
      services/web/test/frontend/features/settings-modal/settings/line-height-setting.test.tsx
  13. 51 0
      services/web/test/frontend/features/settings-modal/settings/math-preview-setting.test.tsx
  14. 99 0
      services/web/test/frontend/features/settings-modal/settings/overall-theme-setting.test.tsx
  15. 30 0
      services/web/test/frontend/features/settings-modal/settings/pdf-viewer-setting.test.tsx
  16. 51 0
      services/web/test/frontend/features/settings-modal/settings/root-document-setting.test.tsx
  17. 50 0
      services/web/test/frontend/features/settings-modal/settings/spell-check-setting.test.tsx

+ 126 - 0
services/web/test/frontend/features/settings-modal/settings-modal.test.tsx

@@ -0,0 +1,126 @@
+import { screen, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { EditorProviders } from '../../helpers/editor-providers'
+import SettingsModal from '@/features/ide-redesign/components/settings/settings-modal'
+import { Folder } from '@ol-types/folder'
+import { ImageName, OverallThemeMeta } from '@ol-types/project-settings'
+
+const selectTab = async (tabName: string) => {
+  const tab = screen.getByRole('tab', { name: tabName })
+  expect(tab).to.exist
+  tab.click()
+}
+
+const assertSettingIsVisible = (settingName: string) => {
+  expect(screen.getByLabelText(settingName)).to.exist
+}
+
+const TAB_SETTINGS = {
+  Editor: [
+    'Auto-complete',
+    'Auto-close brackets',
+    'Code check',
+    'Keybindings',
+    'PDF Viewer',
+    'Reference search',
+    'Spellcheck language',
+    'Dictionary',
+    'Breadcrumbs',
+    'Equation preview',
+  ],
+  Compiler: [
+    'Main document',
+    'Compiler',
+    'TeX Live version',
+    'Compile mode',
+    'Stop on first error',
+    'Autocompile',
+  ],
+  Appearance: [
+    'Overall theme',
+    'Editor theme',
+    'Editor font size',
+    'Editor font family',
+    'Editor line height',
+  ],
+}
+
+describe('<SettingsModal />', function () {
+  const overallThemes: OverallThemeMeta[] = [
+    {
+      name: 'Overall Theme 1',
+      val: '',
+      path: 'https://overleaf.com/overalltheme-1.css',
+    },
+    {
+      name: 'Overall Theme 2',
+      val: 'light-',
+      path: 'https://overleaf.com/overalltheme-2.css',
+    },
+  ]
+
+  const editorThemes = ['editortheme-1', 'editortheme-2', 'editortheme-3']
+  const legacyEditorThemes = ['legacytheme-1', 'legacytheme-2', 'legacytheme-3']
+
+  const imageNames: ImageName[] = [
+    {
+      imageDesc: 'Image 1',
+      imageName: 'img-1',
+      allowed: true,
+    },
+    {
+      imageDesc: 'Image 2',
+      imageName: 'img-2',
+      allowed: true,
+    },
+  ]
+
+  const rootFolder: Folder = {
+    _id: 'root-folder-id',
+    name: 'rootFolder',
+    docs: [
+      {
+        _id: '123abc',
+        name: 'main.tex',
+      },
+    ],
+    fileRefs: [],
+    folders: [],
+  }
+
+  let originalSettings: typeof window.metaAttributesCache
+
+  beforeEach(function () {
+    originalSettings = window.metaAttributesCache.get('ol-ExposedSettings')
+    window.metaAttributesCache.set('ol-ExposedSettings', {
+      validRootDocExtensions: ['tex'],
+      ieeeBrandId: 1234,
+    })
+    window.metaAttributesCache.set('ol-imageNames', imageNames)
+    window.metaAttributesCache.set('ol-overallThemes', overallThemes)
+    window.metaAttributesCache.set('ol-editorThemes', editorThemes)
+    window.metaAttributesCache.set('ol-legacyEditorThemes', legacyEditorThemes)
+  })
+
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+    window.metaAttributesCache.set('ol-ExposedSettings', originalSettings)
+  })
+
+  it('Shows all settings options', async function () {
+    render(
+      <EditorProviders
+        rootFolder={[rootFolder as any]}
+        layoutContext={{ leftMenuShown: true }}
+      >
+        <SettingsModal />
+      </EditorProviders>
+    )
+
+    Object.entries(TAB_SETTINGS).forEach(([tabName, settings]) => {
+      selectTab(tabName)
+      settings.forEach(setting => assertSettingIsVisible(setting))
+    })
+  })
+})

+ 51 - 0
services/web/test/frontend/features/settings-modal/settings/auto-close-brackets-setting.test.tsx

@@ -0,0 +1,51 @@
+import { screen, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import AutoCloseBracketsSetting from '@/features/ide-redesign/components/settings/editor-settings/auto-close-brackets-setting'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+
+describe('<AutoCloseBracketsSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('can toggle', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <AutoCloseBracketsSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const saveSettingsMock = fetchMock.post(
+      `express:/user/settings`,
+      {
+        status: 200,
+      },
+      { delay: 0 }
+    )
+
+    const toggle = screen.getByLabelText('Auto-close brackets')
+    const startingCheckedValue = (toggle as HTMLInputElement).checked
+
+    // Toggle the checkbox
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(!startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { autoPairDelimiters: !startingCheckedValue },
+      })
+    ).to.be.true
+
+    // Toggle back to original value
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { autoPairDelimiters: startingCheckedValue },
+      })
+    ).to.be.true
+  })
+})

+ 51 - 0
services/web/test/frontend/features/settings-modal/settings/auto-complete-setting.test.tsx

@@ -0,0 +1,51 @@
+import { screen, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import AutoCompleteSetting from '@/features/ide-redesign/components/settings/editor-settings/auto-complete-setting'
+
+describe('<AutoCompleteSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('can toggle', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <AutoCompleteSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const saveSettingsMock = fetchMock.post(
+      `express:/user/settings`,
+      {
+        status: 200,
+      },
+      { delay: 0 }
+    )
+
+    const toggle = screen.getByLabelText('Auto-complete')
+    const startingCheckedValue = (toggle as HTMLInputElement).checked
+
+    // Toggle the checkbox
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(!startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { autoComplete: !startingCheckedValue },
+      })
+    ).to.be.true
+
+    // Toggle back to original value
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { autoComplete: startingCheckedValue },
+      })
+    ).to.be.true
+  })
+})

+ 51 - 0
services/web/test/frontend/features/settings-modal/settings/code-check-setting.test.tsx

@@ -0,0 +1,51 @@
+import { screen, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import CodeCheckSetting from '@/features/ide-redesign/components/settings/editor-settings/code-check-setting'
+
+describe('<CodeCheckSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('can toggle', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <CodeCheckSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const saveSettingsMock = fetchMock.post(
+      `express:/user/settings`,
+      {
+        status: 200,
+      },
+      { delay: 0 }
+    )
+
+    const toggle = screen.getByLabelText('Code check')
+    const startingCheckedValue = (toggle as HTMLInputElement).checked
+
+    // Toggle the checkbox
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(!startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { syntaxValidation: !startingCheckedValue },
+      })
+    ).to.be.true
+
+    // Toggle back to original value
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { syntaxValidation: startingCheckedValue },
+      })
+    ).to.be.true
+  })
+})

+ 36 - 0
services/web/test/frontend/features/settings-modal/settings/compiler-setting.test.tsx

@@ -0,0 +1,36 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import CompilerSetting from '@/features/ide-redesign/components/settings/compiler-settings/compiler-setting'
+
+describe('<CompilerSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <CompilerSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Compiler')
+
+    const optionPdfLaTeX = within(select).getByText('pdfLaTeX')
+    expect(optionPdfLaTeX.getAttribute('value')).to.equal('pdflatex')
+
+    const optionLaTeX = within(select).getByText('LaTeX')
+    expect(optionLaTeX.getAttribute('value')).to.equal('latex')
+
+    const optionXeLaTeX = within(select).getByText('XeLaTeX')
+    expect(optionXeLaTeX.getAttribute('value')).to.equal('xelatex')
+
+    const optionLuaLaTeX = within(select).getByText('LuaLaTeX')
+    expect(optionLuaLaTeX.getAttribute('value')).to.equal('lualatex')
+  })
+})

+ 35 - 0
services/web/test/frontend/features/settings-modal/settings/dictionary-setting.test.tsx

@@ -0,0 +1,35 @@
+import { fireEvent, screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import DictionarySetting from '@/features/ide-redesign/components/settings/editor-settings/dictionary-setting'
+import RailModals from '@/features/ide-redesign/components/rail/rail-modals'
+
+describe('<DictionarySetting />', function () {
+  it('open dictionary modal', function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <DictionarySetting />
+          <RailModals />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    screen.getByText('Dictionary')
+
+    const button = screen.getByText('Edit')
+    fireEvent.click(button)
+
+    const modal = screen.getByTestId('dictionary-modal')
+
+    within(modal).getByRole('heading', { name: 'Edit Dictionary' })
+    within(modal).getByText('Your custom dictionary is empty.')
+
+    const [, closeButton] = within(modal).getAllByRole('button', {
+      name: 'Close',
+    })
+    fireEvent.click(closeButton)
+    expect(screen.getByTestId('dictionary-modal')).to.not.be.null
+  })
+})

+ 45 - 0
services/web/test/frontend/features/settings-modal/settings/editor-theme-setting.test.tsx

@@ -0,0 +1,45 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import EditorThemeSetting from '@/features/ide-redesign/components/settings/appearance-settings/editor-theme-setting'
+
+describe('<EditorThemeSetting />', function () {
+  const editorThemes = ['editortheme-1', 'editortheme-2', 'editortheme-3']
+
+  const legacyEditorThemes = ['legacytheme-1', 'legacytheme-2', 'legacytheme-3']
+
+  beforeEach(function () {
+    window.metaAttributesCache.set('ol-editorThemes', editorThemes)
+    window.metaAttributesCache.set('ol-legacyEditorThemes', legacyEditorThemes)
+  })
+
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <EditorThemeSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Editor theme')
+
+    for (const theme of editorThemes) {
+      const option = within(select).getByText(theme.replace(/_/g, ' '))
+      expect(option.getAttribute('value')).to.equal(theme)
+    }
+
+    for (const theme of legacyEditorThemes) {
+      const option = within(select).getByText(
+        theme.replace(/_/g, ' ') + ' (Legacy)'
+      )
+      expect(option.getAttribute('value')).to.equal(theme)
+    }
+  })
+})

+ 35 - 0
services/web/test/frontend/features/settings-modal/settings/font-family-setting.test.tsx

@@ -0,0 +1,35 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import FontFamilySetting from '@/features/ide-redesign/components/settings/appearance-settings/font-family-setting'
+
+describe('<FontFamilySetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <FontFamilySetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Editor font family')
+
+    const optionMonaco = within(select).getByText('Monaco / Menlo / Consolas')
+    expect(optionMonaco.getAttribute('value')).to.equal('monaco')
+
+    const optionLucida = within(select).getByText('Lucida / Source Code Pro')
+    expect(optionLucida.getAttribute('value')).to.equal('lucida')
+
+    const optionOpenDyslexicMono = within(select).getByText('OpenDyslexic Mono')
+    expect(optionOpenDyslexicMono.getAttribute('value')).to.equal(
+      'opendyslexicmono'
+    )
+  })
+})

+ 31 - 0
services/web/test/frontend/features/settings-modal/settings/font-size-setting.test.tsx

@@ -0,0 +1,31 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import FontSizeSetting from '@/features/ide-redesign/components/settings/appearance-settings/font-size-setting'
+
+describe('<FontSizeSetting />', function () {
+  const sizes = ['10', '11', '12', '13', '14', '16', '18', '20', '22', '24']
+
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <FontSizeSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Editor font size')
+
+    for (const size of sizes) {
+      const option = within(select).getByText(`${size}px`)
+      expect(option.getAttribute('value')).to.equal(size)
+    }
+  })
+})

+ 47 - 0
services/web/test/frontend/features/settings-modal/settings/image-name-setting.test.tsx

@@ -0,0 +1,47 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import type { ImageName } from '../../../../../types/project-settings'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import ImageNameSetting from '@/features/ide-redesign/components/settings/compiler-settings/image-name-setting'
+
+describe('<ImageNameSetting />', function () {
+  const imageNames: ImageName[] = [
+    {
+      imageDesc: 'Image 1',
+      imageName: 'img-1',
+      allowed: true,
+    },
+    {
+      imageDesc: 'Image 2',
+      imageName: 'img-2',
+      allowed: true,
+    },
+  ]
+
+  beforeEach(function () {
+    window.metaAttributesCache.set('ol-imageNames', imageNames)
+  })
+
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <ImageNameSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('TeX Live version')
+
+    for (const { imageName, imageDesc } of imageNames) {
+      const option = within(select).getByText(imageDesc)
+      expect(option.getAttribute('value')).to.equal(imageName)
+    }
+  })
+})

+ 33 - 0
services/web/test/frontend/features/settings-modal/settings/keybinding-setting.test.tsx

@@ -0,0 +1,33 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import KeybindingSetting from '@/features/ide-redesign/components/settings/editor-settings/keybinding-setting'
+
+describe('<KeybindingSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <KeybindingSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Keybindings')
+
+    const optionNone = within(select).getByText('None')
+    expect(optionNone.getAttribute('value')).to.equal('default')
+
+    const optionVim = within(select).getByText('Vim')
+    expect(optionVim.getAttribute('value')).to.equal('vim')
+
+    const optionEmacs = within(select).getByText('Emacs')
+    expect(optionEmacs.getAttribute('value')).to.equal('emacs')
+  })
+})

+ 33 - 0
services/web/test/frontend/features/settings-modal/settings/line-height-setting.test.tsx

@@ -0,0 +1,33 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import LineHeightSetting from '@/features/ide-redesign/components/settings/appearance-settings/line-height-setting'
+
+describe('<LineHeightSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <LineHeightSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Editor line height')
+
+    const optionCompact = within(select).getByText('Compact')
+    expect(optionCompact.getAttribute('value')).to.equal('compact')
+
+    const optionNormal = within(select).getByText('Normal')
+    expect(optionNormal.getAttribute('value')).to.equal('normal')
+
+    const optionWide = within(select).getByText('Wide')
+    expect(optionWide.getAttribute('value')).to.equal('wide')
+  })
+})

+ 51 - 0
services/web/test/frontend/features/settings-modal/settings/math-preview-setting.test.tsx

@@ -0,0 +1,51 @@
+import { screen, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import MathPreviewSetting from '@/features/ide-redesign/components/settings/editor-settings/math-preview-setting'
+
+describe('<MathPreviewSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('can toggle', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <MathPreviewSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const saveSettingsMock = fetchMock.post(
+      `express:/user/settings`,
+      {
+        status: 200,
+      },
+      { delay: 0 }
+    )
+
+    const toggle = screen.getByLabelText('Equation preview')
+    const startingCheckedValue = (toggle as HTMLInputElement).checked
+
+    // Toggle the checkbox
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(!startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { mathPreview: !startingCheckedValue },
+      })
+    ).to.be.true
+
+    // Toggle back to original value
+    toggle.click()
+    expect((toggle as HTMLInputElement).checked).to.equal(startingCheckedValue)
+    expect(
+      saveSettingsMock.callHistory.called(`/user/settings`, {
+        body: { mathPreview: startingCheckedValue },
+      })
+    ).to.be.true
+  })
+})

+ 99 - 0
services/web/test/frontend/features/settings-modal/settings/overall-theme-setting.test.tsx

@@ -0,0 +1,99 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import SettingsOverallTheme from '../../../../../frontend/js/features/editor-left-menu/components/settings/settings-overall-theme'
+import type { OverallThemeMeta } from '../../../../../types/project-settings'
+import getMeta from '@/utils/meta'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import OverallThemeSetting from '@/features/ide-redesign/components/settings/appearance-settings/overall-theme-setting'
+
+const IEEE_BRAND_ID = 1234
+const OTHER_BRAND_ID = 2234
+
+describe('<OverallThemeSetting />', function () {
+  const overallThemes: OverallThemeMeta[] = [
+    {
+      name: 'Overall Theme 1',
+      val: '',
+      path: 'https://overleaf.com/overalltheme-1.css',
+    },
+    {
+      name: 'Overall Theme 2',
+      val: 'light-',
+      path: 'https://overleaf.com/overalltheme-2.css',
+    },
+  ]
+
+  beforeEach(function () {
+    window.metaAttributesCache.set('ol-overallThemes', overallThemes)
+    Object.assign(getMeta('ol-ExposedSettings'), {
+      ieeeBrandId: IEEE_BRAND_ID,
+    })
+  })
+
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <OverallThemeSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Overall theme')
+
+    for (const theme of overallThemes) {
+      const option = within(select).getByText(theme.name)
+      expect(option.getAttribute('value')).to.equal(theme.val)
+    }
+  })
+  describe('Branded Project', function () {
+    it('should hide overall theme picker for IEEE branded projects', function () {
+      window.metaAttributesCache.set('ol-brandVariation', {
+        brand_id: IEEE_BRAND_ID,
+      })
+      render(
+        <EditorProviders>
+          <SettingsModalProvider>
+            <SettingsOverallTheme />
+          </SettingsModalProvider>
+        </EditorProviders>
+      )
+      const select = screen.queryByText('Overall theme')
+      expect(select).to.not.exist
+    })
+
+    it('should show overall theme picker for branded projects that are not IEEE', function () {
+      window.metaAttributesCache.set('ol-brandVariation', {
+        brand_id: OTHER_BRAND_ID,
+      })
+      render(
+        <EditorProviders>
+          <SettingsModalProvider>
+            <SettingsOverallTheme />
+          </SettingsModalProvider>
+        </EditorProviders>
+      )
+      const select = screen.getByLabelText('Overall theme')
+      expect(select).to.exist
+    })
+
+    it('should show overall theme picker for non branded projects', function () {
+      window.metaAttributesCache.set('ol-brandVariation', undefined)
+      render(
+        <EditorProviders>
+          <SettingsModalProvider>
+            <SettingsOverallTheme />
+          </SettingsModalProvider>
+        </EditorProviders>
+      )
+      const select = screen.getByLabelText('Overall theme')
+      expect(select).to.exist
+    })
+  })
+})

+ 30 - 0
services/web/test/frontend/features/settings-modal/settings/pdf-viewer-setting.test.tsx

@@ -0,0 +1,30 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import PDFViewerSetting from '@/features/ide-redesign/components/settings/editor-settings/pdf-viewer-setting'
+
+describe('<PDFViewerSetting />', function () {
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <PDFViewerSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('PDF Viewer')
+
+    const optionOverleaf = within(select).getByText('Overleaf')
+    expect(optionOverleaf.getAttribute('value')).to.equal('pdfjs')
+
+    const optionBrowser = within(select).getByText('Browser')
+    expect(optionBrowser.getAttribute('value')).to.equal('native')
+  })
+})

+ 51 - 0
services/web/test/frontend/features/settings-modal/settings/root-document-setting.test.tsx

@@ -0,0 +1,51 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import { Folder } from '../../../../../types/folder'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import RootDocumentSetting from '@/features/ide-redesign/components/settings/compiler-settings/root-document-setting'
+
+describe('<RootDocumentSetting />', function () {
+  const rootFolder: Folder = {
+    _id: 'root-folder-id',
+    name: 'rootFolder',
+    docs: [
+      {
+        _id: '123abc',
+        name: 'main.tex',
+      },
+    ],
+    fileRefs: [],
+    folders: [],
+  }
+
+  let originalSettings: typeof window.metaAttributesCache
+
+  beforeEach(function () {
+    originalSettings = window.metaAttributesCache.get('ol-ExposedSettings')
+    window.metaAttributesCache.set('ol-ExposedSettings', {
+      validRootDocExtensions: ['tex'],
+    })
+  })
+
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+    window.metaAttributesCache.set('ol-ExposedSettings', originalSettings)
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders rootFolder={[rootFolder as any]}>
+        <SettingsModalProvider>
+          <RootDocumentSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Main document')
+
+    const optionOn = within(select).getByText('main.tex')
+    expect(optionOn.getAttribute('value')).to.equal('123abc')
+  })
+})

+ 50 - 0
services/web/test/frontend/features/settings-modal/settings/spell-check-setting.test.tsx

@@ -0,0 +1,50 @@
+import { screen, within, render } from '@testing-library/react'
+import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
+import type { SpellCheckLanguage } from '../../../../../types/project-settings'
+import { EditorProviders } from '../../../helpers/editor-providers'
+import { SettingsModalProvider } from '@/features/ide-redesign/contexts/settings-modal-context'
+import SpellCheckSetting from '@/features/ide-redesign/components/settings/editor-settings/spell-check-setting'
+
+describe('<SpellCheckSetting />', function () {
+  const languages: SpellCheckLanguage[] = [
+    {
+      name: 'Lang 1',
+      code: 'lang-1',
+      dic: 'lang_1',
+    },
+    {
+      name: 'Lang 2',
+      code: 'lang-2',
+      dic: 'lang_2',
+    },
+  ]
+
+  beforeEach(function () {
+    window.metaAttributesCache.set('ol-languages', languages)
+  })
+
+  afterEach(function () {
+    fetchMock.removeRoutes().clearHistory()
+  })
+
+  it('shows correct menu', async function () {
+    render(
+      <EditorProviders>
+        <SettingsModalProvider>
+          <SpellCheckSetting />
+        </SettingsModalProvider>
+      </EditorProviders>
+    )
+
+    const select = screen.getByLabelText('Spellcheck language')
+
+    const optionEmpty = within(select).getByText('Off')
+    expect(optionEmpty.getAttribute('value')).to.equal('')
+
+    for (const language of languages) {
+      const option = within(select).getByText(language.name)
+      expect(option.getAttribute('value')).to.equal(language.code)
+    }
+  })
+})