editor-left-menu.spec.tsx 25 KB

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