Skip to content

Analytics#89

Merged
TatevikGr merged 6 commits into
devfrom
analytics
Jul 7, 2026
Merged

Analytics#89
TatevikGr merged 6 commits into
devfrom
analytics

Conversation

@TatevikGr

@TatevikGr TatevikGr commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added an Analytics page at /analytics with campaign/domain metrics, interactive charts, and performance tables.
    • Wired the page into both client-side navigation and server-side routing.
  • Bug Fixes

    • Improved settings/admin dialogs so their forms reliably reset when reopened.
  • Tests

    • Added integration coverage for the Analytics route and added/expanded Vue unit tests for related views/components.
  • Chores

    • Updated CI/pre-commit linting and formatting to streamline code checks.

Thanks for contributing to phpList!

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@TatevikGr, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f7a06bb4-7612-49d5-8b36-5204d9b32d9a

📥 Commits

Reviewing files that changed from the base of the PR and between 1caec5e and ddf9388.

📒 Files selected for processing (11)
  • .github/workflows/ci.yml
  • tests/Unit/assets/vue/views/CampaignEditView.spec.js
  • tests/Unit/assets/vue/views/CampaignsView.spec.js
  • tests/Unit/assets/vue/views/ListSubscribersView.spec.js
  • tests/Unit/assets/vue/views/ListsView.spec.js
  • tests/Unit/assets/vue/views/PublicPageEditView.spec.js
  • tests/Unit/assets/vue/views/PublicPagesView.spec.js
  • tests/Unit/assets/vue/views/SettingsView.spec.js
  • tests/Unit/assets/vue/views/SubscribersView.spec.js
  • tests/Unit/assets/vue/views/TemplateEditorView.spec.js
  • tests/Unit/assets/vue/views/TemplatesView.spec.js
📝 Walkthrough

Walkthrough

This PR adds an Analytics page end to end, including a Symfony controller, Vue route, dashboard view, and integration/unit tests. It also updates EditAdminModal watcher behavior and adds new Vitest suites for settings components and BouncesView. In addition, existing Vue spec imports were moved to the new asset paths, and Vitest/CI/pre-commit configuration was updated for the relocated tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AnalyticsController
  participant Session
  participant AnalyticsView
  participant statisticsClient

  User->>AnalyticsController: GET /analytics/
  AnalyticsController->>Session: read auth_token
  AnalyticsController-->>User: render SPA shell
  User->>AnalyticsView: mount
  AnalyticsView->>statisticsClient: load analytics datasets
  statisticsClient-->>AnalyticsView: statistics responses
  AnalyticsView-->>User: render dashboard
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the PR, but it is too vague to describe the main change clearly. Use a more specific title, such as "Add analytics dashboard and route", so teammates can understand the primary change at a glance.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch analytics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@assets/vue/views/AnalyticsView.vue`:
- Around line 71-125: The empty-state sections in AnalyticsView.vue are
rendering before loadAnalytics() finishes, which causes a misleading “no data”
flash on first paint. Update the loading logic in AnalyticsView and the affected
empty-state blocks to stay hidden until the initial fetch completes, either by
introducing a hasLoaded flag or by guarding all “No ... found” branches with
!isLoading. Make sure the fix is applied consistently across the
domainConfirmation view and the other matching empty states so the template only
shows “no data” after the data request has actually finished.
- Around line 306-320: The formatDate helper in AnalyticsView.vue currently
passes date-only strings straight into new Date, which can shift the displayed
day in some time zones. Update formatDate to detect YYYY-MM-DD inputs and
normalize them before formatting, while keeping the existing Unknown date
fallback and Intl.DateTimeFormat output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 137c0e28-face-46b0-ab9a-c3fa52df4efc

📥 Commits

Reviewing files that changed from the base of the PR and between 7a456a9 and 4bc4166.

📒 Files selected for processing (6)
  • assets/router/index.js
  • assets/vue/api.js
  • assets/vue/views/AnalyticsView.spec.js
  • assets/vue/views/AnalyticsView.vue
  • src/Controller/AnalyticsController.php
  • tests/Integration/Controller/AnalyticsControllerTest.php

Comment thread assets/vue/views/AnalyticsView.vue
Comment thread assets/vue/views/AnalyticsView.vue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/Unit/assets/vue/views/AnalyticsView.spec.js (1)

19-21: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Mock the same api module the component imports. In tests/Unit/assets/vue/views/AnalyticsView.spec.js:19, vi.mock('../api', ...) resolves to tests/Unit/assets/vue/api, while assets/vue/views/AnalyticsView.vue imports ../api from the source tree. Point the mock at the component’s resolved path so the test doesn’t fall through to the real client.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/assets/vue/views/AnalyticsView.spec.js` around lines 19 - 21, The
mock in AnalyticsView.spec.js is targeting the wrong module path, so
AnalyticsView.vue still resolves its own ../api import to the real client.
Update the vi.mock call to match the component’s actual resolved api module path
used by AnalyticsView.vue, and keep the statisticsClient stub there so the test
intercepts the same import the component loads.
♻️ Duplicate comments (2)
tests/Unit/assets/vue/components/settings/SettingsConfigs.spec.js (1)

12-18: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Same vi.mock relative-path mismatch as in the other settings modal/panel specs.

'../../api' resolves relative to this spec's own directory, landing on tests/Unit/assets/vue/api instead of the real assets/vue/api.js imported by SettingsConfigs.vue, so configClient calls likely bypass the mock.

🔧 Proposed fix
-vi.mock('../../api', () => ({
+vi.mock('../../../../../../assets/vue/api', () => ({
     default: {},
     configClient: {
         getConfigs: getConfigsMock,
         update: updateMock,
     },
 }))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/assets/vue/components/settings/SettingsConfigs.spec.js` around
lines 12 - 18, The SettingsConfigs spec is mocking the wrong relative module
path, so the `configClient` mock may not intercept the import used by
`SettingsConfigs.vue`. Update the `vi.mock` call in `SettingsConfigs.spec.js` to
target the same resolved `assets/vue/api` module path that the component
imports, matching the pattern used in the other settings modal/panel specs, so
`getConfigs` and `update` are properly mocked.
tests/Unit/assets/vue/components/settings/SettingsSubscriberAttributes.spec.js (1)

13-42: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Same vi.mock relative-path mismatch, affecting all 4 mocks in this file.

All of '../../api' (Line 13), '../base/BaseIcon.vue' (Line 20), './CreateSubscriberAttributeModal.vue' (Line 26), and './EditSubscriberAttributeModal.vue' (Line 35) are resolved relative to this spec file's directory, not relative to SettingsSubscriberAttributes.vue's actual location — so none of them likely intercept the real imports the component makes.

🔧 Proposed fix
-vi.mock('../../api', () => ({
+vi.mock('../../../../../../assets/vue/api', () => ({
     subscriberAttributesClient: {
         getAttributeDefinitions: getAttributeDefinitionsMock,
         deleteAttributeDefinition: deleteAttributeDefinitionMock,
     },
 }))

-vi.mock('../base/BaseIcon.vue', () => ({
+vi.mock('../../../../../../assets/vue/components/base/BaseIcon.vue', () => ({
     default: {
         template: '<span />',
     },
 }))

-vi.mock('./CreateSubscriberAttributeModal.vue', () => ({
+vi.mock('../../../../../../assets/vue/components/settings/CreateSubscriberAttributeModal.vue', () => ({
     default: defineComponent({
         name: 'CreateSubscriberAttributeModal',
         props: ['isOpen'],
         emits: ['close', 'created'],
         template: '<div />',
     }),
 }))

-vi.mock('./EditSubscriberAttributeModal.vue', () => ({
+vi.mock('../../../../../../assets/vue/components/settings/EditSubscriberAttributeModal.vue', () => ({
     default: defineComponent({
         name: 'EditSubscriberAttributeModal',
         props: ['isOpen', 'attribute'],
         emits: ['close', 'updated'],
         template: '<div />',
     }),
 }))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tests/Unit/assets/vue/components/settings/SettingsSubscriberAttributes.spec.js`
around lines 13 - 42, The four vi.mock calls in
SettingsSubscriberAttributes.spec.js are using paths relative to the spec file
instead of matching the imports used by SettingsSubscriberAttributes.vue, so
they won’t stub the real dependencies. Update the mocked module specifiers for
subscriberAttributesClient, BaseIcon, CreateSubscriberAttributeModal, and
EditSubscriberAttributeModal to the exact paths that
SettingsSubscriberAttributes.vue imports, using the existing mock names to
locate each entry.
🧹 Nitpick comments (3)
vitest.config.mjs (1)

9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the stale assets/vue/**/*.spec.js glob. It matches nothing now, so tests/Unit/assets/vue/**/*.spec.js is enough.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vitest.config.mjs` at line 9, The Vitest include list still contains the
stale assets/vue/**/*.spec.js glob, which no longer matches any files. Update
the include configuration in vitest.config.mjs to keep only the valid
tests/Unit/assets/vue/**/*.spec.js pattern so the test discovery remains
accurate.
tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js (1)

126-136: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test asserts nothing.

reloads attributes after create only emits 'created' and never asserts getAttributeDefinitionsMock was called again — it will pass regardless of whether the reload actually happens, defeating the purpose of the test.

🔧 Suggested fix
     it('reloads attributes after create', async () => {
         const wrapper = mountComponent()

         await flushPromises()

         getAttributeDefinitionsMock.mockClear()

         const createModal = wrapper.findComponent(CreateStub)
         expect(createModal.exists()).toBe(true)
         await createModal.vm.$emit('created')
+        await flushPromises()
+
+        expect(getAttributeDefinitionsMock).toHaveBeenCalled()
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js`
around lines 126 - 136, The reload-after-create test is not verifying any
behavior because it only emits created and never checks that the refresh
happens. Update the SettingsAdminAttributes.spec.js test around reloads
attributes after create to assert the expected reload behavior by checking
getAttributeDefinitionsMock is called again after the CreateStub emits created,
so the test fails if the refresh does not occur.
tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js (1)

96-116: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fragile selector for the "remove" button.

find(button => !button.text().trim()) picks the first button with empty text, which is brittle if markup adds an icon-only close button elsewhere in the modal. Low priority given "Chill" review mode, but worth a data-testid down the line.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js`
around lines 96 - 116, The remove-button lookup in
CreateSubscriberAttributeModal.spec.js is brittle because it uses an empty-text
button search that can accidentally match other icon-only buttons. Update the
test around mountComponent and the “adds and removes options” case to target a
stable identifier instead, such as a data-testid or a dedicated selector on the
remove control, and use that for removeButton so the assertion only clicks the
intended button.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/Unit/assets/vue/components/bounces/BouncePer.spec.js`:
- Around line 3-4: The vi.mock path in BouncePer.spec.js is targeting the wrong
module, so it does not intercept the api import used by BouncePer.vue. Update
the mock to match the component’s actual relative import for bouncesClient from
assets/vue/api, and keep the test import of BouncePer aligned so the mocked
module is the one exercised by the component under test.

In `@tests/Unit/assets/vue/components/settings/CreateAdminAttributeModal.spec.js`:
- Around line 10-14: The vi.mock target in CreateAdminAttributeModal.spec.js is
pointing at the wrong api module, so the component’s imported
adminAttributeClient is never replaced. Update the mock path to match the same
assets/vue/api module that CreateAdminAttributeModal.vue imports, and keep the
createAttributeDefinition stub wired through adminAttributeClient so the spec
uses the mocked client.

In `@tests/Unit/assets/vue/components/settings/CreateAdminModal.spec.js`:
- Around line 24-34: The mocks in CreateAdminModal.spec.js are targeting paths
that resolve relative to the test file instead of the modules imported by
CreateAdminModal.vue, so they never intercept the real dependencies. Update the
vi.mock calls to use the same module IDs that CreateAdminModal.vue imports for
the API client and BaseIcon component, and verify the mock names still match the
references used in the test setup.

In
`@tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js`:
- Around line 10-20: Fix the Vitest mocks in
CreateSubscriberAttributeModal.spec.js so they intercept the same import paths
used by the component. Update the vi.mock calls for the API module and
BaseIcon.vue to match the component’s assets/vue/api.js and
assets/vue/components/base/BaseIcon.vue imports, ensuring
subscriberAttributesClient.createAttributeDefinition is actually mocked when the
modal tests run.

In `@tests/Unit/assets/vue/components/settings/EditAdminModal.spec.js`:
- Around line 24-34: The test mocks in EditAdminModal.spec.js are using module
ids that do not match the imports in EditAdminModal.vue, so the real adminClient
and BaseIcon dependencies are still being loaded. Update the vi.mock targets to
match the exact import paths used by EditAdminModal (for adminClient) and the
BaseIcon component so the update tests fully stub those dependencies.

In
`@tests/Unit/assets/vue/components/settings/EditSubscriberAttributeModal.spec.js`:
- Around line 10-20: The mocks in EditSubscriberAttributeModal.spec.js are
targeting the wrong module ids, so they do not intercept the imports used by
EditSubscriberAttributeModal.vue. Update the vi.mock calls for the api client
and BaseIcon.vue to point at the same source module paths that the component
imports, using the existing identifiers subscriberAttributesClient and
updateAttributeDefinitionMock so the component receives the intended stubs.

In `@tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js`:
- Around line 13-24: The mocks in SettingsAdminAttributes.spec.js are resolving
from the test file location instead of the component’s import paths, so they do
not intercept the imports used by SettingsAdminAttributes.vue. Update the
vi.mock targets for the api client and BaseIcon.vue to match the component’s
actual relative imports (or switch both to a shared alias) so
adminAttributeClient and BaseIcon are properly mocked during the component test.

In `@tests/Unit/assets/vue/components/settings/SettingsAdmins.spec.js`:
- Around line 13-42: Update the mocks in SettingsAdmins.spec.js so they match
the import paths used by SettingsAdmins.vue rather than resolving from the spec
file; adjust the vi.mock targets for ../../api, ../base/BaseIcon.vue,
CreateAdminModal.vue, and EditAdminModal.vue to the component-relative asset
paths so the component under test actually receives the mocked modules.

In `@tests/Unit/assets/vue/components/subscribers/SubscriberModal.spec.js`:
- Around line 5-6: The test mock for subscribersClient is targeting a different
resolved module than the one SubscriberModal actually imports, so it will not
intercept the real dependency. Update the vi.mock setup in
SubscriberModal.spec.js to mock the same assets/vue/api module path used by
SubscriberModal, and keep the subscribersClient import aligned with that same
module resolution so the component under test receives the mocked client.

---

Outside diff comments:
In `@tests/Unit/assets/vue/views/AnalyticsView.spec.js`:
- Around line 19-21: The mock in AnalyticsView.spec.js is targeting the wrong
module path, so AnalyticsView.vue still resolves its own ../api import to the
real client. Update the vi.mock call to match the component’s actual resolved
api module path used by AnalyticsView.vue, and keep the statisticsClient stub
there so the test intercepts the same import the component loads.

---

Duplicate comments:
In `@tests/Unit/assets/vue/components/settings/SettingsConfigs.spec.js`:
- Around line 12-18: The SettingsConfigs spec is mocking the wrong relative
module path, so the `configClient` mock may not intercept the import used by
`SettingsConfigs.vue`. Update the `vi.mock` call in `SettingsConfigs.spec.js` to
target the same resolved `assets/vue/api` module path that the component
imports, matching the pattern used in the other settings modal/panel specs, so
`getConfigs` and `update` are properly mocked.

In
`@tests/Unit/assets/vue/components/settings/SettingsSubscriberAttributes.spec.js`:
- Around line 13-42: The four vi.mock calls in
SettingsSubscriberAttributes.spec.js are using paths relative to the spec file
instead of matching the imports used by SettingsSubscriberAttributes.vue, so
they won’t stub the real dependencies. Update the mocked module specifiers for
subscriberAttributesClient, BaseIcon, CreateSubscriberAttributeModal, and
EditSubscriberAttributeModal to the exact paths that
SettingsSubscriberAttributes.vue imports, using the existing mock names to
locate each entry.

---

Nitpick comments:
In
`@tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js`:
- Around line 96-116: The remove-button lookup in
CreateSubscriberAttributeModal.spec.js is brittle because it uses an empty-text
button search that can accidentally match other icon-only buttons. Update the
test around mountComponent and the “adds and removes options” case to target a
stable identifier instead, such as a data-testid or a dedicated selector on the
remove control, and use that for removeButton so the assertion only clicks the
intended button.

In `@tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js`:
- Around line 126-136: The reload-after-create test is not verifying any
behavior because it only emits created and never checks that the refresh
happens. Update the SettingsAdminAttributes.spec.js test around reloads
attributes after create to assert the expected reload behavior by checking
getAttributeDefinitionsMock is called again after the CreateStub emits created,
so the test fails if the refresh does not occur.

In `@vitest.config.mjs`:
- Line 9: The Vitest include list still contains the stale
assets/vue/**/*.spec.js glob, which no longer matches any files. Update the
include configuration in vitest.config.mjs to keep only the valid
tests/Unit/assets/vue/**/*.spec.js pattern so the test discovery remains
accurate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 01cd48f1-4350-4439-a850-7a50220d3540

📥 Commits

Reviewing files that changed from the base of the PR and between 7f1932e and e955297.

📒 Files selected for processing (54)
  • .github/workflows/ci.yml
  • .husky/pre-commit
  • assets/vue/components/settings/EditAdminModal.vue
  • tests/Unit/assets/vue/components/base/BaseBadge.spec.js
  • tests/Unit/assets/vue/components/base/BaseButton.spec.js
  • tests/Unit/assets/vue/components/base/BaseCard.spec.js
  • tests/Unit/assets/vue/components/base/BaseIcon.spec.js
  • tests/Unit/assets/vue/components/base/BaseProgressBar.spec.js
  • tests/Unit/assets/vue/components/base/CkEditorField.spec.js
  • tests/Unit/assets/vue/components/bounces/BounceOverview.spec.js
  • tests/Unit/assets/vue/components/bounces/BouncePer.spec.js
  • tests/Unit/assets/vue/components/bounces/BounceRules.spec.js
  • tests/Unit/assets/vue/components/bounces/BouncesActionsPanel.spec.js
  • tests/Unit/assets/vue/components/campaigns/CampaignDirectory.spec.js
  • tests/Unit/assets/vue/components/campaigns/ViewCampaignModal.spec.js
  • tests/Unit/assets/vue/components/dashboard/CampaignsTable.spec.js
  • tests/Unit/assets/vue/components/dashboard/KpiCard.spec.js
  • tests/Unit/assets/vue/components/dashboard/KpiGrid.spec.js
  • tests/Unit/assets/vue/components/dashboard/PerformanceChartCard.spec.js
  • tests/Unit/assets/vue/components/dashboard/QuickActionsCard.spec.js
  • tests/Unit/assets/vue/components/dashboard/RecentCampaignsCard.spec.js
  • tests/Unit/assets/vue/components/lists/AddSubscribersModal.spec.js
  • tests/Unit/assets/vue/components/lists/CreateListModal.spec.js
  • tests/Unit/assets/vue/components/lists/EditListModal.spec.js
  • tests/Unit/assets/vue/components/lists/ListDirectory.spec.js
  • tests/Unit/assets/vue/components/lists/ListSubscribersExportPanel.spec.js
  • tests/Unit/assets/vue/components/public-pages/PublicPageEditor.spec.js
  • tests/Unit/assets/vue/components/public-pages/PublicPagesDirectory.spec.js
  • tests/Unit/assets/vue/components/settings/CreateAdminAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/CreateAdminModal.spec.js
  • tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/EditAdminAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/EditAdminModal.spec.js
  • tests/Unit/assets/vue/components/settings/EditSubscriberAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsActionsPanel.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsAdmins.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsConfigs.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsSubscriberAttributes.spec.js
  • tests/Unit/assets/vue/components/sidebar/AppSidebar.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarLogo.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarNavItem.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarNavSection.spec.js
  • tests/Unit/assets/vue/components/subscribers/ImportResult.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberDirectory.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberFilters.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberModal.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberTable.spec.js
  • tests/Unit/assets/vue/components/templates/TemplateLibrary.spec.js
  • tests/Unit/assets/vue/layouts/AdminLayout.spec.js
  • tests/Unit/assets/vue/views/AnalyticsView.spec.js
  • tests/Unit/assets/vue/views/BouncesView.spec.js
  • tests/Unit/assets/vue/views/DashboardView.spec.js
  • vitest.config.mjs
✅ Files skipped from review due to trivial changes (21)
  • tests/Unit/assets/vue/components/base/BaseButton.spec.js
  • tests/Unit/assets/vue/components/base/BaseCard.spec.js
  • .husky/pre-commit
  • tests/Unit/assets/vue/views/BouncesView.spec.js
  • tests/Unit/assets/vue/components/public-pages/PublicPagesDirectory.spec.js
  • tests/Unit/assets/vue/components/base/BaseProgressBar.spec.js
  • tests/Unit/assets/vue/components/lists/EditListModal.spec.js
  • tests/Unit/assets/vue/components/campaigns/ViewCampaignModal.spec.js
  • tests/Unit/assets/vue/layouts/AdminLayout.spec.js
  • tests/Unit/assets/vue/components/subscribers/ImportResult.spec.js
  • tests/Unit/assets/vue/components/public-pages/PublicPageEditor.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberTable.spec.js
  • tests/Unit/assets/vue/components/base/BaseIcon.spec.js
  • tests/Unit/assets/vue/components/sidebar/AppSidebar.spec.js
  • tests/Unit/assets/vue/components/templates/TemplateLibrary.spec.js
  • tests/Unit/assets/vue/components/lists/ListDirectory.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberFilters.spec.js
  • tests/Unit/assets/vue/components/base/BaseBadge.spec.js
  • tests/Unit/assets/vue/components/settings/EditAdminAttributeModal.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarNavItem.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsActionsPanel.spec.js

Comment thread tests/Unit/assets/vue/components/bounces/BouncePer.spec.js
Comment thread tests/Unit/assets/vue/components/settings/CreateAdminAttributeModal.spec.js Outdated
Comment thread tests/Unit/assets/vue/components/settings/CreateAdminModal.spec.js Outdated
Comment thread tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js Outdated
Comment thread tests/Unit/assets/vue/components/settings/EditAdminModal.spec.js Outdated
Comment thread tests/Unit/assets/vue/components/settings/EditSubscriberAttributeModal.spec.js Outdated
Comment thread tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js Outdated
Comment thread tests/Unit/assets/vue/components/settings/SettingsAdmins.spec.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/Unit/assets/vue/views/DashboardView.spec.js (1)

40-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Only the error path is covered — worth a happy-path test too.

This suite nails the error-banner scenario, but there's no test asserting DashboardView renders correctly with valid data-dashboard-stats (e.g., verifying KpiGrid/PerformanceChartCard/etc. mount without the alert). Since those are already mocked, a quick second it block would round out coverage cheaply.

🧪 Example additional test
it('renders dashboard blocks when stats are provided without an error', async () => {
    document.body.innerHTML = `
      <div
        id="vue-app"
        data-dashboard-stats='{"foo":"bar"}'
      ></div>
    `

    vi.resetModules()

    const { default: DashboardView } = await import('../../../../../assets/vue/views/DashboardView.vue')

    const wrapper = mount(DashboardView)

    expect(wrapper.find('[role="alert"]').exists()).toBe(false)
    expect(wrapper.findAll('.dashboard-block').length).toBeGreaterThan(0)
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/assets/vue/views/DashboardView.spec.js` around lines 40 - 63, Add
a happy-path test for DashboardView alongside the existing error-banner case: in
DashboardView.spec.js, mount the component with valid data-dashboard-stats and
no data-dashboard-error, then assert the alert is absent and the dashboard
content renders (for example, mocked blocks like KpiGrid/PerformanceChartCard
appear). Use the existing DashboardView import/mount pattern so the new test
matches the current suite setup.
tests/Unit/assets/vue/views/CampaignEditView.spec.js (1)

391-440: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead beforeEach mount in "Format Tab" block.

The beforeEach at Line 392-400 mounts a component and clicks to step 2, but the wrapper is discarded — every test below re-mounts and re-clicks independently. This beforeEach does nothing but waste cycles on every test run.

🧹 Suggested cleanup
   describe('Format Tab (Step 2)', () => {
-    beforeEach(async () => {
-      const wrapper = mountComponent()
-      await flushPromises()
-
-      const stepButtons = wrapper.findAll('button')
-      const step2Button = stepButtons.find((btn) => btn.text().includes('2'))
-      await step2Button.trigger('click')
-      await wrapper.vm.$nextTick()
-    })
-
     it('displays template dropdown', async () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/assets/vue/views/CampaignEditView.spec.js` around lines 391 - 440,
The Format Tab (Step 2) suite has a redundant beforeEach that mounts the
component and switches to step 2, but each test in CampaignEditView.spec.js
already does its own mountComponent/flushPromises/click flow. Remove the unused
beforeEach from the Format Tab describe block and keep the setup only in the
individual tests that actually need the wrapper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/Unit/assets/vue/views/TemplateEditorView.spec.js`:
- Around line 6-12: The module-level mockRoute object in
TemplateEditorView.spec.js is being mutated by one test and not reset, which can
leak state into other tests. Update the shared test setup so the original
edit-mode shape of mockRoute is restored before each test, alongside
vi.restoreAllMocks(), and avoid relying on test order; use the mockRoute object
and the template-edit/template-create test cases to locate the affected setup
and mutation points.

---

Nitpick comments:
In `@tests/Unit/assets/vue/views/CampaignEditView.spec.js`:
- Around line 391-440: The Format Tab (Step 2) suite has a redundant beforeEach
that mounts the component and switches to step 2, but each test in
CampaignEditView.spec.js already does its own mountComponent/flushPromises/click
flow. Remove the unused beforeEach from the Format Tab describe block and keep
the setup only in the individual tests that actually need the wrapper.

In `@tests/Unit/assets/vue/views/DashboardView.spec.js`:
- Around line 40-63: Add a happy-path test for DashboardView alongside the
existing error-banner case: in DashboardView.spec.js, mount the component with
valid data-dashboard-stats and no data-dashboard-error, then assert the alert is
absent and the dashboard content renders (for example, mocked blocks like
KpiGrid/PerformanceChartCard appear). Use the existing DashboardView
import/mount pattern so the new test matches the current suite setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4920ac2a-b596-46d4-af09-d10776d008d8

📥 Commits

Reviewing files that changed from the base of the PR and between e955297 and 1caec5e.

📒 Files selected for processing (65)
  • .github/workflows/ci.yml
  • .husky/pre-commit
  • assets/vue/components/dashboard/KpiCard.vue
  • assets/vue/views/DashboardView.spec.js
  • tests/Unit/assets/vue/components/base/BaseBadge.spec.js
  • tests/Unit/assets/vue/components/base/BaseButton.spec.js
  • tests/Unit/assets/vue/components/base/BaseCard.spec.js
  • tests/Unit/assets/vue/components/base/BaseIcon.spec.js
  • tests/Unit/assets/vue/components/base/BaseProgressBar.spec.js
  • tests/Unit/assets/vue/components/base/CkEditorField.spec.js
  • tests/Unit/assets/vue/components/bounces/BounceOverview.spec.js
  • tests/Unit/assets/vue/components/bounces/BouncePer.spec.js
  • tests/Unit/assets/vue/components/bounces/BounceRules.spec.js
  • tests/Unit/assets/vue/components/bounces/BouncesActionsPanel.spec.js
  • tests/Unit/assets/vue/components/campaigns/CampaignDirectory.spec.js
  • tests/Unit/assets/vue/components/campaigns/ViewCampaignModal.spec.js
  • tests/Unit/assets/vue/components/dashboard/CampaignsTable.spec.js
  • tests/Unit/assets/vue/components/dashboard/KpiCard.spec.js
  • tests/Unit/assets/vue/components/dashboard/KpiGrid.spec.js
  • tests/Unit/assets/vue/components/dashboard/PerformanceChartCard.spec.js
  • tests/Unit/assets/vue/components/dashboard/QuickActionsCard.spec.js
  • tests/Unit/assets/vue/components/dashboard/RecentCampaignsCard.spec.js
  • tests/Unit/assets/vue/components/lists/AddSubscribersModal.spec.js
  • tests/Unit/assets/vue/components/lists/CreateListModal.spec.js
  • tests/Unit/assets/vue/components/lists/EditListModal.spec.js
  • tests/Unit/assets/vue/components/lists/ListDirectory.spec.js
  • tests/Unit/assets/vue/components/lists/ListSubscribersExportPanel.spec.js
  • tests/Unit/assets/vue/components/public-pages/PublicPageEditor.spec.js
  • tests/Unit/assets/vue/components/public-pages/PublicPagesDirectory.spec.js
  • tests/Unit/assets/vue/components/settings/CreateAdminAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/CreateAdminModal.spec.js
  • tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/EditAdminAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/EditAdminModal.spec.js
  • tests/Unit/assets/vue/components/settings/EditSubscriberAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsActionsPanel.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsAdmins.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsConfigs.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsSubscriberAttributes.spec.js
  • tests/Unit/assets/vue/components/sidebar/AppSidebar.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarLogo.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarNavItem.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarNavSection.spec.js
  • tests/Unit/assets/vue/components/subscribers/ImportResult.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberDirectory.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberFilters.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberModal.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberTable.spec.js
  • tests/Unit/assets/vue/components/templates/TemplateLibrary.spec.js
  • tests/Unit/assets/vue/layouts/AdminLayout.spec.js
  • tests/Unit/assets/vue/views/AnalyticsView.spec.js
  • tests/Unit/assets/vue/views/BouncesView.spec.js
  • tests/Unit/assets/vue/views/CampaignEditView.spec.js
  • tests/Unit/assets/vue/views/CampaignsView.spec.js
  • tests/Unit/assets/vue/views/DashboardView.spec.js
  • tests/Unit/assets/vue/views/ListSubscribersView.spec.js
  • tests/Unit/assets/vue/views/ListsView.spec.js
  • tests/Unit/assets/vue/views/PublicPageEditView.spec.js
  • tests/Unit/assets/vue/views/PublicPagesView.spec.js
  • tests/Unit/assets/vue/views/SettingsView.spec.js
  • tests/Unit/assets/vue/views/SubscribersView.spec.js
  • tests/Unit/assets/vue/views/TemplateEditorView.spec.js
  • tests/Unit/assets/vue/views/TemplatesView.spec.js
  • vitest.config.mjs
💤 Files with no reviewable changes (1)
  • assets/vue/views/DashboardView.spec.js
✅ Files skipped from review due to trivial changes (18)
  • tests/Unit/assets/vue/components/subscribers/SubscriberTable.spec.js
  • tests/Unit/assets/vue/views/TemplatesView.spec.js
  • tests/Unit/assets/vue/components/dashboard/KpiCard.spec.js
  • tests/Unit/assets/vue/components/base/CkEditorField.spec.js
  • tests/Unit/assets/vue/components/base/BaseIcon.spec.js
  • tests/Unit/assets/vue/components/public-pages/PublicPagesDirectory.spec.js
  • tests/Unit/assets/vue/components/bounces/BouncePer.spec.js
  • tests/Unit/assets/vue/components/dashboard/RecentCampaignsCard.spec.js
  • tests/Unit/assets/vue/components/campaigns/ViewCampaignModal.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberFilters.spec.js
  • tests/Unit/assets/vue/components/lists/CreateListModal.spec.js
  • tests/Unit/assets/vue/components/lists/AddSubscribersModal.spec.js
  • .husky/pre-commit
  • tests/Unit/assets/vue/components/sidebar/SidebarNavSection.spec.js
  • tests/Unit/assets/vue/components/dashboard/CampaignsTable.spec.js
  • tests/Unit/assets/vue/components/bounces/BounceOverview.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarNavItem.spec.js
  • tests/Unit/assets/vue/components/dashboard/PerformanceChartCard.spec.js
🚧 Files skipped from review as they are similar to previous changes (33)
  • tests/Unit/assets/vue/components/base/BaseCard.spec.js
  • tests/Unit/assets/vue/components/base/BaseBadge.spec.js
  • .github/workflows/ci.yml
  • tests/Unit/assets/vue/views/BouncesView.spec.js
  • tests/Unit/assets/vue/components/sidebar/AppSidebar.spec.js
  • tests/Unit/assets/vue/components/sidebar/SidebarLogo.spec.js
  • tests/Unit/assets/vue/components/base/BaseButton.spec.js
  • tests/Unit/assets/vue/components/templates/TemplateLibrary.spec.js
  • tests/Unit/assets/vue/components/dashboard/KpiGrid.spec.js
  • tests/Unit/assets/vue/components/campaigns/CampaignDirectory.spec.js
  • tests/Unit/assets/vue/layouts/AdminLayout.spec.js
  • tests/Unit/assets/vue/components/dashboard/QuickActionsCard.spec.js
  • tests/Unit/assets/vue/components/base/BaseProgressBar.spec.js
  • tests/Unit/assets/vue/components/subscribers/ImportResult.spec.js
  • tests/Unit/assets/vue/components/lists/ListSubscribersExportPanel.spec.js
  • tests/Unit/assets/vue/components/lists/EditListModal.spec.js
  • tests/Unit/assets/vue/components/lists/ListDirectory.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberModal.spec.js
  • tests/Unit/assets/vue/components/bounces/BounceRules.spec.js
  • tests/Unit/assets/vue/components/subscribers/SubscriberDirectory.spec.js
  • tests/Unit/assets/vue/components/settings/EditAdminModal.spec.js
  • tests/Unit/assets/vue/views/AnalyticsView.spec.js
  • tests/Unit/assets/vue/components/settings/CreateAdminAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsSubscriberAttributes.spec.js
  • tests/Unit/assets/vue/components/settings/EditAdminAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsConfigs.spec.js
  • vitest.config.mjs
  • tests/Unit/assets/vue/components/settings/CreateSubscriberAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsAdminAttributes.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsAdmins.spec.js
  • tests/Unit/assets/vue/components/settings/EditSubscriberAttributeModal.spec.js
  • tests/Unit/assets/vue/components/settings/SettingsActionsPanel.spec.js
  • tests/Unit/assets/vue/components/settings/CreateAdminModal.spec.js

Comment on lines +6 to +12
const mockRoute = {
name: 'template-edit',
params: {
templateId: '1',
},
query: {},
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Shared mockRoute mutated without reset — test pollution risk.

mockRoute is a module-level object mutated in-place by the last test (mockRoute.name = 'template-create'; mockRoute.params.templateId = ''), but beforeEach only calls vi.restoreAllMocks(), never resetting mockRoute back to its original edit-mode shape. This works today only because the mutating test happens to run last; reordering tests, adding a new test afterward, or running with --sequence.shuffle will leak create-mode state into unrelated tests.

🔧 Suggested fix
+const defaultRoute = {
+    name: 'template-edit',
+    params: { templateId: '1' },
+    query: {},
+}
+
-const mockRoute = {
-    name: 'template-edit',
-    params: {
-        templateId: '1',
-    },
-    query: {},
-}
+const mockRoute = { ...defaultRoute }

 describe('TemplateEditView', () => {
     beforeEach(() => {
         vi.restoreAllMocks()
+        Object.assign(mockRoute, JSON.parse(JSON.stringify(defaultRoute)))

Also applies to: 100-112

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/assets/vue/views/TemplateEditorView.spec.js` around lines 6 - 12,
The module-level mockRoute object in TemplateEditorView.spec.js is being mutated
by one test and not reset, which can leak state into other tests. Update the
shared test setup so the original edit-mode shape of mockRoute is restored
before each test, alongside vi.restoreAllMocks(), and avoid relying on test
order; use the mockRoute object and the template-edit/template-create test cases
to locate the affected setup and mutation points.

@TatevikGr TatevikGr merged commit c5d0752 into dev Jul 7, 2026
3 checks passed
@TatevikGr TatevikGr deleted the analytics branch July 7, 2026 15:33
TatevikGr added a commit that referenced this pull request Jul 7, 2026
* Feat: statistics

* Feat: statistics

* After review 0

* Add tests

* Add tests 1

* Fix workflow file, add more tests

---------

Co-authored-by: Tatevik <tatevikg1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants