Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v1.401.1 - staging → master #9761

Merged
merged 3 commits into from
Oct 5, 2023
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 packages/manager/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## [2023-10-02] - v1.104.1

### Fixed:

- Add disabled Tokyo RegionSelect menu entry ([#9758](https://github.com/linode/manager/pull/9758))
- Display DC-specific monthly prices to two decimal places and hide blank Region column on past invoices ([#9759](https://github.com/linode/manager/pull/9759))

## [2023-10-02] - v1.104.0

### Added:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import type { InvoiceItem, TaxSummary } from '@linode/api-v4';
import { invoiceFactory, invoiceItemFactory } from '@src/factories';
import { DateTime } from 'luxon';
import { MAGIC_DATE_THAT_DC_SPECIFIC_PRICING_WAS_IMPLEMENTED } from 'support/constants/dc-specific-pricing';
import {
mockGetInvoice,
mockGetInvoiceItems,
Expand All @@ -20,6 +21,18 @@ import { makeFeatureFlagData } from 'support/util/feature-flags';
import { randomItem, randomLabel, randomNumber } from 'support/util/random';
import { chooseRegion, getRegionById } from 'support/util/regions';

/**
* Returns a string representation of a region, as shown on the invoice details page.
*
* @param regionId - ID of region for which to get label.
*
* @returns Region label in `<label> (<id>)` format.
*/
const getRegionLabel = (regionId: string) => {
const region = getRegionById(regionId);
return `${region.label} (${region.id})`;
};

describe('Account invoices', () => {
/*
* - Confirms that invoice items are listed on invoice details page using mock API data.
Expand Down Expand Up @@ -106,7 +119,6 @@ describe('Account invoices', () => {
'@getInvoiceItems',
]);

// TODO: DC Pricing - M3-7073: Remove this and replace with positive assertions when DC pricing goes live.
// Confirm that "Region" table column is not present.
cy.findByLabelText('Invoice Details').within(() => {
cy.get('thead').findByText('Region').should('not.exist');
Expand Down Expand Up @@ -171,15 +183,18 @@ describe('Account invoices', () => {

/*
* - Confirms that invoice item region info is shown when DC-specific pricing is enabled.
* - Confirms that table "Region" column is shown when DC-specific pricing is enabled.
* - Confirms that table "Region" column is shown when DC-specific pricing is enabled on new invoices.
* - Confirms that invoice items that do not have a region are displayed as expected.
* - Confirms that outbound transfer overage items display the associated region when applicable.
* - Confirms that outbound transfer overage items display "Global" when no region is applicable.
*/
it('lists invoice item region when DC-specific pricing flag is enabled', () => {
// TODO: DC Pricing - M3-7073: Delete this test when DC-specific pricing launches and move assertions to above test.
// We don't have to be fancy with the mocks here since we are only concerned with the region.
const mockInvoice = invoiceFactory.build({ id: randomNumber() });
// TODO: DC Pricing - M3-7073: Delete most of this test when DC-specific pricing launches and move assertions to above test. Use this test for the region invoice column.
// We don't have to be fancy with the mocks here since we are only concerned with the region and invoice date.
const mockInvoice = invoiceFactory.build({
id: randomNumber(),
date: MAGIC_DATE_THAT_DC_SPECIFIC_PRICING_WAS_IMPLEMENTED,
});

// Regular invoice items.
const mockInvoiceItemsRegular = [
Expand Down Expand Up @@ -209,18 +224,6 @@ describe('Account invoices', () => {
...mockInvoiceItemsOverages,
];

/**
* Returns a string representation of a region, as shown on the invoice details page.
*
* @param regionId - ID of region for which to get label.
*
* @returns Region label in `<label> (<id>)` format.
*/
const getRegionLabel = (regionId: string) => {
const region = getRegionById(regionId);
return `${region.label} (${region.id})`;
};

mockAppendFeatureFlags({
dcSpecificPricing: makeFeatureFlagData(true),
}).as('getFeatureFlags');
Expand Down Expand Up @@ -291,6 +294,49 @@ describe('Account invoices', () => {
});
});

it('does not list the region on past invoices when DC-specific pricing flag is enabled', () => {
const mockInvoice = invoiceFactory.build({
id: randomNumber(),
date: '2023-09-30 00:00:00Z',
});

// Regular invoice items.
const mockInvoiceItems = [
...buildArray(10, () => invoiceItemFactory.build({ region: null })),
];

mockAppendFeatureFlags({
dcSpecificPricing: makeFeatureFlagData(true),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientstream');
mockGetInvoice(mockInvoice).as('getInvoice');
mockGetInvoiceItems(mockInvoice, mockInvoiceItems).as('getInvoiceItems');

// Visit invoice details page, wait for relevant requests to resolve.
cy.visitWithLogin(`/account/billing/invoices/${mockInvoice.id}`);
cy.wait([
'@getFeatureFlags',
'@getClientstream',
'@getInvoice',
'@getInvoiceItems',
]);

cy.findByLabelText('Invoice Details').within(() => {
// Confirm that "Region" table column is not present in an invoice created before DC-specific pricing was released.
cy.get('thead').findByText('Region').should('not.exist');
});

// Confirm that each regular invoice item is shown, and that the region cell is not displayed for each item.
mockInvoiceItems.forEach((invoiceItem: InvoiceItem) => {
cy.findByText(invoiceItem.label)
.should('be.visible')
.closest('tr')
.within(() => {
cy.get('[data-qa-region]').should('not.exist');
});
});
});

/*
* - Confirms that invoice item pagination works as expected using mock API data.
* - Confirms that the expected number of pages are shown for invoice items.
Expand Down
44 changes: 22 additions & 22 deletions packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ describe('LKE cluster updates for DC-specific prices', () => {
});

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

// Click "Resize Pool" and increase size to 3 nodes.
ui.button
Expand All @@ -850,12 +850,12 @@ describe('LKE cluster updates for DC-specific prices', () => {
.should('be.visible')
.should('be.disabled');

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

cy.findByLabelText('Add 1')
.should('be.visible')
Expand All @@ -865,22 +865,22 @@ describe('LKE cluster updates for DC-specific prices', () => {
.click();

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

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

cy.findByLabelText('Edit Quantity').should('have.value', '3');
cy.findByText('Resized pool: $42/month (3 nodes at $14/month)').should(
'be.visible'
);
cy.findByText(
'Resized pool: $43.20/month (3 nodes at $14.40/month)'
).should('be.visible');

ui.button
.findByTitle('Save Changes')
Expand All @@ -892,7 +892,7 @@ describe('LKE cluster updates for DC-specific prices', () => {
cy.wait(['@resizeNodePool', '@getNodePools']);

// Confirm total price updates in Kube Specs.
cy.findByText('$42.00/month').should('be.visible');
cy.findByText('$43.20/month').should('be.visible');
});

/*
Expand Down Expand Up @@ -939,7 +939,7 @@ describe('LKE cluster updates for DC-specific prices', () => {
cy.findByText('Linode 0 GB', { selector: 'h2' }).should('be.visible');

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

// Add a new node pool, select plan, submit form in drawer.
ui.button
Expand All @@ -961,14 +961,14 @@ describe('LKE cluster updates for DC-specific prices', () => {
.closest('tr')
.within(() => {
// Assert that DC-specific prices are displayed the plan table, then add a node pool with 2 linodes.
cy.findByText('$14').should('be.visible');
cy.findByText('$14.40').should('be.visible');
cy.findByText('$0.021').should('be.visible');
cy.findByLabelText('Add 1').should('be.visible').click().click();
});

// Assert that DC-specific prices are displayed as helper text.
cy.contains(
'This pool will add $28/month (2 nodes at $14/month) to this cluster.'
'This pool will add $28.80/month (2 nodes at $14.40/month) to this cluster.'
).should('be.visible');

ui.button
Expand All @@ -981,7 +981,7 @@ describe('LKE cluster updates for DC-specific prices', () => {
// Wait for API responses.
cy.wait(['@addNodePool', '@getNodePools']);

// Confirm total price updates in Kube Specs: $14/mo existing pool + $28/mo new pool.
cy.findByText('$42.00/month').should('be.visible');
// 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');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ describe('create linode', () => {
const currentPrice = dcPricingMockLinodeTypes[0].region_prices.find(
(regionPrice) => regionPrice.id === initialRegion.id
);
cy.findByText(`$${currentPrice.monthly}/month`).should('be.visible');
cy.findByText(`$${currentPrice.monthly}0/month`).should('be.visible');
});

// Confirms that a notice is shown in the "Region" section of the Linode Create form informing the user of tiered pricing
Expand All @@ -202,7 +202,7 @@ describe('create linode', () => {
const currentPrice = dcPricingMockLinodeTypes[1].region_prices.find(
(regionPrice) => regionPrice.id === newRegion.id
);
cy.findByText(`$${currentPrice.monthly}/month`).should('be.visible');
cy.findByText(`$${currentPrice.monthly}0/month`).should('be.visible');
});

getClick('#linode-label').clear().type(linodeLabel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ export const dcPricingMockLinodeTypes = linodeTypeFactory.buildList(3, {
// Use `us-east` and `us-west` so we do not have to mock regions request,
// which otherwise may not include the actual regions which have DC-specific pricing applied.
id: 'us-east',
monthly: 14,
monthly: 14.4,
},
{
hourly: 0.018,
// Use `us-east` and `us-west` so we do not have to mock regions request,
// which otherwise may not include the actual regions which have DC-specific pricing applied.
id: 'us-west',
monthly: 12,
monthly: 12.2,
},
],
});
Expand All @@ -88,3 +88,6 @@ export const dcPricingLkeClusterPlans: LkePlanDescription[] = dcPricingMockLinod
};
}
);

export const MAGIC_DATE_THAT_DC_SPECIFIC_PRICING_WAS_IMPLEMENTED =
'2023-10-05 00:00:00Z';
2 changes: 1 addition & 1 deletion packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "linode-manager",
"author": "Linode",
"description": "The Linode Manager website",
"version": "1.104.0",
"version": "1.104.1",
"private": true,
"bugs": {
"url": "https://github.com/Linode/manager/issues"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ import { regions } from 'src/__data__/regionsData';
import { getRegionOptions, getSelectedRegionById } from './RegionSelect';

const fakeRegion = { ...regions[0], country: 'fake iso code' };
const flags = {};

describe('Region Select helper functions', () => {
describe('getRegionOptions', () => {
it('should return a list of items grouped by continent', () => {
const groupedRegions = getRegionOptions(regions);
const groupedRegions = getRegionOptions(
regions,
flags,
'/linodes/create'
);
const [r1, r2, r3, r4, r5] = groupedRegions;
expect(groupedRegions).toHaveLength(8);
expect(r1.options).toHaveLength(5);
Expand All @@ -18,7 +23,11 @@ describe('Region Select helper functions', () => {
});

it('should group unrecognized regions as Other', () => {
const groupedRegions = getRegionOptions([fakeRegion]);
const groupedRegions = getRegionOptions(
[fakeRegion],
flags,
'/linodes/create'
);
expect(
groupedRegions.find((group) => group.label === 'Other')
).toBeDefined();
Expand All @@ -27,7 +36,11 @@ describe('Region Select helper functions', () => {

describe('getSelectedRegionById', () => {
it('should return the matching Item from a list of GroupedItems', () => {
const groupedRegions = getRegionOptions(regions);
const groupedRegions = getRegionOptions(
regions,
flags,
'/linodes/create'
);
const selectedID = regions[1].id;
expect(getSelectedRegionById(selectedID, groupedRegions)).toHaveProperty(
'value',
Expand Down
Loading
Loading