Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cypress/component/CreateModal.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ describe('CreateModal', () => {
});

describe('Successful creation', () => {
beforeEach(() => {
cy.intercept('GET', '/api/widget-layout/v1/', {
statusCode: 200,
body: { data: [mockDashboardResponse] },
}).as('getDashboards');
});

it('calls import API and closes modal on success', () => {
cy.intercept('POST', '/api/widget-layout/v1/import', {
statusCode: 200,
Expand Down
7 changes: 6 additions & 1 deletion cypress/component/DashboardTable.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ describe('DashboardTable', () => {

cy.intercept('GET', '/api/widget-layout/v1/', {
statusCode: 200,
body: mockDashboards,
body: { data: mockDashboards },
}).as('getDashboards');

cy.mount(
Expand Down Expand Up @@ -355,6 +355,11 @@ describe('DashboardTable', () => {
body: '',
}).as('deleteDashboard');

cy.intercept('GET', '/api/widget-layout/v1/', {
statusCode: 200,
body: { data: mockDashboards },
}).as('getDashboards');

const refetchStub = cy.stub();

cy.mount(
Expand Down
69 changes: 43 additions & 26 deletions cypress/component/DuplicateModal.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import React from 'react';
import { DuplicateModal } from '../../src/Components/DuplicateModal/DuplicateModal';
import Portal from '@redhat-cloud-services/frontend-components-notifications/Portal';
import { useAtomValue } from 'jotai';
import { useAtomValue, useSetAtom } from 'jotai';
import { notificationsAtom, useRemoveNotification } from '../../src/state/notificationsAtom';
import { dashboardsAtom } from '../../src/state/dashboardsAtom';
import { DashboardTemplate } from '../../src/api/dashboard-templates';

const NotificationPortal = () => {
const notifications = useAtomValue(notificationsAtom);
const removeNotification = useRemoveNotification();
return <Portal notifications={notifications} removeNotification={removeNotification} />;
};

const HydrateDashboards: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const set = useSetAtom(dashboardsAtom);
React.useEffect(() => {
set(mockDashboards as DashboardTemplate[]);
}, []);
return <>{children}</>;
};

const mockDashboards = [
{
id: 1,
Expand Down Expand Up @@ -50,44 +60,44 @@ const mockCopyResponse = {
describe('DuplicateModal', () => {

it('renders modal with title when isOpen=true', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} />);
cy.contains('Duplicate existing dashboard').should('be.visible');
});

it('does not render modal content when isOpen=false', () => {
cy.mount(<DuplicateModal isOpen={false} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<DuplicateModal isOpen={false} onClose={cy.stub()} />);
cy.contains('Duplicate existing dashboard').should('not.exist');
});

it('shows "Duplicate dashboard" button disabled when form is empty', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} />);
cy.contains('button', 'Duplicate dashboard').should('be.visible').and('be.disabled');
});

it('Cancel button calls onClose', () => {
const onClose = cy.stub().as('onClose');
cy.mount(<DuplicateModal isOpen={true} onClose={onClose} dashboards={mockDashboards} />);
cy.mount(<DuplicateModal isOpen={true} onClose={onClose} />);
cy.contains('button', 'Cancel').click();
cy.get('@onClose').should('have.been.calledOnce');
});

it('dashboard name input is present and can be typed into', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} />);
cy.get('#duplicate-dashboard-name')
.should('be.visible')
.type('My Duplicate')
.should('have.value', 'My Duplicate');
});

it('form labels are correct', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} />);
cy.contains('label', 'Select existing dashboard for duplication').should('be.visible');
cy.contains('label', 'New dashboard name').should('be.visible');
cy.contains('Set as homepage').should('be.visible');
});

it('checkbox is unchecked by default and can be toggled', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} />);
cy.get('#set-as-homepage').should('not.be.checked');
cy.get('#set-as-homepage').check();
cy.get('#set-as-homepage').should('be.checked');
Expand All @@ -96,7 +106,7 @@ describe('DuplicateModal', () => {
});

it('dashboard select dropdown shows mocked dashboards', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);
cy.get('select[aria-label="Select a dashboard"]').within(() => {
cy.get('option').should('have.length', 3); // placeholder + 2 dashboards
cy.contains('option', 'Dashboard One').should('exist');
Expand All @@ -105,32 +115,39 @@ describe('DuplicateModal', () => {
});

it('enables "Duplicate dashboard" button when both dashboard is selected AND name is entered', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);
cy.get('select[aria-label="Select a dashboard"]').select('1');
cy.get('#duplicate-dashboard-name').type('My Duplicate');
cy.contains('button', 'Duplicate dashboard').should('not.be.disabled');
});

it('keeps button disabled when only name is entered (no dashboard selected)', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);
cy.get('#duplicate-dashboard-name').type('My Duplicate');
cy.contains('button', 'Duplicate dashboard').should('be.disabled');
});

it('keeps button disabled when only dashboard is selected (no name)', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);
cy.get('select[aria-label="Select a dashboard"]').select('1');
cy.contains('button', 'Duplicate dashboard').should('be.disabled');
});

it('keeps button disabled when name is whitespace only', () => {
cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);
cy.get('select[aria-label="Select a dashboard"]').select('1');
cy.get('#duplicate-dashboard-name').type(' ');
cy.contains('button', 'Duplicate dashboard').should('be.disabled');
});

describe('Successful duplication', () => {
beforeEach(() => {
cy.intercept('GET', '/api/widget-layout/v1/', {
statusCode: 200,
body: { data: [mockCopyResponse] },
}).as('getDashboards');
});

it('calls copy API, shows success notification, calls onSuccess and onClose', () => {
cy.intercept('POST', '/api/widget-layout/v1/1/copy', {
statusCode: 200,
Expand All @@ -141,10 +158,10 @@ describe('DuplicateModal', () => {
const onSuccess = cy.stub().as('onSuccess');

cy.mount(
<>
<HydrateDashboards>
<NotificationPortal />
<DuplicateModal isOpen={true} onClose={onClose} onSuccess={onSuccess} dashboards={mockDashboards} />
</>
<DuplicateModal isOpen={true} onClose={onClose} onSuccess={onSuccess} />
</HydrateDashboards>
);

cy.get('select[aria-label="Select a dashboard"]').select('1');
Expand Down Expand Up @@ -172,10 +189,10 @@ describe('DuplicateModal', () => {
const onClose = cy.stub().as('onClose');

cy.mount(
<>
<HydrateDashboards>
<NotificationPortal />
<DuplicateModal isOpen={true} onClose={onClose} onSuccess={cy.stub()} dashboards={mockDashboards} />
</>
<DuplicateModal isOpen={true} onClose={onClose} onSuccess={cy.stub()} />
</HydrateDashboards>
);

cy.get('select[aria-label="Select a dashboard"]').select('1');
Expand All @@ -201,10 +218,10 @@ describe('DuplicateModal', () => {
}).as('setDefault');

cy.mount(
<>
<HydrateDashboards>
<NotificationPortal />
<DuplicateModal isOpen={true} onClose={cy.stub()} onSuccess={cy.stub()} dashboards={mockDashboards} />
</>
<DuplicateModal isOpen={true} onClose={cy.stub()} onSuccess={cy.stub()} />
</HydrateDashboards>
);

cy.get('select[aria-label="Select a dashboard"]').select('1');
Expand All @@ -226,7 +243,7 @@ describe('DuplicateModal', () => {
delay: 1000,
}).as('copyDashboard');

cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);

cy.get('select[aria-label="Select a dashboard"]').select('1');
cy.get('#duplicate-dashboard-name').type('My Duplicate');
Expand All @@ -245,7 +262,7 @@ describe('DuplicateModal', () => {
body: 'Internal Server Error',
}).as('copyDashboard');

cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);

cy.get('select[aria-label="Select a dashboard"]').select('1');
cy.get('#duplicate-dashboard-name').type('My Duplicate');
Expand All @@ -262,7 +279,7 @@ describe('DuplicateModal', () => {
body: 'Bad Request',
}).as('copyDashboard');

cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);

cy.get('select[aria-label="Select a dashboard"]').select('1');
cy.get('#duplicate-dashboard-name').type('My Duplicate');
Expand All @@ -279,7 +296,7 @@ describe('DuplicateModal', () => {
body: 'Internal Server Error',
}).as('copyDashboard');

cy.mount(<DuplicateModal isOpen={true} onClose={cy.stub()} dashboards={mockDashboards} />);
cy.mount(<HydrateDashboards><DuplicateModal isOpen={true} onClose={cy.stub()} /></HydrateDashboards>);

cy.get('select[aria-label="Select a dashboard"]').select('1');
cy.get('#duplicate-dashboard-name').type('My Duplicate');
Expand Down
54 changes: 49 additions & 5 deletions cypress/component/KebabDropdown.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
import React from 'react';
import { KebabDropdown } from '../../src/Components/Header/Header';
import { MemoryRouter } from 'react-router-dom';
import { ScalprumContext, ScalprumState } from '@scalprum/react-core';
import { DashboardTemplate } from '../../src/api/dashboard-templates';
import { useSetAtom } from 'jotai';
import { dashboardsAtom } from '../../src/state/dashboardsAtom';

const scalprumValue = {
initialized: true,
config: {},
pluginStore: {},
api: {
chrome: {
auth: {
getUser: () => Promise.resolve({
entitlements: {},
identity: {
org_id: '123',
type: 'User',
user: {
username: 'test-user',
email: 'test@test.com',
first_name: 'Test',
last_name: 'User',
is_active: true,
is_internal: false,
is_org_admin: false,
locale: 'en_US',
},
},
}),
},
},
},
} as unknown as ScalprumState;

const mockDashboards: DashboardTemplate[] = [
{
Expand All @@ -17,14 +49,26 @@ const mockDashboards: DashboardTemplate[] = [
},
];

const HydrateDashboards: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const set = useSetAtom(dashboardsAtom);
React.useEffect(() => {
set(mockDashboards);
}, []);
return <>{children}</>;
};

describe('KebabDropdown', () => {
beforeEach(() => {
cy.mount(
<MemoryRouter>
<div style={{ display: 'flex', justifyContent: 'flex-end', padding: '16px' }}>
<KebabDropdown dashboards={mockDashboards} />
</div>
</MemoryRouter>
<ScalprumContext.Provider value={scalprumValue}>
<MemoryRouter>
<HydrateDashboards>
<div style={{ display: 'flex', justifyContent: 'flex-end', padding: '16px' }}>
<KebabDropdown />
</div>
</HydrateDashboards>
</MemoryRouter>
</ScalprumContext.Provider>
);
});

Expand Down
Loading
Loading