templates.spec.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { isExcludedBySharding, startWith } from './helpers/config'
  2. import { ensureUserExists, login } from './helpers/login'
  3. import {
  4. createProject,
  5. NEW_PROJECT_BUTTON_MATCHER,
  6. redirectEditorUrlWithQueryParams,
  7. } from './helpers/project'
  8. const WITHOUT_PROJECTS_USER = 'user-without-projects@example.com'
  9. const ADMIN_USER = 'admin@example.com'
  10. const REGULAR_USER = 'user@example.com'
  11. const TEMPLATES_USER = 'templates@example.com'
  12. // Re-use value for "exists" and "does not exist" tests
  13. const LABEL_BROWSE_TEMPLATES = 'Browse templates'
  14. describe('Templates', function () {
  15. ensureUserExists({ email: TEMPLATES_USER })
  16. ensureUserExists({ email: WITHOUT_PROJECTS_USER })
  17. let OVERLEAF_TEMPLATES_USER_ID: string
  18. before(function () {
  19. login(TEMPLATES_USER)
  20. cy.visit('/')
  21. cy.get('meta[name="ol-user_id"]').then(el => {
  22. OVERLEAF_TEMPLATES_USER_ID = el.attr('content')!
  23. })
  24. })
  25. function varsFn() {
  26. return {
  27. OVERLEAF_TEMPLATES_USER_ID,
  28. OVERLEAF_NEW_PROJECT_TEMPLATE_LINKS:
  29. '[{"name":"All Templates","url":"/templates/all"}]',
  30. }
  31. }
  32. describe('enabled in Server Pro', function () {
  33. if (isExcludedBySharding('PRO_CUSTOM_2')) return
  34. startWith({
  35. pro: true,
  36. varsFn,
  37. })
  38. ensureUserExists({ email: REGULAR_USER })
  39. ensureUserExists({ email: ADMIN_USER, isAdmin: true })
  40. it('should show templates link on welcome page', function () {
  41. login(WITHOUT_PROJECTS_USER)
  42. cy.visit('/')
  43. cy.findByRole('link', { name: LABEL_BROWSE_TEMPLATES }).click()
  44. cy.url().should('match', /\/templates$/)
  45. })
  46. it('should have templates feature', function () {
  47. login(TEMPLATES_USER)
  48. const name = `Template ${Date.now()}`
  49. const description = `Template Description ${Date.now()}`
  50. cy.visit('/')
  51. createProject(name, { type: 'Example project' }).as('templateProjectId')
  52. cy.findByRole('navigation', {
  53. name: 'Project actions',
  54. })
  55. .findByRole('button', { name: 'Menu' })
  56. .click()
  57. cy.findByRole('button', { name: 'Manage Template' }).click()
  58. cy.findByLabelText('Template Description').type(description)
  59. cy.findByRole('button', { name: 'Publish' }).click()
  60. cy.findByRole('button', { name: 'Publishing…' }).should('be.disabled')
  61. cy.findByRole('button', { name: 'Publish' }).should('not.exist')
  62. cy.findByRole('button', { name: 'Unpublish', timeout: 60_000 })
  63. cy.findByRole('button', { name: 'Republish' })
  64. cy.findByRole('link', { name: 'View it in the template gallery' }).click()
  65. cy.url()
  66. .should('match', /\/templates\/[a-f0-9]{24}$/)
  67. .as('templateURL')
  68. cy.findByRole('heading', { level: 2 }).findByText(name)
  69. cy.findByText(description)
  70. cy.findByRole('link', { name: 'Open as Template' })
  71. cy.findByRole('button', { name: 'Unpublish' })
  72. cy.findByRole('button', { name: 'Republish' })
  73. cy.get('img')
  74. .should('have.attr', 'src')
  75. .and('match', /\/v\/0\//)
  76. cy.findByRole('button', { name: 'Republish' }).click()
  77. cy.findByRole('button', { name: 'Publishing…' }).should('be.disabled')
  78. cy.findByRole('button', { name: 'Republish', timeout: 60_000 })
  79. cy.get('img', { timeout: 60_000 })
  80. .should('have.attr', 'src')
  81. .and('match', /\/v\/1\//)
  82. // custom tag
  83. const tagName = `${Date.now()}`
  84. cy.visit('/')
  85. cy.findByRole('checkbox', { name: `Select ${name}` }).check()
  86. cy.findByRole('navigation', { name: 'Project categories and tags' })
  87. .findByRole('button', { name: 'New tag' })
  88. .click()
  89. cy.focused().type(tagName)
  90. cy.findByRole('button', { name: 'Create' }).click()
  91. cy.findByRole('navigation', {
  92. name: 'Project categories and tags',
  93. }).should('contain', `${tagName} (1)`)
  94. // Check listing
  95. cy.visit('/templates')
  96. cy.findByRole('link', { name: tagName })
  97. cy.visit('/templates/all')
  98. cy.findByRole('heading', { name })
  99. cy.visit(`/templates/${tagName}`)
  100. cy.findByRole('heading', { name })
  101. // Unpublish via template page
  102. cy.get('@templateURL').then(url => cy.visit(`${url}`))
  103. cy.findByRole('button', { name: 'Unpublish' }).click()
  104. cy.url().should('match', /\/templates$/)
  105. cy.get('@templateURL').then(url =>
  106. cy.visit(`${url}`, {
  107. failOnStatusCode: false,
  108. })
  109. )
  110. cy.findByRole('heading', { name: 'Not found' })
  111. cy.visit('/templates/all')
  112. cy.findByRole('heading', { name }).should('not.exist')
  113. cy.visit(`/templates/${tagName}`)
  114. cy.findByRole('heading', { name }).should('not.exist')
  115. // Publish again
  116. redirectEditorUrlWithQueryParams(false)
  117. cy.get('@templateProjectId').then(projectId =>
  118. cy.visit(`/project/${projectId}`)
  119. )
  120. cy.findByRole('navigation', {
  121. name: 'Project actions',
  122. })
  123. .findByRole('button', { name: 'Menu' })
  124. .click()
  125. cy.findByRole('button', { name: 'Manage Template' }).click()
  126. cy.findByRole('button', { name: 'Publish' }).click()
  127. cy.findByRole('button', { name: 'Unpublish', timeout: 60_000 })
  128. // Should assign a new template id
  129. cy.findByRole('link', { name: 'View it in the template gallery' }).click()
  130. cy.url()
  131. .should('match', /\/templates\/[a-f0-9]{24}$/)
  132. .as('newTemplateURL')
  133. cy.get('@newTemplateURL').then(newURL => {
  134. cy.get('@templateURL').then(prevURL => {
  135. expect(newURL).to.match(/\/templates\/[a-f0-9]{24}$/)
  136. expect(prevURL).to.not.equal(newURL)
  137. })
  138. })
  139. // Open project from template
  140. login(REGULAR_USER)
  141. cy.visit('/templates')
  142. cy.findByRole('link', { name: tagName }).click()
  143. cy.findByRole('link', { name }).click()
  144. redirectEditorUrlWithQueryParams(false)
  145. cy.findByRole('link', { name: 'Open as Template' }).click()
  146. cy.findByRole('navigation', { name: 'Project actions' }).findByText(
  147. /Your Paper/i
  148. ) // might have (1) suffix
  149. cy.findByRole('navigation', {
  150. name: 'Project actions',
  151. })
  152. .findByRole('button', { name: 'Menu' })
  153. .click()
  154. cy.findByRole('button', { name: 'Word Count' }).click() // wait for lazy loading
  155. cy.findByRole('button', { name: 'Manage Template' }).should('not.exist')
  156. // Check management as regular user
  157. cy.get('@newTemplateURL').then(url => cy.visit(`${url}`))
  158. cy.findByRole('link', { name: 'Open as Template' })
  159. cy.findByRole('button', { name: 'Unpublish' }).should('not.exist')
  160. cy.findByRole('button', { name: 'Republish' }).should('not.exist')
  161. // Check management as admin user
  162. login(ADMIN_USER)
  163. cy.get('@newTemplateURL').then(url => cy.visit(`${url}`))
  164. cy.findByRole('link', { name: 'Open as Template' })
  165. cy.findByRole('button', { name: 'Unpublish' })
  166. cy.findByRole('button', { name: 'Republish' })
  167. redirectEditorUrlWithQueryParams(false)
  168. cy.get('@templateProjectId').then(projectId =>
  169. cy.visit(`/project/${projectId}`)
  170. )
  171. cy.findByRole('navigation', {
  172. name: 'Project actions',
  173. })
  174. .findByRole('button', { name: 'Menu' })
  175. .click()
  176. cy.findByRole('button', { name: 'Manage Template' }).click()
  177. cy.findByRole('button', { name: 'Unpublish' })
  178. // Back to templates user
  179. login(TEMPLATES_USER)
  180. // Unpublish via editor
  181. redirectEditorUrlWithQueryParams(false)
  182. cy.get('@templateProjectId').then(projectId =>
  183. cy.visit(`/project/${projectId}`)
  184. )
  185. cy.findByRole('navigation', {
  186. name: 'Project actions',
  187. })
  188. .findByRole('button', { name: 'Menu' })
  189. .click()
  190. cy.findByRole('button', { name: 'Manage Template' }).click()
  191. cy.findByRole('button', { name: 'Unpublish' }).click()
  192. cy.findByRole('button', { name: 'Publish' })
  193. cy.visit('/templates/all')
  194. cy.findByRole('link', { name }).should('not.exist')
  195. // check for template links, after creating the first project
  196. cy.visit('/')
  197. cy.findAllByRole('button', { name: NEW_PROJECT_BUTTON_MATCHER }).click()
  198. cy.findByRole('menuitem', { name: /All Templates/ }).should(
  199. 'have.attr',
  200. 'href',
  201. '/templates/all'
  202. )
  203. })
  204. })
  205. function checkDisabled() {
  206. it('should not have templates feature', function () {
  207. login(TEMPLATES_USER)
  208. cy.visit('/')
  209. createProject('maybe templates')
  210. cy.findByRole('navigation', {
  211. name: 'Project actions',
  212. })
  213. .findByRole('button', { name: 'Menu' })
  214. .click()
  215. cy.findByRole('button', { name: 'Word Count' }) // wait for lazy loading
  216. cy.findByRole('button', { name: 'Manage Template' }).should('not.exist')
  217. cy.visit('/templates', { failOnStatusCode: false })
  218. cy.findByRole('heading', { name: 'Not found' })
  219. cy.visit('/templates/all', { failOnStatusCode: false })
  220. cy.findByRole('heading', { name: 'Not found' })
  221. // check for template links, after creating the first project
  222. cy.visit('/')
  223. cy.findAllByRole('button', { name: NEW_PROJECT_BUTTON_MATCHER }).click()
  224. cy.findByRole('menuitem', { name: /All Templates/ }).should('not.exist')
  225. })
  226. it('should not show templates link on welcome page', function () {
  227. login(WITHOUT_PROJECTS_USER)
  228. cy.visit('/')
  229. cy.findByRole('button', { name: NEW_PROJECT_BUTTON_MATCHER }) // wait for lazy loading
  230. cy.findByRole('link', { name: LABEL_BROWSE_TEMPLATES }).should(
  231. 'not.exist'
  232. )
  233. })
  234. }
  235. describe('disabled Server Pro', function () {
  236. if (isExcludedBySharding('PRO_DEFAULT_2')) return
  237. startWith({ pro: true })
  238. checkDisabled()
  239. })
  240. describe('unavailable in CE', function () {
  241. if (isExcludedBySharding('CE_CUSTOM_1')) return
  242. startWith({
  243. pro: false,
  244. varsFn,
  245. })
  246. checkDisabled()
  247. })
  248. })