From 20565d09c0771a3d229edc795affa2aef66268b4 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Wed, 18 May 2022 12:15:30 +0530 Subject: [PATCH] fix: Update report method to fix issues with special characters (#4697) --- .../dashboard/helper/downloadHelper.js | 11 ++- .../helper/specs/downloadHelper.spec.js | 22 +----- .../modules/specs/reports/actions.spec.js | 77 ++++++++----------- .../csat_survey_responses/download.csv.erb | 2 +- .../api/v2/accounts/reports/agents.csv.erb | 4 +- .../api/v2/accounts/reports/inboxes.csv.erb | 6 +- .../api/v2/accounts/reports/labels.csv.erb | 4 +- .../api/v2/accounts/reports/teams.csv.erb | 4 +- 8 files changed, 51 insertions(+), 79 deletions(-) diff --git a/app/javascript/dashboard/helper/downloadHelper.js b/app/javascript/dashboard/helper/downloadHelper.js index 150ac1fd8..68c3b5cee 100644 --- a/app/javascript/dashboard/helper/downloadHelper.js +++ b/app/javascript/dashboard/helper/downloadHelper.js @@ -1,11 +1,16 @@ import fromUnixTime from 'date-fns/fromUnixTime'; import format from 'date-fns/format'; -export const downloadCsvFile = (fileName, fileContent) => { +export const downloadCsvFile = (fileName, content) => { + const contentType = 'data:text/csv;charset=utf-8;'; + const blob = new Blob([content], { type: contentType }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); - link.download = fileName; - link.href = `data:text/csv;charset=utf-8,` + encodeURI(fileContent); + link.setAttribute('download', fileName); + link.setAttribute('href', url); link.click(); + return link; }; export const generateFileName = ({ type, to }) => diff --git a/app/javascript/dashboard/helper/specs/downloadHelper.spec.js b/app/javascript/dashboard/helper/specs/downloadHelper.spec.js index b294dfe16..6cdec4160 100644 --- a/app/javascript/dashboard/helper/specs/downloadHelper.spec.js +++ b/app/javascript/dashboard/helper/specs/downloadHelper.spec.js @@ -1,24 +1,4 @@ -import { downloadCsvFile, generateFileName } from '../downloadHelper'; - -const fileName = 'test.csv'; -const fileData = `Agent name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) -Pranav,36,114,28411`; - -describe('#downloadCsvFile', () => { - it('should download the csv file', () => { - const link = { - click: jest.fn(), - }; - jest.spyOn(document, 'createElement').mockImplementation(() => link); - - downloadCsvFile(fileName, fileData); - expect(link.download).toEqual(fileName); - expect(link.href).toEqual( - `data:text/csv;charset=utf-8,${encodeURI(fileData)}` - ); - expect(link.click).toHaveBeenCalledTimes(1); - }); -}); +import { generateFileName } from '../downloadHelper'; describe('#generateFileName', () => { it('should generate the correct file name', () => { diff --git a/app/javascript/dashboard/store/modules/specs/reports/actions.spec.js b/app/javascript/dashboard/store/modules/specs/reports/actions.spec.js index 0ba89ff81..a6e87e1a1 100644 --- a/app/javascript/dashboard/store/modules/specs/reports/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/reports/actions.spec.js @@ -1,102 +1,89 @@ import axios from 'axios'; import { actions } from '../../reports'; - +import DownloadHelper from 'dashboard/helper/downloadHelper'; global.open = jest.fn(); global.axios = axios; jest.mock('axios'); -const createElementSpy = () => { - const element = document.createElement('a'); - jest.spyOn(document, 'createElement').mockImplementation(() => element); - return element; -}; +jest.mock('dashboard/helper/downloadHelper', () => ({ + downloadCsvFile: jest.fn(), +})); describe('#actions', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - describe('#downloadAgentReports', () => { it('open CSV download prompt if API is success', async () => { - axios.get.mockResolvedValue({ - data: `Agent name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) - Pranav,36,114,28411`, - }); + const data = `Agent name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) + Pranav,36,114,28411`; + axios.get.mockResolvedValue({ data }); + const param = { from: 1630504922510, to: 1630504922510, fileName: 'agent-report-01-09-2021.csv', }; - const mockAgentDownloadElement = createElementSpy(); await actions.downloadAgentReports(1, param); - expect(mockAgentDownloadElement.href).toEqual( - 'data:text/csv;charset=utf-8,Agent%20name,Conversations%20count,Avg%20first%20response%20time%20(Minutes),Avg%20resolution%20time%20(Minutes)%0A%20%20%20%20%20%20%20%20Pranav,36,114,28411' + expect(DownloadHelper.downloadCsvFile).toBeCalledWith( + param.fileName, + data ); - expect(mockAgentDownloadElement.download).toEqual(param.fileName); }); }); describe('#downloadLabelReports', () => { it('open CSV download prompt if API is success', async () => { - axios.get.mockResolvedValue({ - data: `Label Title,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) - website,0,0,0`, - }); + const data = `Label Title,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) + website,0,0,0`; + axios.get.mockResolvedValue({ data }); const param = { from: 1632335400, to: 1632853800, type: 'label', fileName: 'label-report-01-09-2021.csv', }; - const mockLabelDownloadElement = createElementSpy(); await actions.downloadLabelReports(1, param); - expect(mockLabelDownloadElement.href).toEqual( - 'data:text/csv;charset=utf-8,Label%20Title,Conversations%20count,Avg%20first%20response%20time%20(Minutes),Avg%20resolution%20time%20(Minutes)%0A%20%20%20%20%20%20%20%20website,0,0,0' + expect(DownloadHelper.downloadCsvFile).toBeCalledWith( + param.fileName, + data ); - expect(mockLabelDownloadElement.download).toEqual(param.fileName); }); }); describe('#downloadInboxReports', () => { it('open CSV download prompt if API is success', async () => { - axios.get.mockResolvedValue({ - data: `Inbox name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) - Fayaz,2,127,0 - EMa,0,0,0 - Twillio WA,0,0,0`, - }); + const data = `Inbox name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) + Fayaz,2,127,0 + EMa,0,0,0 + Twillio WA,0,0,0`; + axios.get.mockResolvedValue({ data }); const param = { from: 1631039400, to: 1635013800, fileName: 'inbox-report-24-10-2021.csv', }; - const mockInboxDownloadElement = createElementSpy(); await actions.downloadInboxReports(1, param); - expect(mockInboxDownloadElement.href).toEqual( - 'data:text/csv;charset=utf-8,Inbox%20name,Conversations%20count,Avg%20first%20response%20time%20(Minutes),Avg%20resolution%20time%20(Minutes)%0A%20%20%20%20%20%20%20%20Fayaz,2,127,0%0A%20%20%20%20%20%20%20%20EMa,0,0,0%0A%20%20%20%20%20%20%20%20Twillio%20WA,0,0,0' + expect(DownloadHelper.downloadCsvFile).toBeCalledWith( + param.fileName, + data ); - expect(mockInboxDownloadElement.download).toEqual(param.fileName); }); }); describe('#downloadTeamReports', () => { it('open CSV download prompt if API is success', async () => { - axios.get.mockResolvedValue({ - data: `Team name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) - sales team,0,0,0 - Reporting period 2021-09-23 to 2021-09-29`, - }); + const data = `Team name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes) + sales team,0,0,0 + Reporting period 2021-09-23 to 2021-09-29`; + axios.get.mockResolvedValue({ data }); const param = { from: 1631039400, to: 1635013800, fileName: 'inbox-report-24-10-2021.csv', }; - const mockInboxDownloadElement = createElementSpy(); await actions.downloadInboxReports(1, param); - expect(mockInboxDownloadElement.href).toEqual( - 'data:text/csv;charset=utf-8,Team%20name,Conversations%20count,Avg%20first%20response%20time%20(Minutes),Avg%20resolution%20time%20(Minutes)%0A%20%20%20%20%20%20%20%20sales%20team,0,0,0%0A%20%20%20%20%20%20%20%20Reporting%20period%202021-09-23%20to%202021-09-29' + expect(DownloadHelper.downloadCsvFile).toBeCalledWith( + param.fileName, + data ); - expect(mockInboxDownloadElement.download).toEqual(param.fileName); }); }); }); diff --git a/app/views/api/v1/accounts/csat_survey_responses/download.csv.erb b/app/views/api/v1/accounts/csat_survey_responses/download.csv.erb index 8b694ed35..5c0524480 100644 --- a/app/views/api/v1/accounts/csat_survey_responses/download.csv.erb +++ b/app/views/api/v1/accounts/csat_survey_responses/download.csv.erb @@ -24,7 +24,7 @@ contact&.phone_number.present? ? contact&.phone_number: nil, conversation ? app_account_conversation_url(account_id: Current.account.id, id: conversation.display_id): nil, csat_response.created_at, - ]) +]).html_safe -%> <% end %> <%= diff --git a/app/views/api/v2/accounts/reports/agents.csv.erb b/app/views/api/v2/accounts/reports/agents.csv.erb index 6738fe51b..9c8f1f814 100644 --- a/app/views/api/v2/accounts/reports/agents.csv.erb +++ b/app/views/api/v2/accounts/reports/agents.csv.erb @@ -5,7 +5,7 @@ I18n.t('reports.agent_csv.avg_resolution_time') ] %> -<%= CSV.generate_line headers %> +<%= CSV.generate_line headers -%> <% Current.account.users.each do |agent| %> <% agent_report = V2::ReportBuilder.new(Current.account, { type: :agent, @@ -14,6 +14,6 @@ until: params[:until] }).summary %> <% row = [ agent.name, agent_report[:conversations_count], (agent_report[:avg_first_response_time]/60).to_i, (agent_report[:avg_resolution_time]/60).to_i ] %> - <%= CSV.generate_line row %> +<%= CSV.generate_line row -%> <% end %> <%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %> diff --git a/app/views/api/v2/accounts/reports/inboxes.csv.erb b/app/views/api/v2/accounts/reports/inboxes.csv.erb index 24548449a..c82be2df4 100644 --- a/app/views/api/v2/accounts/reports/inboxes.csv.erb +++ b/app/views/api/v2/accounts/reports/inboxes.csv.erb @@ -1,5 +1,5 @@ <% headers = ['Inbox name', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %> -<%= CSV.generate_line headers %> +<%= CSV.generate_line headers -%> <% Current.account.inboxes.each do |inbox| %> <% inbox_report = V2::ReportBuilder.new(Current.account, { type: :inbox, @@ -8,5 +8,5 @@ until: params[:until] }).summary %> <% row = [ inbox.name, inbox_report[:conversations_count], (inbox_report[:avg_first_response_time]/60).to_i, (inbox_report[:avg_resolution_time]/60).to_i ] %> -<%= CSV.generate_line row %> -<% end %> \ No newline at end of file +<%= CSV.generate_line row -%> +<% end %> diff --git a/app/views/api/v2/accounts/reports/labels.csv.erb b/app/views/api/v2/accounts/reports/labels.csv.erb index 70ebbdf35..dcbc2b974 100644 --- a/app/views/api/v2/accounts/reports/labels.csv.erb +++ b/app/views/api/v2/accounts/reports/labels.csv.erb @@ -1,5 +1,5 @@ <% headers = ['Label Title', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %> -<%= CSV.generate_line headers %> +<%= CSV.generate_line headers -%> <% Current.account.labels.each do |label| %> <% label_report = V2::ReportBuilder.new(Current.account, { type: :label, @@ -8,5 +8,5 @@ until: params[:until] }).summary %> <% row = [ label.title, label_report[:conversations_count], (label_report[:avg_first_response_time]/60).to_i, (label_report[:avg_resolution_time]/60).to_i ] %> -<%= CSV.generate_line row %> +<%= CSV.generate_line row -%> <% end %> diff --git a/app/views/api/v2/accounts/reports/teams.csv.erb b/app/views/api/v2/accounts/reports/teams.csv.erb index 55c679273..f7b82cb1d 100644 --- a/app/views/api/v2/accounts/reports/teams.csv.erb +++ b/app/views/api/v2/accounts/reports/teams.csv.erb @@ -5,7 +5,7 @@ I18n.t('reports.team_csv.avg_resolution_time') ] %> -<%= CSV.generate_line headers %> +<%= CSV.generate_line headers -%> <% Current.account.teams.each do |team| %> <% team_report = V2::ReportBuilder.new(Current.account, { type: :team, @@ -14,6 +14,6 @@ until: params[:until] }).summary %> <% row = [ team.name, team_report[:conversations_count], (team_report[:avg_first_response_time]/60).to_i, (team_report[:avg_resolution_time]/60).to_i ] %> - <%= CSV.generate_line row %> +<%= CSV.generate_line row -%> <% end %> <%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>