diff --git a/.circleci/config.yml b/.circleci/config.yml index c67063ae6b31d..15c40dc77f81c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ defaults: &defaults working_directory: ~/build docker: # specify the version you desire here - - image: cimg/ruby:3.2.2-browsers + - image: cimg/ruby:3.3.3-browsers # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images @@ -130,10 +130,7 @@ jobs: command: | mkdir -p ~/tmp/test-results/frontend_specs ~/tmp/cc-test-reporter before-build - TESTFILES=$(circleci tests glob **/specs/*.spec.js | circleci tests split --split-by=timings) - yarn test:coverage --profile 10 \ - --out ~/tmp/test-results/yarn.xml \ - -- ${TESTFILES} + yarn test:coverage - run: name: Code Climate Test Coverage command: | diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 17021d1e7538b..a804bb15caf4d 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -12,7 +12,7 @@ services: args: VARIANT: "ubuntu-22.04" NODE_VERSION: "20.9.0" - RUBY_VERSION: "3.2.2" + RUBY_VERSION: "3.3.3" # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000. USER_UID: "1000" USER_GID: "1000" @@ -25,7 +25,7 @@ services: args: VARIANT: "ubuntu-22.04" NODE_VERSION: "20.9.0" - RUBY_VERSION: "3.2.2" + RUBY_VERSION: "3.3.3" # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000. USER_UID: "1000" USER_GID: "1000" diff --git a/.eslintrc.js b/.eslintrc.js index 03e7e995b8f26..18162ee762a06 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -65,10 +65,10 @@ module.exports = { }, env: { browser: true, - jest: true, node: true, }, globals: { bus: true, + vi: true, }, }; diff --git a/.ruby-version b/.ruby-version index be94e6f53db6b..619b537668489 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.2 +3.3.3 diff --git a/Gemfile b/Gemfile index 5e1e5917fa223..f690e6876d61f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -ruby '3.2.2' +ruby '3.3.3' ##-- base gems for rails --## gem 'rack-cors', '2.0.0', require: 'rack/cors' @@ -111,12 +111,12 @@ gem 'elastic-apm', require: false gem 'newrelic_rpm', require: false gem 'newrelic-sidekiq-metrics', '>= 1.6.2', require: false gem 'scout_apm', require: false -gem 'sentry-rails', '>= 5.18.0', require: false +gem 'sentry-rails', '>= 5.18.1', require: false gem 'sentry-ruby', require: false -gem 'sentry-sidekiq', '>= 5.18.0', require: false +gem 'sentry-sidekiq', '>= 5.18.1', require: false ##-- background job processing --## -gem 'sidekiq', '>= 7.2.4' +gem 'sidekiq', '>= 7.3.0' # We want cron jobs gem 'sidekiq-cron', '>= 1.12.0' diff --git a/Gemfile.lock b/Gemfile.lock index 04a8fb534aaf4..91d3b3d8b6ad3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -174,7 +174,9 @@ GEM crack (0.4.5) rexml crass (1.0.6) - csv-safe (3.2.1) + csv (3.3.0) + csv-safe (3.3.1) + csv (~> 3.0) cypress-on-rails (1.16.0) rack database_cleaner (2.0.2) @@ -183,13 +185,16 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) + datadog-ci (0.8.3) + msgpack date (3.3.4) - ddtrace (1.11.1) - debase-ruby_core_source (>= 0.10.16, <= 3.2.0) - libdatadog (~> 2.0.0.1.0) - libddwaf (~> 1.8.2.0.0) + ddtrace (1.23.2) + datadog-ci (~> 0.8.1) + debase-ruby_core_source (= 3.3.1) + libdatadog (~> 7.0.0.1.0) + libddwaf (~> 1.14.0.0.0) msgpack - debase-ruby_core_source (3.2.0) + debase-ruby_core_source (3.3.1) debug (1.8.0) irb (>= 1.5.0) reline (>= 0.3.1) @@ -416,15 +421,15 @@ GEM addressable (~> 2.8) letter_opener (1.8.1) launchy (>= 2.2, < 3) - libdatadog (2.0.0.1.0) - libdatadog (2.0.0.1.0-x86_64-linux) - libddwaf (1.8.2.0.0) + libdatadog (7.0.0.1.0) + libdatadog (7.0.0.1.0-x86_64-linux) + libddwaf (1.14.0.0.0) ffi (~> 1.0) - libddwaf (1.8.2.0.0-arm64-darwin) + libddwaf (1.14.0.0.0-arm64-darwin) ffi (~> 1.0) - libddwaf (1.8.2.0.0-x86_64-darwin) + libddwaf (1.14.0.0.0-x86_64-darwin) ffi (~> 1.0) - libddwaf (1.8.2.0.0-x86_64-linux) + libddwaf (1.14.0.0.0-x86_64-linux) ffi (~> 1.0) line-bot-api (1.28.0) liquid (5.4.0) @@ -434,6 +439,7 @@ GEM llhttp-ffi (0.4.0) ffi-compiler (~> 1.0) rake (~> 13.0) + logger (1.6.0) lograge (0.14.0) actionpack (>= 4) activesupport (>= 4) @@ -460,7 +466,7 @@ GEM mini_magick (4.12.0) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.0) + minitest (5.24.1) mock_redis (0.36.0) ruby2_keywords msgpack (1.7.0) @@ -703,23 +709,24 @@ GEM activesupport (>= 4) selectize-rails (0.12.6) semantic_range (3.0.0) - sentry-rails (5.18.0) + sentry-rails (5.18.1) railties (>= 5.0) - sentry-ruby (~> 5.18.0) - sentry-ruby (5.18.0) + sentry-ruby (~> 5.18.1) + sentry-ruby (5.18.1) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) - sentry-sidekiq (5.18.0) - sentry-ruby (~> 5.18.0) + sentry-sidekiq (5.18.1) + sentry-ruby (~> 5.18.1) sidekiq (>= 3.0) sexp_processor (4.17.0) shoulda-matchers (5.3.0) activesupport (>= 5.2.0) - sidekiq (7.2.4) + sidekiq (7.3.0) concurrent-ruby (< 2) connection_pool (>= 2.3.0) + logger rack (>= 2.2.4) - redis-client (>= 0.19.0) + redis-client (>= 0.22.2) sidekiq-cron (1.12.0) fugit (~> 1.8) globalid (>= 1.0.1) @@ -931,11 +938,11 @@ DEPENDENCIES scout_apm scss_lint seed_dump - sentry-rails (>= 5.18.0) + sentry-rails (>= 5.18.1) sentry-ruby - sentry-sidekiq (>= 5.18.0) + sentry-sidekiq (>= 5.18.1) shoulda-matchers - sidekiq (>= 7.2.4) + sidekiq (>= 7.3.0) sidekiq-cron (>= 1.12.0) simplecov (= 0.17.1) slack-ruby-client (~> 2.2.0) @@ -960,7 +967,7 @@ DEPENDENCIES working_hours RUBY VERSION - ruby 3.2.2p185 + ruby 3.3.3p89 BUNDLED WITH - 2.4.6 + 2.5.14 diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index e656c25501e94..332f1528fc1e5 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -37,7 +37,7 @@ def set_global_config end def set_dashboard_scripts - @dashboard_scripts = GlobalConfig.get_value('DASHBOARD_SCRIPTS') + @dashboard_scripts = sensitive_path? ? nil : GlobalConfig.get_value('DASHBOARD_SCRIPTS') end def ensure_installation_onboarding @@ -75,4 +75,14 @@ def set_application_pack 'application' end end + + def sensitive_path? + # dont load dashboard scripts on sensitive paths like password reset + sensitive_paths = [edit_user_password_path].freeze + + # remove app prefix + current_path = request.path.gsub(%r{^/app}, '') + + sensitive_paths.include?(current_path) + end end diff --git a/app/javascript/dashboard/App.Vue.spec.js b/app/javascript/dashboard/App.Vue.spec.js deleted file mode 100644 index 43c6a74edc43f..0000000000000 --- a/app/javascript/dashboard/App.Vue.spec.js +++ /dev/null @@ -1,11 +0,0 @@ -import App from './App'; -import '../../test-matchers'; - -describe(`App component`, () => { - it(`should be a component`, () => { - // Arrange - // Act - expect(App).toBeVueComponent('App'); - // Assert - }); -}); diff --git a/app/javascript/dashboard/api/enterprise/specs/account.spec.js b/app/javascript/dashboard/api/enterprise/specs/account.spec.js index 6c9dca986dea3..4fb1bd0ee26bd 100644 --- a/app/javascript/dashboard/api/enterprise/specs/account.spec.js +++ b/app/javascript/dashboard/api/enterprise/specs/account.spec.js @@ -15,10 +15,10 @@ describe('#enterpriseAccountAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/account.spec.js b/app/javascript/dashboard/api/specs/account.spec.js index 7e213b2a81184..4da8b3a465e12 100644 --- a/app/javascript/dashboard/api/specs/account.spec.js +++ b/app/javascript/dashboard/api/specs/account.spec.js @@ -15,10 +15,10 @@ describe('#accountAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/accountActions.spec.js b/app/javascript/dashboard/api/specs/accountActions.spec.js index 330c117ff7f36..dc73e49489642 100644 --- a/app/javascript/dashboard/api/specs/accountActions.spec.js +++ b/app/javascript/dashboard/api/specs/accountActions.spec.js @@ -10,10 +10,10 @@ describe('#ContactsAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/agents.spec.js b/app/javascript/dashboard/api/specs/agents.spec.js index 20dd36688ad1a..0df0fd8d98b9e 100644 --- a/app/javascript/dashboard/api/specs/agents.spec.js +++ b/app/javascript/dashboard/api/specs/agents.spec.js @@ -14,7 +14,7 @@ describe('#AgentAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/article.spec.js b/app/javascript/dashboard/api/specs/article.spec.js index 02c0f82d80519..71128682c9913 100644 --- a/app/javascript/dashboard/api/specs/article.spec.js +++ b/app/javascript/dashboard/api/specs/article.spec.js @@ -14,10 +14,10 @@ describe('#PortalAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -44,10 +44,10 @@ describe('#PortalAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -71,10 +71,10 @@ describe('#PortalAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -98,10 +98,10 @@ describe('#PortalAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -129,10 +129,10 @@ describe('#PortalAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/assignableAgents.spec.js b/app/javascript/dashboard/api/specs/assignableAgents.spec.js index 5280162b35809..d553d55cb5385 100644 --- a/app/javascript/dashboard/api/specs/assignableAgents.spec.js +++ b/app/javascript/dashboard/api/specs/assignableAgents.spec.js @@ -4,10 +4,10 @@ describe('#AssignableAgentsAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/channel/fbChannel.spec.js b/app/javascript/dashboard/api/specs/channel/fbChannel.spec.js index 2cdcec56e3c0e..c79051977f5d1 100644 --- a/app/javascript/dashboard/api/specs/channel/fbChannel.spec.js +++ b/app/javascript/dashboard/api/specs/channel/fbChannel.spec.js @@ -13,10 +13,10 @@ describe('#FBChannel', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/contacts.spec.js b/app/javascript/dashboard/api/specs/contacts.spec.js index b4eaf73332b06..0059518b0a66f 100644 --- a/app/javascript/dashboard/api/specs/contacts.spec.js +++ b/app/javascript/dashboard/api/specs/contacts.spec.js @@ -17,10 +17,10 @@ describe('#ContactsAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/conversations.spec.js b/app/javascript/dashboard/api/specs/conversations.spec.js index 6b3db7404e7c1..7ae4eb774773f 100644 --- a/app/javascript/dashboard/api/specs/conversations.spec.js +++ b/app/javascript/dashboard/api/specs/conversations.spec.js @@ -16,10 +16,10 @@ describe('#ConversationApi', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/csatReports.spec.js b/app/javascript/dashboard/api/specs/csatReports.spec.js index 7c1707e1ee3ec..788f0eba2e726 100644 --- a/app/javascript/dashboard/api/specs/csatReports.spec.js +++ b/app/javascript/dashboard/api/specs/csatReports.spec.js @@ -11,10 +11,10 @@ describe('#Reports API', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/inbox/conversation.spec.js b/app/javascript/dashboard/api/specs/inbox/conversation.spec.js index 3287d7477997d..dd1615802daa4 100644 --- a/app/javascript/dashboard/api/specs/inbox/conversation.spec.js +++ b/app/javascript/dashboard/api/specs/inbox/conversation.spec.js @@ -24,10 +24,10 @@ describe('#ConversationAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/inbox/message.spec.js b/app/javascript/dashboard/api/specs/inbox/message.spec.js index 0d45b21575985..941f5c99c1192 100644 --- a/app/javascript/dashboard/api/specs/inbox/message.spec.js +++ b/app/javascript/dashboard/api/specs/inbox/message.spec.js @@ -15,10 +15,10 @@ describe('#ConversationAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/inboxes.spec.js b/app/javascript/dashboard/api/specs/inboxes.spec.js index 8834ceb077705..628ce0f34b757 100644 --- a/app/javascript/dashboard/api/specs/inboxes.spec.js +++ b/app/javascript/dashboard/api/specs/inboxes.spec.js @@ -17,10 +17,10 @@ describe('#InboxesAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/integrations.spec.js b/app/javascript/dashboard/api/specs/integrations.spec.js index 5ccbda436b9a9..cc20fd8f68bf1 100644 --- a/app/javascript/dashboard/api/specs/integrations.spec.js +++ b/app/javascript/dashboard/api/specs/integrations.spec.js @@ -18,10 +18,10 @@ describe('#integrationAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/integrations/dyte.spec.js b/app/javascript/dashboard/api/specs/integrations/dyte.spec.js index 4bbe0484a658a..1c544f9765444 100644 --- a/app/javascript/dashboard/api/specs/integrations/dyte.spec.js +++ b/app/javascript/dashboard/api/specs/integrations/dyte.spec.js @@ -11,10 +11,10 @@ describe('#accountAPI', () => { describe('createAMeeting', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -39,10 +39,10 @@ describe('#accountAPI', () => { describe('addParticipantToMeeting', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/integrations/linear.spec.js b/app/javascript/dashboard/api/specs/integrations/linear.spec.js index cc16feb16a5e0..e4bf679a66b9e 100644 --- a/app/javascript/dashboard/api/specs/integrations/linear.spec.js +++ b/app/javascript/dashboard/api/specs/integrations/linear.spec.js @@ -16,10 +16,10 @@ describe('#linearAPI', () => { describe('getTeams', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -41,10 +41,10 @@ describe('#linearAPI', () => { describe('getTeamEntities', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -66,10 +66,10 @@ describe('#linearAPI', () => { describe('createIssue', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -96,10 +96,10 @@ describe('#linearAPI', () => { describe('link_issue', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -125,10 +125,10 @@ describe('#linearAPI', () => { describe('getLinkedIssue', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -150,10 +150,10 @@ describe('#linearAPI', () => { describe('unlinkIssue', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { @@ -178,10 +178,10 @@ describe('#linearAPI', () => { describe('searchIssues', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/notifications.spec.js b/app/javascript/dashboard/api/specs/notifications.spec.js index fe748fe199b99..770a6840da9f8 100644 --- a/app/javascript/dashboard/api/specs/notifications.spec.js +++ b/app/javascript/dashboard/api/specs/notifications.spec.js @@ -13,10 +13,10 @@ describe('#NotificationAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/reports.spec.js b/app/javascript/dashboard/api/specs/reports.spec.js index 05d4a152c1767..e458633d09603 100644 --- a/app/javascript/dashboard/api/specs/reports.spec.js +++ b/app/javascript/dashboard/api/specs/reports.spec.js @@ -20,10 +20,10 @@ describe('#Reports API', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/slaReports.spec.js b/app/javascript/dashboard/api/specs/slaReports.spec.js index f540b6acc75ab..827b44cad29e8 100644 --- a/app/javascript/dashboard/api/specs/slaReports.spec.js +++ b/app/javascript/dashboard/api/specs/slaReports.spec.js @@ -12,10 +12,10 @@ describe('#SLAReports API', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/api/specs/teams.spec.js b/app/javascript/dashboard/api/specs/teams.spec.js index 3a59f2c510452..c7bfc4d1ce088 100644 --- a/app/javascript/dashboard/api/specs/teams.spec.js +++ b/app/javascript/dashboard/api/specs/teams.spec.js @@ -16,10 +16,10 @@ describe('#TeamsAPI', () => { describe('API calls', () => { const originalAxios = window.axios; const axiosMock = { - post: jest.fn(() => Promise.resolve()), - get: jest.fn(() => Promise.resolve()), - patch: jest.fn(() => Promise.resolve()), - delete: jest.fn(() => Promise.resolve()), + post: vi.fn(() => Promise.resolve()), + get: vi.fn(() => Promise.resolve()), + patch: vi.fn(() => Promise.resolve()), + delete: vi.fn(() => Promise.resolve()), }; beforeEach(() => { diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index eb410f7d55ca3..ee53540274d3b 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -111,15 +111,6 @@ @updateFolder="onUpdateSavedFilter" /> - - - @@ -152,10 +143,6 @@ import { isOnUnattendedView, } from '../store/modules/conversations/helpers/actionHelpers'; import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events'; -import { CMD_SNOOZE_CONVERSATION } from 'dashboard/routes/dashboard/commands/commandBarBusEvents'; -import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers'; -import { getUnixTime } from 'date-fns'; -import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue'; import IntersectionObserver from './IntersectionObserver.vue'; export default { @@ -170,7 +157,6 @@ export default { ConversationBulkActions, IntersectionObserver, VirtualList, - CustomSnoozeModal, }, mixins: [ timeMixin, @@ -247,7 +233,6 @@ export default { root: this.$refs.conversationList, rootMargin: '100px 0px 100px 0px', }, - showCustomSnoozeModal: false, itemComponent: ConversationItem, // virtualListExtraProps is to pass the props to the conversationItem component. @@ -283,7 +268,6 @@ export default { campaigns: 'campaigns/getAllCampaigns', labels: 'labels/getLabels', selectedConversations: 'bulkActions/getSelectedConversationIds', - contextMenuChatId: 'getContextMenuChatId', }), hasAppliedFilters() { return this.appliedFilters.length !== 0; @@ -517,11 +501,6 @@ export default { this.$emitter.on('fetch_conversation_stats', () => { this.$store.dispatch('conversationStats/get', this.conversationFilters); }); - - this.$emitter.on(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation); - }, - beforeDestroy() { - this.$emitter.off(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation); }, methods: { updateVirtualListProps(key, value) { @@ -999,43 +978,6 @@ export default { onContextMenuToggle(state) { this.isContextMenuOpen = state; }, - onCmdSnoozeConversation(snoozeType) { - if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) { - this.showCustomSnoozeModal = true; - } else { - this.toggleStatus( - wootConstants.STATUS_TYPE.SNOOZED, - findSnoozeTime(snoozeType) || null - ); - } - }, - chooseSnoozeTime(customSnoozeTime) { - this.showCustomSnoozeModal = false; - if (customSnoozeTime) { - this.toggleStatus( - wootConstants.STATUS_TYPE.SNOOZED, - getUnixTime(customSnoozeTime) - ); - } - }, - toggleStatus(status, snoozedUntil) { - this.$store - .dispatch('toggleStatus', { - conversationId: this.currentChat?.id || this.contextMenuChatId, - status, - snoozedUntil, - }) - .then(() => { - this.$store.dispatch('setContextMenuChatId', null); - this.showAlert(this.$t('CONVERSATION.CHANGE_STATUS')); - }); - }, - hideCustomSnoozeModal() { - // if we select custom snooze and then the custom snooze modal is open - // Then if the custom snooze modal is closed and set the context menu chat id to null - this.$store.dispatch('setContextMenuChatId', null); - this.showCustomSnoozeModal = false; - }, }, }; diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue index 0f3a1a85758dc..4ccaa97dfab89 100644 --- a/app/javascript/dashboard/components/layout/Sidebar.vue +++ b/app/javascript/dashboard/components/layout/Sidebar.vue @@ -20,7 +20,7 @@ :teams="teams" :custom-views="customViews" :menu-config="activeSecondaryMenu" - :current-role="currentRole" + :current-user="currentUser" :is-on-chatwoot-cloud="isOnChatwootCloud" @add-label="showAddLabelPopup" @toggle-accounts="toggleAccountModal" @@ -37,7 +37,8 @@ import alertMixin from 'shared/mixins/alertMixin'; import PrimarySidebar from './sidebarComponents/Primary.vue'; import SecondarySidebar from './sidebarComponents/Secondary.vue'; import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins'; -import router from '../../routes'; +import router, { routesWithPermissions } from '../../routes'; +import { hasPermissions } from '../../helper/permissionsHelper'; export default { components: { @@ -98,9 +99,13 @@ export default { return getSidebarItems(this.accountId); }, primaryMenuItems() { + const userPermissions = this.currentUser.permissions; const menuItems = this.sideMenuConfig.primaryMenu; return menuItems.filter(menuItem => { - const isAvailableForTheUser = menuItem.roles.includes(this.currentRole); + const isAvailableForTheUser = hasPermissions( + routesWithPermissions[menuItem.toStateName], + userPermissions + ); if (!isAvailableForTheUser) { return false; diff --git a/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js b/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js index 7513e3d1c5642..92b8765c6ed53 100644 --- a/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js +++ b/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js @@ -9,7 +9,6 @@ const primaryMenuItems = accountId => [ featureFlag: FEATURE_FLAGS.INBOX_VIEW, toState: frontendURL(`accounts/${accountId}/inbox-view`), toStateName: 'inbox_view', - roles: ['administrator', 'agent'], }, { icon: 'chat', @@ -17,7 +16,6 @@ const primaryMenuItems = accountId => [ label: 'CONVERSATIONS', toState: frontendURL(`accounts/${accountId}/dashboard`), toStateName: 'home', - roles: ['administrator', 'agent'], }, { icon: 'book-contacts', @@ -26,7 +24,6 @@ const primaryMenuItems = accountId => [ featureFlag: FEATURE_FLAGS.CRM, toState: frontendURL(`accounts/${accountId}/contacts`), toStateName: 'contacts_dashboard', - roles: ['administrator', 'agent'], }, { icon: 'arrow-trending-lines', @@ -34,8 +31,7 @@ const primaryMenuItems = accountId => [ label: 'REPORTS', featureFlag: FEATURE_FLAGS.REPORTS, toState: frontendURL(`accounts/${accountId}/reports`), - toStateName: 'settings_account_reports', - roles: ['administrator'], + toStateName: 'account_overview_reports', }, { icon: 'megaphone', @@ -44,7 +40,6 @@ const primaryMenuItems = accountId => [ featureFlag: FEATURE_FLAGS.CAMPAIGNS, toState: frontendURL(`accounts/${accountId}/campaigns`), toStateName: 'ongoing_campaigns', - roles: ['administrator'], }, { icon: 'library', @@ -54,7 +49,6 @@ const primaryMenuItems = accountId => [ alwaysVisibleOnChatwootInstances: true, toState: frontendURL(`accounts/${accountId}/portals`), toStateName: 'default_portal_articles', - roles: ['administrator'], }, { icon: 'settings', @@ -62,7 +56,6 @@ const primaryMenuItems = accountId => [ label: 'SETTINGS', toState: frontendURL(`accounts/${accountId}/settings`), toStateName: 'settings_home', - roles: ['administrator', 'agent'], }, ]; diff --git a/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js b/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js index f450bd7a78474..f7d63f6a1aa41 100644 --- a/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js +++ b/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js @@ -24,7 +24,6 @@ const settings = accountId => ({ 'settings_inbox_list', 'settings_inbox_new', 'settings_inbox_show', - 'settings_inbox', 'settings_inboxes_add_agents', 'settings_inboxes_page_channel', 'settings_integrations_dashboard_apps', @@ -46,6 +45,9 @@ const settings = accountId => ({ icon: 'briefcase', label: 'ACCOUNT_SETTINGS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/general`), toStateName: 'general_settings_index', }, @@ -53,6 +55,9 @@ const settings = accountId => ({ icon: 'people', label: 'AGENTS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/agents/list`), toStateName: 'agent_list', featureFlag: FEATURE_FLAGS.AGENT_MANAGEMENT, @@ -61,6 +66,9 @@ const settings = accountId => ({ icon: 'people-team', label: 'TEAMS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/teams/list`), toStateName: 'settings_teams_list', featureFlag: FEATURE_FLAGS.TEAM_MANAGEMENT, @@ -69,6 +77,9 @@ const settings = accountId => ({ icon: 'mail-inbox-all', label: 'INBOXES', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/inboxes/list`), toStateName: 'settings_inbox_list', featureFlag: FEATURE_FLAGS.INBOX_MANAGEMENT, @@ -77,6 +88,9 @@ const settings = accountId => ({ icon: 'tag', label: 'LABELS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/labels/list`), toStateName: 'labels_list', featureFlag: FEATURE_FLAGS.LABELS, @@ -85,6 +99,9 @@ const settings = accountId => ({ icon: 'code', label: 'CUSTOM_ATTRIBUTES', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL( `accounts/${accountId}/settings/custom-attributes/list` ), @@ -95,6 +112,9 @@ const settings = accountId => ({ icon: 'automation', label: 'AUTOMATION', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/automation/list`), toStateName: 'automation_list', featureFlag: FEATURE_FLAGS.AUTOMATIONS, @@ -103,6 +123,9 @@ const settings = accountId => ({ icon: 'bot', label: 'AGENT_BOTS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, globalConfigFlag: 'csmlEditorHost', toState: frontendURL(`accounts/${accountId}/settings/agent-bots`), toStateName: 'agent_bots', @@ -112,6 +135,9 @@ const settings = accountId => ({ icon: 'flash-settings', label: 'MACROS', hasSubMenu: false, + meta: { + permissions: ['administrator', 'agent'], + }, toState: frontendURL(`accounts/${accountId}/settings/macros`), toStateName: 'macros_wrapper', featureFlag: FEATURE_FLAGS.MACROS, @@ -120,6 +146,9 @@ const settings = accountId => ({ icon: 'chat-multiple', label: 'CANNED_RESPONSES', hasSubMenu: false, + meta: { + permissions: ['administrator', 'agent'], + }, toState: frontendURL( `accounts/${accountId}/settings/canned-response/list` ), @@ -130,6 +159,9 @@ const settings = accountId => ({ icon: 'flash-on', label: 'INTEGRATIONS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/integrations`), toStateName: 'settings_integrations', featureFlag: FEATURE_FLAGS.INTEGRATIONS, @@ -138,6 +170,9 @@ const settings = accountId => ({ icon: 'star-emphasis', label: 'APPLICATIONS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/applications`), toStateName: 'settings_applications', featureFlag: FEATURE_FLAGS.INTEGRATIONS, @@ -146,6 +181,9 @@ const settings = accountId => ({ icon: 'key', label: 'AUDIT_LOGS', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/audit-log/list`), toStateName: 'auditlogs_list', isEnterpriseOnly: true, @@ -156,6 +194,9 @@ const settings = accountId => ({ icon: 'document-list-clock', label: 'SLA', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/sla/list`), toStateName: 'sla_list', isEnterpriseOnly: true, @@ -166,6 +207,9 @@ const settings = accountId => ({ icon: 'credit-card-person', label: 'BILLING', hasSubMenu: false, + meta: { + permissions: ['administrator'], + }, toState: frontendURL(`accounts/${accountId}/settings/billing`), toStateName: 'billing_settings_index', showOnlyOnCloud: true, diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue index f291d221e9d54..b4cd3ca90c652 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue @@ -29,6 +29,8 @@ import SecondaryNavItem from './SecondaryNavItem.vue'; import AccountContext from './AccountContext.vue'; import { mapGetters } from 'vuex'; import { FEATURE_FLAGS } from '../../../featureFlags'; +import { hasPermissions } from '../../../helper/permissionsHelper'; +import { routesWithPermissions } from '../../../routes'; export default { components: { @@ -60,9 +62,9 @@ export default { type: Object, default: () => {}, }, - currentRole: { - type: String, - default: '', + currentUser: { + type: Object, + default: () => {}, }, isOnChatwootCloud: { type: Boolean, @@ -80,16 +82,16 @@ export default { return this.customViews.filter(view => view.filter_type === 'contact'); }, accessibleMenuItems() { - if (!this.currentRole) { - return []; - } - const menuItemsFilteredByRole = this.menuConfig.menuItems.filter( - menuItem => - window.roleWiseRoutes[this.currentRole].indexOf( - menuItem.toStateName - ) > -1 + const menuItemsFilteredByPermissions = this.menuConfig.menuItems.filter( + menuItem => { + const { permissions: userPermissions = [] } = this.currentUser; + return hasPermissions( + routesWithPermissions[menuItem.toStateName], + userPermissions + ); + } ); - return menuItemsFilteredByRole.filter(item => { + return menuItemsFilteredByPermissions.filter(item => { if (item.showOnlyOnCloud) { return this.isOnChatwootCloud; } diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue index a7009523ab2f1..bc3b036463cf8 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue @@ -65,27 +65,29 @@ :show-child-count="showChildCount(child.count)" :child-item-count="child.count" /> - -
  • - - - {{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }} - - -
  • -
    + + +
  • + + + {{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }} + + +
  • +
    +
    @@ -105,9 +107,10 @@ import { isOnMentionsView, isOnUnattendedView, } from '../../../store/modules/conversations/helpers/actionHelpers'; +import Policy from '../../policy.vue'; export default { - components: { SecondaryChildNavItem }, + components: { SecondaryChildNavItem, Policy }, mixins: [adminMixin, configMixin], props: { menuItem: { diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/specs/AccountSelector.spec.js b/app/javascript/dashboard/components/layout/sidebarComponents/specs/AccountSelector.spec.js index b10ba9a579b4c..fb76e0ff4a9dd 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/specs/AccountSelector.spec.js +++ b/app/javascript/dashboard/components/layout/sidebarComponents/specs/AccountSelector.spec.js @@ -1,12 +1,12 @@ -import AccountSelector from '../AccountSelector'; +import AccountSelector from '../AccountSelector.vue'; import { createLocalVue, mount } from '@vue/test-utils'; import Vuex from 'vuex'; import VueI18n from 'vue-i18n'; import i18n from 'dashboard/i18n'; -import WootModal from 'dashboard/components/Modal'; -import WootModalHeader from 'dashboard/components/ModalHeader'; -import FluentIcon from 'shared/components/FluentIcon/DashboardIcon'; +import WootModal from 'dashboard/components/Modal.vue'; +import WootModalHeader from 'dashboard/components/ModalHeader.vue'; +import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue'; const localVue = createLocalVue(); localVue.component('woot-modal', WootModal); diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/specs/AgentDetails.spec.js b/app/javascript/dashboard/components/layout/sidebarComponents/specs/AgentDetails.spec.js index 073881a58c0f8..ca6c00a43ada5 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/specs/AgentDetails.spec.js +++ b/app/javascript/dashboard/components/layout/sidebarComponents/specs/AgentDetails.spec.js @@ -1,12 +1,12 @@ -import AgentDetails from '../AgentDetails'; +import AgentDetails from '../AgentDetails.vue'; import { createLocalVue, shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; import VueI18n from 'vue-i18n'; import VTooltip from 'v-tooltip'; import i18n from 'dashboard/i18n'; -import Thumbnail from 'dashboard/components/widgets/Thumbnail'; -import WootButton from 'dashboard/components/ui/WootButton'; +import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue'; +import WootButton from 'dashboard/components/ui/WootButton.vue'; const localVue = createLocalVue(); localVue.use(Vuex); localVue.use(VueI18n); diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/specs/NotificationBell.spec.js b/app/javascript/dashboard/components/layout/sidebarComponents/specs/NotificationBell.spec.js index 64d1fe7918eef..85596bb3e9d5c 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/specs/NotificationBell.spec.js +++ b/app/javascript/dashboard/components/layout/sidebarComponents/specs/NotificationBell.spec.js @@ -1,13 +1,15 @@ -import NotificationBell from '../NotificationBell'; +import NotificationBell from '../NotificationBell.vue'; import { createLocalVue, shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; import VueI18n from 'vue-i18n'; +import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue'; import i18n from 'dashboard/i18n'; const localVue = createLocalVue(); localVue.use(Vuex); localVue.use(VueI18n); +localVue.component('fluent-icon', FluentIcon); const i18nConfig = new VueI18n({ locale: 'en', @@ -27,7 +29,7 @@ describe('notificationBell', () => { beforeEach(() => { actions = { - showNotification: jest.fn(), + showNotification: vi.fn(), }; modules = { auth: { diff --git a/app/javascript/dashboard/components/layout/specs/AvailabilityStatus.spec.js b/app/javascript/dashboard/components/layout/specs/AvailabilityStatus.spec.js index 50d959b33b7ff..e6864d0520fb2 100644 --- a/app/javascript/dashboard/components/layout/specs/AvailabilityStatus.spec.js +++ b/app/javascript/dashboard/components/layout/specs/AvailabilityStatus.spec.js @@ -2,15 +2,21 @@ import AvailabilityStatus from '../AvailabilityStatus.vue'; import { createLocalVue, mount } from '@vue/test-utils'; import Vuex from 'vuex'; import VueI18n from 'vue-i18n'; +import VTooltip from 'v-tooltip'; + +import WootButton from 'dashboard/components/ui/WootButton.vue'; +import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue'; +import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue'; +import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader.vue'; +import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider.vue'; +import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue'; -import WootButton from 'dashboard/components/ui/WootButton'; -import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem'; -import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu'; -import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader'; -import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider'; import i18n from 'dashboard/i18n'; const localVue = createLocalVue(); +localVue.use(VTooltip, { + defaultHtml: false, +}); localVue.use(Vuex); localVue.use(VueI18n); localVue.component('woot-button', WootButton); @@ -18,12 +24,14 @@ localVue.component('woot-dropdown-header', WootDropdownHeader); localVue.component('woot-dropdown-menu', WootDropdownMenu); localVue.component('woot-dropdown-divider', WootDropdownDivider); localVue.component('woot-dropdown-item', WootDropdownItem); +localVue.component('fluent-icon', FluentIcon); const i18nConfig = new VueI18n({ locale: 'en', messages: i18n }); describe('AvailabilityStatus', () => { const currentAvailability = 'online'; const currentAccountId = '1'; + const currentUserAutoOffline = false; let store = null; let actions = null; let modules = null; @@ -31,7 +39,7 @@ describe('AvailabilityStatus', () => { beforeEach(() => { actions = { - updateAvailability: jest.fn(() => { + updateAvailability: vi.fn(() => { return Promise.resolve(); }), }; @@ -41,6 +49,7 @@ describe('AvailabilityStatus', () => { getters: { getCurrentUserAvailability: () => currentAvailability, getCurrentAccountId: () => currentAccountId, + getCurrentUserAutoOffline: () => currentUserAutoOffline, }, }, }; diff --git a/app/javascript/dashboard/components/policy.vue b/app/javascript/dashboard/components/policy.vue new file mode 100644 index 0000000000000..f888de5a29425 --- /dev/null +++ b/app/javascript/dashboard/components/policy.vue @@ -0,0 +1,23 @@ + + + diff --git a/app/javascript/dashboard/components/specs/SidemenuIcon.spec.js b/app/javascript/dashboard/components/specs/SidemenuIcon.spec.js index fea394572deac..5029eed48e5b7 100644 --- a/app/javascript/dashboard/components/specs/SidemenuIcon.spec.js +++ b/app/javascript/dashboard/components/specs/SidemenuIcon.spec.js @@ -1,5 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import SidemenuIcon from '../SidemenuIcon'; +import SidemenuIcon from '../SidemenuIcon.vue'; describe('SidemenuIcon', () => { test('matches snapshot', () => { diff --git a/app/javascript/dashboard/components/specs/__snapshots__/SidemenuIcon.spec.js.snap b/app/javascript/dashboard/components/specs/__snapshots__/SidemenuIcon.spec.js.snap index 577dbb3aae32b..0229af06e2944 100644 --- a/app/javascript/dashboard/components/specs/__snapshots__/SidemenuIcon.spec.js.snap +++ b/app/javascript/dashboard/components/specs/__snapshots__/SidemenuIcon.spec.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`SidemenuIcon matches snapshot 1`] = ` +exports[`SidemenuIcon > matches snapshot 1`] = `