diff --git a/cmd/lists.go b/cmd/lists.go index 0454a178..353693e0 100644 --- a/cmd/lists.go +++ b/cmd/lists.go @@ -45,7 +45,7 @@ func handleGetLists(c echo.Context) error { if !single && minimal { // Minimal query simply returns the list of all lists with no additional metadata. This is fast. - if err := app.queries.GetLists.Select(&out.Results, ""); err != nil { + if err := app.queries.GetLists.Select(&out.Results, "", "id"); err != nil { app.log.Printf("error fetching lists: %v", err) return echo.NewHTTPError(http.StatusInternalServerError, app.i18n.Ts("globals.messages.errorFetching", diff --git a/cmd/public.go b/cmd/public.go index d3f9b469..687a29e4 100644 --- a/cmd/public.go +++ b/cmd/public.go @@ -270,7 +270,7 @@ func handleSubscriptionFormPage(c echo.Context) error { // Get all public lists. var lists []models.List - if err := app.queries.GetLists.Select(&lists, models.ListTypePublic); err != nil { + if err := app.queries.GetLists.Select(&lists, models.ListTypePublic, "name"); err != nil { app.log.Printf("error fetching public lists for form: %s", pqErrMsg(err)) return c.Render(http.StatusInternalServerError, tplMessage, makeMsgTpl(app.i18n.T("public.errorTitle"), "", diff --git a/frontend/cypress/integration/campaigns.js b/frontend/cypress/integration/campaigns.js index a064878b..3009b339 100644 --- a/frontend/cypress/integration/campaigns.js +++ b/frontend/cypress/integration/campaigns.js @@ -1,6 +1,6 @@ const apiUrl = Cypress.env('apiUrl'); -describe('Subscribers', () => { +describe('Campaigns', () => { it('Opens campaigns page', () => { cy.resetDB(); cy.loginAndVisit('/campaigns'); @@ -22,7 +22,7 @@ describe('Subscribers', () => { // Change the list. cy.get('.list-selector a.delete').click(); cy.get('.list-selector input').click(); - cy.get('.list-selector .autocomplete a').eq(1).click(); + cy.get('.list-selector .autocomplete a').eq(0).click(); // Clear and redo tags. cy.get('input[name=tags]').type('{backspace}new-tag{enter}'); @@ -63,7 +63,7 @@ describe('Subscribers', () => { expect(data.body).to.equal('new-content'); expect(data.lists.length).to.equal(1); - expect(data.lists[0].id).to.equal(2); + expect(data.lists[0].id).to.equal(1); expect(data.tags.length).to.equal(1); expect(data.tags[0]).to.equal('new-tag'); }); @@ -137,9 +137,10 @@ describe('Subscribers', () => { cy.wait(250); // Insert content. - cy.get('.ql-editor').type(`hello${n} \{\{ .Subscriber.Name \}\}`, { parseSpecialCharSequences: false }); - cy.get('.ql-editor').type('{enter}'); - cy.get('.ql-editor').type('\{\{ .Subscriber.Attribs.city \}\}', { parseSpecialCharSequences: false }); + cy.window().then((win) => { + win.tinymce.editors[0].setContent(`hello${n} \{\{ .Subscriber.Name \}\}\n\{\{ .Subscriber.Attribs.city \}\}`); + }); + cy.wait(200); // Select content type. cy.get(`label[data-cy=check-${c}]`).click(); diff --git a/frontend/cypress/integration/dashboard.js b/frontend/cypress/integration/dashboard.js index a19310d9..4a32da90 100644 --- a/frontend/cypress/integration/dashboard.js +++ b/frontend/cypress/integration/dashboard.js @@ -1,28 +1,27 @@ describe('Dashboard', () => { it('Opens dashboard', () => { + cy.resetDB(); cy.loginAndVisit('/'); // List counts. + cy.get('[data-cy=lists] .title').contains('2'); cy.get('[data-cy=lists]') - .should('contain', '2 Lists') .and('contain', '1 Public') .and('contain', '1 Private') .and('contain', '1 Single opt-in') .and('contain', '1 Double opt-in'); // Campaign counts. - cy.get('[data-cy=campaigns]') - .should('contain', '1 Campaign') - .and('contain', '1 draft'); + cy.get('[data-cy=campaigns] .title').contains('1'); + cy.get('[data-cy=campaigns]').contains('1 draft'); // Subscriber counts. + cy.get('[data-cy=subscribers] .title').contains('2'); cy.get('[data-cy=subscribers]') - .should('contain', '2 Subscribers') - .and('contain', '0 Blocklisted') + .should('contain', '0 Blocklisted') .and('contain', '0 Orphans'); // Message count. - cy.get('[data-cy=messages]') - .should('contain', '0 Messages sent'); + cy.get('[data-cy=messages] .title').contains('0'); }); }); diff --git a/frontend/cypress/integration/forms.js b/frontend/cypress/integration/forms.js index e86aa8c4..8d053578 100644 --- a/frontend/cypress/integration/forms.js +++ b/frontend/cypress/integration/forms.js @@ -44,7 +44,7 @@ describe('Forms', () => { // There should be no errors and two new subscribers should be subscribed to two lists. for (let i = 0; i < 2; i++) { for (let j = 0; j < 2; j++) { - cy.loginAndVisit('/subscription/form'); + cy.loginAndVisit(`${apiUrl}/subscription/form`); cy.get('input[name=email]').clear().type(`test${i}@test.com`); cy.get('input[name=name]').clear().type(`test${i}`); cy.get('input[type=checkbox]').eq(j).click(); diff --git a/frontend/cypress/integration/subscribers.js b/frontend/cypress/integration/subscribers.js index 3237ca40..10f94094 100644 --- a/frontend/cypress/integration/subscribers.js +++ b/frontend/cypress/integration/subscribers.js @@ -58,7 +58,7 @@ describe('Subscribers', () => { { radio: 'check-list-remove', lists: [0, 1], rows: { 1: [] } }, { radio: 'check-list-add', lists: [0, 1], rows: { 0: ['unsubscribed', 'unsubscribed'], 1: ['unconfirmed', 'unconfirmed'] } }, { radio: 'check-list-remove', lists: [0], rows: { 0: ['unsubscribed'] } }, - { radio: 'check-list-add', lists: [0], rows: { 0: ['unsubscribed', 'unconfirmed'] } }, + { radio: 'check-list-add', lists: [0], rows: { 0: ['unconfirmed', 'unsubscribed'] } }, ]; @@ -214,9 +214,9 @@ describe('Subscribers', () => { cases.forEach((c) => { cy.sortTable(`thead th.${c}`, asc); - cy.wait(100); + cy.wait(250); cy.sortTable(`thead th.${c}`, desc); - cy.wait(100); + cy.wait(250); }); }); }); @@ -319,5 +319,4 @@ describe('Domain blocklist', () => { expect(response.status).to.equal(200); }); }); - -}) +}); diff --git a/frontend/cypress/support/commands.js b/frontend/cypress/support/commands.js index a3e14324..88337bf6 100644 --- a/frontend/cypress/support/commands.js +++ b/frontend/cypress/support/commands.js @@ -14,6 +14,7 @@ Cypress.Commands.add('resetDB', () => { // table against the given IDs, asserting the expected order of sort. Cypress.Commands.add('sortTable', (theadSelector, ordIDs) => { cy.get(theadSelector).click(); + cy.wait(500); cy.get('tbody td[data-id]').each(($el, index) => { expect(ordIDs[index]).to.equal(parseInt($el.attr('data-id'))); }); diff --git a/queries.sql b/queries.sql index c8264b91..35d4fb9d 100644 --- a/queries.sql +++ b/queries.sql @@ -30,7 +30,8 @@ SELECT * FROM lists ELSE TRUE END) AND (CASE WHEN $5 != '' THEN subscriber_lists.status = $5::subscription_status END) - AND (CASE WHEN $6 != '' THEN lists.optin = $6::list_optin ELSE TRUE END); + AND (CASE WHEN $6 != '' THEN lists.optin = $6::list_optin ELSE TRUE END) + ORDER BY id; -- name: get-subscriber-lists-lazy -- Get lists associations of subscribers given a list of subscriber IDs. @@ -335,7 +336,8 @@ UPDATE subscriber_lists SET status='unsubscribed', updated_at=NOW() -- lists -- name: get-lists -SELECT * FROM lists WHERE (CASE WHEN $1 = '' THEN 1=1 ELSE type=$1::list_type END) ORDER by name DESC; +SELECT * FROM lists WHERE (CASE WHEN $1 = '' THEN 1=1 ELSE type=$1::list_type END) + ORDER BY CASE WHEN $2 = 'id' THEN id END, CASE WHEN $2 = 'name' THEN name END; -- name: query-lists WITH ls AS (