[DI-30013]: automate E2E coverage for CloudPulse Logs service dashboard - #639
Conversation
…i/manager into logs-integration-feature_venky_February_26
…service dashboard
There was a problem hiding this comment.
Pull request overview
This pull request adds end-to-end test coverage for the CloudPulse Logs service dashboard, enabling monitoring of log delivery status with metrics like success/error upload counts and error rates.
Changes:
- Added comprehensive E2E tests for Logs dashboard functionality including widget display, filtering, preferences, and user interactions
- Integrated Logs service type across CloudPulse components with proper mocking and test data
- Added status code filter support with validation and UI components
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/manager/src/queries/cloudpulse/queries.ts | Added deliveryQueries import and logs case for resource resolution |
| packages/manager/src/mocks/serverHandlers.ts | Added mock endpoints for logs service, streams, dashboards, and metric definitions |
| packages/manager/src/mocks/presets/crud/handlers/cloudpulsedashboards.ts | New comprehensive mock handlers file for CloudPulse dashboards (974 lines) |
| packages/manager/src/mocks/presets/crud/delivery.ts | Integrated CloudPulse handlers into delivery CRUD preset |
| packages/manager/src/features/Delivery/Streams/StreamForm/StreamEdit.tsx | Embedded CloudPulseDashboardWithFilters component in stream edit page |
| packages/manager/src/features/CloudPulse/shared/*.tsx | Added logs service support and status_code filter to dashboard components |
| packages/manager/src/features/CloudPulse/Utils/*.ts | Added status code validation, constants, and filter configurations |
| packages/manager/cypress/e2e/core/cloudpulse/*.spec.ts | Three comprehensive test files covering template building, preferences, and widgets |
| packages/manager/cypress/fixtures/Logs.xlsx | Excel fixture for generating JSON test data |
| packages/api-v4/src/cloudpulse/types.ts | Added 'logs' to CloudPulseServiceType union |
Comments suppressed due to low confidence (1)
packages/manager/src/features/CloudPulse/Utils/constants.ts:139
- The
PLACEHOLDER_TEXTmapping is missing an entry forSTATUS_CODE, even thoughSTATUS_CODE_PLACEHOLDER_TEXTis defined. This will cause the status code input to not have a placeholder text. Add the missing mapping:
[STATUS_CODE]: STATUS_CODE_PLACEHOLDER_TEXT,
export const PLACEHOLDER_TEXT: Record<string, string> = {
[PORT]: PORTS_PLACEHOLDER_TEXT,
[INTERFACE_ID]: INTERFACE_IDS_PLACEHOLDER_TEXT,
};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** | ||
| * @file Integration Tests for CloudPulse Volume(blockstorage) Dashboard. | ||
| */ |
There was a problem hiding this comment.
The JSDoc comment header is incorrect. It states "Integration Tests for CloudPulse Volume(blockstorage) Dashboard" but this file actually contains tests for the CloudPulse Logs Dashboard. The comment should be updated to reflect the correct service type being tested.
| export const areValidStatusCodes = ( | ||
| statusCodes: string | ||
| ): string | undefined => { | ||
| if (statusCodes === '') { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (statusCodes.length > 100) { | ||
| return STATUS_CODE_LIMIT_ERROR_MESSAGE; | ||
| } | ||
| if (statusCodes.startsWith(',')) { | ||
| return STATUS_CODE_LEADING_COMMA_ERROR_MESSAGE; | ||
| } | ||
|
|
||
| if (statusCodes.includes(',,')) { | ||
| return STATUS_CODE_CONSECUTIVE_COMMAS_ERROR_MESSAGE; | ||
| } | ||
|
|
||
| if (!/^[\d,]+$/.test(statusCodes)) { | ||
| return STATUS_CODE_ERROR_MESSAGE; | ||
| } | ||
|
|
||
| return undefined; | ||
| }; |
There was a problem hiding this comment.
The areValidStatusCodes function is missing important validation checks that are present in the similar arePortsValid function:
- No validation for leading zeros (status codes like "0200" should be invalid)
- No range validation (HTTP status codes should be in the range 100-599)
Consider adding these validations for better data integrity. For example:
- Check for leading zeros:
if (code.startsWith('0') && code !== '0') - Validate range:
const codeNum = parseInt(code, 10); if (codeNum < 100 || codeNum > 599)
Even though the STATUS_CODE_LEADING_ZERO_ERROR_MESSAGE constant is defined, it's not being used in the validation logic.
…service dashboard
…service dashboard
…rvice-preference-verification.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…hboards.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…hboards.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…cation.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
run_cypress |
…service dashboard
…github.com/agorthi-akamai/manager into logs-integration-feature_venky_February_26
…service dashboard
…service dashboard
…service dashboard
…service dashboard
|
run_cypress |
…service dashboard
|
run_cypress |
…service dashboard
|
run_cypress |
|
run_cypress |
|
run_cypress |
|
run_cypress |
|
run_cypress |
|
@ACLPManager/reviewers : please review this |
|
run_cypress |
|
run_cypress |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 14 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const mockGetStreams = (streams: Stream[]): Cypress.Chainable<null> => { | ||
| return cy.intercept( | ||
| 'GET', | ||
| apiMatcher('monitor/streams?page_size=500'), | ||
| paginateResponse(streams) | ||
| ); |
There was a problem hiding this comment.
mockGetStreams intercept matches only the exact URL monitor/streams?page_size=500, which is brittle if the app requests streams with default pagination (e.g. page/page_size) or any other query params. Consider matching monitor/streams* (or accepting params and building the matcher accordingly) so stream-related E2E tests don’t silently miss the intercept and hit the network/MSW fallback.
| mockGetCloudPulseMetricDefinitions(serviceType, metricDefinitions); | ||
| mockGetCloudPulseDashboards(serviceType, [dashboard]).as('fetchDashboard'); | ||
| mockGetCloudPulseServices([serviceType]).as('fetchServices'); | ||
| mockGetCloudPulseDashboard(id, dashboard).as('fetchDashboard'); | ||
| mockCreateCloudPulseJWEToken(serviceType); |
There was a problem hiding this comment.
Both mockGetCloudPulseDashboards(...).as('fetchDashboard') and mockGetCloudPulseDashboard(...).as('fetchDashboard') register the same alias. This will cause cy.wait('@fetchDashboard') to resolve against whichever intercept was registered last, and the subsequent assertion treats the response as a paginated list (response.body.data), which will fail if it resolves to the single-dashboard response. Use distinct aliases (e.g. fetchDashboards vs fetchDashboardById) and update the waits/assertions accordingly.
| const initialPreference = { | ||
| dashboardId: 11, | ||
| groupBy: ['entity_id', 'status_code'], | ||
| resources: ['1'], | ||
| status_code: '200', | ||
| widgets: { |
There was a problem hiding this comment.
initialPreference.dashboardId is hard-coded to 11 even though this spec already pulls the dashboard id from widgetDetails.logs. This creates an easy-to-miss mismatch if widgetDetails.logs.id changes. Use the id constant (and/or derive it from the constructed dashboard) to keep the preference payload aligned with the test’s mocked dashboard.
| it('MetricDefinitionResponse JSON response', () => { | ||
| cy.writeFile(METRIC_DEF_PATH, metricDefinitionResponse, { flag: 'w' }); | ||
| }); | ||
|
|
||
| // eslint-disable-next-line sonarjs/no-skipped-tests | ||
|
|
||
| describe('Dashboard Builder Response', () => { | ||
| it('has correct top-level structure', () => { | ||
| expect(dashboardResponse.page).to.equal(1); | ||
| expect(dashboardResponse.pages).to.equal(1); | ||
| expect(dashboardResponse.results).to.equal(1); | ||
| expect(dashboardResponse.data).to.have.length(1); | ||
| }); | ||
|
|
||
| it('dashboard has correct fields', () => { | ||
| const dashboard = dashboardResponse.data[0]; | ||
| expect(dashboard).to.have.property('label').and.not.be.empty; | ||
| expect(dashboard).to.have.property('type', 'standard'); | ||
| expect(dashboard).to.have.property('service_type'); | ||
| expect(dashboard).to.have.property('group_by').and.be.an('array'); | ||
| expect(dashboard).to.have.property('widgets').and.be.an('array'); | ||
| }); | ||
|
|
||
| it('dashboard has correct number of widgets from Excel', () => { | ||
| const dashboard = dashboardResponse.data[0]; | ||
| expect(dashboard.widgets).to.have.length(6); // 8 widgets in Centralized Dashboard sheet | ||
| }); | ||
|
|
||
| it('each widget has required fields', () => { | ||
| dashboardResponse.data[0].widgets.forEach((widget) => { | ||
| expect(widget).to.have.property('metric').and.not.be.empty; | ||
| expect(widget).to.have.property('unit'); | ||
| expect(widget).to.have.property('label').and.not.be.empty; | ||
| expect(widget).to.have.property('color'); | ||
| expect(widget).to.have.property('size').and.be.a('number'); | ||
| expect(widget).to.have.property('chart_type'); | ||
| expect(widget).to.have.property('y_label'); | ||
| expect(widget).to.have.property('aggregate_function'); | ||
| }); | ||
| }); | ||
|
|
||
|
|
||
| }); | ||
| // eslint-disable-next-line sonarjs/no-skipped-tests | ||
|
|
||
| it.skip('MetricDefinitionResponse JSON response', () => { | ||
| cy.writeFile( | ||
| 'cypress/e2e/core/cloudpulse/api-response/netloadbalancer-metric-definition.json', | ||
| metricDefinitionResponse | ||
| ); | ||
| }); | ||
| // eslint-disable-next-line sonarjs/no-skipped-tests | ||
|
|
||
| it.skip('dashboardResponse JSON response', () => { | ||
| cy.writeFile( | ||
| 'cypress/e2e/core/cloudpulse/api-response/netloadbalancer-dashboard-response.json', | ||
| dashboardResponse | ||
| ); | ||
| it('dashboardResponse JSON response', () => { | ||
| cy.writeFile(DASHBOARD_PATH, dashboardResponse, { flag: 'w' }); | ||
| }); |
There was a problem hiding this comment.
This spec no longer asserts anything about the generated responses; it only writes files to the repo workspace during the E2E run. Since cypress/e2e/core/**/*.spec.* runs in CI, this adds side effects (file writes) without validating behavior and can mask regressions. Consider restoring structural/content assertions and moving JSON generation to a separate, manually-run script (or at least skipping these tests in CI).
|
run_cypress |
|
run_cypress_metrics |
|
run_cypress_metrics |
Description 📝
Highlight the Pull Request's context and intentions.
Changes 🔄
List any change(s) relevant to the reviewer.
Scope 🚢
Upon production release, changes in this PR will be visible to:
Target release date 🗓️
Please specify a release date (and environment, if applicable) to guarantee timely review of this PR. If exact date is not known, please approximate and update it as needed.
Preview 📷
Include a screenshot
<img src="" />or video<video src="" />of the change.🔒 Use the Mask Sensitive Data setting for security.
💡 For changes requiring multiple steps to validate, prefer a video for clarity.
How to test 🧪
Prerequisites
(How to setup test environment)
Reproduction steps
(How to reproduce the issue, if applicable)
Verification steps
(How to verify changes)
Author Checklists
As an Author, to speed up the review process, I considered 🤔
Check all that apply
As an Author, before moving this PR from Draft to Open, I confirmed ✅