subscribers.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. describe('Subscribers', () => {
  2. it('Opens subscribers page', () => {
  3. cy.resetDB();
  4. cy.loginAndVisit('/subscribers');
  5. });
  6. it('Counts subscribers', () => {
  7. cy.get('tbody td[data-label=Status]').its('length').should('eq', 2);
  8. });
  9. it('Searches subscribers', () => {
  10. const cases = [
  11. { value: 'john{enter}', count: 1, contains: 'john@example.com' },
  12. { value: 'anon{enter}', count: 1, contains: 'anon@example.com' },
  13. { value: '{enter}', count: 2, contains: null },
  14. ];
  15. cases.forEach((c) => {
  16. cy.get('[data-cy=search]').clear().type(c.value);
  17. cy.get('tbody td[data-label=Status]').its('length').should('eq', c.count);
  18. if (c.contains) {
  19. cy.get('tbody td[data-label=E-mail]').contains(c.contains);
  20. }
  21. });
  22. });
  23. it('Advanced searches subscribers', () => {
  24. cy.get('[data-cy=btn-advanced-search]').click();
  25. const cases = [
  26. { value: 'subscribers.attribs->>\'city\'=\'Bengaluru\'', count: 2 },
  27. { value: 'subscribers.attribs->>\'city\'=\'Bengaluru\' AND id=1', count: 1 },
  28. { value: '(subscribers.attribs->>\'good\')::BOOLEAN = true AND name like \'Anon%\'', count: 1 },
  29. ];
  30. cases.forEach((c) => {
  31. cy.get('[data-cy=query]').clear().type(c.value);
  32. cy.get('[data-cy=btn-query]').click();
  33. cy.get('tbody td[data-label=Status]').its('length').should('eq', c.count);
  34. });
  35. cy.get('[data-cy=btn-query-reset]').click();
  36. cy.get('tbody td[data-label=Status]').its('length').should('eq', 2);
  37. });
  38. it('Does bulk subscriber list add and remove', () => {
  39. const cases = [
  40. // radio: action to perform, rows: table rows to select and perform on: [expected statuses of those rows after thea action]
  41. { radio: 'check-list-add', lists: [0, 1], rows: { 0: ['unconfirmed', 'unconfirmed'] } },
  42. { radio: 'check-list-unsubscribe', lists: [0, 1], rows: { 0: ['unsubscribed', 'unsubscribed'], 1: ['unsubscribed'] } },
  43. { radio: 'check-list-remove', lists: [0, 1], rows: { 1: [] } },
  44. { radio: 'check-list-add', lists: [0, 1], rows: { 0: ['unsubscribed', 'unsubscribed'], 1: ['unconfirmed', 'unconfirmed'] } },
  45. { radio: 'check-list-remove', lists: [0], rows: { 0: ['unsubscribed'] } },
  46. { radio: 'check-list-add', lists: [0], rows: { 0: ['unconfirmed', 'unsubscribed'] } },
  47. ];
  48. cases.forEach((c, n) => {
  49. // Select one of the 2 subscribers in the table.
  50. Object.keys(c.rows).forEach((r) => {
  51. cy.get('tbody td.checkbox-cell .checkbox').eq(r).click();
  52. });
  53. // Open the 'manage lists' modal.
  54. cy.get('[data-cy=btn-manage-lists]').click();
  55. // Check both lists in the modal.
  56. c.lists.forEach((l) => {
  57. cy.get('.list-selector input').click();
  58. cy.get('.list-selector .autocomplete a').first().click();
  59. });
  60. // Select the radio option in the modal.
  61. cy.get(`[data-cy=${c.radio}] .check`).click();
  62. // Save.
  63. cy.get('.modal button.is-primary').click();
  64. // Check the status of the lists on the subscriber.
  65. Object.keys(c.rows).forEach((r) => {
  66. cy.get('tbody td[data-label=E-mail]').eq(r).find('.tags').then(($el) => {
  67. cy.wrap($el).find('.tag').should('have.length', c.rows[r].length);
  68. c.rows[r].forEach((status, n) => {
  69. // eg: .tag(n).unconfirmed
  70. cy.wrap($el).find('.tag').eq(n).should('have.class', status);
  71. });
  72. });
  73. });
  74. });
  75. });
  76. it('Resets subscribers page', () => {
  77. cy.resetDB();
  78. cy.loginAndVisit('/subscribers');
  79. });
  80. it('Edits subscribers', () => {
  81. const status = ['enabled', 'blocklisted'];
  82. const json = '{"string": "hello", "ints": [1,2,3], "null": null, "sub": {"bool": true}}';
  83. // Collect values being edited on each sub to confirm the changes in the next step
  84. // index by their ID shown in the modal.
  85. const rows = {};
  86. // Open the edit popup and edit the default lists.
  87. cy.get('[data-cy=btn-edit]').each(($el, n) => {
  88. const email = `email-${n}@email.com`;
  89. const name = `name-${n}`;
  90. // Open the edit modal.
  91. cy.wrap($el).click();
  92. // Get the ID from the header and proceed to fill the form.
  93. let id = 0;
  94. cy.get('[data-cy=id]').then(($el) => {
  95. id = $el.text();
  96. cy.get('input[name=email]').clear().type(email);
  97. cy.get('input[name=name]').clear().type(name);
  98. cy.get('select[name=status]').select(status[n]);
  99. cy.get('.list-selector input').click();
  100. cy.get('.list-selector .autocomplete a').first().click();
  101. cy.get('textarea[name=attribs]').clear().type(json, { parseSpecialCharSequences: false, delay: 0 });
  102. cy.get('.modal-card-foot button[type=submit]').click();
  103. rows[id] = { email, name, status: status[n] };
  104. });
  105. });
  106. // Confirm the edits on the table.
  107. cy.wait(250);
  108. cy.get('tbody tr').each(($el) => {
  109. cy.wrap($el).find('td[data-id]').invoke('attr', 'data-id').then((id) => {
  110. cy.wrap($el).find('td[data-label=E-mail]').contains(rows[id].email);
  111. cy.wrap($el).find('td[data-label=Name]').contains(rows[id].name);
  112. cy.wrap($el).find('td[data-label=Status]').contains(rows[id].status, { matchCase: false });
  113. // Both lists on the enabled sub should be 'unconfirmed' and the blocklisted one, 'unsubscribed.'
  114. cy.wrap($el).find(`.tags .${rows[id].status === 'enabled' ? 'unconfirmed' : 'unsubscribed'}`)
  115. .its('length').should('eq', 2);
  116. cy.wrap($el).find('td[data-label=Lists]').then((l) => {
  117. cy.expect(parseInt(l.text().trim())).to.equal(rows[id].status === 'blocklisted' ? 0 : 2);
  118. });
  119. });
  120. });
  121. });
  122. it('Deletes subscribers', () => {
  123. // Delete all visible lists.
  124. cy.get('tbody tr').each(() => {
  125. cy.get('tbody a[data-cy=btn-delete]').first().click();
  126. cy.get('.modal button.is-primary').click();
  127. });
  128. // Confirm deletion.
  129. cy.get('table tr.is-empty');
  130. });
  131. it('Creates new subscribers', () => {
  132. const statuses = ['enabled', 'blocklisted'];
  133. const lists = [[1], [2], [1, 2]];
  134. const json = '{"string": "hello", "ints": [1,2,3], "null": null, "sub": {"bool": true}}';
  135. // Cycle through each status and each list ID combination and create subscribers.
  136. const n = 0;
  137. for (let n = 0; n < 6; n++) {
  138. const email = `email-${n}@email.com`;
  139. const name = `name-${n}`;
  140. const status = statuses[(n + 1) % statuses.length];
  141. const list = lists[(n + 1) % lists.length];
  142. cy.get('[data-cy=btn-new]').click();
  143. cy.get('input[name=email]').type(email);
  144. cy.get('input[name=name]').type(name);
  145. cy.get('select[name=status]').select(status);
  146. list.forEach((l) => {
  147. cy.get('.list-selector input').click();
  148. cy.get('.list-selector .autocomplete a').first().click();
  149. });
  150. cy.get('textarea[name=attribs]').clear().type(json, { parseSpecialCharSequences: false, delay: 0 });
  151. cy.get('.modal-card-foot button[type=submit]').click();
  152. // Confirm the addition by inspecting the newly created list row,
  153. // which is always the first row in the table.
  154. cy.wait(250);
  155. const tr = cy.get('tbody tr:nth-child(1)').then(($el) => {
  156. cy.wrap($el).find('td[data-label=E-mail]').contains(email);
  157. cy.wrap($el).find('td[data-label=Name]').contains(name);
  158. cy.wrap($el).find('td[data-label=Status]').contains(status, { matchCase: false });
  159. cy.wrap($el).find(`.tags .${status === 'enabled' ? 'unconfirmed' : 'unsubscribed'}`)
  160. .its('length').should('eq', list.length);
  161. cy.wrap($el).find('td[data-label=Lists]').then((l) => {
  162. cy.expect(parseInt(l.text().trim())).to.equal(status === 'blocklisted' ? 0 : list.length);
  163. });
  164. });
  165. }
  166. });
  167. it('Sorts subscribers', () => {
  168. const asc = [3, 4, 5, 6, 7, 8];
  169. const desc = [8, 7, 6, 5, 4, 3];
  170. const cases = ['cy-status', 'cy-email', 'cy-name', 'cy-created_at', 'cy-updated_at'];
  171. cases.forEach((c) => {
  172. cy.sortTable(`thead th.${c}`, asc);
  173. cy.wait(100);
  174. cy.sortTable(`thead th.${c}`, desc);
  175. cy.wait(100);
  176. });
  177. });
  178. });