diff --git a/pkg/kubewarden/components/Dashboard/Card.vue b/pkg/kubewarden/components/Dashboard/Card.vue index 7abdd825..184f0661 100644 --- a/pkg/kubewarden/components/Dashboard/Card.vue +++ b/pkg/kubewarden/components/Dashboard/Card.vue @@ -27,24 +27,23 @@ export default { :class="setLoading" >
- -

- {{ t(card.title) }} -

-
+
+ +
+ +

+ {{ t(card.title) }} +

+
+

+

+
+ +
-

- - - {{ t(card.linkText) }} - - -


-
-

{{ t(card.slotTitle) }}

- +
@@ -55,6 +54,7 @@ export default { flex-direction: column; align-items: flex-start; height: 100%; + width: 100%; padding: $space-m; grid-auto-rows: 1fr; gap: $space-m; @@ -65,15 +65,38 @@ export default { .d-header { display: flex; align-items: center; + justify-content: space-between; + width: 100%; + + .title-container { + display: flex; + align-items: center; + width: 55%; + } + + .title { + display: flex; + flex-direction: column; + justify-content: center; + } h1 { margin: 0; - font-size: 18px; + color: var(--link); + } + + h1:hover { + color: var(--body-text); + } + + p { + font-size: 10.5px; + font-weight: 700; } - } - p { - min-height: 48px; + .action { + width: 45%; + } } .d-slot { @@ -115,7 +138,12 @@ export default { } } } + + .d-action { + width: auto; + } } + .loading { min-height: 325px; overflow: hidden; diff --git a/pkg/kubewarden/components/Dashboard/DashboardView.vue b/pkg/kubewarden/components/Dashboard/DashboardView.vue index 2003eeb0..494b0707 100644 --- a/pkg/kubewarden/components/Dashboard/DashboardView.vue +++ b/pkg/kubewarden/components/Dashboard/DashboardView.vue @@ -1,38 +1,35 @@ + + + + diff --git a/pkg/kubewarden/components/Dashboard/EventsGauge.vue b/pkg/kubewarden/components/Dashboard/EventsGauge.vue new file mode 100644 index 00000000..62e0dbe0 --- /dev/null +++ b/pkg/kubewarden/components/Dashboard/EventsGauge.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/pkg/kubewarden/components/Dashboard/Masthead.vue b/pkg/kubewarden/components/Dashboard/Masthead.vue new file mode 100644 index 00000000..a3040ddd --- /dev/null +++ b/pkg/kubewarden/components/Dashboard/Masthead.vue @@ -0,0 +1,303 @@ + + + + + \ No newline at end of file diff --git a/pkg/kubewarden/components/Dashboard/Modes.vue b/pkg/kubewarden/components/Dashboard/Modes.vue new file mode 100644 index 00000000..84ec0b0d --- /dev/null +++ b/pkg/kubewarden/components/Dashboard/Modes.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/pkg/kubewarden/components/Graph/Bar.vue b/pkg/kubewarden/components/Graph/Bar.vue new file mode 100644 index 00000000..fcae3aad --- /dev/null +++ b/pkg/kubewarden/components/Graph/Bar.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/pkg/kubewarden/config/table-headers.ts b/pkg/kubewarden/config/table-headers.ts index b8439a06..b5215fcc 100644 --- a/pkg/kubewarden/config/table-headers.ts +++ b/pkg/kubewarden/config/table-headers.ts @@ -151,17 +151,6 @@ export const POLICY_HEADERS = [ ]; export const DASHBOARD_HEADERS = [ - { - isEnabled: true, - isLoaded: true, - icon: 'icon-question-mark', - cta: createKubewardenRoute({ name: 'c-cluster-product-resource-create', params: { resource: KUBEWARDEN.POLICY_SERVER } }), - link: createKubewardenRoute({ name: 'c-cluster-product-resource', params: { resource: KUBEWARDEN.POLICY_SERVER } }), - linkText: 'kubewarden.dashboard.headers.policyServer.linkText', - description: 'kubewarden.dashboard.headers.policyServer.description', - slotTitle: 'kubewarden.dashboard.headers.policyServer.slotTitle', - title: 'kubewarden.dashboard.headers.policyServer.title' - }, { isEnabled: true, isLoaded: true, @@ -183,7 +172,18 @@ export const DASHBOARD_HEADERS = [ description: 'kubewarden.dashboard.headers.clusterAdmissionPolicy.description', slotTitle: 'kubewarden.dashboard.headers.clusterAdmissionPolicy.slotTitle', title: 'kubewarden.dashboard.headers.clusterAdmissionPolicy.title' - } + }, + { + isEnabled: true, + isLoaded: true, + icon: 'icon-question-mark', + cta: createKubewardenRoute({ name: 'c-cluster-product-resource-create', params: { resource: KUBEWARDEN.POLICY_SERVER } }), + link: createKubewardenRoute({ name: 'c-cluster-product-resource', params: { resource: KUBEWARDEN.POLICY_SERVER } }), + linkText: 'kubewarden.dashboard.headers.policyServer.linkText', + description: 'kubewarden.dashboard.headers.policyServer.description', + slotTitle: 'kubewarden.dashboard.headers.policyServer.slotTitle', + title: 'kubewarden.dashboard.headers.policyServer.title' + }, ]; export const TRACE_HEADERS = [ diff --git a/pkg/kubewarden/l10n/en-us.yaml b/pkg/kubewarden/l10n/en-us.yaml index dfeb53bf..47834f08 100644 --- a/pkg/kubewarden/l10n/en-us.yaml +++ b/pkg/kubewarden/l10n/en-us.yaml @@ -30,23 +30,25 @@ kubewarden: headers: policyServer: title: Policy Servers - description: Kubewarden uses Policy Servers to receive requests to be validated. It does that by executing Kubewarden's policies. + description: Policy Servers validate requests linkText: Create Policy Server - slotTitle: Policy Server Pods admissionPolicy: - title: Admission Policies - description: An Admission Policy is a namespace-wide resource. The policy will process only the requests that are targeting the Namespace where the Admission Policy is defined. - linkText: Create Admission Policy - slotTitle: Policies + title: Namespace Policies + description: Namespace-wide policy validation + linkText: Create Namespace Policy clusterAdmissionPolicy: - title: Cluster Admission Policies - description: The Cluster Admission Policy resource is the core of the Kubewarden stack. This resource defines how policies evaluate requests. - linkText: Create Cluster Admission Policy - slotTitle: Policies + title: Cluster Policies + description: Cluster-wide policy validation + linkText: Create Cluster Policy modes: - title: Policy Modes + title: Modes monitor: Monitor protect: Protect + events: + title: Events + success: Success + fail: Fail + error: Error appInstall: title: Kubewarden App Install description: This will take you to the app installation page for Kubewarden. diff --git a/tests/unit/components/Dashboard/DashboardView.spec.ts b/tests/unit/components/Dashboard/DashboardView.spec.ts index a12adf04..03d47eb9 100644 --- a/tests/unit/components/Dashboard/DashboardView.spec.ts +++ b/tests/unit/components/Dashboard/DashboardView.spec.ts @@ -1,14 +1,16 @@ -import { shallowMount } from '@vue/test-utils'; +import { mount } from '@vue/test-utils'; import { describe, expect, it } from '@jest/globals'; -import { SHOW_PRE_RELEASE } from '@shell/store/prefs'; - -import DashboardView from '@kubewarden/components/Dashboard/DashboardView.vue'; -import DefaultsBanner from '@kubewarden/components/DefaultsBanner'; import ConsumptionGauge from '@shell/components/ConsumptionGauge'; +import Loading from '@shell/components/Loading'; -import DEFAULTS_APP from '@tests/unit/_templates_/defaultsApp'; -import { controllerCharts } from '@tests/unit/_templates_/controllerCharts'; +import DashboardView from '@kubewarden/components/Dashboard/DashboardView.vue'; +import Card from '@kubewarden/components/Dashboard/Card.vue'; +import Masthead from '@kubewarden/components/Dashboard/Masthead.vue'; +import Modes from '@kubewarden/components/Dashboard/Modes.vue'; +import Events from '@kubewarden/components/Dashboard/Events.vue'; +import EventsGauge from '@kubewarden/components/Dashboard/EventsGauge.vue'; +import { DASHBOARD_HEADERS } from '@kubewarden/config/table-headers'; describe('component: DashboardView', () => { const commonMocks = { @@ -16,286 +18,201 @@ describe('component: DashboardView', () => { $store: { getters: { currentCluster: () => 'current_cluster', - 'kubewarden/hideBannerDefaults': jest.fn(), 'i18n/t': jest.fn(), 'catalog/chart': jest.fn(), 'catalog/charts': jest.fn(), 'cluster/all': jest.fn(), - 'cluster/canList': () => true, + 'cluster/canList': jest.fn(() => true), 'prefs/get': jest.fn(), }, }, }; const commonComputed = { - controllerApp: () => null, - controllerChart: () => null, - defaultsApp: () => DEFAULTS_APP, - hideBannerDefaults: () => false, globalPolicies: () => [], namespacedPolicies: () => [], - version: () => '1.25', - upgradeAvailable: () => null, + allPolicyServers: () => [], + policyServerCounts: () => ({ + status: { + running: 0, stopped: 0, pending: 0 + }, + total: 0 + }), }; + const commonStubs = { 'router-link': { template: '' } }; + const createWrapper = (overrides) => { - return shallowMount(DashboardView, { + return mount(DashboardView, { mocks: commonMocks, computed: commonComputed, + stubs: commonStubs, ...overrides, }); }; - it('renders defaults banner when default app is not found', () => { - const wrapper = createWrapper({ - stubs: { - Card: { template: '' }, - ConsumptionGauge: { template: '' }, - }, - }); + it('renders the Masthead component', () => { + const wrapper = createWrapper({}); - const banner = wrapper.findComponent(DefaultsBanner); - - expect(banner.exists()).toBe(true); + expect(wrapper.findComponent(Masthead).exists()).toBe(true); }); - it('renders correct gauge info of policy servers', () => { - const pods = [ - { - metadata: { - state: { - name: 'running', - error: false, - transitioning: false, - }, - }, - }, - { - metadata: { - state: { - name: 'pending', - error: false, - transitioning: true, - }, - }, - }, - ]; - + it('renders the Loading component when fetch state is pending', () => { const wrapper = createWrapper({ - computed: { policyServerPods: () => pods }, - stubs: { DefaultsBanner: { template: '' } }, - }); - - const gauges = wrapper.findAllComponents(ConsumptionGauge); - - expect(gauges.at(0).props().capacity).toStrictEqual(pods.length as Number); - expect(gauges.at(0).props().used).toStrictEqual(1 as Number); - }); - - it('renders correct gauge info of admission policies', () => { - const policies = [ - { - status: { - policyStatus: 'active', - error: false, - }, - spec: { mode: 'protect' }, - }, - { - status: { - policyStatus: 'pending', - error: false, - }, - spec: { mode: 'protect' }, - }, - { - status: { - policyStatus: 'unschedulable', - error: true, - }, - spec: { mode: 'monitor' }, + mocks: { + ...commonMocks, + $fetchState: { pending: true }, }, - ]; - - const wrapper = createWrapper({ - computed: { namespacedPolicies: () => policies }, - stubs: { DefaultsBanner: { template: '' } }, }); - const gauges = wrapper.findAllComponents(ConsumptionGauge); - - expect(gauges.at(1).props().capacity).toStrictEqual( - policies.length as Number - ); - expect(gauges.at(1).props().used).toStrictEqual(1 as Number); + expect(wrapper.findComponent(Loading).exists()).toBe(true); }); - it('renders the Upgradable button when an upgrade is available', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.4', appVersion: 'v1.9.0' } } } }; - - const wrapper = createWrapper({ - computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => 'v1.9.0', - }, - }); - - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + it('renders the correct number of Card components based on DASHBOARD_HEADERS', () => { + const wrapper = createWrapper({}); + const cardComponents = wrapper.findAllComponents(Card); - expect(upgradeButton.exists()).toBe(true); - expect(upgradeButton.text()).toContain( - '%kubewarden.dashboard.upgrade.appUpgrade%: v1.9.0 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.5' - ); + expect(cardComponents.length).toBe(DASHBOARD_HEADERS.length); }); - it('calculates the correct chart for a supported MAJOR version upgrade', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '0.4.6', appVersion: 'v0.5.5' } } } }; - + it('renders ConsumptionGauge component with correct props', () => { const wrapper = createWrapper({ computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => 'v0.5.5', - }, + globalPolicies: () => [], + namespacedPolicies: () => [], + allPolicyServers: () => [], + policyServerCounts: () => ({ + status: { + running: 1, stopped: 0, pending: 0 + }, + total: 2 + }) + } }); - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + const gauge = wrapper.findComponent(ConsumptionGauge); - expect(upgradeButton.exists()).toBe(true); - expect(upgradeButton.text()).toContain( - '%kubewarden.dashboard.upgrade.appUpgrade%: v1.0.0 - %kubewarden.dashboard.upgrade.controllerChart%: 1.0.0' - - ); - }); - - it('calculates the correct chart for a supported MINOR version upgrade', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '1.1.1', appVersion: 'v1.1.0' } } } }; - - const wrapper = createWrapper({ - computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => 'v1.1.0', - }, + expect(gauge.exists()).toBe(true); + expect(gauge.props('capacity')).toBe(2); + expect(gauge.props('used')).toBe(1); + expect(gauge.props('colorStops')).toEqual({ + 25: '--error', 50: '--warning', 70: '--info' }); - - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); - - expect(upgradeButton.exists()).toBe(true); - expect(upgradeButton.text()).toContain( - '%kubewarden.dashboard.upgrade.appUpgrade%: v1.1.1 - %kubewarden.dashboard.upgrade.controllerChart%: 1.2.3' - ); }); - it('calculates the correct chart for a supported PATCH version upgrade', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.0', appVersion: 'v1.8.0' } } } }; - + it('correctly applies class names based on policy server counts', () => { const wrapper = createWrapper({ computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => 'v1.8.0', - }, + globalPolicies: () => [], + namespacedPolicies: () => [], + allPolicyServers: () => [{}, {}] + } }); - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + const btn = wrapper.find('.role-secondary'); - expect(upgradeButton.exists()).toBe(true); - expect(upgradeButton.text()).toContain( - '%kubewarden.dashboard.upgrade.appUpgrade%: v1.9.0 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.5' - ); - expect(wrapper.vm.controllerUpgradeAvailable).toBe(controllerCharts.versions[1]); + expect(btn.exists()).toBe(true); }); - it('calculates the correct chart for multiple appVersions with chart PATCH available', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '1.6.0', appVersion: 'v1.7.0' } } } }; - + it('renders EventsGauge component when namespacedPolicies are present', () => { const wrapper = createWrapper({ computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => 'v1.7.0', - }, + globalPolicies: () => [], + namespacedPolicies: () => [{}, {}], + allPolicyServers: () => [] + } }); - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + const eventsGauge = wrapper.findComponent(EventsGauge); - expect(upgradeButton.exists()).toBe(true); - expect(upgradeButton.text()).toContain( - '%kubewarden.dashboard.upgrade.appUpgrade%: v1.8.0 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.2' - ); + expect(eventsGauge.exists()).toBe(true); }); - it('calculates the correct chart for inconsistent appVersion semantics', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '1.4.0', appVersion: '1.5.0' } } } }; - + it('renders Events and Modes components for namespaced policies', () => { const wrapper = createWrapper({ computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => '1.5.0', - }, + globalPolicies: () => [], + namespacedPolicies: () => [{}, {}], + allPolicyServers: () => [] + } }); - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); - - expect(upgradeButton.exists()).toBe(true); - expect(upgradeButton.text()).toContain( - '%kubewarden.dashboard.upgrade.appUpgrade%: v1.6.0 - %kubewarden.dashboard.upgrade.controllerChart%: 1.5.3' - ); + expect(wrapper.findComponent(Events).exists()).toBe(true); + expect(wrapper.findComponent(Modes).exists()).toBe(true); }); - it('calculates the correct chart for pre-release versions', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.5', appVersion: 'v1.9.0' } } } }; - - commonMocks.$store.getters['prefs/get'].mockImplementation((key) => { - if ( key === SHOW_PRE_RELEASE ) { - return true; - } - - return undefined; - }); + it('renders correct gauge info of policy servers', () => { + const pods = [ + { + metadata: { + state: { + name: 'running', + error: false, + transitioning: false, + }, + }, + }, + { + metadata: { + state: { + name: 'pending', + error: false, + transitioning: true, + }, + }, + }, + ]; const wrapper = createWrapper({ computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => 'v1.9.0', - showPreRelease: () => true, + globalPolicies: () => [], + namespacedPolicies: () => [], + allPolicyServers: () => [{}], + policyServerPods: () => pods }, + stubs: { Masthead: { template: '' } }, }); - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + const gauges = wrapper.findComponent(ConsumptionGauge); - expect(upgradeButton.exists()).toBe(true); - expect(upgradeButton.text()).toContain( - '%kubewarden.dashboard.upgrade.appUpgrade%: v1.10.0-rc1 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.6-rc1' - ); + expect(gauges.props().capacity).toStrictEqual(pods.length as Number); + expect(gauges.props().used).toStrictEqual(1 as Number); }); - it('does not show pre-release upgrades when preference is false', () => { - const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.5', appVersion: 'v1.9.0' } } } }; - - commonMocks.$store.getters['prefs/get'].mockImplementation((key) => { - if ( key === SHOW_PRE_RELEASE ) { - return false; - } - - return undefined; - }); + it('renders correct gauge info for admission policy events', () => { + const policies = [ + { + result: 'pass', + policy: 'namespaced-policy-1', + }, + { + result: 'fail', + policy: 'namespaced-policy-2', + }, + { + result: 'error', + policy: 'namespaced-policy-3', + }, + ]; + const summary = { + status: { + error: 1, fail: 1, success: 1 + }, + total: 3 + }; const wrapper = createWrapper({ computed: { - controllerApp: () => oldControllerApp, - controllerChart: () => controllerCharts, - version: () => 'v1.9.0', - showPreRelease: () => false, - }, + admissionPolicyResults: () => policies, + namespacedPolicies: () => [], + globalPolicies: () => [], + allPolicyServers: () => [], + } }); - const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + const gauges = wrapper.findAllComponents(EventsGauge); - expect(upgradeButton.exists()).toBe(false); + expect(gauges.at(0).exists()).toBe(true); + expect(gauges.at(0).props().events).toStrictEqual(summary); }); }); diff --git a/tests/unit/components/Dashboard/Masthead.spec.ts b/tests/unit/components/Dashboard/Masthead.spec.ts new file mode 100644 index 00000000..993ffbf9 --- /dev/null +++ b/tests/unit/components/Dashboard/Masthead.spec.ts @@ -0,0 +1,229 @@ +import { shallowMount } from '@vue/test-utils'; +import { describe, expect, it } from '@jest/globals'; + +import { SHOW_PRE_RELEASE } from '@shell/store/prefs'; + +import Masthead from '@kubewarden/components/Dashboard/Masthead.vue'; +import DefaultsBanner from '@kubewarden/components/DefaultsBanner'; + +import DEFAULTS_APP from '@tests/unit/_templates_/defaultsApp'; +import { controllerCharts } from '@tests/unit/_templates_/controllerCharts'; + +describe('component: Masthead', () => { + const commonMocks = { + $fetchState: { pending: false }, + $store: { + getters: { + currentCluster: () => 'current_cluster', + 'kubewarden/hideBannerDefaults': jest.fn(), + 'i18n/t': jest.fn(), + 'catalog/chart': jest.fn(), + 'catalog/charts': jest.fn(), + 'cluster/all': jest.fn(), + 'cluster/canList': () => true, + 'prefs/get': jest.fn(), + }, + }, + }; + + const commonComputed = { + controllerApp: () => null, + controllerChart: () => null, + defaultsApp: () => DEFAULTS_APP, + hideBannerDefaults: () => false, + globalPolicies: () => [], + namespacedPolicies: () => [], + version: () => '1.25', + upgradeAvailable: () => null, + }; + + const createWrapper = (overrides) => { + return shallowMount(Masthead, { + mocks: commonMocks, + computed: commonComputed, + ...overrides, + }); + }; + + it('renders defaults banner when default app is not found', () => { + const wrapper = createWrapper({ + stubs: { + Card: { template: '' }, + ConsumptionGauge: { template: '' }, + }, + }); + + const banner = wrapper.findComponent(DefaultsBanner); + + expect(banner.exists()).toBe(true); + }); + + it('renders the Upgradable button when an upgrade is available', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.4', appVersion: 'v1.9.0' } } } }; + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => 'v1.9.0', + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(true); + expect(upgradeButton.text()).toContain( + '%kubewarden.dashboard.upgrade.appUpgrade%: v1.9.0 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.5' + ); + }); + + it('calculates the correct chart for a supported MAJOR version upgrade', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '0.4.6', appVersion: 'v0.5.5' } } } }; + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => 'v0.5.5', + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(true); + expect(upgradeButton.text()).toContain( + '%kubewarden.dashboard.upgrade.appUpgrade%: v1.0.0 - %kubewarden.dashboard.upgrade.controllerChart%: 1.0.0' + + ); + }); + + it('calculates the correct chart for a supported MINOR version upgrade', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '1.1.1', appVersion: 'v1.1.0' } } } }; + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => 'v1.1.0', + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(true); + expect(upgradeButton.text()).toContain( + '%kubewarden.dashboard.upgrade.appUpgrade%: v1.1.1 - %kubewarden.dashboard.upgrade.controllerChart%: 1.2.3' + ); + }); + + it('calculates the correct chart for a supported PATCH version upgrade', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.0', appVersion: 'v1.8.0' } } } }; + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => 'v1.8.0', + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(true); + expect(upgradeButton.text()).toContain( + '%kubewarden.dashboard.upgrade.appUpgrade%: v1.9.0 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.5' + ); + expect(wrapper.vm.controllerUpgradeAvailable).toBe(controllerCharts.versions[1]); + }); + + it('calculates the correct chart for multiple appVersions with chart PATCH available', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '1.6.0', appVersion: 'v1.7.0' } } } }; + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => 'v1.7.0', + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(true); + expect(upgradeButton.text()).toContain( + '%kubewarden.dashboard.upgrade.appUpgrade%: v1.8.0 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.2' + ); + }); + + it('calculates the correct chart for inconsistent appVersion semantics', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '1.4.0', appVersion: '1.5.0' } } } }; + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => '1.5.0', + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(true); + expect(upgradeButton.text()).toContain( + '%kubewarden.dashboard.upgrade.appUpgrade%: v1.6.0 - %kubewarden.dashboard.upgrade.controllerChart%: 1.5.3' + ); + }); + + it('calculates the correct chart for pre-release versions', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.5', appVersion: 'v1.9.0' } } } }; + + commonMocks.$store.getters['prefs/get'].mockImplementation((key) => { + if ( key === SHOW_PRE_RELEASE ) { + return true; + } + + return undefined; + }); + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => 'v1.9.0', + showPreRelease: () => true, + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(true); + expect(upgradeButton.text()).toContain( + '%kubewarden.dashboard.upgrade.appUpgrade%: v1.10.0-rc1 - %kubewarden.dashboard.upgrade.controllerChart%: 2.0.6-rc1' + ); + }); + + it('does not show pre-release upgrades when preference is false', () => { + const oldControllerApp = { spec: { chart: { metadata: { version: '2.0.5', appVersion: 'v1.9.0' } } } }; + + commonMocks.$store.getters['prefs/get'].mockImplementation((key) => { + if ( key === SHOW_PRE_RELEASE ) { + return false; + } + + return undefined; + }); + + const wrapper = createWrapper({ + computed: { + controllerApp: () => oldControllerApp, + controllerChart: () => controllerCharts, + version: () => 'v1.9.0', + showPreRelease: () => false, + }, + }); + + const upgradeButton = wrapper.find('[data-testid="kw-app-controller-upgrade-button"]'); + + expect(upgradeButton.exists()).toBe(false); + }); +});