editor-left-menu.spec.tsx 24 KB

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