@@ -0,0 +1,80 @@
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
describe('Archive', () => {
|
||||
it('Opens campaigns page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
cy.wait(500);
|
||||
});
|
||||
|
||||
it('Clones campaign', () => {
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
cy.get('[data-cy=btn-clone]').first().click();
|
||||
cy.get('.modal input').clear().type('clone').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.wait(250);
|
||||
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
cy.get('[data-cy=btn-clone]').first().click();
|
||||
cy.get('.modal input').clear().type('clone2').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.wait(250);
|
||||
|
||||
cy.clickMenu('all-campaigns');
|
||||
});
|
||||
|
||||
it('Starts campaigns', () => {
|
||||
cy.get('td[data-label=Status] a').eq(0).click();
|
||||
cy.get('[data-cy=btn-start]').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
|
||||
cy.get('td[data-label=Status] a').eq(1).click();
|
||||
cy.get('[data-cy=btn-start]').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.wait(1000);
|
||||
});
|
||||
|
||||
it('Enables archive on one campaign (no slug)', () => {
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
cy.wait(250);
|
||||
cy.get('td[data-label=Status] a').eq(0).click();
|
||||
|
||||
// Switch to archive tab and enable archive.
|
||||
cy.get('.b-tabs nav a').eq(3).click();
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy=btn-archive] .check').click();
|
||||
cy.get('[data-cy=archive-slug]').clear();
|
||||
cy.get('[data-cy=archive-meta]').clear()
|
||||
.type('{"email": "archive@domain.com", "name": "Archive", "attribs": { "city": "Bengaluru"}}', { parseSpecialCharSequences: false });
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(250);
|
||||
});
|
||||
|
||||
it('Enables archive on one campaign', () => {
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
cy.wait(250);
|
||||
cy.get('td[data-label=Status] a').eq(1).click();
|
||||
|
||||
// Switch to archive tab and enable archive.
|
||||
cy.get('.b-tabs nav a').eq(3).click();
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy=btn-archive] .check').click();
|
||||
cy.get('[data-cy=archive-slug]').clear().type('my-archived-campaign');
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(250);
|
||||
});
|
||||
|
||||
it('Opens campaign archive page', () => {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
cy.loginAndVisit(`${apiUrl}/archive`);
|
||||
cy.get('li a').eq(i).click();
|
||||
cy.wait(250);
|
||||
if (i === 0) {
|
||||
cy.get('h3').contains('Hi Archive!');
|
||||
cy.get('p').eq(0).contains('Bengaluru');
|
||||
} else {
|
||||
cy.get('h3').contains('Hi Subscriber!');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
describe('Bounces', () => {
|
||||
const subs = [];
|
||||
|
||||
it('Enable bounces', () => {
|
||||
cy.resetDB();
|
||||
|
||||
cy.loginAndVisit('/admin/settings');
|
||||
cy.get('.b-tabs nav a').eq(6).click();
|
||||
cy.get('[data-cy=btn-enable-bounce] .switch').click();
|
||||
cy.get('[data-cy=btn-enable-bounce-webhook] .switch').click();
|
||||
|
||||
// The last <select> box, pick 'delete' option.
|
||||
cy.get('select[name="bounce.action"]').last().select('delete');
|
||||
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
|
||||
cy.waitForBackend();
|
||||
});
|
||||
|
||||
it('Post bounces', () => {
|
||||
cy.loginAndVisit('/admin/subscribers/bounces');
|
||||
|
||||
// Get campaign.
|
||||
let camp = {};
|
||||
cy.request(`${apiUrl}/api/campaigns`).then((resp) => {
|
||||
camp = resp.body.data.results[0];
|
||||
}).then(() => {
|
||||
console.log('campaign is ', camp.uuid);
|
||||
});
|
||||
|
||||
// Get subscribers.
|
||||
let subs = [];
|
||||
cy.request(`${apiUrl}/api/subscribers`).then((resp) => {
|
||||
subs = resp.body.data.results;
|
||||
}).then(() => {
|
||||
// Register soft bounces do nothing.
|
||||
let sub = {};
|
||||
cy.request('POST', `${apiUrl}/webhooks/bounce`, { source: 'api', type: 'soft', email: subs[0].email });
|
||||
cy.request('POST', `${apiUrl}/webhooks/bounce`, { source: 'api', type: 'soft', email: subs[0].email });
|
||||
cy.request(`${apiUrl}/api/subscribers/${subs[0].id}`).then((resp) => {
|
||||
sub = resp.body.data;
|
||||
}).then(() => {
|
||||
cy.expect(sub.status).to.equal('enabled');
|
||||
});
|
||||
|
||||
// Hard bounces blocklist.
|
||||
cy.request('POST', `${apiUrl}/webhooks/bounce`, { source: 'api', type: 'hard', email: subs[0].email });
|
||||
cy.request('POST', `${apiUrl}/webhooks/bounce`, { source: 'api', type: 'hard', email: subs[0].email });
|
||||
|
||||
cy.request(`${apiUrl}/api/subscribers/${subs[0].id}`).then((resp) => {
|
||||
sub = resp.body.data;
|
||||
}).then(() => {
|
||||
cy.expect(sub.status).to.equal('blocklisted');
|
||||
});
|
||||
|
||||
// Complaint bounces delete.
|
||||
cy.request('POST', `${apiUrl}/webhooks/bounce`, { source: 'api', type: 'complaint', email: subs[1].email });
|
||||
cy.request('POST', `${apiUrl}/webhooks/bounce`, { source: 'api', type: 'complaint', email: subs[1].email });
|
||||
cy.request({ url: `${apiUrl}/api/subscribers/${subs[1].id}`, failOnStatusCode: false }).then((resp) => {
|
||||
expect(resp.status).to.eq(400);
|
||||
});
|
||||
|
||||
cy.loginAndVisit('/admin/subscribers/bounces');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,525 @@
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
const headers = '[{"X-Custom": "Custom-Value"}]';
|
||||
|
||||
describe('Campaigns', () => {
|
||||
it('Opens campaigns page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
});
|
||||
|
||||
it('Counts campaigns', () => {
|
||||
cy.get('tbody td[data-label=Status]').should('have.length', 1);
|
||||
});
|
||||
|
||||
it('Creates campaign', () => {
|
||||
cy.get('a[data-cy=btn-new]').click();
|
||||
|
||||
// Fill fields.
|
||||
cy.get('input[name=name]').clear().type('new-attach');
|
||||
cy.get('input[name=subject]').clear().type('new-subject');
|
||||
cy.get('input[name=from_email]').clear().type('new <from@email>');
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').eq(0).click();
|
||||
|
||||
cy.get('button[data-cy=btn-continue]').click();
|
||||
cy.wait(500);
|
||||
|
||||
cy.get('a[data-cy=btn-attach]').click();
|
||||
cy.get('button[data-cy=btn-toggle-upload]').click();
|
||||
cy.wait(500);
|
||||
cy.get('input[type=file]').attachFile('example.json');
|
||||
cy.get('form[data-cy="upload"] button').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.wait(500);
|
||||
cy.get('.modal a.thumb-link').click();
|
||||
cy.get('button[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
|
||||
// Re-open and check that the file still exists.
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
cy.get('td[data-label=Status] a').eq(0).click();
|
||||
cy.get('.b-tabs nav a').eq(1).click();
|
||||
cy.get('div.field[data-cy=media]').contains('example');
|
||||
|
||||
// Start.
|
||||
cy.get('button[data-cy=btn-start]').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.wait(500);
|
||||
cy.get('tbody tr').eq(0).within(() => {
|
||||
cy.get('td[data-label=Status] .tag').should(($tag) => {
|
||||
expect($tag.hasClass('running') || $tag.hasClass('finished')).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Edits campaign', () => {
|
||||
cy.get('td[data-label=Status] a').eq(1).click();
|
||||
|
||||
// Fill fields.
|
||||
cy.get('input[name=name]').clear().type('new-name');
|
||||
cy.get('input[name=subject]').clear().type('new-subject');
|
||||
cy.get('input[name=from_email]').clear().type('new <from@email>');
|
||||
|
||||
// Change the list.
|
||||
cy.get('.list-selector a.delete').click();
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').eq(0).click();
|
||||
|
||||
// Clear and redo tags.
|
||||
cy.get('input[name=tags]').type('{backspace}new-tag{enter}');
|
||||
|
||||
// Enable schedule.
|
||||
cy.get('[data-cy=btn-send-later] .check').click();
|
||||
cy.wait(100);
|
||||
cy.get('.datepicker input').click();
|
||||
cy.wait(100);
|
||||
cy.get('.datepicker-header .control:nth-child(2) select').select((new Date().getFullYear() + 1).toString());
|
||||
cy.wait(100);
|
||||
cy.get('.datepicker-body a.is-selectable:first').click();
|
||||
cy.wait(100);
|
||||
cy.get('body').click(1, 1);
|
||||
|
||||
// Add custom headers.
|
||||
cy.get('[data-cy=btn-headers]').click();
|
||||
cy.get('textarea[name=headers]').invoke('val', headers).trigger('input');
|
||||
|
||||
// Switch to content tab.
|
||||
cy.get('.b-tabs nav a').eq(1).click();
|
||||
|
||||
// Switch format to plain text.
|
||||
cy.get('select[name=content_type]').select('plain');
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
|
||||
// Enter body value.
|
||||
cy.get('textarea[name=content]').clear().type('new-content');
|
||||
cy.get('button[data-cy=btn-save]').click();
|
||||
|
||||
// Schedule.
|
||||
cy.get('button[data-cy=btn-schedule]').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
|
||||
cy.wait(250);
|
||||
|
||||
// Verify the changes.
|
||||
cy.request(`${apiUrl}/api/campaigns/1`).should((response) => {
|
||||
const { data } = response.body;
|
||||
expect(data.status).to.equal('scheduled');
|
||||
expect(data.name).to.equal('new-name');
|
||||
expect(data.subject).to.equal('new-subject');
|
||||
expect(data.content_type).to.equal('plain');
|
||||
expect(data.altbody).to.equal(null);
|
||||
expect(data.send_at).to.not.equal(null);
|
||||
expect(data.body).to.equal('new-content');
|
||||
|
||||
expect(data.lists.length).to.equal(1);
|
||||
expect(data.lists[0].id).to.equal(1);
|
||||
expect(data.tags.length).to.equal(1);
|
||||
expect(data.tags[0]).to.equal('new-tag');
|
||||
expect(data.headers[0]['X-Custom']).to.equal('Custom-Value');
|
||||
});
|
||||
|
||||
cy.get('tbody td[data-label=Status] .tag.scheduled');
|
||||
});
|
||||
|
||||
it('Unschedules campaign', () => {
|
||||
cy.get('td[data-label=Status] a').eq(1).click();
|
||||
cy.wait(250);
|
||||
cy.get('button[data-cy=btn-unschedule]').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.wait(250);
|
||||
cy.visit('/admin/campaigns');
|
||||
|
||||
// Check if the status label has the inner text `Draft`.
|
||||
cy.get('td[data-label=Status] .tag.draft').should('have.length', 1);
|
||||
});
|
||||
|
||||
it('Switches formats', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
const formats = ['html', 'markdown', 'plain'];
|
||||
const htmlBody = '<strong>hello</strong> \{\{ .Subscriber.Name \}\} from {\{ .Subscriber.Attribs.city \}\}';
|
||||
const plainBody = 'hello Demo Subscriber from Bengaluru';
|
||||
|
||||
// Set test content the first time.
|
||||
cy.get('td[data-label=Status] a').click();
|
||||
cy.get('.b-tabs nav a').eq(1).click();
|
||||
cy.window().then((win) => {
|
||||
win.tinymce.editors[0].setContent(htmlBody);
|
||||
win.tinymce.editors[0].save();
|
||||
});
|
||||
cy.get('button[data-cy=btn-save]').click();
|
||||
|
||||
formats.forEach((c) => {
|
||||
cy.visit('/admin/campaigns');
|
||||
cy.get('td[data-label=Status] a').click();
|
||||
|
||||
// Switch to content tab.
|
||||
cy.get('.b-tabs nav a').eq(1).click();
|
||||
|
||||
// Switch format.
|
||||
cy.get('select[name=content_type]').select(c);
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
|
||||
// Check content via the preview API (the iframe is sandboxed and inaccessible to JS).
|
||||
if (c !== 'plain') {
|
||||
cy.location('pathname').then((p) => {
|
||||
cy.request(`${apiUrl}/api/campaigns/${p.split('/').at(-1).replace(/#.*/, '')}/preview`).then((resp) => {
|
||||
// Strip HTML tags and normalize whitespace.
|
||||
const text = resp.body.replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/\s+/g, ' ').trim();
|
||||
expect(text).to.contain(plainBody);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cy.visit('/admin/campaigns');
|
||||
});
|
||||
});
|
||||
|
||||
it('Clones campaign', () => {
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
for (let n = 0; n < 3; n++) {
|
||||
// Clone the campaign.
|
||||
cy.get('[data-cy=btn-clone]').first().click();
|
||||
cy.get('.modal input').clear().type(`clone${n}`).click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.wait(250);
|
||||
cy.clickMenu('all-campaigns');
|
||||
cy.wait(100);
|
||||
|
||||
// Verify the newly created row.
|
||||
cy.get('tbody td[data-label="Name"]').first().contains(`clone${n}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Searches campaigns', () => {
|
||||
cy.get('input[name=query]').clear().type('clone2{enter}');
|
||||
cy.get('tbody tr').its('length').should('eq', 1);
|
||||
cy.get('tbody td[data-label="Name"]').first().contains('clone2');
|
||||
cy.get('input[name=query]').clear().type('{enter}');
|
||||
});
|
||||
|
||||
it('Deletes campaigns', () => {
|
||||
cy.wait(1000);
|
||||
// Delete all visible lists.
|
||||
cy.get('tbody tr').each(() => {
|
||||
cy.get('tbody a[data-cy=btn-delete]').first().click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
});
|
||||
|
||||
// Confirm deletion.
|
||||
cy.get('table tr.is-empty');
|
||||
});
|
||||
|
||||
it('Adds new campaigns', () => {
|
||||
const lists = [[1], [1, 2]];
|
||||
const cTypes = ['richtext', 'html', 'markdown', 'plain', 'visual'];
|
||||
|
||||
let n = 0;
|
||||
cTypes.forEach((c) => {
|
||||
lists.forEach((l) => {
|
||||
// Click the 'new button'
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.wait(100);
|
||||
|
||||
// Fill fields.
|
||||
cy.get('input[name=name]').clear().type(`name${n}`);
|
||||
cy.get('input[name=subject]').clear().type(`subject${n}`);
|
||||
|
||||
l.forEach(() => {
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').first().click();
|
||||
});
|
||||
|
||||
// Add tags.
|
||||
for (let i = 0; i < 3; i++) {
|
||||
cy.get('input[name=tags]').type(`tag${i}{enter}`);
|
||||
}
|
||||
|
||||
// Add headers.
|
||||
cy.get('[data-cy=btn-headers]').click();
|
||||
cy.get('textarea[name=headers]').invoke('val', `[{"X-Header-${n}": "Value-${n}"}]`).trigger('input');
|
||||
|
||||
// Hit 'Continue'.
|
||||
cy.get('button[data-cy=btn-continue]').click();
|
||||
cy.wait(250);
|
||||
|
||||
// Verify the changes.
|
||||
(function (n) {
|
||||
cy.location('pathname').then((p) => {
|
||||
cy.request(`${apiUrl}/api/campaigns/${p.split('/').at(-1).replace(/#.*/, '')}`).should((response) => {
|
||||
const { data } = response.body;
|
||||
expect(data.status).to.equal('draft');
|
||||
expect(data.name).to.equal(`name${n}`);
|
||||
expect(data.subject).to.equal(`subject${n}`);
|
||||
expect(data.content_type).to.equal('richtext');
|
||||
expect(data.altbody).to.equal(null);
|
||||
expect(data.send_at).to.equal(null);
|
||||
expect(data.headers[0][`X-Header-${n}`]).to.equal(`Value-${n}`);
|
||||
});
|
||||
});
|
||||
}(n));
|
||||
|
||||
// Select content type.
|
||||
cy.get('select[name=content_type]').select(c);
|
||||
|
||||
// Insert content.
|
||||
const htmlBody = `<strong>hello${n}</strong> \{\{ .Subscriber.Name \}\} from {\{ .Subscriber.Attribs.city \}\}`;
|
||||
const plainBody = `hello${n} Demo Subscriber from Bengaluru`;
|
||||
const markdownBody = `**hello${n}** Demo Subscriber from Bengaluru`;
|
||||
|
||||
cy.log(`format = ${c}`);
|
||||
if (c === 'richtext') {
|
||||
cy.window().then((win) => {
|
||||
win.tinymce.editors[0].setContent(htmlBody);
|
||||
win.tinymce.editors[0].save();
|
||||
});
|
||||
cy.wait(500);
|
||||
} else if (c === 'html') {
|
||||
// Use CodeMirror 6 via the Vue component's editor instance.
|
||||
cy.get('.code-editor').then(($el) => {
|
||||
const view = $el[0].__vue__.editor;
|
||||
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: htmlBody } });
|
||||
});
|
||||
} else if (c === 'markdown') {
|
||||
cy.get('.code-editor').then(($el) => {
|
||||
const view = $el[0].__vue__.editor;
|
||||
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: markdownBody } });
|
||||
});
|
||||
} else if (c === 'plain') {
|
||||
cy.get('textarea[name=content]').invoke('val', plainBody).trigger('input');
|
||||
} else if (c === 'visual') {
|
||||
cy.wait(200);
|
||||
cy.get('iframe').then((el) => {
|
||||
cy.wait(200);
|
||||
cy.wrap(el.contents()).find('table td').click();
|
||||
cy.wait(200);
|
||||
cy.wrap(el.contents()).find('textarea').eq(0).type(plainBody);
|
||||
});
|
||||
}
|
||||
|
||||
// Save.
|
||||
cy.get('button[data-cy=btn-save]').click();
|
||||
|
||||
// Verify the preview content via the API (the iframe is sandboxed).
|
||||
if (c !== 'plain') {
|
||||
cy.location('pathname').then((p) => {
|
||||
cy.request(`${apiUrl}/api/campaigns/${p.split('/').at(-1).replace(/#.*/, '')}/preview`).then((resp) => {
|
||||
const text = resp.body.replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/\s+/g, ' ').trim();
|
||||
expect(text).to.contain(plainBody);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cy.clickMenu('all-campaigns');
|
||||
cy.wait(500);
|
||||
|
||||
// Verify the newly created campaign in the table.
|
||||
cy.get('tbody td[data-label="Name"]').first().contains(`name${n}`);
|
||||
cy.get('tbody td[data-label="Name"]').first().contains(`subject${n}`);
|
||||
cy.get('tbody td[data-label="Lists"]').first().then(($el) => {
|
||||
cy.wrap($el).find('li').should('have.length', l.length);
|
||||
});
|
||||
|
||||
n++;
|
||||
});
|
||||
});
|
||||
|
||||
// Fetch the campaigns API and verfiy the values that couldn't be verified on the table UI.
|
||||
cy.request(`${apiUrl}/api/campaigns?order=asc&order_by=created_at`).should((response) => {
|
||||
const { data } = response.body;
|
||||
expect(data.total).to.equal(lists.length * cTypes.length);
|
||||
|
||||
let n = 0;
|
||||
cTypes.forEach((c) => {
|
||||
lists.forEach((l) => {
|
||||
expect(data.results[n].content_type).to.equal(c);
|
||||
expect(data.results[n].lists.map((ls) => ls.id)).to.deep.equal(l);
|
||||
n++;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Starts and cancels campaigns', () => {
|
||||
for (let n = 1; n <= 2; n++) {
|
||||
cy.get(`tbody tr:nth-child(${n}) [data-cy=btn-start]`).click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.wait(250);
|
||||
cy.get(`tbody tr:nth-child(${n}) td[data-label=Status] .tag.running`);
|
||||
|
||||
if (n > 1) {
|
||||
cy.get(`tbody tr:nth-child(${n}) [data-cy=btn-cancel]`).click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.wait(250);
|
||||
cy.get(`tbody tr:nth-child(${n}) td[data-label=Status] .tag.cancelled`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('Sorts campaigns', () => {
|
||||
const asc = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
|
||||
const desc = [14, 13, 12, 11, 10, 9, 8, 7, 6, 5];
|
||||
const cases = ['cy-name', 'cy-timestamp'];
|
||||
|
||||
cases.forEach((c) => {
|
||||
cy.sortTable(`thead th.${c}`, asc);
|
||||
cy.wait(250);
|
||||
cy.sortTable(`thead th.${c}`, desc);
|
||||
cy.wait(250);
|
||||
});
|
||||
});
|
||||
|
||||
it('Bulk deletes campaigns', () => {
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
const params = {
|
||||
name: 'test-campaign-bulk',
|
||||
subject: 'subject',
|
||||
type: 'regular',
|
||||
content_type: 'richtext',
|
||||
template_id: 1,
|
||||
lists: [1],
|
||||
};
|
||||
|
||||
// Create 30 in a loop.
|
||||
for (let i = 0; i < 30; i += 1) {
|
||||
cy.request('POST', `${apiUrl}/api/campaigns`, params);
|
||||
}
|
||||
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
|
||||
// Bulk delete with the `all` flag.
|
||||
cy.window().scrollTo('top');
|
||||
cy.wait(500);
|
||||
cy.get('thead input[type="checkbox"]').click({ force: true });
|
||||
cy.get('a[data-cy=select-all-campaigns]').click();
|
||||
cy.get('a[data-cy=btn-delete-campaigns]').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.get('table tr.is-empty');
|
||||
|
||||
// Bulk delete with the selected IDs.
|
||||
// Create 5 campaigns in a loop.
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
cy.request('POST', `${apiUrl}/api/campaigns`, params);
|
||||
}
|
||||
|
||||
cy.visit('/admin/campaigns');
|
||||
cy.wait(500);
|
||||
cy.get('thead input[type="checkbox"]').click({ force: true });
|
||||
cy.get('a[data-cy=btn-delete-campaigns]').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.get('table tr.is-empty');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Campaign image embed', () => {
|
||||
const mailhog = 'http://localhost:8025';
|
||||
const logoUrl = `${apiUrl}/uploads/logo.png`;
|
||||
|
||||
// Create a campaign of the given content type via the API and open its content tab.
|
||||
const newCampaign = (name, contentType) => cy.request('POST', `${apiUrl}/api/campaigns`, {
|
||||
name, subject: name, type: 'regular', content_type: contentType, lists: [1],
|
||||
}).then((r) => cy.visit(`/admin/campaigns/${r.body.data.id}#content`).then(() => r.body.data.id));
|
||||
|
||||
// Start the campaign and assert (via the MailHog API) whether the delivered e-mail
|
||||
// embedded the image as a MIME/CID part (embed=true) or left it as a plain URL.
|
||||
const send = (id, name, embed) => {
|
||||
cy.request('PUT', `${apiUrl}/api/campaigns/${id}/status`, { status: 'running' });
|
||||
cy.waitUntil(() => cy.request(`${mailhog}/api/v2/search?kind=containing&query=${name}`)
|
||||
.then((r) => r.body.total > 0), { timeout: 20000, interval: 1000 });
|
||||
cy.request(`${mailhog}/api/v2/search?kind=containing&query=${name}`).then((r) => {
|
||||
const raw = JSON.stringify(r.body.items[0]);
|
||||
expect(/image\/png/.test(raw), 'has inline image part').to.equal(embed);
|
||||
expect(/cid:/.test(raw), 'references cid').to.equal(embed);
|
||||
});
|
||||
};
|
||||
|
||||
// Open the image dialog's media picker and upload/select image.
|
||||
const pickLogo = (upload) => {
|
||||
cy.window().then((win) => win.tinymce.editors[0].execCommand('mceImage'));
|
||||
cy.get('.tox-dialog .tox-browse-url').click();
|
||||
if (upload) {
|
||||
cy.get('[data-cy=btn-toggle-upload]').click();
|
||||
cy.get('input[type=file]').attachFile('logo.png');
|
||||
cy.get('form[data-cy="upload"] button').click();
|
||||
}
|
||||
cy.get('.modal a.thumb-link', { timeout: 10000 }).first().click();
|
||||
cy.get('.tox-dialog input[type=url]').should('have.value', logoUrl);
|
||||
};
|
||||
|
||||
const richtext = (name, embed, upload) => {
|
||||
let id;
|
||||
newCampaign(name, 'richtext').then((cid) => { id = cid; });
|
||||
cy.window().its('tinymce.editors.0').should('exist');
|
||||
pickLogo(upload);
|
||||
if (embed) {
|
||||
cy.get('.tox-dialog input[type=checkbox]').check({ force: true });
|
||||
}
|
||||
cy.get('.tox-dialog__footer button').last().click();
|
||||
cy.get('button[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
|
||||
// The 'embed?' selection is stored on the <img data-embed> and must persist across reloads.
|
||||
cy.then(() => cy.visit(`/admin/campaigns/${id}#content`));
|
||||
cy.window().its('tinymce.editors.0').should('exist');
|
||||
cy.window().then((win) => {
|
||||
const ed = win.tinymce.editors[0];
|
||||
ed.selection.select(ed.getBody().querySelector('img'));
|
||||
ed.execCommand('mceImage');
|
||||
});
|
||||
cy.get('.tox-dialog input[type=checkbox]').should(embed ? 'be.checked' : 'not.be.checked');
|
||||
cy.get('.tox-dialog__footer button').first().click();
|
||||
|
||||
cy.then(() => send(id, name, embed));
|
||||
};
|
||||
|
||||
const visual = (name, embed) => {
|
||||
let id;
|
||||
newCampaign(name, 'visual').then((cid) => { id = cid; });
|
||||
|
||||
// Add an image block pointing at the uploaded image.
|
||||
cy.waitUntil(() => cy.get('#visual-editor')
|
||||
.then(($f) => Boolean($f[0].contentWindow.EmailBuilder
|
||||
&& $f[0].contentWindow.EmailBuilder.isRendered('visual-editor-container'))));
|
||||
cy.get('#visual-editor').then(($f) => $f[0].contentWindow.EmailBuilder.resetDocument({
|
||||
root: { type: 'EmailLayout', data: { childrenIds: ['blk'] } },
|
||||
blk: { type: 'Image', data: { props: { url: logoUrl, embed: false }, style: {} } },
|
||||
}));
|
||||
cy.wait(500);
|
||||
|
||||
// Select image and check 'embed?' checkbox.
|
||||
const clickImage = () => cy.get('#visual-editor')
|
||||
.then(($f) => $f[0].contentDocument.querySelector('#visual-editor-container img').click());
|
||||
clickImage();
|
||||
cy.wait(300);
|
||||
if (embed) {
|
||||
cy.get('#visual-editor').then(($f) => $f[0].contentDocument.querySelector('input[type=checkbox]').click());
|
||||
cy.wait(300);
|
||||
}
|
||||
cy.get('button[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
|
||||
// Reload and confirm checkbox peristence.
|
||||
cy.then(() => cy.visit(`/admin/campaigns/${id}#content`));
|
||||
cy.waitUntil(() => cy.get('#visual-editor')
|
||||
.then(($f) => Boolean($f[0].contentDocument.querySelector('#visual-editor-container img'))));
|
||||
clickImage();
|
||||
cy.wait(300);
|
||||
cy.get('#visual-editor').then(($f) => {
|
||||
expect($f[0].contentDocument.querySelector('input[type=checkbox]').checked).to.equal(embed);
|
||||
});
|
||||
|
||||
cy.then(() => send(id, name, embed));
|
||||
};
|
||||
|
||||
it('Resets DB and MailHog', () => {
|
||||
cy.resetDB();
|
||||
cy.request('DELETE', `${mailhog}/api/v1/messages`);
|
||||
cy.loginAndVisit('/admin/campaigns');
|
||||
});
|
||||
|
||||
it('Embeds a rich text image as a MIME/CID part', () => richtext('embedrich', true, true));
|
||||
it('Sends a rich text image as a plain URL', () => richtext('plainrich', false, false));
|
||||
it('Embeds a visual image as a MIME/CID part', () => visual('embedvis', true));
|
||||
it('Sends a visual image as a plain URL', () => visual('plainvis', false));
|
||||
});
|
||||
@@ -0,0 +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]')
|
||||
.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] .title').contains('1');
|
||||
cy.get('[data-cy=campaigns-draft]').contains('1');
|
||||
|
||||
// Subscriber counts.
|
||||
cy.get('[data-cy=subscribers] .title').contains('2');
|
||||
cy.get('[data-cy=subscribers]')
|
||||
.should('contain', '0 Blocklisted')
|
||||
.and('contain', '0 Orphans');
|
||||
|
||||
// Message count.
|
||||
cy.get('[data-cy=messages] .title').contains('0');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,127 @@
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
describe('Forms', () => {
|
||||
it('Opens forms page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/lists/forms');
|
||||
});
|
||||
|
||||
it('Checks public lists', () => {
|
||||
cy.get('ul[data-cy=lists] li')
|
||||
.should('contain', 'Opt-in list')
|
||||
.its('length')
|
||||
.should('eq', 1);
|
||||
|
||||
cy.get('[data-cy=form] [role=textbox]').should('not.exist');
|
||||
});
|
||||
|
||||
it('Selects public list', () => {
|
||||
// Click the list checkbox.
|
||||
cy.get('ul[data-cy=lists] .checkbox').click();
|
||||
|
||||
// Check that the ID of the list in the checkbox appears in the HTML.
|
||||
cy.get('ul[data-cy=lists] input').then(($inp) => {
|
||||
cy.get('[role=textbox]').contains($inp.val());
|
||||
});
|
||||
|
||||
// Click the list checkbox.
|
||||
cy.get('ul[data-cy=lists] .checkbox').click();
|
||||
cy.get('[data-cy=form] pre').should('not.exist');
|
||||
});
|
||||
|
||||
it('Subscribes from public form page', () => {
|
||||
// Create a public test list.
|
||||
cy.request('POST', `${apiUrl}/api/lists`, { name: 'test-list', type: 'public', optin: 'single' });
|
||||
|
||||
// Open the public page and subscribe to alternating lists multiple times.
|
||||
// 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(`${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();
|
||||
cy.get('button').click();
|
||||
cy.wait(250);
|
||||
cy.get('.wrap').contains(/has been sent|successfully|retry/); // If SMTP is not configured, it shows retry message.
|
||||
}
|
||||
}
|
||||
|
||||
// Verify form subscriptions.
|
||||
cy.request(`${apiUrl}/api/subscribers`).should((response) => {
|
||||
const { data } = response.body;
|
||||
|
||||
// Two new + two dummy subscribers that are there by default.
|
||||
expect(data.total).to.equal(4);
|
||||
|
||||
// The two new subscribers should each have two list subscriptions.
|
||||
for (let i = 0; i < 2; i++) {
|
||||
expect(data.results.find((s) => s.email === `test${i}@test.com`).lists.length).to.equal(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Unsubscribes', () => {
|
||||
// Add all lists to the dummy campaign.
|
||||
cy.request('PUT', `${apiUrl}/api/campaigns/1`, { lists: [2] });
|
||||
|
||||
cy.request('GET', `${apiUrl}/api/subscribers`).then((response) => {
|
||||
const subUUID = response.body.data.results[0].uuid;
|
||||
|
||||
cy.request('GET', `${apiUrl}/api/campaigns`).then((response) => {
|
||||
const campUUID = response.body.data.results[0].uuid;
|
||||
cy.loginAndVisit(`${apiUrl}/subscription/${campUUID}/${subUUID}`);
|
||||
});
|
||||
});
|
||||
|
||||
cy.wait(500);
|
||||
|
||||
// Unsubscribe from one list.
|
||||
cy.get('button').click();
|
||||
cy.request('GET', `${apiUrl}/api/subscribers`).then((response) => {
|
||||
const { data } = response.body;
|
||||
expect(data.results[0].lists.find((s) => s.id === 2).subscription_status).to.equal('unsubscribed');
|
||||
expect(data.results[0].lists.find((s) => s.id === 3).subscription_status).to.equal('unconfirmed');
|
||||
});
|
||||
|
||||
// Go back.
|
||||
cy.url().then((u) => {
|
||||
cy.loginAndVisit(u);
|
||||
});
|
||||
|
||||
// Unsubscribe from all.
|
||||
cy.get('#privacy-blocklist').click();
|
||||
cy.get('button').click();
|
||||
|
||||
cy.request('GET', `${apiUrl}/api/subscribers`).then((response) => {
|
||||
const { data } = response.body;
|
||||
expect(data.results[0].status).to.equal('blocklisted');
|
||||
expect(data.results[0].lists.find((s) => s.id === 2).subscription_status).to.equal('unsubscribed');
|
||||
expect(data.results[0].lists.find((s) => s.id === 3).subscription_status).to.equal('unsubscribed');
|
||||
});
|
||||
});
|
||||
|
||||
it('Manages subscription preferences', () => {
|
||||
cy.request('GET', `${apiUrl}/api/subscribers`).then((response) => {
|
||||
const subUUID = response.body.data.results[1].uuid;
|
||||
|
||||
cy.request('GET', `${apiUrl}/api/campaigns`).then((response) => {
|
||||
const campUUID = response.body.data.results[0].uuid;
|
||||
cy.loginAndVisit(`${apiUrl}/subscription/${campUUID}/${subUUID}?manage=1`);
|
||||
cy.get('a').contains('Manage').click();
|
||||
});
|
||||
});
|
||||
|
||||
// Change name and unsubscribe from one list.
|
||||
cy.get('input[name=name]').clear().type('new-name');
|
||||
cy.get('ul.lists input:first').click();
|
||||
cy.get('button:first').click();
|
||||
|
||||
cy.request('GET', `${apiUrl}/api/subscribers`).then((response) => {
|
||||
const { data } = response.body;
|
||||
expect(data.results[1].name).to.equal('new-name');
|
||||
expect(data.results[1].lists.find((s) => s.id === 2).subscription_status).to.equal('unsubscribed');
|
||||
expect(data.results[1].lists.find((s) => s.id === 3).subscription_status).to.equal('unconfirmed');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
describe('Import', () => {
|
||||
it('Opens import page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/subscribers/import');
|
||||
});
|
||||
|
||||
it('Imports subscribers', () => {
|
||||
const cases = [
|
||||
{
|
||||
chkMode: 'subscribe', status: 'enabled', chkSubStatus: 'unconfirmed', subStatus: 'unconfirmed', overwrite: true, count: 102,
|
||||
},
|
||||
{
|
||||
chkMode: 'subscribe', status: 'enabled', chkSubStatus: 'confirmed', subStatus: 'confirmed', overwrite: true, count: 102,
|
||||
},
|
||||
{
|
||||
chkMode: 'subscribe', status: 'enabled', chkSubStatus: 'unconfirmed', subStatus: 'confirmed', overwrite: false, count: 102,
|
||||
},
|
||||
{
|
||||
chkMode: 'blocklist', status: 'blocklisted', chkSubStatus: 'unsubscribed', subStatus: 'unsubscribed', overwrite: false, count: 102,
|
||||
},
|
||||
];
|
||||
|
||||
cases.forEach((c) => {
|
||||
cy.get(`[data-cy=check-${c.chkMode}] .check`).click();
|
||||
cy.get(`[data-cy=check-${c.chkSubStatus}] .check`).click();
|
||||
|
||||
if (c.overwrite) {
|
||||
cy.get('[data-cy=overwrite-user-info]').click();
|
||||
cy.get('[data-cy=overwrite-sub-status]').click();
|
||||
}
|
||||
|
||||
if (c.status === 'enabled') {
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').first().click();
|
||||
}
|
||||
|
||||
cy.fixture('subs.csv').then((data) => {
|
||||
cy.get('input[type="file"]').attachFile({
|
||||
fileContent: data.toString(),
|
||||
fileName: 'subs.csv',
|
||||
mimeType: 'text/csv',
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('button.is-primary').click();
|
||||
|
||||
// ONLY if .modal button.is-primary is present, click it.
|
||||
if (c.overwrite) {
|
||||
cy.get('.modal button.is-primary').click();
|
||||
}
|
||||
|
||||
cy.get('section.wrap .has-text-success');
|
||||
cy.get('button.is-primary').click();
|
||||
cy.wait(100);
|
||||
|
||||
// Verify that 100 (+2 default) subs are imported.
|
||||
cy.loginAndVisit('/admin/subscribers');
|
||||
cy.wait(100);
|
||||
cy.get('[data-cy=count]').then(($el) => {
|
||||
cy.expect(parseInt($el.text().trim())).to.equal(c.count);
|
||||
});
|
||||
|
||||
// Subscription status.
|
||||
// cy.get('tbody td[data-label=E-mail]').each(($el) => {
|
||||
// cy.wrap($el).find(`.tag.${c.subStatus}`);
|
||||
// });
|
||||
|
||||
cy.loginAndVisit('/admin/subscribers/import');
|
||||
cy.wait(100);
|
||||
});
|
||||
});
|
||||
|
||||
it('Imports subscribers incorrectly', () => {
|
||||
cy.wait(1000);
|
||||
cy.resetDB();
|
||||
cy.wait(1000);
|
||||
cy.loginAndVisit('/admin/subscribers/import');
|
||||
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').first().click();
|
||||
cy.get('input[name=delim]').clear().type('|');
|
||||
|
||||
cy.fixture('subs.csv').then((data) => {
|
||||
cy.get('input[type="file"]').attachFile({
|
||||
fileContent: data.toString(),
|
||||
fileName: 'subs.csv',
|
||||
mimeType: 'text/csv',
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('button.is-primary').click();
|
||||
cy.wait(250);
|
||||
cy.get('section.wrap .has-text-danger');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
describe('Lists', () => {
|
||||
it('Opens lists page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/lists');
|
||||
});
|
||||
|
||||
it('Counts subscribers in default lists', () => {
|
||||
cy.get('tbody td[data-label=Subscribers]').contains('1');
|
||||
});
|
||||
|
||||
it('Creates campaign for list', () => {
|
||||
cy.get('tbody a[data-cy=btn-campaign]').first().click();
|
||||
cy.location('pathname').should('contain', '/campaigns/new');
|
||||
cy.get('.list-tags .tag').contains('Default list');
|
||||
|
||||
cy.clickMenu('lists', 'all-lists');
|
||||
cy.get('.modal button.is-primary').click();
|
||||
});
|
||||
|
||||
it('Creates opt-in campaign for list', () => {
|
||||
cy.get('tbody a[data-cy=btn-send-optin-campaign]').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.location('pathname').should('contain', '/campaigns/2');
|
||||
cy.clickMenu('lists', 'all-lists');
|
||||
});
|
||||
|
||||
it('Checks individual subscribers in lists', () => {
|
||||
const subs = [{ listID: 1, email: 'john@example.com' },
|
||||
{ listID: 2, email: 'anon@example.com' }];
|
||||
|
||||
// Click on each list on the lists page, go the subscribers page
|
||||
// for that list, and check the subscriber details.
|
||||
subs.forEach((s, n) => {
|
||||
cy.get('tbody td[data-label=Subscribers] a').eq(n).click();
|
||||
cy.location('pathname').should('contain', `/subscribers/lists/${s.listID}`);
|
||||
cy.get('tbody tr').its('length').should('eq', 1);
|
||||
cy.get('tbody td[data-label="E-mail"]').contains(s.email);
|
||||
cy.clickMenu('lists', 'all-lists');
|
||||
});
|
||||
});
|
||||
|
||||
it('Edits lists', () => {
|
||||
// Open the edit popup and edit the default lists.
|
||||
cy.get('[data-cy=btn-edit]').each(($el, n) => {
|
||||
cy.wrap($el).click();
|
||||
cy.get('input[name=name]').clear().type(`list-${n}`);
|
||||
cy.get('select[name=type]').select('public');
|
||||
cy.get('select[name=optin]').select('double');
|
||||
cy.get('input[name=tags]').clear().type(`tag${n}{enter}`);
|
||||
cy.get('textarea[name=description]').clear().type(`desc${n}`);
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(100);
|
||||
});
|
||||
cy.wait(250);
|
||||
|
||||
// Confirm the edits.
|
||||
cy.get('tbody tr').each(($el, n) => {
|
||||
cy.wrap($el).find('td[data-label=Name]').contains(`list-${n}`);
|
||||
cy.wrap($el).find('.tags')
|
||||
.should('contain', 'test')
|
||||
.and('contain', `tag${n}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('Deletes lists', () => {
|
||||
// Delete all visible lists.
|
||||
cy.get('tbody tr').each(() => {
|
||||
cy.get('tbody a[data-cy=btn-delete]').first().click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
});
|
||||
|
||||
// Confirm deletion.
|
||||
cy.get('table tr.is-empty');
|
||||
});
|
||||
|
||||
// Add new lists.
|
||||
it('Adds new lists', () => {
|
||||
// Open the list form and create lists of multiple type/optin combinations.
|
||||
const types = ['private', 'public'];
|
||||
const optin = ['single', 'double'];
|
||||
|
||||
let n = 0;
|
||||
types.forEach((t) => {
|
||||
optin.forEach((o) => {
|
||||
const name = `list-${t}-${o}-${n}`;
|
||||
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.get('input[name=name]').type(name);
|
||||
cy.get('select[name=type]').select(t);
|
||||
cy.get('select[name=optin]').select(o);
|
||||
cy.get('input[name=tags]').type(`tag${n}{enter}${t}{enter}${o}{enter}`);
|
||||
cy.get('textarea[name=description]').clear().type(`desc-${t}-${n}`);
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(200);
|
||||
|
||||
// Confirm the addition by inspecting the newly created list row.
|
||||
const tr = `tbody tr:nth-child(${n + 1})`;
|
||||
cy.get(`${tr} td[data-label=Name]`).contains(name);
|
||||
cy.get(`${tr} td[data-label=Type] .tag[data-cy=type-${t}]`);
|
||||
cy.get(`${tr} td[data-label=Type] .tag[data-cy=optin-${o}]`);
|
||||
n++;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Searches lists', () => {
|
||||
cy.get('[data-cy=query]').clear().type('list-public-single-2{enter}');
|
||||
cy.wait(200);
|
||||
cy.get('tbody tr').its('length').should('eq', 1);
|
||||
cy.get('tbody td[data-label="Name"]').first().contains('list-public-single-2');
|
||||
cy.get('[data-cy=query]').clear().type('{enter}');
|
||||
});
|
||||
|
||||
// Sort lists by clicking on various headers. At this point, there should be four
|
||||
// lists with IDs = [3, 4, 5, 6]. Sort the items be columns and match them with
|
||||
// the expected order of IDs.
|
||||
it('Sorts lists', () => {
|
||||
cy.sortTable('thead th.cy-name', [4, 3, 6, 5]);
|
||||
cy.sortTable('thead th.cy-name', [5, 6, 3, 4]);
|
||||
|
||||
cy.sortTable('thead th.cy-type', [3, 4, 5, 6]);
|
||||
cy.sortTable('thead th.cy-type', [6, 5, 4, 3]);
|
||||
|
||||
cy.sortTable('thead th.cy-created_at', [3, 4, 5, 6]);
|
||||
cy.sortTable('thead th.cy-created_at', [6, 5, 4, 3]);
|
||||
|
||||
cy.sortTable('thead th.cy-updated_at', [3, 4, 5, 6]);
|
||||
cy.sortTable('thead th.cy-updated_at', [6, 5, 4, 3]);
|
||||
});
|
||||
|
||||
it('Opens forms page', () => {
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
cy.loginAndVisit(`${apiUrl}/subscription/form`);
|
||||
cy.get('ul li').its('length').should('eq', 2);
|
||||
|
||||
const cases = [
|
||||
{ name: 'list-public-single-2', description: 'desc-public-2' },
|
||||
{ name: 'list-public-double-3', description: 'desc-public-3' },
|
||||
];
|
||||
|
||||
cases.forEach((c, n) => {
|
||||
cy.get('ul li').eq(n).then(($el) => {
|
||||
cy.wrap($el).get('label').contains(c.name);
|
||||
cy.wrap($el).get('.description').contains(c.description);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Bulk deletes lists', () => {
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
// Create 30 in a loop.
|
||||
for (let i = 0; i < 30; i += 1) {
|
||||
cy.request('POST', `${apiUrl}/api/lists`, { name: `test-list-${i}`, type: 'public', optin: 'single' });
|
||||
}
|
||||
|
||||
cy.loginAndVisit('/admin/lists');
|
||||
|
||||
// Bulk delete with the `all` flag.
|
||||
cy.window().scrollTo('top');
|
||||
cy.wait(500);
|
||||
cy.get('thead input[type="checkbox"]').click({ force: true });
|
||||
cy.get('a[data-cy=select-all-lists]').click();
|
||||
cy.get('a[data-cy=btn-delete-lists]').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.get('table tr.is-empty');
|
||||
|
||||
// Bulk delete with the selected IDs.
|
||||
// Create 5 lists in a loop.
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
cy.request('POST', `${apiUrl}/api/lists`, { name: `test-list-bulk-${i}`, type: 'public', optin: 'single' });
|
||||
}
|
||||
|
||||
cy.visit('/admin/lists');
|
||||
cy.wait(500);
|
||||
cy.get('thead input[type="checkbox"]').click({ force: true });
|
||||
cy.get('a[data-cy=btn-delete-lists]').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.get('table tr.is-empty');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
const rootURL = 'http://127.0.0.1:9000';
|
||||
const faveURL = 'http://127.0.0.1:9000/public/static/logo.png';
|
||||
|
||||
describe('Settings', () => {
|
||||
it('Opens settings page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/settings');
|
||||
});
|
||||
|
||||
it('Changes some settings', () => {
|
||||
cy.get('.b-tabs nav a').eq(0).click();
|
||||
|
||||
cy.get('input[name="app.root_url"]').clear().type(rootURL);
|
||||
cy.get('input[name="app.favicon_url"]').type(faveURL);
|
||||
cy.get('.b-tabs nav a').eq(1).click();
|
||||
cy.get('.tab-item:visible').find('.field').first()
|
||||
.find('button')
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Enable / disable SMTP and delete one.
|
||||
cy.get('.b-tabs nav a').eq(5).click();
|
||||
cy.get('.tab-item:visible [data-cy=btn-enable-smtp]').eq(1).click();
|
||||
cy.get('.tab-item:visible [data-cy=btn-delete-smtp]').first().click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
|
||||
cy.waitForBackend();
|
||||
});
|
||||
|
||||
it('Verify settings change', () => {
|
||||
// Verify the changes.
|
||||
cy.request(`${apiUrl}/api/settings`).should((response) => {
|
||||
const { data } = response.body;
|
||||
expect(data['app.root_url']).to.equal(rootURL);
|
||||
expect(data['app.favicon_url']).to.equal(faveURL);
|
||||
expect(data['app.concurrency']).to.equal(9);
|
||||
|
||||
expect(data.smtp.length).to.equal(1);
|
||||
expect(data.smtp[0].enabled).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,370 @@
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
describe('Subscribers', () => {
|
||||
it('Opens subscribers page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/subscribers');
|
||||
});
|
||||
|
||||
it('Counts subscribers', () => {
|
||||
cy.get('tbody td[data-label=E-mail]').its('length').should('eq', 2);
|
||||
});
|
||||
|
||||
it('Searches subscribers', () => {
|
||||
const cases = [
|
||||
{ value: 'john{enter}', count: 1, contains: 'john@example.com' },
|
||||
{ value: 'anon{enter}', count: 1, contains: 'anon@example.com' },
|
||||
{ value: '{enter}', count: 2, contains: null },
|
||||
];
|
||||
|
||||
cases.forEach((c) => {
|
||||
cy.get('[data-cy=search]').clear().type(c.value);
|
||||
cy.get('tbody td[data-label=E-mail]').its('length').should('eq', c.count);
|
||||
if (c.contains) {
|
||||
cy.get('tbody td[data-label=E-mail]').contains(c.contains);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Exports subscribers', () => {
|
||||
const cases = [
|
||||
{
|
||||
listIDs: [], ids: [], query: '', length: 3,
|
||||
},
|
||||
{
|
||||
listIDs: [], ids: [], query: "name ILIKE '%anon%'", length: 2,
|
||||
},
|
||||
{
|
||||
listIDs: [], ids: [], query: "name like 'nope'", length: 1,
|
||||
},
|
||||
];
|
||||
|
||||
// listIDs[] and ids[] are unused for now as Cypress doesn't support encoding of arrays in `qs`.
|
||||
cases.forEach((c) => {
|
||||
cy.request({ url: `${apiUrl}/api/subscribers/export`, qs: { query: c.query, list_id: c.listIDs, id: c.ids } }).then((resp) => {
|
||||
cy.expect(resp.body.trim().split('\n')).to.have.lengthOf(c.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Advanced searches subscribers', () => {
|
||||
cy.get('[data-cy=btn-advanced-search]').click();
|
||||
|
||||
const cases = [
|
||||
{ value: 'subscribers.attribs->>\'city\'=\'Bengaluru\'', count: 2 },
|
||||
{ value: 'subscribers.attribs->>\'city\'=\'Bengaluru\' AND id=1', count: 1 },
|
||||
{ value: '(subscribers.attribs->>\'good\')::BOOLEAN = true AND name like \'Anon%\'', count: 1 },
|
||||
];
|
||||
|
||||
cases.forEach((c) => {
|
||||
cy.get('[data-cy=query]').clear().type(c.value);
|
||||
cy.get('[data-cy=btn-query]').click();
|
||||
cy.get('tbody td[data-label=E-mail]').its('length').should('eq', c.count);
|
||||
});
|
||||
|
||||
cy.get('[data-cy=btn-query-reset]').click();
|
||||
cy.wait(1000);
|
||||
cy.get('tbody td[data-label=E-mail]').its('length').should('eq', 2);
|
||||
});
|
||||
|
||||
it('Does bulk subscriber list add and remove', () => {
|
||||
const cases = [
|
||||
// radio: action to perform, rows: table rows to select and perform on: [expected statuses of those rows after thea action]
|
||||
{ radio: 'check-list-add', lists: [0, 1], rows: { 0: ['confirmed', 'confirmed'] } },
|
||||
{ radio: 'check-list-unsubscribe', lists: [0, 1], rows: { 0: ['unsubscribed', 'unsubscribed'], 1: ['unsubscribed'] } },
|
||||
{ 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: ['unconfirmed', 'unsubscribed'] } },
|
||||
];
|
||||
|
||||
cases.forEach((c, n) => {
|
||||
// Select one of the 2 subscribers in the table.
|
||||
Object.keys(c.rows).forEach((r) => {
|
||||
cy.get('tbody td.checkbox-cell .checkbox').eq(r).click();
|
||||
});
|
||||
|
||||
// Open the 'manage lists' modal.
|
||||
cy.get('[data-cy=btn-manage-lists]').click();
|
||||
|
||||
// Check both lists in the modal.
|
||||
c.lists.forEach((l) => {
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').first().click();
|
||||
});
|
||||
|
||||
// Select the radio option in the modal.
|
||||
cy.get(`[data-cy=${c.radio}] .check`).click();
|
||||
|
||||
// For the first test, check the optin preconfirm box.
|
||||
if (n === 0) {
|
||||
cy.get('[data-cy=preconfirm]').click();
|
||||
}
|
||||
|
||||
// Save.
|
||||
cy.get('.modal button.is-primary').click();
|
||||
|
||||
// Check the status of the lists on the subscriber.
|
||||
Object.keys(c.rows).forEach((r) => {
|
||||
cy.get('tbody td[data-label=E-mail]').eq(r).find('.tags').then(($el) => {
|
||||
cy.wrap($el).find('.tag').should('have.length', c.rows[r].length);
|
||||
c.rows[r].forEach((status, n) => {
|
||||
// eg: .tag(n).unconfirmed
|
||||
cy.wrap($el).find('.tag').eq(n).should('have.class', status);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Resets subscribers page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/subscribers');
|
||||
});
|
||||
|
||||
it('Edits subscribers', () => {
|
||||
const status = ['enabled', 'blocklisted'];
|
||||
const json = '{"string": "hello", "ints": [1,2,3], "null": null, "sub": {"bool": true}}';
|
||||
|
||||
// Collect values being edited on each sub to confirm the changes in the next step
|
||||
// index by their ID shown in the modal.
|
||||
const rows = {};
|
||||
|
||||
// Open the edit popup and edit the default lists.
|
||||
cy.get('[data-cy=btn-edit]').each(($el, n) => {
|
||||
const email = `email-${n}@EMAIL.com`;
|
||||
const name = `name-${n}`;
|
||||
|
||||
// Open the edit modal.
|
||||
cy.wrap($el).click();
|
||||
|
||||
// Get the ID from the header and proceed to fill the form.
|
||||
let id = 0;
|
||||
cy.get('[data-cy=id]').then(($el) => {
|
||||
id = parseInt($el.text());
|
||||
|
||||
cy.get('input[name=email]').clear().type(email);
|
||||
cy.get('input[name=name]').clear().type(name);
|
||||
|
||||
if (status[n] === 'blocklisted') {
|
||||
cy.get('select[name=status]').select(status[n]);
|
||||
}
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').first().click();
|
||||
cy.get('textarea[name=attribs]').clear().type(json, { parseSpecialCharSequences: false, delay: 0 });
|
||||
cy.get('.modal-card-foot button[type=submit]').click();
|
||||
|
||||
rows[id] = { email, name, status: status[n] };
|
||||
});
|
||||
});
|
||||
|
||||
// Confirm the edits on the table.
|
||||
cy.wait(500);
|
||||
cy.log(rows);
|
||||
cy.get('tbody tr').each(($el) => {
|
||||
cy.wrap($el).find('td[data-id]').invoke('attr', 'data-id').then((idStr) => {
|
||||
const id = parseInt(idStr);
|
||||
cy.wrap($el).find('td[data-label=E-mail]').contains(rows[id].email.toLowerCase());
|
||||
cy.wrap($el).find('td[data-label=Name]').contains(rows[id].name);
|
||||
|
||||
if (rows[id].status === 'blocklisted') {
|
||||
cy.wrap($el).find('[data-cy=blocklisted]');
|
||||
}
|
||||
|
||||
// Both lists on the enabled sub should be 'unconfirmed' and the blocklisted one, 'unsubscribed.'
|
||||
cy.wrap($el).find(`.tags .${rows[id].status === 'enabled' ? 'unconfirmed' : 'unsubscribed'}`)
|
||||
.its('length').should('eq', 2);
|
||||
cy.wrap($el).find('td[data-label=Lists]').then((l) => {
|
||||
cy.expect(parseInt(l.text().trim())).to.equal(rows[id].status === 'blocklisted' ? 0 : 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Deletes subscribers', () => {
|
||||
// Delete all visible lists.
|
||||
cy.get('tbody tr').each(() => {
|
||||
cy.get('tbody a[data-cy=btn-delete]').first().click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
});
|
||||
|
||||
// Confirm deletion.
|
||||
cy.get('table tr.is-empty');
|
||||
});
|
||||
|
||||
it('Creates new subscribers', () => {
|
||||
const statuses = ['enabled', 'blocklisted'];
|
||||
const lists = [[1], [2], [1, 2]];
|
||||
const json = '{"string": "hello", "ints": [1,2,3], "null": null, "sub": {"bool": true}}';
|
||||
|
||||
// Cycle through each status and each list ID combination and create subscribers.
|
||||
const n = 0;
|
||||
for (let n = 0; n < 6; n++) {
|
||||
const email = `email-${n}@EMAIL.com`;
|
||||
const name = `name-${n}`;
|
||||
const status = statuses[(n + 1) % statuses.length];
|
||||
const list = lists[(n + 1) % lists.length];
|
||||
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.get('input[name=email]').type(email);
|
||||
cy.get('input[name=name]').type(name);
|
||||
cy.get('select[name=status]').select(status);
|
||||
|
||||
list.forEach((l) => {
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').first().click();
|
||||
});
|
||||
cy.get('textarea[name=attribs]').clear().type(json, { parseSpecialCharSequences: false, delay: 0 });
|
||||
cy.get('.modal-card-foot button[type=submit]').click();
|
||||
|
||||
// Confirm the addition by inspecting the newly created list row,
|
||||
// which is always the first row in the table.
|
||||
cy.wait(250);
|
||||
const tr = cy.get('tbody tr:nth-child(1)').then(($el) => {
|
||||
cy.wrap($el).find('td[data-label=E-mail]').contains(email.toLowerCase());
|
||||
cy.wrap($el).find('td[data-label=Name]').contains(name);
|
||||
|
||||
if (status === 'blocklisted') {
|
||||
cy.wrap($el).find('[data-cy=blocklisted]');
|
||||
}
|
||||
cy.wrap($el).find(`.tags .${status === 'enabled' ? 'unconfirmed' : 'unsubscribed'}`)
|
||||
.its('length').should('eq', list.length);
|
||||
cy.wrap($el).find('td[data-label=Lists]').then((l) => {
|
||||
cy.expect(parseInt(l.text().trim())).to.equal(status === 'blocklisted' ? 0 : list.length);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('Sorts subscribers', () => {
|
||||
const asc = [3, 4, 5, 6, 7, 8];
|
||||
const desc = [8, 7, 6, 5, 4, 3];
|
||||
const cases = ['cy-email', 'cy-name', 'cy-created_at', 'cy-updated_at'];
|
||||
|
||||
cases.forEach((c) => {
|
||||
cy.sortTable(`thead th.${c}`, asc);
|
||||
cy.wait(250);
|
||||
cy.sortTable(`thead th.${c}`, desc);
|
||||
cy.wait(250);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Domain blocklist', () => {
|
||||
it('Opens settings page', () => {
|
||||
cy.resetDB();
|
||||
});
|
||||
|
||||
it('Add domains to blocklist', () => {
|
||||
cy.loginAndVisit('/admin/settings');
|
||||
cy.get('.b-tabs nav a').eq(2).click();
|
||||
cy.get('textarea[name="privacy.domain_blocklist"]').clear().type('ban.net\n\nBaN.OrG\n\nban.com\n\n');
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.waitForBackend();
|
||||
});
|
||||
|
||||
it('Try subscribing via public page', () => {
|
||||
cy.visit(`${apiUrl}/subscription/form`);
|
||||
cy.get('input[name=email]').clear().type('test@noban.net');
|
||||
cy.get('button[type=submit]').click();
|
||||
cy.get('h2').contains('Subscribe');
|
||||
|
||||
cy.visit(`${apiUrl}/subscription/form`);
|
||||
cy.get('input[name=email]').clear().type('test@ban.net');
|
||||
cy.get('button[type=submit]').click();
|
||||
cy.get('h2').contains('Error');
|
||||
});
|
||||
|
||||
// Post to the admin API.
|
||||
it('Try via admin API', () => {
|
||||
cy.wait(1000);
|
||||
|
||||
// Add non-banned domain.
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${apiUrl}/api/subscribers`,
|
||||
failOnStatusCode: true,
|
||||
body: {
|
||||
email: 'test1@noban.net', name: 'test', lists: [1], status: 'enabled',
|
||||
},
|
||||
}).should((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
|
||||
// Add banned domain.
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${apiUrl}/api/subscribers`,
|
||||
failOnStatusCode: false,
|
||||
body: {
|
||||
email: 'test1@ban.com', name: 'test', lists: [1], status: 'enabled',
|
||||
},
|
||||
}).should((response) => {
|
||||
expect(response.status).to.equal(400);
|
||||
});
|
||||
|
||||
// Modify an existinb subscriber to a banned domain.
|
||||
cy.request({
|
||||
method: 'PUT',
|
||||
url: `${apiUrl}/api/subscribers/1`,
|
||||
failOnStatusCode: false,
|
||||
body: {
|
||||
email: 'test3@ban.org', name: 'test', lists: [1], status: 'enabled',
|
||||
},
|
||||
}).should((response) => {
|
||||
expect(response.status).to.equal(400);
|
||||
});
|
||||
});
|
||||
|
||||
it('Try via import', () => {
|
||||
cy.loginAndVisit('/admin/subscribers/import');
|
||||
cy.get('.list-selector input').click();
|
||||
cy.get('.list-selector .autocomplete a').first().click();
|
||||
|
||||
cy.fixture('subs-domain-blocklist.csv').then((data) => {
|
||||
cy.get('input[type="file"]').attachFile({
|
||||
fileContent: data.toString(),
|
||||
fileName: 'subs.csv',
|
||||
mimeType: 'text/csv',
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('button.is-primary').click();
|
||||
cy.get('section.wrap .has-text-success');
|
||||
// cy.get('button.is-primary').click();
|
||||
cy.get('.log-view').should('contain', 'ban1-import@BAN.net').and('contain', 'ban2-import@ban.ORG');
|
||||
cy.wait(100);
|
||||
});
|
||||
|
||||
it('Clear blocklist and try', () => {
|
||||
cy.loginAndVisit('/admin/settings');
|
||||
cy.get('.b-tabs nav a').eq(2).click();
|
||||
cy.get('textarea[name="privacy.domain_blocklist"]').clear();
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.waitForBackend();
|
||||
|
||||
// Add banned domain.
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${apiUrl}/api/subscribers`,
|
||||
failOnStatusCode: true,
|
||||
body: {
|
||||
email: 'test4@BAN.com', name: 'test', lists: [1], status: 'enabled',
|
||||
},
|
||||
}).should((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
|
||||
// Modify an existinb subscriber to a banned domain.
|
||||
cy.request({
|
||||
method: 'PUT',
|
||||
url: `${apiUrl}/api/subscribers/1`,
|
||||
failOnStatusCode: true,
|
||||
body: {
|
||||
email: 'test4@BAN.org', name: 'test', lists: [1], status: 'enabled',
|
||||
},
|
||||
}).should((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
describe('Templates', () => {
|
||||
it('Opens templates page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/campaigns/templates');
|
||||
});
|
||||
|
||||
it('Counts default templates', () => {
|
||||
cy.get('tbody td[data-label=Name]').should('have.length', 4);
|
||||
});
|
||||
|
||||
it('Clones campaign template', () => {
|
||||
cy.get('[data-cy=btn-clone]').first().click();
|
||||
cy.get('.modal input').clear().type('cloned campaign').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.wait(250);
|
||||
|
||||
// Verify the newly created row.
|
||||
cy.get('tbody td[data-label="Name"]').contains('td', 'cloned campaign');
|
||||
});
|
||||
|
||||
it('Clones tx template', () => {
|
||||
cy.get('tbody td[data-label="Name"]').contains('td', 'Sample transactional template').then((el) => {
|
||||
cy.wrap(el).parent().find('[data-cy=btn-clone]').click();
|
||||
cy.get('.modal input').clear().type('cloned tx').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.wait(250);
|
||||
});
|
||||
|
||||
// Verify the newly created row.
|
||||
cy.get('tbody td[data-label="Name"]').contains('td', 'cloned tx');
|
||||
});
|
||||
|
||||
it('Edits template', () => {
|
||||
cy.get('tbody td.actions [data-cy=btn-edit]').first().click();
|
||||
cy.wait(250);
|
||||
cy.get('input[name=name]').clear().type('edited');
|
||||
|
||||
const htmlBody = '<span>test</span><div class="wrap">{{ template "content" . }}</div>';
|
||||
cy.get('[role="textbox"]').invoke('text', htmlBody);
|
||||
|
||||
cy.get('.modal-card-foot button.is-primary').click();
|
||||
cy.wait(250);
|
||||
cy.get('tbody td[data-label="Name"] a').contains('edited');
|
||||
});
|
||||
|
||||
it('Previews campaign templates', () => {
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
// Edited one should have a bare body.
|
||||
cy.request(`${apiUrl}/api/templates/1/preview`).then((resp) => {
|
||||
expect(resp.body).to.contain('test');
|
||||
expect(resp.body).to.contain('Hi there');
|
||||
});
|
||||
|
||||
// Cloned one should have the full template with wrap and unsubscribe.
|
||||
cy.request(`${apiUrl}/api/templates/5/preview`).then((resp) => {
|
||||
expect(resp.body).to.contain('Hi there');
|
||||
expect(resp.body).to.contain('Unsubscribe');
|
||||
});
|
||||
});
|
||||
|
||||
it('Previews tx templates', () => {
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
// Cloned tx template.
|
||||
cy.request(`${apiUrl}/api/templates/6/preview`).then((resp) => {
|
||||
expect(resp.body).to.contain('Order number');
|
||||
});
|
||||
});
|
||||
|
||||
it('Sets default', () => {
|
||||
cy.get('tbody td[data-label="Name"]').contains('td', 'cloned campaign').then((el) => {
|
||||
cy.wrap(el).parent().find('[data-cy=btn-set-default]').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
});
|
||||
|
||||
// The original default shouldn't have default and the new one should have.
|
||||
cy.get('tbody').contains('td', 'edited').parent().find('[data-cy=btn-delete]')
|
||||
.should('exist');
|
||||
cy.get('tbody').contains('td', 'cloned campaign').parent().find('[data-cy=btn-delete]')
|
||||
.should('not.exist');
|
||||
});
|
||||
|
||||
it('Deletes template', () => {
|
||||
cy.wait(250);
|
||||
|
||||
['Default archive template', 'Sample transactional template'].forEach((t) => {
|
||||
cy.get('tbody td[data-label="Name"]').contains('td', t).then((el) => {
|
||||
cy.wrap(el).parent().find('[data-cy=btn-delete]').click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
});
|
||||
cy.wait(250);
|
||||
});
|
||||
|
||||
cy.get('tbody td.actions').should('have.length', 4);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
const apiUrl = Cypress.env('apiUrl');
|
||||
|
||||
describe('First time user setup', () => {
|
||||
it('Sets up the superadmin user', () => {
|
||||
cy.resetDBBlank();
|
||||
cy.visit('/admin/login');
|
||||
|
||||
cy.get('input[name=email]').type('super@domain');
|
||||
cy.get('input[name=username]').type('super');
|
||||
cy.get('input[name=password]').type('super123');
|
||||
cy.get('input[name=password2]').type('super123');
|
||||
cy.get('button[type=submit]').click();
|
||||
cy.wait(500);
|
||||
cy.visit('/admin/users');
|
||||
|
||||
cy.get('[data-cy=btn-edit]').first().click();
|
||||
cy.get('select[name=user_role]').should('have.value', '1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('User roles', () => {
|
||||
it('Opens user roles page', () => {
|
||||
cy.resetDB();
|
||||
cy.loginAndVisit('/admin/users/roles/users');
|
||||
});
|
||||
|
||||
it('Adds new roles', () => {
|
||||
// first - no global list perms.
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.get('input[name=name]').type('first');
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
|
||||
// second - all perms.
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.get('input[name=name]').type('second');
|
||||
cy.get('input[type=checkbox]').each((e) => {
|
||||
cy.get(e).check({ force: true });
|
||||
});
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(200);
|
||||
});
|
||||
|
||||
it('Edits role', () => {
|
||||
cy.get('[data-cy=btn-edit]').first().click();
|
||||
cy.get('input[value="users:get"]').check({ force: true });
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
});
|
||||
|
||||
it('Deletes role', () => {
|
||||
cy.get('[data-cy=btn-clone]').last().click();
|
||||
cy.get('.modal-card-foot button.is-primary').click();
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy=btn-delete]').last().click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
|
||||
cy.get('tbody tr').should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('List roles', () => {
|
||||
it('Opens roles page', () => {
|
||||
cy.loginAndVisit('/admin/users/roles/lists');
|
||||
});
|
||||
|
||||
it('Adds new roles', () => {
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.get('input[name=name]').type('first');
|
||||
cy.get('.box button.is-primary').click();
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.get('input[name=name]').type('second');
|
||||
cy.get('.box button.is-primary').click();
|
||||
cy.get('.box button.is-primary').click();
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
});
|
||||
|
||||
it('Edits role', () => {
|
||||
cy.get('[data-cy=btn-edit]').eq(1).click();
|
||||
|
||||
// Uncheck "manage" permission on the second item.
|
||||
cy.get('input[type=checkbox]').eq(3).uncheck({ force: true });
|
||||
cy.get('[data-cy=btn-save]').click();
|
||||
});
|
||||
|
||||
it('Deletes role', () => {
|
||||
cy.get('[data-cy=btn-clone]').last().click();
|
||||
cy.get('.modal-card-foot button.is-primary').click();
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy=btn-delete]').last().click();
|
||||
cy.get('.modal button.is-primary').click();
|
||||
|
||||
cy.get('tbody tr').should('have.length', 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Users ', () => {
|
||||
it('Opens users page', () => {
|
||||
cy.loginAndVisit('/admin/users');
|
||||
});
|
||||
|
||||
it('Adds new users', () => {
|
||||
['first', 'second', 'third'].forEach((name) => {
|
||||
cy.get('[data-cy=btn-new]').click();
|
||||
cy.get('input[name=username]').type(name);
|
||||
cy.get('input[name=name]').type(name);
|
||||
cy.get('input[name=email]').type(`${name}@domain`);
|
||||
cy.get('input[name=password_login]').check({ force: true });
|
||||
cy.get('input[name=password]').type(`${name}000000`);
|
||||
cy.get('input[name=password2]').type(`${name}000000`);
|
||||
|
||||
const role = name !== 'third' ? name : 'first';
|
||||
cy.get('select[name=user_role]').select(role);
|
||||
cy.get('select[name=list_role]').select(role);
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.wait(500);
|
||||
});
|
||||
});
|
||||
|
||||
it('Edits user', () => {
|
||||
cy.get('[data-cy=btn-edit]').last().click();
|
||||
cy.get('input[name=password_login]').uncheck({ force: true });
|
||||
cy.get('select[name=user_role]').select('second');
|
||||
cy.get('select[name=list_role]').select('second');
|
||||
cy.get('.modal button.is-primary').click();
|
||||
cy.wait(500);
|
||||
|
||||
// Fetch the campaigns API and verfiy the values that couldn't be verified on the table UI.
|
||||
cy.request(`${apiUrl}/api/users/4`).should((response) => {
|
||||
const { data } = response.body;
|
||||
|
||||
expect(data.password_login).to.equal(false);
|
||||
expect(data.user_role.name).to.equal('second');
|
||||
expect(data.list_role.name).to.equal('second');
|
||||
});
|
||||
});
|
||||
|
||||
it('Deletes a user', () => {
|
||||
cy.get('[data-cy=btn-delete]').last().click();
|
||||
cy.get('.modal-card-foot button.is-primary').click();
|
||||
cy.wait(500);
|
||||
cy.get('tbody tr').should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Login ', () => {
|
||||
it('Logs in as first', () => {
|
||||
cy.visit('/admin/login?next=/admin/lists');
|
||||
cy.get('input[name=username]').invoke('val', 'first');
|
||||
cy.get('input[name=password]').invoke('val', 'first000000');
|
||||
cy.get('button').click();
|
||||
|
||||
// first=only default list.
|
||||
cy.get('tbody tr').should('have.length', 1);
|
||||
cy.get('tbody td[data-label=Name]').contains('Default list');
|
||||
cy.get('[data-cy=btn-new]').should('not.exist');
|
||||
cy.get('[data-cy=btn-edit]').should('exist');
|
||||
cy.get('[data-cy=btn-delete]').should('exist');
|
||||
});
|
||||
|
||||
it('Logs in as second', () => {
|
||||
cy.visit('/admin/login?next=/admin/lists');
|
||||
cy.get('input[name=username]').invoke('val', 'second');
|
||||
cy.get('input[name=password]').invoke('val', 'second000000');
|
||||
cy.get('button').click();
|
||||
|
||||
// first=only default list.
|
||||
cy.get('tbody tr').should('have.length', 2);
|
||||
cy.get('tbody tr:nth-child(1) [data-cy=btn-edit]').should('exist');
|
||||
cy.get('tbody tr:nth-child(1) [data-cy=btn-delete]').should('exist');
|
||||
cy.get('tbody tr:nth-child(2) [data-cy=btn-edit]').should('exist');
|
||||
cy.get('tbody tr:nth-child(2) [data-cy=btn-delete]').should('exist');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,5 @@
|
||||
email,name,attributes
|
||||
noban1-import@mail.com,First0 Last0,"{""age"": 29, ""city"": ""Bangalore"", ""clientId"": ""DAXX79""}"
|
||||
ban1-import@BAN.net,First1 Last1,"{""age"": 43, ""city"": ""Bangalore"", ""clientId"": ""DAXX71""}"
|
||||
noban2-import1@mail.com,First2 Last2,"{""age"": 47, ""city"": ""Bangalore"", ""clientId"": ""DAXX70""}"
|
||||
ban2-import@ban.ORG,First1 Last1,"{""age"": 43, ""city"": ""Bangalore"", ""clientId"": ""DAXX71""}"
|
||||
|
@@ -0,0 +1,101 @@
|
||||
email,name,attributes
|
||||
user0@mail.com,First0 Last0,"{""age"": 29, ""city"": ""Bangalore"", ""clientId"": ""DAXX79""}"
|
||||
user1@mail.com,First1 Last1,"{""age"": 43, ""city"": ""Bangalore"", ""clientId"": ""DAXX71""}"
|
||||
user2@mail.com,First2 Last2,"{""age"": 47, ""city"": ""Bangalore"", ""clientId"": ""DAXX70""}"
|
||||
user3@mail.com,First3 Last3,"{""age"": 67, ""city"": ""Bangalore"", ""clientId"": ""DAXX32""}"
|
||||
user4@mail.com,First4 Last4,"{""age"": 63, ""city"": ""Bangalore"", ""clientId"": ""DAXX30""}"
|
||||
user5@mail.com,First5 Last5,"{""age"": 69, ""city"": ""Bangalore"", ""clientId"": ""DAXX64""}"
|
||||
user6@mail.com,First6 Last6,"{""age"": 68, ""city"": ""Bangalore"", ""clientId"": ""DAXX22""}"
|
||||
user7@mail.com,First7 Last7,"{""age"": 56, ""city"": ""Bangalore"", ""clientId"": ""DAXX54""}"
|
||||
user8@mail.com,First8 Last8,"{""age"": 58, ""city"": ""Bangalore"", ""clientId"": ""DAXX65""}"
|
||||
user9@mail.com,First9 Last9,"{""age"": 51, ""city"": ""Bangalore"", ""clientId"": ""DAXX66""}"
|
||||
user10@mail.com,First10 Last10,"{""age"": 53, ""city"": ""Bangalore"", ""clientId"": ""DAXX31""}"
|
||||
user11@mail.com,First11 Last11,"{""age"": 46, ""city"": ""Bangalore"", ""clientId"": ""DAXX59""}"
|
||||
user12@mail.com,First12 Last12,"{""age"": 41, ""city"": ""Bangalore"", ""clientId"": ""DAXX80""}"
|
||||
user13@mail.com,First13 Last13,"{""age"": 27, ""city"": ""Bangalore"", ""clientId"": ""DAXX96""}"
|
||||
user14@mail.com,First14 Last14,"{""age"": 51, ""city"": ""Bangalore"", ""clientId"": ""DAXX22""}"
|
||||
user15@mail.com,First15 Last15,"{""age"": 31, ""city"": ""Bangalore"", ""clientId"": ""DAXX97""}"
|
||||
user16@mail.com,First16 Last16,"{""age"": 59, ""city"": ""Bangalore"", ""clientId"": ""DAXX41""}"
|
||||
user17@mail.com,First17 Last17,"{""age"": 29, ""city"": ""Bangalore"", ""clientId"": ""DAXX93""}"
|
||||
user18@mail.com,First18 Last18,"{""age"": 39, ""city"": ""Bangalore"", ""clientId"": ""DAXX35""}"
|
||||
user19@mail.com,First19 Last19,"{""age"": 67, ""city"": ""Bangalore"", ""clientId"": ""DAXX21""}"
|
||||
user20@mail.com,First20 Last20,"{""age"": 66, ""city"": ""Bangalore"", ""clientId"": ""DAXX56""}"
|
||||
user21@mail.com,First21 Last21,"{""age"": 39, ""city"": ""Bangalore"", ""clientId"": ""DAXX26""}"
|
||||
user22@mail.com,First22 Last22,"{""age"": 44, ""city"": ""Bangalore"", ""clientId"": ""DAXX98""}"
|
||||
user23@mail.com,First23 Last23,"{""age"": 66, ""city"": ""Bangalore"", ""clientId"": ""DAXX64""}"
|
||||
user24@mail.com,First24 Last24,"{""age"": 48, ""city"": ""Bangalore"", ""clientId"": ""DAXX41""}"
|
||||
user25@mail.com,First25 Last25,"{""age"": 38, ""city"": ""Bangalore"", ""clientId"": ""DAXX80""}"
|
||||
user26@mail.com,First26 Last26,"{""age"": 27, ""city"": ""Bangalore"", ""clientId"": ""DAXX26""}"
|
||||
user27@mail.com,First27 Last27,"{""age"": 59, ""city"": ""Bangalore"", ""clientId"": ""DAXX55""}"
|
||||
user28@mail.com,First28 Last28,"{""age"": 49, ""city"": ""Bangalore"", ""clientId"": ""DAXX45""}"
|
||||
user29@mail.com,First29 Last29,"{""age"": 45, ""city"": ""Bangalore"", ""clientId"": ""DAXX74""}"
|
||||
user30@mail.com,First30 Last30,"{""age"": 47, ""city"": ""Bangalore"", ""clientId"": ""DAXX27""}"
|
||||
user31@mail.com,First31 Last31,"{""age"": 21, ""city"": ""Bangalore"", ""clientId"": ""DAXX37""}"
|
||||
user32@mail.com,First32 Last32,"{""age"": 21, ""city"": ""Bangalore"", ""clientId"": ""DAXX50""}"
|
||||
user33@mail.com,First33 Last33,"{""age"": 70, ""city"": ""Bangalore"", ""clientId"": ""DAXX29""}"
|
||||
user34@mail.com,First34 Last34,"{""age"": 59, ""city"": ""Bangalore"", ""clientId"": ""DAXX95""}"
|
||||
user35@mail.com,First35 Last35,"{""age"": 36, ""city"": ""Bangalore"", ""clientId"": ""DAXX79""}"
|
||||
user36@mail.com,First36 Last36,"{""age"": 47, ""city"": ""Bangalore"", ""clientId"": ""DAXX30""}"
|
||||
user37@mail.com,First37 Last37,"{""age"": 36, ""city"": ""Bangalore"", ""clientId"": ""DAXX92""}"
|
||||
user38@mail.com,First38 Last38,"{""age"": 29, ""city"": ""Bangalore"", ""clientId"": ""DAXX48""}"
|
||||
user39@mail.com,First39 Last39,"{""age"": 23, ""city"": ""Bangalore"", ""clientId"": ""DAXX12""}"
|
||||
user40@mail.com,First40 Last40,"{""age"": 39, ""city"": ""Bangalore"", ""clientId"": ""DAXX40""}"
|
||||
user41@mail.com,First41 Last41,"{""age"": 41, ""city"": ""Bangalore"", ""clientId"": ""DAXX51""}"
|
||||
user42@mail.com,First42 Last42,"{""age"": 22, ""city"": ""Bangalore"", ""clientId"": ""DAXX49""}"
|
||||
user43@mail.com,First43 Last43,"{""age"": 68, ""city"": ""Bangalore"", ""clientId"": ""DAXX58""}"
|
||||
user44@mail.com,First44 Last44,"{""age"": 45, ""city"": ""Bangalore"", ""clientId"": ""DAXX15""}"
|
||||
user45@mail.com,First45 Last45,"{""age"": 44, ""city"": ""Bangalore"", ""clientId"": ""DAXX75""}"
|
||||
user46@mail.com,First46 Last46,"{""age"": 42, ""city"": ""Bangalore"", ""clientId"": ""DAXX99""}"
|
||||
user47@mail.com,First47 Last47,"{""age"": 61, ""city"": ""Bangalore"", ""clientId"": ""DAXX39""}"
|
||||
user48@mail.com,First48 Last48,"{""age"": 57, ""city"": ""Bangalore"", ""clientId"": ""DAXX13""}"
|
||||
user49@mail.com,First49 Last49,"{""age"": 28, ""city"": ""Bangalore"", ""clientId"": ""DAXX97""}"
|
||||
user50@mail.com,First50 Last50,"{""age"": 61, ""city"": ""Bangalore"", ""clientId"": ""DAXX75""}"
|
||||
user51@mail.com,First51 Last51,"{""age"": 27, ""city"": ""Bangalore"", ""clientId"": ""DAXX55""}"
|
||||
user52@mail.com,First52 Last52,"{""age"": 62, ""city"": ""Bangalore"", ""clientId"": ""DAXX35""}"
|
||||
user53@mail.com,First53 Last53,"{""age"": 24, ""city"": ""Bangalore"", ""clientId"": ""DAXX67""}"
|
||||
user54@mail.com,First54 Last54,"{""age"": 25, ""city"": ""Bangalore"", ""clientId"": ""DAXX36""}"
|
||||
user55@mail.com,First55 Last55,"{""age"": 39, ""city"": ""Bangalore"", ""clientId"": ""DAXX74""}"
|
||||
user56@mail.com,First56 Last56,"{""age"": 53, ""city"": ""Bangalore"", ""clientId"": ""DAXX28""}"
|
||||
user57@mail.com,First57 Last57,"{""age"": 32, ""city"": ""Bangalore"", ""clientId"": ""DAXX36""}"
|
||||
user58@mail.com,First58 Last58,"{""age"": 64, ""city"": ""Bangalore"", ""clientId"": ""DAXX44""}"
|
||||
user59@mail.com,First59 Last59,"{""age"": 47, ""city"": ""Bangalore"", ""clientId"": ""DAXX65""}"
|
||||
user60@mail.com,First60 Last60,"{""age"": 62, ""city"": ""Bangalore"", ""clientId"": ""DAXX11""}"
|
||||
user61@mail.com,First61 Last61,"{""age"": 24, ""city"": ""Bangalore"", ""clientId"": ""DAXX55""}"
|
||||
user62@mail.com,First62 Last62,"{""age"": 61, ""city"": ""Bangalore"", ""clientId"": ""DAXX49""}"
|
||||
user63@mail.com,First63 Last63,"{""age"": 52, ""city"": ""Bangalore"", ""clientId"": ""DAXX83""}"
|
||||
user64@mail.com,First64 Last64,"{""age"": 38, ""city"": ""Bangalore"", ""clientId"": ""DAXX16""}"
|
||||
user65@mail.com,First65 Last65,"{""age"": 48, ""city"": ""Bangalore"", ""clientId"": ""DAXX54""}"
|
||||
user66@mail.com,First66 Last66,"{""age"": 35, ""city"": ""Bangalore"", ""clientId"": ""DAXX74""}"
|
||||
user67@mail.com,First67 Last67,"{""age"": 70, ""city"": ""Bangalore"", ""clientId"": ""DAXX22""}"
|
||||
user68@mail.com,First68 Last68,"{""age"": 21, ""city"": ""Bangalore"", ""clientId"": ""DAXX98""}"
|
||||
user69@mail.com,First69 Last69,"{""age"": 46, ""city"": ""Bangalore"", ""clientId"": ""DAXX24""}"
|
||||
user70@mail.com,First70 Last70,"{""age"": 58, ""city"": ""Bangalore"", ""clientId"": ""DAXX75""}"
|
||||
user71@mail.com,First71 Last71,"{""age"": 50, ""city"": ""Bangalore"", ""clientId"": ""DAXX57""}"
|
||||
user72@mail.com,First72 Last72,"{""age"": 63, ""city"": ""Bangalore"", ""clientId"": ""DAXX30""}"
|
||||
user73@mail.com,First73 Last73,"{""age"": 54, ""city"": ""Bangalore"", ""clientId"": ""DAXX77""}"
|
||||
user74@mail.com,First74 Last74,"{""age"": 67, ""city"": ""Bangalore"", ""clientId"": ""DAXX91""}"
|
||||
user75@mail.com,First75 Last75,"{""age"": 61, ""city"": ""Bangalore"", ""clientId"": ""DAXX30""}"
|
||||
user76@mail.com,First76 Last76,"{""age"": 50, ""city"": ""Bangalore"", ""clientId"": ""DAXX28""}"
|
||||
user77@mail.com,First77 Last77,"{""age"": 62, ""city"": ""Bangalore"", ""clientId"": ""DAXX41""}"
|
||||
user78@mail.com,First78 Last78,"{""age"": 66, ""city"": ""Bangalore"", ""clientId"": ""DAXX18""}"
|
||||
user79@mail.com,First79 Last79,"{""age"": 40, ""city"": ""Bangalore"", ""clientId"": ""DAXX89""}"
|
||||
user80@mail.com,First80 Last80,"{""age"": 21, ""city"": ""Bangalore"", ""clientId"": ""DAXX72""}"
|
||||
user81@mail.com,First81 Last81,"{""age"": 43, ""city"": ""Bangalore"", ""clientId"": ""DAXX31""}"
|
||||
user82@mail.com,First82 Last82,"{""age"": 33, ""city"": ""Bangalore"", ""clientId"": ""DAXX89""}"
|
||||
user83@mail.com,First83 Last83,"{""age"": 38, ""city"": ""Bangalore"", ""clientId"": ""DAXX88""}"
|
||||
user84@mail.com,First84 Last84,"{""age"": 24, ""city"": ""Bangalore"", ""clientId"": ""DAXX77""}"
|
||||
user85@mail.com,First85 Last85,"{""age"": 27, ""city"": ""Bangalore"", ""clientId"": ""DAXX40""}"
|
||||
user86@mail.com,First86 Last86,"{""age"": 67, ""city"": ""Bangalore"", ""clientId"": ""DAXX46""}"
|
||||
user87@mail.com,First87 Last87,"{""age"": 20, ""city"": ""Bangalore"", ""clientId"": ""DAXX53""}"
|
||||
user88@mail.com,First88 Last88,"{""age"": 45, ""city"": ""Bangalore"", ""clientId"": ""DAXX79""}"
|
||||
user89@mail.com,First89 Last89,"{""age"": 31, ""city"": ""Bangalore"", ""clientId"": ""DAXX11""}"
|
||||
user90@mail.com,First90 Last90,"{""age"": 51, ""city"": ""Bangalore"", ""clientId"": ""DAXX71""}"
|
||||
user91@mail.com,First91 Last91,"{""age"": 49, ""city"": ""Bangalore"", ""clientId"": ""DAXX20""}"
|
||||
user92@mail.com,First92 Last92,"{""age"": 26, ""city"": ""Bangalore"", ""clientId"": ""DAXX20""}"
|
||||
user93@mail.com,First93 Last93,"{""age"": 67, ""city"": ""Bangalore"", ""clientId"": ""DAXX64""}"
|
||||
user94@mail.com,First94 Last94,"{""age"": 60, ""city"": ""Bangalore"", ""clientId"": ""DAXX53""}"
|
||||
user95@mail.com,First95 Last95,"{""age"": 64, ""city"": ""Bangalore"", ""clientId"": ""DAXX91""}"
|
||||
user96@mail.com,First96 Last96,"{""age"": 27, ""city"": ""Bangalore"", ""clientId"": ""DAXX53""}"
|
||||
user97@mail.com,First97 Last97,"{""age"": 29, ""city"": ""Bangalore"", ""clientId"": ""DAXX46""}"
|
||||
user98@mail.com,First98 Last98,"{""age"": 26, ""city"": ""Bangalore"", ""clientId"": ""DAXX49""}"
|
||||
user99@mail.com,First99 Last99,"{""age"": 49, ""city"": ""Bangalore"", ""clientId"": ""DAXX26""}"
|
||||
|
@@ -0,0 +1,50 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
const { execSync, spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
const rootDir = path.resolve(__dirname, '..', '..', '..');
|
||||
|
||||
on('task', {
|
||||
// Kill eaglecast, reset the DB, and start the server in the background.
|
||||
resetServer({ blank = false } = {}) {
|
||||
try {
|
||||
execSync('pkill -9 eaglecast', { stdio: 'ignore' });
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// Run install.
|
||||
const env = blank
|
||||
? { ...process.env }
|
||||
: { ...process.env, EAGLECAST_ADMIN_USER: 'admin', EAGLECAST_ADMIN_PASSWORD: 'eaglecast' };
|
||||
|
||||
execSync('./eaglecast --install --yes', { cwd: rootDir, env, stdio: 'ignore' });
|
||||
|
||||
// Replace the first SMTP block with local MailHog.
|
||||
const smtpSQL = "UPDATE settings SET value = (SELECT jsonb_agg(smtp || jsonb_build_object('host','localhost','port',1025,'tls_type','none')) FROM jsonb_array_elements(value) AS smtp) WHERE key = 'smtp';";
|
||||
try {
|
||||
execSync('docker exec -i eaglecast_db psql -U eaglecast -d eaglecast', {
|
||||
input: smtpSQL,
|
||||
stdio: ['pipe', 'ignore', 'ignore'],
|
||||
});
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// Start the server.
|
||||
const child = spawn('./eaglecast', [], {
|
||||
cwd: rootDir,
|
||||
detached: true,
|
||||
stdio: 'ignore',
|
||||
});
|
||||
child.unref();
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
import 'cypress-file-upload';
|
||||
import 'cypress-wait-until';
|
||||
|
||||
Cypress.Commands.add('resetDB', () => {
|
||||
// Although cypress clearly states that a webserver should not be run
|
||||
// from within it, eaglecast is killed, the DB reset, and run again
|
||||
// in the background. If the DB is reset without restarting eaglecast,
|
||||
// the live Postgres connections in the app throw errors because the
|
||||
// schema changes midway.
|
||||
cy.task('resetServer');
|
||||
cy.waitForBackend();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('resetDBBlank', () => {
|
||||
cy.task('resetServer', { blank: true });
|
||||
cy.waitForBackend();
|
||||
});
|
||||
|
||||
// Takes a th class selector of a Buefy table, clicks it sorting the table,
|
||||
// then compares the values of [td.data-id] attri of all the rows in the
|
||||
// table against the given IDs, asserting the expected order of sort.
|
||||
Cypress.Commands.add('sortTable', (theadSelector, ordIDs) => {
|
||||
cy.get(theadSelector).click();
|
||||
cy.wait(250);
|
||||
cy.get('tbody td[data-id]').each(($el, index) => {
|
||||
expect(ordIDs[index]).to.equal(parseInt($el.attr('data-id')));
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('loginAndVisit', (url) => {
|
||||
cy.visit(`/admin/login?next=${url}`);
|
||||
|
||||
const username = Cypress.env('EAGLECAST_ADMIN_USER') || 'admin';
|
||||
const password = Cypress.env('EAGLECAST_ADMIN_PASSWORD') || 'eaglecast';
|
||||
|
||||
// Fill the username and passowrd and login.
|
||||
cy.get('input[name=username]').invoke('val', username);
|
||||
cy.get('input[name=password]').invoke('val', password);
|
||||
|
||||
// Submit form.
|
||||
cy.get('button').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('clickMenu', (...selectors) => {
|
||||
selectors.forEach((s) => {
|
||||
cy.get(`.menu a[data-cy="${s}"]`).click();
|
||||
});
|
||||
});
|
||||
|
||||
// https://www.nicknish.co/blog/cypress-targeting-elements-inside-iframes
|
||||
Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe, callback = () => { }) => cy
|
||||
.wrap($iframe)
|
||||
.should((iframe) => expect(iframe.contents().find('body')).to.exist)
|
||||
.then((iframe) => cy.wrap(iframe.contents().find('body')))
|
||||
.within({}, callback));
|
||||
|
||||
Cypress.Commands.add('waitForBackend', () => {
|
||||
// The server restarts after a 500ms delay on settings change.
|
||||
// Wait for the server to go down
|
||||
cy.wait(1000);
|
||||
|
||||
// Keep polling the public /health endpoint until the (new) server
|
||||
// is live. Use fetch() as cy.request() throws on ECONNREFUSED even with failOnStatusCode:false.
|
||||
cy.waitUntil(
|
||||
() => cy.wrap(null, { log: false }).then(() => fetch('/health')
|
||||
.then((res) => res.status === 200)
|
||||
.catch(() => false)),
|
||||
{
|
||||
timeout: 60000,
|
||||
interval: 2000,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Cypress.on('uncaught:exception', (err, runnable) => {
|
||||
if (err.hasOwnProperty('request')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import './commands';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '/sockjs-node/**', (req) => {
|
||||
req.destroy();
|
||||
});
|
||||
|
||||
cy.intercept('GET', '/api/health', (req) => {
|
||||
req.reply({});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
pkill -9 eaglecast
|
||||
cd ../
|
||||
./eaglecast --install --yes
|
||||
./eaglecast > /dev/null 2>/dev/null &
|
||||
Reference in New Issue
Block a user