diff --git a/packages/manager/.changeset/pr-12541-fixed-1753084680182.md b/packages/manager/.changeset/pr-12541-fixed-1753084680182.md new file mode 100644 index 00000000000..f4c4c4d3f00 --- /dev/null +++ b/packages/manager/.changeset/pr-12541-fixed-1753084680182.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Swap IPv4 Addresses amongst linodes in the same region ([#12541](https://github.com/linode/manager/pull/12541)) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.test.ts b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.test.ts deleted file mode 100644 index 81794f9cfcc..00000000000 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { getLinodeIPv6Ranges } from './IPTransfer'; - -describe('getLinodeIPv6Ranges', () => { - const ipv6Ranges = [ - { - prefix: 64, - range: '2fff:db08:e003:1::', - region: 'us-east', - route_target: '2600:3c03::f03c:92ff:fe8d:5c0c', - }, - { - prefix: 64, - range: '2fff:db08:e003:2::', - region: 'us-east', - route_target: '2600:3c03::f03c:92ff:fe8d:5c0d', - }, - { - prefix: 56, - range: '2fff:db08:e003:3::', - region: 'us-east', - route_target: '2600:3c03::f03c:92ff:fe8d:5c0d', - }, - ]; - - it('returns an array of ranges', () => { - expect( - getLinodeIPv6Ranges(ipv6Ranges, '2600:3c03::f03c:92ff:fe8d:5c0d/128') - ).toEqual(['2fff:db08:e003:2::/64', '2fff:db08:e003:3::/56']); - }); - - it('returns an empty array if at least one argument is undefined or null', () => { - expect( - getLinodeIPv6Ranges(undefined, '2600:3c03::f03c:92ff:fe8d:5b1b/128') - ).toEqual([]); - expect(getLinodeIPv6Ranges(ipv6Ranges, null)).toEqual([]); - expect(getLinodeIPv6Ranges(undefined, null)).toEqual([]); - }); -}); diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.test.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.test.tsx new file mode 100644 index 00000000000..d05d4a5c5c8 --- /dev/null +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.test.tsx @@ -0,0 +1,134 @@ +import { linodeFactory, linodeIPFactory } from '@linode/utilities'; +import { screen, waitForElementToBeRemoved } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import * as React from 'react'; + +import { makeResourcePage } from 'src/mocks/serverHandlers'; +import { http, HttpResponse, server } from 'src/mocks/testServer'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { getLinodeIPv6Ranges, IPTransfer } from './IPTransfer'; + +describe('getLinodeIPv6Ranges', () => { + const ipv6Ranges = [ + { + prefix: 64, + range: '2fff:db08:e003:1::', + region: 'us-east', + route_target: '2600:3c03::f03c:92ff:fe8d:5c0c', + }, + { + prefix: 64, + range: '2fff:db08:e003:2::', + region: 'us-east', + route_target: '2600:3c03::f03c:92ff:fe8d:5c0d', + }, + { + prefix: 56, + range: '2fff:db08:e003:3::', + region: 'us-east', + route_target: '2600:3c03::f03c:92ff:fe8d:5c0d', + }, + ]; + + it('returns an array of ranges', () => { + expect( + getLinodeIPv6Ranges(ipv6Ranges, '2600:3c03::f03c:92ff:fe8d:5c0d/128') + ).toEqual(['2fff:db08:e003:2::/64', '2fff:db08:e003:3::/56']); + }); + + it('returns an empty array if at least one argument is undefined or null', () => { + expect( + getLinodeIPv6Ranges(undefined, '2600:3c03::f03c:92ff:fe8d:5b1b/128') + ).toEqual([]); + expect(getLinodeIPv6Ranges(ipv6Ranges, null)).toEqual([]); + expect(getLinodeIPv6Ranges(undefined, null)).toEqual([]); + }); + + it('sets the default Linode and IP address when the "Swap With" action is selected.', async () => { + const [selectedLinode, destinationLinode] = linodeFactory.buildList(2, { + ipv6: null, + }); + const mockLinodeIPs = linodeIPFactory.build({ ipv6: undefined }); + server.use( + http.get('*/linode/instances', () => { + return HttpResponse.json( + makeResourcePage([selectedLinode, destinationLinode]) + ); + }), + http.get('*/linode/instances/:linodeId', () => { + return HttpResponse.json(selectedLinode); + }), + http.get('*/linode/instances/:linodeId/ips', () => { + return HttpResponse.json(mockLinodeIPs); + }), + http.get('*/networking/ipv6/ranges', () => { + return HttpResponse.json(makeResourcePage([])); + }) + ); + + const { + getAllByTestId, + getByPlaceholderText, + findByText, + findByPlaceholderText, + } = renderWithTheme( + + ); + + await waitForElementToBeRemoved(getAllByTestId('circle-progress')); + + const actionSelect = getByPlaceholderText('Select Action'); + await userEvent.click(actionSelect); + await userEvent.click(await findByText('Swap With')); + expect(actionSelect).toHaveValue('Swap With'); + + const linodeInput = await findByPlaceholderText('Select Linode'); + expect(linodeInput).toHaveValue(destinationLinode.label); + + const IpInput = await screen.findByPlaceholderText('Select IP Address'); + expect(IpInput).toHaveValue(destinationLinode.ipv4[0]); + }); + + it('sets the default Linode when the "Move To" action is selected.', async () => { + const [selectedLinode, destinationLinode] = linodeFactory.buildList(2, { + ipv6: null, + }); + const mockLinodeIPs = linodeIPFactory.build({ ipv6: undefined }); + server.use( + http.get('*/linode/instances', () => { + return HttpResponse.json( + makeResourcePage([selectedLinode, destinationLinode]) + ); + }), + http.get('*/linode/instances/:linodeId', () => { + return HttpResponse.json(selectedLinode); + }), + http.get('*/linode/instances/:linodeId/ips', () => { + return HttpResponse.json(mockLinodeIPs); + }), + http.get('*/networking/ipv6/ranges', () => { + return HttpResponse.json(makeResourcePage([])); + }) + ); + + const { + getAllByTestId, + getByPlaceholderText, + findByText, + findByPlaceholderText, + } = renderWithTheme( + + ); + + await waitForElementToBeRemoved(getAllByTestId('circle-progress')); + + const actionSelect = getByPlaceholderText('Select Action'); + await userEvent.click(actionSelect); + await userEvent.click(await findByText('Move To')); + expect(actionSelect).toHaveValue('Move To'); + + const linodeInput = await findByPlaceholderText('Select Linode'); + expect(linodeInput).toHaveValue(destinationLinode.label); + }); +}); diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.tsx index 2fec9e6092d..6a868e60545 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/IPTransfer.tsx @@ -172,6 +172,7 @@ export const IPTransfer = (props: Props) => { ...firstLinode.ipv4, ...linodeIPv6Ranges, ]; + swapState['selectedLinodeID'] = firstLinode.id; break; /** When switching back to none, reset the ipState. */ @@ -247,6 +248,7 @@ export const IPTransfer = (props: Props) => { return ( { noMarginTop onChange={onSelectedIPChange(sourceIP)} options={IPList} + placeholder="Select IP Address" textFieldProps={{ dataAttrs: { 'data-qa-swap-ip-action-menu': true,