Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
cpathipa committed Oct 5, 2023
2 parents 0b58eb2 + 73bcf62 commit 170d71b
Show file tree
Hide file tree
Showing 29 changed files with 401 additions and 86 deletions.
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
80 changes: 63 additions & 17 deletions packages/manager/cypress/e2e/core/billing/billing-invoices.spec.ts
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

0 comments on commit 170d71b

Please sign in to comment.