From 64b4424251fd2e9e7219490900aae87dbddc8981 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Wed, 21 Oct 2026 02:10:40 +0530 Subject: [PATCH] cover SMTP authentication and revert campaign date changes --- .../features/admin/inbox/WidgetCampaigns.vue | 27 ++++--------- frontend/cypress/e2e/ui/emailInboxForm.cy.js | 25 +++++++++++- .../cypress/e2e/ui/livechatFormBranches.cy.js | 39 ------------------- .../e2e/ui/notificationSettingsForm.cy.js | 33 +++++++++++++++- 4 files changed, 61 insertions(+), 63 deletions(-) diff --git a/frontend/apps/main/src/features/admin/inbox/WidgetCampaigns.vue b/frontend/apps/main/src/features/admin/inbox/WidgetCampaigns.vue index 9bf33aff..85768e5b 100644 --- a/frontend/apps/main/src/features/admin/inbox/WidgetCampaigns.vue +++ b/frontend/apps/main/src/features/admin/inbox/WidgetCampaigns.vue @@ -102,8 +102,8 @@ const businessHours = ref([]) const error = ref('') const pendingDelete = ref(null) const stats = ref([]) -const from = ref('') -const to = ref('') +const from = ref(new Date(Date.now() - 30 * 86400000).toISOString().slice(0, 10)) +const to = ref(new Date().toISOString().slice(0, 10)) const brandSenderName = computed(() => props.brandName.trim() || t('widget.brandBot')) const campaignEventExample = computed( () => `window.Libredesk.trackEvent(${JSON.stringify(campaign.value?.event || 'pricing_viewed')})` @@ -285,26 +285,13 @@ const refreshStats = async () => { } onMounted(async () => { - const [hoursResult, settingsResult] = await Promise.allSettled([ - api.getAllBusinessHours(), - api.getSettings('general') - ]) - if (hoursResult.status === 'fulfilled') { - const data = hoursResult.value.data.data + try { + const response = await api.getAllBusinessHours() + const data = response.data.data businessHours.value = data.results || data + } catch (err) { + error.value = handleHTTPError(err).message } - const failed = [hoursResult, settingsResult].find((result) => result.status === 'rejected') - if (failed) error.value = handleHTTPError(failed.reason).message - const dateFormat = new Intl.DateTimeFormat('en-CA', { - timeZone: - (settingsResult.status === 'fulfilled' && settingsResult.value.data.data['app.timezone']) || - 'UTC', - year: 'numeric', - month: '2-digit', - day: '2-digit' - }) - from.value = dateFormat.format(new Date(Date.now() - 30 * 86400000)) - to.value = dateFormat.format(new Date()) await refreshStats() }) diff --git a/frontend/cypress/e2e/ui/emailInboxForm.cy.js b/frontend/cypress/e2e/ui/emailInboxForm.cy.js index 989afe8c..323070af 100644 --- a/frontend/cypress/e2e/ui/emailInboxForm.cy.js +++ b/frontend/cypress/e2e/ui/emailInboxForm.cy.js @@ -140,8 +140,13 @@ describe('Email inbox form', () => { cy.contains('New support inbox').should('be.visible') }) - for (const protocol of ['None', 'Login']) { - it(`validates empty SMTP credentials with ${protocol} authentication`, () => { + for (const [protocol, value] of [ + ['None', 'none'], + ['Login', 'login'], + ['CRAM', 'cram'], + ['Plain', 'plain'] + ]) { + it(`validates and saves SMTP credentials with ${protocol} authentication`, () => { openManualForm() fillRequiredFields() field('smtp.username').clear() @@ -160,6 +165,22 @@ describe('Email inbox form', () => { cy.get('@createInbox.all').should('have.length', 0) field('smtp.username').parent().parent().should('contain.text', 'Required') field('smtp.password').parent().parent().should('contain.text', 'Required') + field('smtp.username').type('support@example.com') + submit() + field('smtp.password').should('have.attr', 'aria-invalid', 'true') + cy.get('@createInbox.all').should('have.length', 0) + field('smtp.username').clear() + field('smtp.password').type('test-password') + submit() + field('smtp.username').should('have.attr', 'aria-invalid', 'true') + cy.get('@createInbox.all').should('have.length', 0) + field('smtp.username').type('support@example.com') + submit() + cy.wait('@createInbox').then(({ request }) => { + expect(request.body.config.smtp[0].auth_protocol).to.eq(value) + expect(request.body.config.smtp[0].username).to.eq('support@example.com') + expect(request.body.config.smtp[0].password).to.eq('test-password') + }) } }) } diff --git a/frontend/cypress/e2e/ui/livechatFormBranches.cy.js b/frontend/cypress/e2e/ui/livechatFormBranches.cy.js index 67518e97..e3ec72ad 100644 --- a/frontend/cypress/e2e/ui/livechatFormBranches.cy.js +++ b/frontend/cypress/e2e/ui/livechatFormBranches.cy.js @@ -300,45 +300,6 @@ describe('Live chat inbox form: branches the other specs skip', () => { }) }) - for (const { name, path, message, skipBootCall } of [ - { name: 'settings', path: 'settings/general', message: 'Settings unavailable', skipBootCall: true }, - { name: 'business hours', path: 'business-hours', message: 'Business hours unavailable', skipBootCall: false } - ]) { - it(`loads proactive message stats when the ${name} request fails`, () => { - cy.intercept('GET', `**/api/v1/${path}`, { - statusCode: 500, - body: { status: 'error', message, error_type: 'GeneralException', data: null } - }) - // The app loads general settings once at boot, before the form mounts. Only later calls fail. - if (skipBootCall) { - cy.intercept({ method: 'GET', url: `**/api/v1/${path}`, times: 1 }, (req) => req.continue()) - } - cy.intercept('GET', `**/api/v1/inboxes/${inboxId}/campaign-stats*`).as('campaignStats') - - editInbox(inboxId) - cy.wait('@campaignStats').then(({ request, response }) => { - expect(response.statusCode).to.eq(200) - expect(new URL(request.url).searchParams.get('from')).to.match(/^\d{4}-\d{2}-\d{2}$/) - }) - cy.contains('[role="alert"]', message).should('exist') - }) - } - - it('uses workspace dates for campaign statistics across a UTC day boundary', () => { - cy.clock(Date.UTC(2026, 0, 1, 23, 30), ['Date']) - cy.intercept('GET', '**/api/v1/settings/general', (req) => { - req.continue((res) => { res.body.data['app.timezone'] = 'Pacific/Kiritimati' }) - }) - cy.intercept('GET', `**/api/v1/inboxes/${inboxId}/campaign-stats*`).as('workspaceStats') - editInbox(inboxId) - cy.wait('@workspaceStats').then(({ request, response }) => { - expect(response.statusCode).to.eq(200) - const params = new URL(request.url).searchParams - expect(params.get('from')).to.eq('2025-12-03') - expect(params.get('to')).to.eq('2026-01-02') - }) - }) - it('saves a proactive message condition and removes it again', () => { cy.intercept('PUT', `**/api/v1/inboxes/${inboxId}`).as('updateInbox') diff --git a/frontend/cypress/e2e/ui/notificationSettingsForm.cy.js b/frontend/cypress/e2e/ui/notificationSettingsForm.cy.js index d6948de2..8403f061 100644 --- a/frontend/cypress/e2e/ui/notificationSettingsForm.cy.js +++ b/frontend/cypress/e2e/ui/notificationSettingsForm.cy.js @@ -148,8 +148,13 @@ describe('Notification settings form', () => { cy.get('@saveNotification.all').should('have.length', 0) }) - for (const protocol of ['None', 'Login']) { - it(`validates empty notification SMTP credentials with ${protocol} authentication`, () => { + for (const [protocol, value] of [ + ['None', 'none'], + ['Login', 'login'], + ['CRAM-MD5', 'cram'], + ['Plain', 'plain'] + ]) { + it(`validates and saves notification SMTP credentials with ${protocol} authentication`, () => { cy.intercept('PUT', '**/api/v1/settings/notifications/email').as('saveNotification') cy.visit(path) cy.get('input[name="host"]').should('not.have.value', '') @@ -169,6 +174,30 @@ describe('Notification settings form', () => { cy.get('@saveNotification.all').should('have.length', 0) cy.get('input[name="username"]').parent().parent().should('contain.text', 'Required') cy.get('input[name="password"]').parent().parent().should('contain.text', 'Required') + cy.get('input[name="username"]').type(seedUsername) + cy.get('button[type="submit"]').click() + cy.get('input[name="password"]').should('have.attr', 'aria-invalid', 'true') + cy.get('@saveNotification.all').should('have.length', 0) + cy.get('input[name="username"]').clear() + cy.get('input[name="password"]').type('test-password') + cy.get('button[type="submit"]').click() + cy.get('input[name="username"]').should('have.attr', 'aria-invalid', 'true') + cy.get('@saveNotification.all').should('have.length', 0) + cy.get('input[name="username"]').type(seedUsername) + cy.get('button[type="submit"]').click() + cy.wait('@saveNotification').then(({ request, response }) => { + expect(response.statusCode).to.eq(200) + expect(request.body[`${prefix}auth_protocol`]).to.eq(value) + expect(request.body[`${prefix}username`]).to.eq(seedUsername) + expect(request.body[`${prefix}password`]).to.eq('test-password') + }) + cy.reload() + cy.contains('label', 'Authentication Protocol') + .parent() + .find('[role="combobox"]') + .should('contain.text', protocol) + cy.get('input[name="username"]').should('have.value', seedUsername) + cy.get('input[name="password"]').should('have.value', maskedPassword) } }) }