Skip to content

Commit

Permalink
fix: [M3-7739, M3-7741, M3-7746, M3-7747] - Cherry pick zero price fi…
Browse files Browse the repository at this point in the history
…xes for release (#10177)

* fix: [M3-7741] - Hide error notices for $0 regions in Resize Pool and Add a Node Pool drawers (#10157)

* Allow -zsh LKE prices without error notices in Resize Pool and Add Pool drawers

* Fix loading spinner displaying above what was supposed to be loading

* Fix conditional to render notice if either price is invalid

* Add test coverage

* Added changeset: Hide error notices for /bin/sh regions for LKE Resize and Add Node Pools

* Fix changeset wording

* Address feedback: use invalid price util

* fix: [M3-7746] - Fix $0 region price error in "Enable All Backups" drawer (#10161)

* Remove error indicator for Linodes in $0 regions

* Fix $0 total price display issue

* Cover $0 pricing cases in Cypress backup tests

* Add BackupLinodeRow tests to account for error states and $0 regions

* Add unit tests for BackupDrawer component

* fix: [M3-7747] - Fix Linode Migration dialog hidden $0 price (#10166)

* Add unit tests for MigrationPricing component

* Accounting for $0 prices in MigrationPricing component

* fix: [M3-7739] - Fix error when enabling backups for Linodes in regions with $0 price (#10153)

* Fix error when enabling backups for Linodes in regions with $0 price

* Add unit tests for EnableBackupsDialog

---------

Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>

* Replace "toBeDisabled" with "toHaveAttribute" assertion

---------

Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
  • Loading branch information
jdamore-linode and mjac0bs authored Feb 12, 2024
1 parent 0abd0a8 commit 9d5fad5
Show file tree
Hide file tree
Showing 23 changed files with 990 additions and 90 deletions.
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10153-fixed-1707249479775.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Fixed
---

Error when enabling backups for Linodes in regions with $0 pricing ([#10153](https://github.com/linode/manager/pull/10153))
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10157-fixed-1707328749030.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Fixed
---

Error notices for $0 regions in LKE Resize and Add Node Pools drawers ([#10157](https://github.com/linode/manager/pull/10157))
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10161-fixed-1707341493849.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Fixed
---

Error in Enable All Backups drawer when one or more Linode is in a $0 region ([#10161](https://github.com/linode/manager/pull/10161))
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10166-fixed-1707414781493.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Fixed
---

Display $0.00 prices in Linode Migration dialog ([#10166](https://github.com/linode/manager/pull/10166))
225 changes: 225 additions & 0 deletions packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1011,4 +1011,229 @@ describe('LKE cluster updates for DC-specific prices', () => {
// Confirm total price updates in Kube Specs: $14.40/mo existing pool + $28.80/mo new pool.
cy.findByText('$43.20/month').should('be.visible');
});

/*
* - Confirms node pool resize UI flow using mocked API responses.
* - Confirms that pool size can be changed.
* - Confirms that drawer reflects $0 pricing.
* - Confirms that details page still shows $0 pricing after resizing.
*/
it('can resize pools with region prices of $0', () => {
const dcSpecificPricingRegion = getRegionById('us-southeast');

const mockCluster = kubernetesClusterFactory.build({
k8s_version: latestKubernetesVersion,
region: dcSpecificPricingRegion.id,
control_plane: {
high_availability: false,
},
});

const mockNodePoolResized = nodePoolFactory.build({
count: 3,
type: dcPricingMockLinodeTypes[2].id,
nodes: kubeLinodeFactory.buildList(3),
});

const mockNodePoolInitial = {
...mockNodePoolResized,
count: 1,
nodes: [mockNodePoolResized.nodes[0]],
};

const mockLinodes: Linode[] = mockNodePoolResized.nodes.map(
(node: PoolNodeResponse): Linode => {
return linodeFactory.build({
id: node.instance_id ?? undefined,
ipv4: [randomIp()],
region: dcSpecificPricingRegion.id,
type: dcPricingMockLinodeTypes[2].id,
});
}
);

const mockNodePoolDrawerTitle = 'Resize Pool: Linode 2 GB Plan';

mockGetCluster(mockCluster).as('getCluster');
mockGetClusterPools(mockCluster.id, [mockNodePoolInitial]).as(
'getNodePools'
);
mockGetLinodes(mockLinodes).as('getLinodes');
mockGetLinodeType(dcPricingMockLinodeTypes[2]).as('getLinodeType');
mockGetKubernetesVersions().as('getVersions');
mockGetDashboardUrl(mockCluster.id);
mockGetApiEndpoints(mockCluster.id);

cy.visitWithLogin(`/kubernetes/clusters/${mockCluster.id}`);
cy.wait([
'@getCluster',
'@getNodePools',
'@getLinodes',
'@getVersions',
'@getLinodeType',
]);

// Confirm that nodes are visible.
mockNodePoolInitial.nodes.forEach((node: PoolNodeResponse) => {
cy.get(`tr[data-qa-node-row="${node.id}"]`)
.should('be.visible')
.within(() => {
const nodeLinode = mockLinodes.find(
(linode: Linode) => linode.id === node.instance_id
);
if (nodeLinode) {
cy.findByText(nodeLinode.label).should('be.visible');
}
});
});

// Confirm total price is listed in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');

// Click "Resize Pool" and increase size to 4 nodes.
ui.button
.findByTitle('Resize Pool')
.should('be.visible')
.should('be.enabled')
.click();

mockUpdateNodePool(mockCluster.id, mockNodePoolResized).as(
'resizeNodePool'
);
mockGetClusterPools(mockCluster.id, [mockNodePoolResized]).as(
'getNodePools'
);

ui.drawer
.findByTitle(mockNodePoolDrawerTitle)
.should('be.visible')
.within(() => {
ui.button
.findByTitle('Save Changes')
.should('be.visible')
.should('be.disabled');

cy.findByText('Current pool: $0/month (1 node at $0/month)').should(
'be.visible'
);
cy.findByText('Resized pool: $0/month (1 node at $0/month)').should(
'be.visible'
);

cy.findByLabelText('Add 1')
.should('be.visible')
.should('be.enabled')
.click()
.click()
.click();

cy.findByLabelText('Edit Quantity').should('have.value', '4');
cy.findByText('Current pool: $0/month (1 node at $0/month)').should(
'be.visible'
);
cy.findByText('Resized pool: $0/month (4 nodes at $0/month)').should(
'be.visible'
);

ui.button
.findByTitle('Save Changes')
.should('be.visible')
.should('be.enabled')
.click();
});

cy.wait(['@resizeNodePool', '@getNodePools']);

// Confirm total price is still $0 in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');
});

/*
* - Confirms UI flow when adding node pools using mocked API responses.
* - Confirms that drawer reflects $0 prices.
* - Confirms that details page still shows $0 pricing after adding node pool.
*/
it('can add node pools with region prices of $0', () => {
const dcSpecificPricingRegion = getRegionById('us-southeast');

const mockCluster = kubernetesClusterFactory.build({
k8s_version: latestKubernetesVersion,
region: dcSpecificPricingRegion.id,
control_plane: {
high_availability: false,
},
});

const mockNewNodePool = nodePoolFactory.build({
count: 2,
type: dcPricingMockLinodeTypes[2].id,
nodes: kubeLinodeFactory.buildList(2),
});

const mockNodePool = nodePoolFactory.build({
count: 1,
type: dcPricingMockLinodeTypes[2].id,
nodes: kubeLinodeFactory.buildList(1),
});

mockGetCluster(mockCluster).as('getCluster');
mockGetClusterPools(mockCluster.id, [mockNodePool]).as('getNodePools');
mockGetKubernetesVersions().as('getVersions');
mockAddNodePool(mockCluster.id, mockNewNodePool).as('addNodePool');
mockGetLinodeType(dcPricingMockLinodeTypes[2]).as('getLinodeType');
mockGetLinodeTypes(dcPricingMockLinodeTypes);
mockGetDashboardUrl(mockCluster.id);
mockGetApiEndpoints(mockCluster.id);

cy.visitWithLogin(`/kubernetes/clusters/${mockCluster.id}`);
cy.wait(['@getCluster', '@getNodePools', '@getVersions', '@getLinodeType']);

// Assert that initial node pool is shown on the page.
cy.findByText('Linode 2 GB', { selector: 'h2' }).should('be.visible');

// Confirm total price of $0 is listed in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');

// Add a new node pool, select plan, submit form in drawer.
ui.button
.findByTitle('Add a Node Pool')
.should('be.visible')
.should('be.enabled')
.click();

mockGetClusterPools(mockCluster.id, [mockNodePool, mockNewNodePool]).as(
'getNodePools'
);

ui.drawer
.findByTitle(`Add a Node Pool: ${mockCluster.label}`)
.should('be.visible')
.within(() => {
cy.findByText('Linode 2 GB')
.should('be.visible')
.closest('tr')
.within(() => {
// Assert that $0 prices are displayed the plan table, then add a node pool with 2 linodes.
cy.findAllByText('$0').should('have.length', 2);
cy.findByLabelText('Add 1').should('be.visible').click().click();
});

// Assert that $0 prices are displayed as helper text.
cy.contains(
'This pool will add $0/month (2 nodes at $0/month) to this cluster.'
).should('be.visible');

ui.button
.findByTitle('Add pool')
.should('be.visible')
.should('be.enabled')
.click();
});

// Wait for API responses.
cy.wait(['@addNodePool', '@getNodePools']);

// Confirm total price is still $0 in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');
});
});
19 changes: 15 additions & 4 deletions packages/manager/cypress/e2e/core/linodes/backup-linode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,16 +270,22 @@ describe('"Enable Linode Backups" banner', () => {
// See `dcPricingMockLinodeTypes` exported from `support/constants/dc-specific-pricing.ts`.
linodeFactory.build({
label: randomLabel(),
region: 'us-east',
region: 'us-ord',
backups: { enabled: false },
type: dcPricingMockLinodeTypesForBackups[0].id,
}),
linodeFactory.build({
label: randomLabel(),
region: 'us-west',
region: 'us-east',
backups: { enabled: false },
type: dcPricingMockLinodeTypesForBackups[1].id,
}),
linodeFactory.build({
label: randomLabel(),
region: 'us-west',
backups: { enabled: false },
type: dcPricingMockLinodeTypesForBackups[2].id,
}),
linodeFactory.build({
label: randomLabel(),
region: 'us-central',
Expand Down Expand Up @@ -317,6 +323,7 @@ describe('"Enable Linode Backups" banner', () => {

// The expected backup price for each Linode, as shown in backups drawer table.
const expectedPrices = [
'$0.00/mo', // us-ord mocked price.
'$3.57/mo', // us-east mocked price.
'$4.17/mo', // us-west mocked price.
'$2.00/mo', // regular price.
Expand Down Expand Up @@ -358,7 +365,7 @@ describe('"Enable Linode Backups" banner', () => {
);

// Confirm that expected total cost is shown.
cy.contains(`Total for 3 Linodes: ${expectedTotal}`).should(
cy.contains(`Total for 4 Linodes: ${expectedTotal}`).should(
'be.visible'
);

Expand All @@ -377,6 +384,10 @@ describe('"Enable Linode Backups" banner', () => {
.closest('tr')
.within(() => {
cy.findByText(expectedPrice).should('be.visible');
// Confirm no error indicator appears for $0.00 prices.
cy.findByLabelText(
'There was an error loading the price.'
).should('not.exist');
});
});

Expand All @@ -398,7 +409,7 @@ describe('"Enable Linode Backups" banner', () => {
cy.wait([...enableBackupAliases, '@updateAccountSettings']);

ui.toast.assertMessage(
'3 Linodes have been enrolled in automatic backups, and all new Linodes will automatically be backed up.'
'4 Linodes have been enrolled in automatic backups, and all new Linodes will automatically be backed up.'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ export const dcPricingMockLinodeTypes = linodeTypeFactory.buildList(3, {
monthly: 12.2,
},
{
hourly: 0.006,
// Mock a DC with $0 region prices, which is possible in some circumstances (e.g. Limited Availability).
hourly: 0.0,
id: 'us-southeast',
monthly: 4.67,
monthly: 0.0,
},
],
});
Expand All @@ -92,6 +93,11 @@ export const dcPricingMockLinodeTypesForBackups = linodeTypeFactory.buildList(
monthly: 2.0,
},
region_prices: [
{
hourly: 0,
id: 'us-ord',
monthly: 0,
},
{
hourly: 0.0048,
id: 'us-east',
Expand Down
1 change: 1 addition & 0 deletions packages/manager/src/factories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export * from './statusPage';
export * from './subnets';
export * from './support';
export * from './tags';
export * from './types';
export * from './volume';
export * from './vlans';
export * from './vpcs';
Expand Down
Loading

0 comments on commit 9d5fad5

Please sign in to comment.