From 1720ef19f4d3c29f3f990d6bd8e26d6653cce8bd Mon Sep 17 00:00:00 2001
From: David <33458145+davidmcpowell@users.noreply.github.com>
Date: Tue, 21 Oct 2025 11:45:54 +0100
Subject: [PATCH] Merge pull request #29113 from
overleaf/dp-settings-modal-tests
Add frontend tests for new editor settings modal
GitOrigin-RevId: a7142b5b45e9484126d159445f2dddd7d3c86584
---
.../settings-modal/settings-modal.test.tsx | 126 ++++++++++++++++++
.../auto-close-brackets-setting.test.tsx | 51 +++++++
.../settings/auto-complete-setting.test.tsx | 51 +++++++
.../settings/code-check-setting.test.tsx | 51 +++++++
.../settings/compiler-setting.test.tsx | 36 +++++
.../settings/dictionary-setting.test.tsx | 35 +++++
.../settings/editor-theme-setting.test.tsx | 45 +++++++
.../settings/font-family-setting.test.tsx | 35 +++++
.../settings/font-size-setting.test.tsx | 31 +++++
.../settings/image-name-setting.test.tsx | 47 +++++++
.../settings/keybinding-setting.test.tsx | 33 +++++
.../settings/line-height-setting.test.tsx | 33 +++++
.../settings/math-preview-setting.test.tsx | 51 +++++++
.../settings/overall-theme-setting.test.tsx | 99 ++++++++++++++
.../settings/pdf-viewer-setting.test.tsx | 30 +++++
.../settings/root-document-setting.test.tsx | 51 +++++++
.../settings/spell-check-setting.test.tsx | 50 +++++++
17 files changed, 855 insertions(+)
create mode 100644 services/web/test/frontend/features/settings-modal/settings-modal.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/auto-close-brackets-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/auto-complete-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/code-check-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/compiler-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/dictionary-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/editor-theme-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/font-family-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/font-size-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/image-name-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/keybinding-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/line-height-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/math-preview-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/overall-theme-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/pdf-viewer-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/root-document-setting.test.tsx
create mode 100644 services/web/test/frontend/features/settings-modal/settings/spell-check-setting.test.tsx
diff --git a/services/web/test/frontend/features/settings-modal/settings-modal.test.tsx b/services/web/test/frontend/features/settings-modal/settings-modal.test.tsx
new file mode 100644
index 0000000000..b28f19bbb6
--- /dev/null
+++ b/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('', 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(
+
+
+
+ )
+
+ Object.entries(TAB_SETTINGS).forEach(([tabName, settings]) => {
+ selectTab(tabName)
+ settings.forEach(setting => assertSettingIsVisible(setting))
+ })
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/auto-close-brackets-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/auto-close-brackets-setting.test.tsx
new file mode 100644
index 0000000000..71843fabf8
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('can toggle', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/auto-complete-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/auto-complete-setting.test.tsx
new file mode 100644
index 0000000000..3c0f33b8a1
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('can toggle', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/code-check-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/code-check-setting.test.tsx
new file mode 100644
index 0000000000..e135a9951a
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('can toggle', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/compiler-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/compiler-setting.test.tsx
new file mode 100644
index 0000000000..6411c09ebd
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('shows correct menu', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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')
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/dictionary-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/dictionary-setting.test.tsx
new file mode 100644
index 0000000000..27949d215a
--- /dev/null
+++ b/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('', function () {
+ it('open dictionary modal', function () {
+ render(
+
+
+
+
+
+
+ )
+
+ 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
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/editor-theme-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/editor-theme-setting.test.tsx
new file mode 100644
index 0000000000..3bd5e6ae1e
--- /dev/null
+++ b/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('', 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(
+
+
+
+
+
+ )
+
+ 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)
+ }
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/font-family-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/font-family-setting.test.tsx
new file mode 100644
index 0000000000..d08845cdc3
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('shows correct menu', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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'
+ )
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/font-size-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/font-size-setting.test.tsx
new file mode 100644
index 0000000000..ec49a9317b
--- /dev/null
+++ b/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('', 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(
+
+
+
+
+
+ )
+
+ 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)
+ }
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/image-name-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/image-name-setting.test.tsx
new file mode 100644
index 0000000000..fdda2d46c8
--- /dev/null
+++ b/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('', 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(
+
+
+
+
+
+ )
+
+ 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)
+ }
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/keybinding-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/keybinding-setting.test.tsx
new file mode 100644
index 0000000000..491647da0f
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('shows correct menu', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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')
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/line-height-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/line-height-setting.test.tsx
new file mode 100644
index 0000000000..72bc117d6f
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('shows correct menu', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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')
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/math-preview-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/math-preview-setting.test.tsx
new file mode 100644
index 0000000000..f96d768cf3
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('can toggle', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/overall-theme-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/overall-theme-setting.test.tsx
new file mode 100644
index 0000000000..9f88363653
--- /dev/null
+++ b/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('', 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(
+
+
+
+
+
+ )
+
+ 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(
+
+
+
+
+
+ )
+ 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(
+
+
+
+
+
+ )
+ 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(
+
+
+
+
+
+ )
+ const select = screen.getByLabelText('Overall theme')
+ expect(select).to.exist
+ })
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/pdf-viewer-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/pdf-viewer-setting.test.tsx
new file mode 100644
index 0000000000..51c828dedb
--- /dev/null
+++ b/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('', function () {
+ afterEach(function () {
+ fetchMock.removeRoutes().clearHistory()
+ })
+
+ it('shows correct menu', async function () {
+ render(
+
+
+
+
+
+ )
+
+ 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')
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/root-document-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/root-document-setting.test.tsx
new file mode 100644
index 0000000000..35312341b4
--- /dev/null
+++ b/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('', 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(
+
+
+
+
+
+ )
+
+ const select = screen.getByLabelText('Main document')
+
+ const optionOn = within(select).getByText('main.tex')
+ expect(optionOn.getAttribute('value')).to.equal('123abc')
+ })
+})
diff --git a/services/web/test/frontend/features/settings-modal/settings/spell-check-setting.test.tsx b/services/web/test/frontend/features/settings-modal/settings/spell-check-setting.test.tsx
new file mode 100644
index 0000000000..0d8facb094
--- /dev/null
+++ b/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('', 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(
+
+
+
+
+
+ )
+
+ 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)
+ }
+ })
+})