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

feat: [M3-6968] Add DC specific pricing info to migration modal #9570

Merged
merged 25 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
93fefcc
feat: [M3-6968] Initial commit - set up current data
abailly-akamai Aug 21, 2023
e1d8c9a
feat: [M3-6968] UI for migration dynamic pricing
abailly-akamai Aug 21, 2023
0b0525a
feat: [M3-6968] styling adjustment
abailly-akamai Aug 21, 2023
e8ba766
feat: [M3-6968] with with prop forwarding
abailly-akamai Aug 22, 2023
19dbbe9
feat: [M3-6968] ad unit test
abailly-akamai Aug 22, 2023
6cd7212
feat: [M3-6968] improve unit test
abailly-akamai Aug 22, 2023
9623ea2
feat: [M3-6968] post rebase fix
abailly-akamai Aug 22, 2023
21c8ec4
feat: [M3-6968] fix test
abailly-akamai Aug 22, 2023
c166d00
Added changeset: DC Dynamic pricing information for migration flow
abailly-akamai Aug 22, 2023
1131c9d
feat: [M3-6968] improve display logic based on fedback
abailly-akamai Aug 23, 2023
054131d
feat: [M3-6968] save work
abailly-akamai Aug 23, 2023
ef3a60c
feat: [M3-6968] save work
abailly-akamai Aug 24, 2023
8deef0c
feat: [M3-6968] test update
abailly-akamai Aug 24, 2023
6ffb5d6
Update packages/manager/.changeset/pr-9570-changed-1692743440177.md
abailly-akamai Aug 24, 2023
b9ac261
Update packages/manager/.changeset/pr-9570-changed-1692743440177.md
abailly-akamai Aug 24, 2023
1c0368d
feat: [M3-6968] cleanup
abailly-akamai Aug 24, 2023
7b37217
feat: [M3-6968] fix hourly display and improve test
abailly-akamai Aug 24, 2023
3397333
feat: [M3-6968] handle backups
abailly-akamai Aug 27, 2023
52b815a
feat: [M3-6968] test for backups
abailly-akamai Aug 28, 2023
7cc4608
feat: [M3-6968] cleanup 1
abailly-akamai Aug 28, 2023
7f16474
feat: [M3-6968] fix identation issue
abailly-akamai Aug 28, 2023
70c2e0d
feat: [M3-6968] fix identation issue
abailly-akamai Aug 28, 2023
2ffd41c
feat: [M3-6968] fix failing test
abailly-akamai Aug 28, 2023
5a8e753
feat: [M3-6968] small feedback
abailly-akamai Aug 29, 2023
a9a2d33
feat: [M3-6968] fix test
abailly-akamai Aug 29, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add DC dynamic pricing information for Linode migration flow ([#9570](https://github.com/linode/manager/pull/9570))
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Currency } from 'src/components/Currency';
import { Typography } from 'src/components/Typography';

export interface DisplayPriceProps {
decimalPlaces?: number;
fontSize?: string;
interval?: string;
price: number;
Expand All @@ -14,7 +15,7 @@ export const displayPrice = (price: number) => `$${price.toFixed(2)}`;

export const DisplayPrice = (props: DisplayPriceProps) => {
const theme = useTheme<Theme>();
const { fontSize, interval, price } = props;
const { decimalPlaces, fontSize, interval, price } = props;

const sx: SxProps = {
color: theme.palette.text.primary,
Expand All @@ -25,7 +26,7 @@ export const DisplayPrice = (props: DisplayPriceProps) => {
return (
<>
<Typography sx={sx} variant="h3">
<Currency quantity={price} />
<Currency decimalPlaces={decimalPlaces} quantity={price} />
</Typography>
{interval && (
<Typography sx={sx} variant="h3">
Expand Down
6 changes: 3 additions & 3 deletions packages/manager/src/factories/linodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ export const linodeTransferFactory = Factory.Sync.makeFactory<RegionalNetworkUti
{
billable: 0,
quota: 1950,
used: 13956637,
region_transfers: [
{ id: 'id-cgk', billable: 0, quota: 10000, used: 10 },
{ id: 'br-gru', billable: 0, quota: 15000, used: 20 },
{ billable: 0, id: 'id-cgk', quota: 10000, used: 10 },
{ billable: 0, id: 'br-gru', quota: 15000, used: 20 },
],
used: 13956637,
}
);

Expand Down
8 changes: 4 additions & 4 deletions packages/manager/src/factories/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ export const typeFactory = Factory.Sync.makeFactory<LinodeType>({
addons: {
backups: {
price: {
hourly: 10,
monthly: 10,
hourly: 0.004,
monthly: 2.5,
},
region_prices: [
{
Expand All @@ -30,8 +30,8 @@ export const typeFactory = Factory.Sync.makeFactory<LinodeType>({
memory: 16384,
network_out: 10000,
price: {
hourly: 0,
monthly: 0,
hourly: 0.015,
monthly: 10,
},
region_prices: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ const StyledRootDiv = styled('div', { label: 'StyledRootDiv' })(
})
);

const StyledVolumeUl = styled('div', { label: 'StyledVolumeUl' })(
const StyledVolumeUl = styled('ul', { label: 'StyledVolumeUl' })(
({ theme }) => ({
'& li': {
fontFamily: theme.font.bold,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { styled } from '@mui/material/styles';

import { Box } from 'src/components/Box';
import { Paper } from 'src/components/Paper';

export const StyledPaper = styled(Paper, { label: 'StyledPaper' })(
({ theme }) => ({
'& > p:first-of-type': {
color: theme.color.label,
fontFamily: theme.font.bold,
marginBottom: theme.spacing(),
marginTop: theme.spacing(2),
},
marginTop: theme.spacing(4),
padding: 0,
})
);

export const StyledDiv = styled('div', { label: 'StyledDiv' })(({ theme }) => ({
alignItems: 'center',
display: 'flex',
flexDirection: 'row',
gap: theme.spacing(),
marginBottom: theme.spacing(2),
}));

export const StyledSpan = styled('span', {
label: 'StyledSpan',
})(({ theme }) => ({
color: theme.color.label,
display: 'block',
fontFamily: theme.font.bold,
fontSize: theme.typography.body1.fontSize,
lineHeight: '1.43rem',
marginBottom: theme.spacing(1),
marginTop: theme.spacing(2),
}));

export const StyledMigrationContainer = styled(Box, {
label: 'StyledMigrationContainer',
})(({ theme }) => ({
[theme.breakpoints.up('md')]: {
display: 'flex',
justifyContent: 'space-between',
},
}));

export const StyledMigrationBox = styled(Box, {
label: 'StyledMigrationBox',
})(({ theme }) => ({
[theme.breakpoints.up('md')]: {
width: '50%',
},
width: '100%',
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { waitFor } from '@testing-library/react';
import React from 'react';

import { typeFactory } from 'src/factories/types';
import { renderWithTheme, wrapWithTheme } from 'src/utilities/testHelpers';

import { ConfigureForm } from './ConfigureForm';

// Mock the useFlags hook
jest.mock('src/hooks/useFlags', () => ({
useFlags: () => ({
dcSpecificPricing: true, // Mock the flag value
}),
}));

// Mock the useTypeQuery hook
jest.mock('src/queries/types', () => ({
useTypeQuery: () => ({
data: typeFactory.build(),
}),
}));

// Mock the useRegionsQuery hook
jest.mock('src/queries/regions', () => ({
useRegionsQuery: () => ({
data: [],
}),
}));

describe('ConfigureForm component with price comparison', () => {
const handleSelectRegion = jest.fn();
const currentPriceLabel = 'Current Price';
const newPriceLabel = 'New Price';
const currentPricePanel = 'current-price-panel';
const newPricePanel = 'new-price-panel';

const props = {
backupEnabled: true,
currentRegion: 'us-east',
handleSelectRegion,
linodeType: 'g6-standard-1',
selectedRegion: '',
};

const {
getByLabelText,
getByTestId,
getByText,
queryByText,
} = renderWithTheme(<ConfigureForm {...props} />);

interface SelectNewRegionOptions {
backupEnabled?: boolean;
currentRegionId?: string;
selectedRegionId: string;
}

const selectNewRegion = ({
backupEnabled = true,
currentRegionId = 'us-east',
selectedRegionId,
}: SelectNewRegionOptions) => {
const { rerender } = renderWithTheme(<ConfigureForm {...props} />);

rerender(
wrapWithTheme(
<ConfigureForm
{...props}
backupEnabled={backupEnabled}
currentRegion={currentRegionId}
selectedRegion={selectedRegionId}
/>
)
);
};

it('should render the initial ConfigureForm fields', () => {
// Test whether the initial component renders the expected content
expect(getByText('Configure Migration')).toBeInTheDocument();
expect(getByText('Current Region')).toBeInTheDocument();

// Verify that the RegionSelect component is rendered
const regionSelect = getByLabelText('New Region');
expect(regionSelect).toBeInTheDocument();

// Verify that the MigrationPricing component content is not rendered on page load
expect(queryByText(currentPriceLabel)).not.toBeInTheDocument();
expect(queryByText(newPriceLabel)).not.toBeInTheDocument();
});

it("shouldn't render the MigrationPricing component when the current region is selected", async () => {
selectNewRegion({ selectedRegionId: 'us-east' });
await waitFor(() => {
expect(queryByText(currentPriceLabel)).not.toBeInTheDocument();
expect(queryByText(newPriceLabel)).not.toBeInTheDocument();
});
});

it("shouldn't render the MigrationPricing component when a region without price increase is selected", async () => {
selectNewRegion({ selectedRegionId: 'us-west' });
await waitFor(() => {
expect(queryByText(currentPriceLabel)).not.toBeInTheDocument();
expect(queryByText(newPriceLabel)).not.toBeInTheDocument();
});
});

it('should render the MigrationPricing component when a region with price increase is selected', async () => {
selectNewRegion({ selectedRegionId: 'br-gru' });
await waitFor(() => {
expect(getByTestId(currentPricePanel)).toBeDefined();
expect(getByTestId(newPricePanel)).toBeDefined();
});
});

it('should render the MigrationPricing component when a region with price decrease is selected', async () => {
selectNewRegion({ currentRegionId: 'br-gru', selectedRegionId: 'us-east' });
await waitFor(() => {
expect(getByTestId(currentPricePanel)).toBeDefined();
expect(getByTestId(newPricePanel)).toBeDefined();
});
});

it('should provide a proper price comparison', async () => {
selectNewRegion({ selectedRegionId: 'br-gru' });
expect(getByTestId(currentPricePanel)).toHaveTextContent(
'$10.00/month, $0.015/hour | Backups $2.50/month'
);
expect(getByTestId(newPricePanel)).toHaveTextContent(
'$14.00/month, $0.021/hour | Backups $4.17/month'
);
});

it("shouldn't render the Backup pricing comparison if backups are disabled", () => {
selectNewRegion({ backupEnabled: false, selectedRegionId: 'br-gru' });
expect(getByTestId(currentPricePanel)).toHaveTextContent(
'$10.00/month, $0.015/hour'
);
expect(getByTestId(newPricePanel)).toHaveTextContent(
'$14.00/month, $0.021/hour'
);
});

it("shouldn't render the MigrationPricingComponent if the flag is disabled", () => {
jest.isolateModules(async () => {
jest.mock('src/hooks/useFlags', () => ({
useFlags: () => ({
dcSpecificPricing: false,
}),
}));

await waitFor(() => {
expect(queryByText('Current Price')).not.toBeInTheDocument();
expect(queryByText('New Price')).not.toBeInTheDocument();
});
});
});
});
Loading