editor-left-menu.spec.tsx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. import '../../helpers/bootstrap-3'
  2. import EditorLeftMenu from '../../../../frontend/js/features/editor-left-menu/components/editor-left-menu'
  3. import {
  4. AllowedImageName,
  5. OverallThemeMeta,
  6. SpellCheckLanguage,
  7. } from '../../../../types/project-settings'
  8. import { EditorProviders } from '../../helpers/editor-providers'
  9. import { mockScope } from './scope'
  10. import { Folder } from '../../../../types/folder'
  11. import { docsInFolder } from '@/features/file-tree/util/docs-in-folder'
  12. import getMeta from '@/utils/meta'
  13. describe('<EditorLeftMenu />', function () {
  14. beforeEach(function () {
  15. cy.viewport(800, 800)
  16. cy.interceptCompile()
  17. })
  18. describe('for non-anonymous users', function () {
  19. const overallThemes: OverallThemeMeta[] = [
  20. {
  21. name: 'Overall Theme 1',
  22. val: '',
  23. path: 'https://overleaf.com/overalltheme-1.css',
  24. },
  25. {
  26. name: 'Overall Theme 2',
  27. val: 'light-',
  28. path: 'https://overleaf.com/overalltheme-2.css',
  29. },
  30. ]
  31. const allowedImageNames: AllowedImageName[] = [
  32. {
  33. imageDesc: 'Image 1',
  34. imageName: 'img-1',
  35. },
  36. {
  37. imageDesc: 'Image 2',
  38. imageName: 'img-2',
  39. },
  40. ]
  41. beforeEach(function () {
  42. window.metaAttributesCache.set('ol-overallThemes', overallThemes)
  43. window.metaAttributesCache.set('ol-allowedImageNames', allowedImageNames)
  44. window.metaAttributesCache.set('ol-anonymous', false)
  45. window.metaAttributesCache.set('ol-gitBridgeEnabled', true)
  46. window.metaAttributesCache.set('ol-showSupport', true)
  47. Object.assign(getMeta('ol-ExposedSettings'), { ieeeBrandId: 123 })
  48. window.metaAttributesCache.set('ol-user', {
  49. email: 'sherlock@holmes.co.uk',
  50. first_name: 'Sherlock',
  51. last_name: 'Holmes',
  52. })
  53. })
  54. it('render full menu', function () {
  55. const scope = mockScope({
  56. ui: {
  57. leftMenuShown: true,
  58. },
  59. })
  60. cy.mount(
  61. <EditorProviders scope={scope}>
  62. <EditorLeftMenu />
  63. </EditorProviders>
  64. )
  65. // Download Menu
  66. cy.findByRole('heading', { name: 'Download' })
  67. cy.findByRole('link', { name: 'Source' })
  68. cy.findByRole('link', { name: 'PDF' })
  69. // Actions Menu
  70. cy.findByRole('heading', { name: 'Actions' })
  71. cy.findByRole('button', { name: 'Copy Project' })
  72. cy.findByRole('button', { name: 'Word Count' })
  73. // Sync Menu
  74. cy.findByRole('heading', { name: 'Sync' })
  75. cy.findByRole('button', { name: 'Dropbox' })
  76. cy.findByRole('button', { name: 'Git' })
  77. cy.findByRole('button', { name: 'GitHub' })
  78. // Settings Menu
  79. cy.findByRole('heading', { name: 'Settings' })
  80. cy.findByLabelText('Compiler')
  81. cy.findByLabelText('TeX Live version')
  82. cy.findByLabelText('Main document')
  83. cy.findByLabelText('Spell check')
  84. cy.findByLabelText('Auto-complete')
  85. cy.findByLabelText('Auto-close Brackets')
  86. cy.findByLabelText('Code check')
  87. cy.findByLabelText('Editor theme')
  88. cy.findByLabelText('Overall theme')
  89. cy.findByLabelText('Keybindings')
  90. cy.findByLabelText('Font Size')
  91. cy.findByLabelText('Font Family')
  92. cy.findByLabelText('Line Height')
  93. cy.findByLabelText('PDF Viewer')
  94. // Help Menu
  95. cy.findByRole('heading', { name: 'Help' })
  96. cy.findByRole('button', { name: 'Show Hotkeys' })
  97. cy.findByRole('link', { name: 'Documentation' })
  98. cy.findByRole('button', { name: 'Contact Us' })
  99. })
  100. describe('download menu', function () {
  101. it('have a correct source & pdf download url', function () {
  102. const scope = mockScope({
  103. ui: {
  104. leftMenuShown: true,
  105. },
  106. })
  107. cy.mount(
  108. <EditorProviders scope={scope}>
  109. <EditorLeftMenu />
  110. </EditorProviders>
  111. )
  112. cy.findByRole('link', { name: 'Source' }).should(
  113. 'have.attr',
  114. 'href',
  115. '/project/project123/download/zip'
  116. )
  117. cy.findByRole('link', { name: 'PDF' })
  118. .should('have.attr', 'href')
  119. .and('match', /\/download\/project\/project123\/build/)
  120. })
  121. })
  122. describe('actions menu', function () {
  123. it('shows copy project modal correctly', function () {
  124. cy.intercept('POST', '/project/*/clone', {
  125. body: {
  126. project_id: 'new_project_id',
  127. },
  128. })
  129. const scope = mockScope({
  130. ui: {
  131. leftMenuShown: true,
  132. },
  133. })
  134. cy.mount(
  135. <EditorProviders scope={scope}>
  136. <EditorLeftMenu />
  137. </EditorProviders>
  138. )
  139. cy.findByRole('button', { name: 'Copy Project' }).click()
  140. cy.findByRole('heading', { name: 'Copy Project' })
  141. // try closing & re-opening the modal with different methods
  142. cy.findByRole('button', { name: 'Close' }).click()
  143. cy.findByRole('button', { name: 'Copy Project' }).click()
  144. cy.findByRole('button', { name: 'Cancel' }).click()
  145. cy.findByRole('button', { name: 'Copy Project' }).click()
  146. cy.findByLabelText('New Name').focus()
  147. cy.findByLabelText('New Name').clear()
  148. cy.findByLabelText('New Name').type('Project Renamed')
  149. cy.get('#clone-project-form-name[value="Project Renamed"')
  150. })
  151. it('shows word count modal correctly', function () {
  152. cy.intercept('GET', '/project/*/wordcount*', {
  153. texcount: {
  154. encode: 'ascii',
  155. textWords: 781,
  156. headWords: 66,
  157. outside: 11,
  158. headers: 41,
  159. elements: 2,
  160. mathInline: 6,
  161. mathDisplay: 1,
  162. errors: 0,
  163. },
  164. }).as('wordCount')
  165. const scope = mockScope({
  166. ui: {
  167. leftMenuShown: true,
  168. },
  169. })
  170. cy.mount(
  171. <EditorProviders scope={scope}>
  172. <EditorLeftMenu />
  173. </EditorProviders>
  174. )
  175. cy.findByRole('button', { name: 'Word Count' }).click()
  176. cy.wait('@wordCount')
  177. cy.findByText('Total Words:')
  178. cy.findByText('781')
  179. cy.findByText('Headers:')
  180. cy.findByText('41')
  181. cy.findByText('Math Inline:')
  182. cy.findByText('6')
  183. cy.findByText('Math Display:')
  184. cy.findByText('1')
  185. })
  186. })
  187. describe('sync menu', function () {
  188. it('shows dropbox modal correctly', function () {
  189. cy.intercept('GET', '/dropbox/status', {
  190. registered: true,
  191. })
  192. const scope = mockScope({
  193. ui: {
  194. leftMenuShown: true,
  195. },
  196. project: {
  197. members: [],
  198. owner: {
  199. _id: '123',
  200. },
  201. },
  202. user: {
  203. features: {
  204. dropbox: false,
  205. },
  206. },
  207. })
  208. cy.mount(
  209. <EditorProviders scope={scope}>
  210. <EditorLeftMenu />
  211. </EditorProviders>
  212. )
  213. cy.findByRole('button', { name: 'Dropbox' }).click()
  214. cy.findByText('Dropbox Sync')
  215. })
  216. it('shows git modal correctly', function () {
  217. const scope = mockScope({
  218. ui: {
  219. leftMenuShown: true,
  220. },
  221. project: {
  222. owner: {
  223. _id: '123',
  224. },
  225. features: {
  226. gitBridge: true,
  227. },
  228. },
  229. })
  230. cy.mount(
  231. <EditorProviders scope={scope}>
  232. <EditorLeftMenu />
  233. </EditorProviders>
  234. )
  235. cy.findByRole('button', { name: 'Git' }).click()
  236. cy.findByText('Clone with Git')
  237. cy.findByText(/clone your project by using the link below/)
  238. })
  239. it('shows git modal paywall correctly', function () {
  240. const scope = mockScope({
  241. ui: {
  242. leftMenuShown: true,
  243. },
  244. project: {
  245. owner: {
  246. _id: '123',
  247. },
  248. features: {
  249. gitBridge: false,
  250. },
  251. },
  252. })
  253. cy.mount(
  254. <EditorProviders scope={scope}>
  255. <EditorLeftMenu />
  256. </EditorProviders>
  257. )
  258. cy.findByRole('button', { name: 'Git' }).click()
  259. cy.findByText('Collaborate online and offline, using your own workflow')
  260. })
  261. it('shows github modal correctly', function () {
  262. cy.intercept('GET', '/user/github-sync/status', {
  263. available: false,
  264. enabled: false,
  265. }).as('user-status')
  266. cy.intercept('GET', '/project/*/github-sync/status', {
  267. enabled: false,
  268. }).as('project-status')
  269. const scope = mockScope({
  270. ui: {
  271. leftMenuShown: true,
  272. },
  273. })
  274. cy.mount(
  275. <EditorProviders scope={scope}>
  276. <EditorLeftMenu />
  277. </EditorProviders>
  278. )
  279. cy.wait('@compile')
  280. cy.findByRole('button', { name: 'GitHub' }).click()
  281. cy.findByText('GitHub Sync')
  282. cy.wait(['@user-status', '@project-status'])
  283. cy.findByText('Push to GitHub, pull to Overleaf')
  284. })
  285. it('hides the entire sync section when git bridge is disabled', function () {
  286. window.metaAttributesCache.set('ol-gitBridgeEnabled', false)
  287. cy.findByRole('button', { name: 'Dropbox' }).should('not.exist')
  288. cy.findByRole('button', { name: 'Git' }).should('not.exist')
  289. cy.findByRole('button', { name: 'GitHub' }).should('not.exist')
  290. })
  291. })
  292. describe('settings menu', function () {
  293. it('shows compiler menu correctly', function () {
  294. const scope = mockScope({
  295. ui: {
  296. leftMenuShown: true,
  297. },
  298. })
  299. cy.mount(
  300. <EditorProviders scope={scope}>
  301. <EditorLeftMenu />
  302. </EditorProviders>
  303. )
  304. cy.get<HTMLOptionElement>('#settings-menu-compiler option').then(
  305. options => {
  306. const values = [...options].map(o => o.value)
  307. expect(values).to.deep.eq([
  308. 'pdflatex',
  309. 'latex',
  310. 'xelatex',
  311. 'lualatex',
  312. ])
  313. const texts = [...options].map(o => o.text)
  314. expect(texts).to.deep.eq([
  315. 'pdfLaTeX',
  316. 'LaTeX',
  317. 'XeLaTeX',
  318. 'LuaLaTeX',
  319. ])
  320. }
  321. )
  322. })
  323. it('shows texlive version menu correctly', function () {
  324. const scope = mockScope({
  325. ui: {
  326. leftMenuShown: true,
  327. },
  328. })
  329. cy.mount(
  330. <EditorProviders scope={scope}>
  331. <EditorLeftMenu />
  332. </EditorProviders>
  333. )
  334. cy.get<HTMLOptionElement>('#settings-menu-imageName option').then(
  335. options => {
  336. const values = [...options].map(o => o.value)
  337. expect(values).to.deep.eq(['img-1', 'img-2'])
  338. const texts = [...options].map(o => o.text)
  339. expect(texts).to.deep.eq(['Image 1', 'Image 2'])
  340. }
  341. )
  342. })
  343. it('shows document menu correctly', function () {
  344. const rootFolder: Folder = {
  345. _id: 'root-folder-id',
  346. name: 'rootFolder',
  347. docs: [
  348. {
  349. _id: 'id1',
  350. name: 'main.tex',
  351. },
  352. {
  353. _id: 'id2',
  354. name: 'main2.tex',
  355. },
  356. ],
  357. fileRefs: [],
  358. folders: [],
  359. }
  360. const scope = mockScope({
  361. ui: {
  362. leftMenuShown: true,
  363. },
  364. })
  365. cy.mount(
  366. <EditorProviders scope={scope} rootFolder={[rootFolder as any]}>
  367. <EditorLeftMenu />
  368. </EditorProviders>
  369. )
  370. const docs = docsInFolder(rootFolder)
  371. cy.get<HTMLOptionElement>('#settings-menu-rootDocId option').then(
  372. options => {
  373. const values = [...options].map(o => o.value)
  374. expect(values).to.deep.eq(docs.map(doc => doc.doc.id))
  375. const texts = [...options].map(o => o.text)
  376. expect(texts).to.deep.eq(docs.map(doc => doc.path))
  377. }
  378. )
  379. })
  380. it('shows spellcheck menu correctly', function () {
  381. const languages: SpellCheckLanguage[] = [
  382. {
  383. name: 'Lang 1',
  384. code: 'lang-1',
  385. dic: 'lang_1',
  386. },
  387. {
  388. name: 'Lang 2',
  389. code: 'lang-2',
  390. dic: 'lang_2',
  391. },
  392. ]
  393. window.metaAttributesCache.set('ol-languages', languages)
  394. const scope = mockScope({
  395. ui: {
  396. leftMenuShown: true,
  397. },
  398. })
  399. cy.mount(
  400. <EditorProviders scope={scope}>
  401. <EditorLeftMenu />
  402. </EditorProviders>
  403. )
  404. cy.get<HTMLOptionElement>(
  405. '#settings-menu-spellCheckLanguage option'
  406. ).then(options => {
  407. const values = [...options].map(o => o.value)
  408. expect(values).to.deep.eq(['', 'lang-1', 'lang-2'])
  409. const texts = [...options].map(o => o.text)
  410. expect(texts).to.deep.eq(['Off', 'Lang 1', 'Lang 2'])
  411. })
  412. })
  413. it('shows dictionary modal correctly', function () {
  414. const scope = mockScope({
  415. ui: {
  416. leftMenuShown: true,
  417. },
  418. })
  419. cy.mount(
  420. <EditorProviders scope={scope}>
  421. <EditorLeftMenu />
  422. </EditorProviders>
  423. )
  424. cy.get('label[for="dictionary-settings"] ~ button').click()
  425. cy.findByText('Edit Dictionary')
  426. cy.findByText('Your custom dictionary is empty.')
  427. })
  428. it('shows auto-complete menu correctly', function () {
  429. const scope = mockScope({
  430. ui: {
  431. leftMenuShown: true,
  432. },
  433. })
  434. cy.mount(
  435. <EditorProviders scope={scope}>
  436. <EditorLeftMenu />
  437. </EditorProviders>
  438. )
  439. cy.get<HTMLOptionElement>('#settings-menu-autoComplete option').then(
  440. options => {
  441. const values = [...options].map(o => o.value)
  442. expect(values).to.deep.eq(['true', 'false'])
  443. const texts = [...options].map(o => o.text)
  444. expect(texts).to.deep.eq(['On', 'Off'])
  445. }
  446. )
  447. })
  448. it('shows auto-close brackets menu correctly', function () {
  449. const scope = mockScope({
  450. ui: {
  451. leftMenuShown: true,
  452. },
  453. })
  454. cy.mount(
  455. <EditorProviders scope={scope}>
  456. <EditorLeftMenu />
  457. </EditorProviders>
  458. )
  459. cy.get<HTMLOptionElement>(
  460. '#settings-menu-autoPairDelimiters option'
  461. ).then(options => {
  462. const values = [...options].map(o => o.value)
  463. expect(values).to.deep.eq(['true', 'false'])
  464. const texts = [...options].map(o => o.text)
  465. expect(texts).to.deep.eq(['On', 'Off'])
  466. })
  467. })
  468. it('shows code check menu correctly', function () {
  469. const scope = mockScope({
  470. ui: {
  471. leftMenuShown: true,
  472. },
  473. })
  474. cy.mount(
  475. <EditorProviders scope={scope}>
  476. <EditorLeftMenu />
  477. </EditorProviders>
  478. )
  479. cy.get<HTMLOptionElement>(
  480. '#settings-menu-syntaxValidation option'
  481. ).then(options => {
  482. const values = [...options].map(o => o.value)
  483. expect(values).to.deep.eq(['true', 'false'])
  484. const texts = [...options].map(o => o.text)
  485. expect(texts).to.deep.eq(['On', 'Off'])
  486. })
  487. })
  488. it('shows editor theme menu correctly', function () {
  489. const editorThemes = ['editortheme-1', 'editortheme-2', 'editortheme-3']
  490. const legacyEditorThemes = [
  491. 'legacytheme-1',
  492. 'legacytheme-2',
  493. 'legacytheme-3',
  494. ]
  495. window.metaAttributesCache.set('ol-editorThemes', editorThemes)
  496. window.metaAttributesCache.set(
  497. 'ol-legacyEditorThemes',
  498. legacyEditorThemes
  499. )
  500. const scope = mockScope({
  501. ui: {
  502. leftMenuShown: true,
  503. },
  504. })
  505. cy.mount(
  506. <EditorProviders scope={scope}>
  507. <EditorLeftMenu />
  508. </EditorProviders>
  509. )
  510. cy.get<HTMLOptionElement>('#settings-menu-editorTheme option').then(
  511. options => {
  512. const values = [...options].map(o => o.value)
  513. expect(values).to.deep.eq([
  514. 'editortheme-1',
  515. 'editortheme-2',
  516. 'editortheme-3',
  517. '-',
  518. 'legacytheme-1',
  519. 'legacytheme-2',
  520. 'legacytheme-3',
  521. ])
  522. const texts = [...options].map(o => o.text)
  523. expect(texts).to.deep.eq([
  524. 'editortheme-1',
  525. 'editortheme-2',
  526. 'editortheme-3',
  527. '—————————————————',
  528. 'legacytheme-1 (Legacy)',
  529. 'legacytheme-2 (Legacy)',
  530. 'legacytheme-3 (Legacy)',
  531. ])
  532. }
  533. )
  534. })
  535. it('shows overall theme menu correctly', function () {
  536. const scope = mockScope({
  537. ui: {
  538. leftMenuShown: true,
  539. },
  540. })
  541. cy.mount(
  542. <EditorProviders scope={scope}>
  543. <EditorLeftMenu />
  544. </EditorProviders>
  545. )
  546. cy.get<HTMLOptionElement>('#settings-menu-overallTheme option').then(
  547. options => {
  548. const values = [...options].map(o => o.value)
  549. expect(values).to.deep.eq(['', 'light-'])
  550. const texts = [...options].map(o => o.text)
  551. expect(texts).to.deep.eq(['Overall Theme 1', 'Overall Theme 2'])
  552. }
  553. )
  554. })
  555. it('shows keybindings menu correctly', function () {
  556. const scope = mockScope({
  557. ui: {
  558. leftMenuShown: true,
  559. },
  560. })
  561. cy.mount(
  562. <EditorProviders scope={scope}>
  563. <EditorLeftMenu />
  564. </EditorProviders>
  565. )
  566. cy.get<HTMLOptionElement>('#settings-menu-mode option').then(
  567. options => {
  568. const values = [...options].map(o => o.value)
  569. expect(values).to.deep.eq(['default', 'vim', 'emacs'])
  570. const texts = [...options].map(o => o.text)
  571. expect(texts).to.deep.eq(['None', 'Vim', 'Emacs'])
  572. }
  573. )
  574. })
  575. it('shows font size menu correctly', function () {
  576. const scope = mockScope({
  577. ui: {
  578. leftMenuShown: true,
  579. },
  580. })
  581. cy.mount(
  582. <EditorProviders scope={scope}>
  583. <EditorLeftMenu />
  584. </EditorProviders>
  585. )
  586. cy.get<HTMLOptionElement>('#settings-menu-fontSize option').then(
  587. options => {
  588. const values = [...options].map(o => o.value)
  589. expect(values).to.deep.eq([
  590. '10',
  591. '11',
  592. '12',
  593. '13',
  594. '14',
  595. '16',
  596. '18',
  597. '20',
  598. '22',
  599. '24',
  600. ])
  601. const texts = [...options].map(o => o.text)
  602. expect(texts).to.deep.eq([
  603. '10px',
  604. '11px',
  605. '12px',
  606. '13px',
  607. '14px',
  608. '16px',
  609. '18px',
  610. '20px',
  611. '22px',
  612. '24px',
  613. ])
  614. }
  615. )
  616. })
  617. it('shows font family menu correctly', function () {
  618. const scope = mockScope({
  619. ui: {
  620. leftMenuShown: true,
  621. },
  622. })
  623. cy.mount(
  624. <EditorProviders scope={scope}>
  625. <EditorLeftMenu />
  626. </EditorProviders>
  627. )
  628. cy.get<HTMLOptionElement>('#settings-menu-fontFamily option').then(
  629. options => {
  630. const values = [...options].map(o => o.value)
  631. expect(values).to.deep.eq(['monaco', 'lucida', 'opendyslexicmono'])
  632. const texts = [...options].map(o => o.text)
  633. expect(texts).to.deep.eq([
  634. 'Monaco / Menlo / Consolas',
  635. 'Lucida / Source Code Pro',
  636. 'OpenDyslexic Mono',
  637. ])
  638. }
  639. )
  640. })
  641. it('shows line height menu correctly', function () {
  642. const scope = mockScope({
  643. ui: {
  644. leftMenuShown: true,
  645. },
  646. })
  647. cy.mount(
  648. <EditorProviders scope={scope}>
  649. <EditorLeftMenu />
  650. </EditorProviders>
  651. )
  652. cy.get<HTMLOptionElement>('#settings-menu-lineHeight option').then(
  653. options => {
  654. const values = [...options].map(o => o.value)
  655. expect(values).to.deep.eq(['compact', 'normal', 'wide'])
  656. const texts = [...options].map(o => o.text)
  657. expect(texts).to.deep.eq(['Compact', 'Normal', 'Wide'])
  658. }
  659. )
  660. })
  661. it('shows pdf viewer menu correctly', function () {
  662. const scope = mockScope({
  663. ui: {
  664. leftMenuShown: true,
  665. },
  666. })
  667. cy.mount(
  668. <EditorProviders scope={scope}>
  669. <EditorLeftMenu />
  670. </EditorProviders>
  671. )
  672. cy.get<HTMLOptionElement>('#settings-menu-pdfViewer option').then(
  673. options => {
  674. const values = [...options].map(o => o.value)
  675. expect(values).to.deep.eq(['pdfjs', 'native'])
  676. const texts = [...options].map(o => o.text)
  677. expect(texts).to.deep.eq(['Overleaf', 'Browser'])
  678. }
  679. )
  680. })
  681. })
  682. describe('help menu', function () {
  683. it('shows hotkeys modal correctly', function () {
  684. const scope = mockScope({
  685. ui: {
  686. leftMenuShown: true,
  687. },
  688. })
  689. cy.mount(
  690. <EditorProviders scope={scope}>
  691. <EditorLeftMenu />
  692. </EditorProviders>
  693. )
  694. cy.findByRole('button', { name: 'Show Hotkeys' }).click()
  695. cy.findByText('Hotkeys')
  696. })
  697. it('shows correct url for documentation', function () {
  698. const scope = mockScope({
  699. ui: {
  700. leftMenuShown: true,
  701. },
  702. })
  703. cy.mount(
  704. <EditorProviders scope={scope}>
  705. <EditorLeftMenu />
  706. </EditorProviders>
  707. )
  708. cy.findByRole('link', { name: 'Documentation' }).should(
  709. 'have.attr',
  710. 'href',
  711. '/learn'
  712. )
  713. })
  714. it('shows correct contact us modal', function () {
  715. const scope = mockScope({
  716. ui: {
  717. leftMenuShown: true,
  718. },
  719. })
  720. cy.mount(
  721. <EditorProviders scope={scope}>
  722. <EditorLeftMenu />
  723. </EditorProviders>
  724. )
  725. cy.findByRole('button', { name: 'Contact Us' }).click()
  726. cy.findByText('Affected project URL (Optional)')
  727. })
  728. })
  729. })
  730. describe('for anonymous users', function () {
  731. it('render minimal menu', function () {
  732. const scope = mockScope({
  733. ui: {
  734. leftMenuShown: true,
  735. },
  736. })
  737. window.metaAttributesCache.set('ol-anonymous', true)
  738. Object.assign(getMeta('ol-ExposedSettings'), { ieeeBrandId: 123 })
  739. cy.mount(
  740. <EditorProviders scope={scope}>
  741. <EditorLeftMenu />
  742. </EditorProviders>
  743. )
  744. // Download Menu
  745. cy.findByRole('heading', { name: 'Download' })
  746. cy.findByRole('link', { name: 'Source' })
  747. cy.findByRole('link', { name: 'PDF' })
  748. // Actions Menu
  749. cy.findByRole('heading', { name: 'Actions' }).should('not.exist')
  750. cy.findByRole('button', { name: 'Copy Project' }).should('not.exist')
  751. cy.findByRole('button', { name: 'Word Count' }).should('not.exist')
  752. // Sync Menu
  753. cy.findByRole('heading', { name: 'Sync' }).should('not.exist')
  754. cy.findByRole('button', { name: 'Dropbox' }).should('not.exist')
  755. cy.findByRole('button', { name: 'Git' }).should('not.exist')
  756. cy.findByRole('button', { name: 'GitHub' }).should('not.exist')
  757. // Settings Menu
  758. cy.findByRole('heading', { name: 'Settings' }).should('not.exist')
  759. cy.findByLabelText('Compiler').should('not.exist')
  760. cy.findByLabelText('TeX Live version').should('not.exist')
  761. cy.findByLabelText('Main document').should('not.exist')
  762. cy.findByLabelText('Spell check').should('not.exist')
  763. cy.findByLabelText('Auto-complete').should('not.exist')
  764. cy.findByLabelText('Auto-close Brackets').should('not.exist')
  765. cy.findByLabelText('Code check').should('not.exist')
  766. cy.findByLabelText('Editor theme').should('not.exist')
  767. cy.findByLabelText('Overall theme').should('not.exist')
  768. cy.findByLabelText('Keybindings').should('not.exist')
  769. cy.findByLabelText('Font Size').should('not.exist')
  770. cy.findByLabelText('Font Family').should('not.exist')
  771. cy.findByLabelText('Line Height').should('not.exist')
  772. cy.findByLabelText('PDF Viewer').should('not.exist')
  773. // Help Menu
  774. cy.findByRole('heading', { name: 'Help' })
  775. cy.findByRole('button', { name: 'Show Hotkeys' })
  776. cy.findByRole('button', { name: 'Documentation' }).should('not.exist')
  777. cy.findByRole('link', { name: 'Contact Us' }).should('not.exist')
  778. })
  779. })
  780. })