templates.spec.ts 9.6 KB

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