Skip to content

Commit c5e1324

Browse files
authored
test: [M3-7124] - Add Cypress integration tests for VPC assign/unassign flows (#9818)
* EW-7124 Add Cypress integration tests for VPC assign/unassign flows * Added changeset: Add integration tests for VPC assign/unassign flows
1 parent 26b544c commit c5e1324

File tree

6 files changed

+409
-19
lines changed

6 files changed

+409
-19
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Added
3+
---
4+
5+
Add integration tests for VPC assign/unassign flows ([#9818](https://github.com/linode/manager/pull/9818))

packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import { randomLabel, randomString } from 'support/util/random';
1212
import { ui } from 'support/ui';
1313
import { chooseRegion } from 'support/util/regions';
14-
import { interceptGetLinodeConfigs } from 'support/intercepts/linodes';
14+
import { interceptGetLinodeConfigs } from 'support/intercepts/configs';
1515
import { cleanUp } from 'support/util/cleanup';
1616

1717
// Local storage override to force volume table to list up to 100 items.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
/**
2+
* @file Integration tests for VPC assign/unassign Linodes flows.
3+
*/
4+
5+
import {
6+
mockAppendFeatureFlags,
7+
mockGetFeatureFlagClientstream,
8+
} from 'support/intercepts/feature-flags';
9+
import { makeFeatureFlagData } from 'support/util/feature-flags';
10+
import {
11+
mockGetSubnets,
12+
mockCreateSubnet,
13+
mockGetVPC,
14+
mockGetVPCs,
15+
} from 'support/intercepts/vpc';
16+
import {
17+
subnetFactory,
18+
vpcFactory,
19+
linodeFactory,
20+
linodeConfigFactory,
21+
LinodeConfigInterfaceFactoryWithVPC,
22+
} from '@src/factories';
23+
import { ui } from 'support/ui';
24+
import { randomNumber, randomLabel } from 'support/util/random';
25+
import { mockGetLinodes } from 'support/intercepts/linodes';
26+
import {
27+
mockCreateLinodeConfigInterfaces,
28+
mockGetLinodeConfigs,
29+
mockDeleteLinodeConfigInterface,
30+
} from 'support/intercepts/configs';
31+
import {
32+
vpcAssignLinodeRebootNotice,
33+
vpcUnassignLinodeRebootNotice,
34+
} from 'support/constants/vpc';
35+
import { VPC, Linode, Config } from '@linode/api-v4/types';
36+
37+
describe('VPC assign/unassign flows', () => {
38+
let mockVPCs: VPC[];
39+
let mockLinode: Linode;
40+
let mockConfig: Config;
41+
42+
before(() => {
43+
mockVPCs = vpcFactory.buildList(5);
44+
45+
mockLinode = linodeFactory.build({
46+
id: randomNumber(),
47+
label: randomLabel(),
48+
});
49+
50+
mockConfig = linodeConfigFactory.build({
51+
id: randomNumber(),
52+
});
53+
});
54+
55+
/*
56+
* - Confirms that can assign a Linode to the VPC when feature is enabled.
57+
*/
58+
it('can assign Linode(s) to a VPC', () => {
59+
const mockSubnet = subnetFactory.build({
60+
id: randomNumber(2),
61+
label: randomLabel(),
62+
});
63+
64+
const mockVPC = vpcFactory.build({
65+
id: randomNumber(),
66+
label: randomLabel(),
67+
});
68+
69+
const mockVPCAfterSubnetCreation = vpcFactory.build({
70+
...mockVPC,
71+
subnets: [mockSubnet],
72+
});
73+
74+
const mockSubnetAfterLinodeAssignment = subnetFactory.build({
75+
...mockSubnet,
76+
linodes: [mockLinode],
77+
});
78+
79+
const mockVPCAfterLinodeAssignment = vpcFactory.build({
80+
...mockVPCAfterSubnetCreation,
81+
subnets: [mockSubnetAfterLinodeAssignment],
82+
});
83+
84+
mockAppendFeatureFlags({
85+
vpc: makeFeatureFlagData(true),
86+
}).as('getFeatureFlags');
87+
mockGetFeatureFlagClientstream().as('getClientStream');
88+
mockGetVPCs(mockVPCs).as('getVPCs');
89+
mockGetVPC(mockVPC).as('getVPC');
90+
mockGetSubnets(mockVPC.id, []).as('getSubnets');
91+
mockCreateSubnet(mockVPC.id).as('createSubnet');
92+
93+
cy.visitWithLogin(`/vpcs/${mockVPC.id}`);
94+
cy.wait(['@getFeatureFlags', '@getClientStream', '@getVPC', '@getSubnets']);
95+
96+
// confirm that vpc and subnet details get displayed
97+
cy.findByText(mockVPC.label).should('be.visible');
98+
cy.findByText('Subnets (0)');
99+
cy.findByText('No Subnets are assigned.');
100+
101+
ui.button.findByTitle('Create Subnet').should('be.visible').click();
102+
103+
mockGetVPC(mockVPCAfterSubnetCreation).as('getVPC');
104+
mockGetSubnets(mockVPC.id, [mockSubnet]).as('getSubnets');
105+
106+
ui.drawer
107+
.findByTitle('Create Subnet')
108+
.should('be.visible')
109+
.within(() => {
110+
cy.findByText('Subnet Label')
111+
.should('be.visible')
112+
.click()
113+
.type(mockSubnet.label);
114+
115+
cy.findByTestId('create-subnet-drawer-button')
116+
.should('be.visible')
117+
.should('be.enabled')
118+
.click();
119+
});
120+
121+
cy.wait(['@createSubnet', '@getVPC', '@getSubnets']);
122+
123+
// confirm that newly created subnet should now appear on VPC's detail page
124+
cy.findByText(mockVPC.label).should('be.visible');
125+
cy.findByText('Subnets (1)').should('be.visible');
126+
cy.findByText(mockSubnet.label).should('be.visible');
127+
128+
// assign a linode to the subnet
129+
ui.actionMenu
130+
.findByTitle(`Action menu for Subnet ${mockSubnet.label}`)
131+
.should('be.visible')
132+
.click();
133+
134+
mockGetLinodes([mockLinode]).as('getLinodes');
135+
ui.actionMenuItem
136+
.findByTitle('Assign Linodes')
137+
.should('be.visible')
138+
.click();
139+
cy.wait('@getLinodes');
140+
141+
ui.drawer
142+
.findByTitle(`Assign Linodes to subnet: ${mockSubnet.label} (0.0.0.0/0)`)
143+
.should('be.visible')
144+
.within(() => {
145+
// confirm that the user is warned that a reboot is required
146+
cy.findByText(vpcAssignLinodeRebootNotice).should('be.visible');
147+
148+
ui.button
149+
.findByTitle('Assign Linode')
150+
.should('be.visible')
151+
.should('be.disabled');
152+
153+
mockGetLinodeConfigs(mockLinode.id, [mockConfig]).as(
154+
'getLinodeConfigs'
155+
);
156+
cy.findByText('Linodes')
157+
.should('be.visible')
158+
.click()
159+
.type(mockLinode.label);
160+
cy.findByRole('presentation').should('be.visible').click();
161+
cy.wait('@getLinodeConfigs');
162+
163+
mockCreateLinodeConfigInterfaces(mockLinode.id, mockConfig).as(
164+
'createLinodeConfigInterfaces'
165+
);
166+
mockGetVPC(mockVPCAfterLinodeAssignment).as('getVPCLinodeAssignment');
167+
mockGetSubnets(mockVPC.id, [mockSubnetAfterLinodeAssignment]).as(
168+
'getSubnetsLinodeAssignment'
169+
);
170+
ui.button
171+
.findByTitle('Assign Linode')
172+
.should('be.visible')
173+
.should('be.enabled')
174+
.click();
175+
cy.wait([
176+
'@createLinodeConfigInterfaces',
177+
'@getVPCLinodeAssignment',
178+
'@getSubnetsLinodeAssignment',
179+
]);
180+
181+
ui.button
182+
.findByTitle('Done')
183+
.should('be.visible')
184+
.should('be.enabled')
185+
.click();
186+
});
187+
188+
cy.get('[aria-label="View Details"]')
189+
.closest('tbody')
190+
.within(() => {
191+
// after assigning Linode(s) to a VPC, VPC page increases number in 'Linodes' column
192+
cy.findByText('1').should('be.visible');
193+
});
194+
});
195+
196+
/*
197+
* - Confirms that can unassign a Linode from the VPC when feature is enabled.
198+
*/
199+
it('can unassign Linode(s) from a VPC', () => {
200+
const mockSecondLinode = linodeFactory.build({
201+
id: randomNumber(),
202+
label: randomLabel(),
203+
});
204+
205+
const mockSubnet = subnetFactory.build({
206+
id: randomNumber(2),
207+
label: randomLabel(),
208+
linodes: [mockLinode, mockSecondLinode],
209+
});
210+
211+
const mockVPC = vpcFactory.build({
212+
id: randomNumber(),
213+
label: randomLabel(),
214+
subnets: [mockSubnet],
215+
});
216+
217+
const mockLinodeConfig = linodeConfigFactory.build({
218+
interfaces: [
219+
LinodeConfigInterfaceFactoryWithVPC.build({
220+
vpc_id: mockVPC.id,
221+
subnet_id: mockSubnet.id,
222+
}),
223+
],
224+
});
225+
226+
mockAppendFeatureFlags({
227+
vpc: makeFeatureFlagData(true),
228+
}).as('getFeatureFlags');
229+
mockGetFeatureFlagClientstream().as('getClientStream');
230+
mockGetVPCs(mockVPCs).as('getVPCs');
231+
mockGetVPC(mockVPC).as('getVPC');
232+
mockGetSubnets(mockVPC.id, [mockSubnet]).as('getSubnets');
233+
234+
cy.visitWithLogin(`/vpcs/${mockVPC.id}`);
235+
cy.wait(['@getFeatureFlags', '@getClientStream', '@getVPC', '@getSubnets']);
236+
237+
// confirm that subnet should get displayed on VPC's detail page
238+
cy.findByText(mockVPC.label).should('be.visible');
239+
cy.findByText('Subnets (1)').should('be.visible');
240+
cy.findByText(mockSubnet.label).should('be.visible');
241+
242+
// unassign a linode to the subnet
243+
ui.actionMenu
244+
.findByTitle(`Action menu for Subnet ${mockSubnet.label}`)
245+
.should('be.visible')
246+
.click();
247+
248+
mockGetLinodes([mockLinode, mockSecondLinode]).as('getLinodes');
249+
ui.actionMenuItem
250+
.findByTitle('Unassign Linodes')
251+
.should('be.visible')
252+
.click();
253+
cy.wait('@getLinodes');
254+
255+
ui.drawer
256+
.findByTitle(
257+
`Unassign Linodes from subnet: ${mockSubnet.label} (0.0.0.0/0)`
258+
)
259+
.should('be.visible')
260+
.within(() => {
261+
// confirm that the user is warned that a reboot is required
262+
cy.findByText(vpcUnassignLinodeRebootNotice).should('be.visible');
263+
264+
ui.button
265+
.findByTitle('Unassign Linodes')
266+
.should('be.visible')
267+
.should('be.disabled');
268+
269+
// confirm that unassign a single Linode from the VPC correctly
270+
mockGetLinodeConfigs(mockLinode.id, [mockLinodeConfig]).as(
271+
'getLinodeConfigs'
272+
);
273+
cy.findByText('Linodes')
274+
.should('be.visible')
275+
.click()
276+
.type(mockLinode.label);
277+
cy.findByRole('presentation').should('be.visible').click();
278+
cy.wait('@getLinodeConfigs');
279+
280+
// the select option won't disappear unless click on somewhere else
281+
cy.findByText(vpcUnassignLinodeRebootNotice).click();
282+
// confirm that unassigned Linode(s) are displayed on the details page
283+
cy.findByText('Linodes to be Unassigned from Subnet (1)').should(
284+
'be.visible'
285+
);
286+
cy.findByText(mockLinode.label).should('be.visible');
287+
288+
// confirm that unassign multiple Linodes from the VPC correctly
289+
mockGetLinodeConfigs(mockSecondLinode.id, [mockLinodeConfig]).as(
290+
'getLinodeConfigs'
291+
);
292+
cy.findByText('Linodes')
293+
.should('be.visible')
294+
.click()
295+
.type(mockSecondLinode.label);
296+
cy.findByText(mockSecondLinode.label).should('be.visible').click();
297+
cy.wait('@getLinodeConfigs');
298+
299+
// confirm that unassigned Linode(s) are displayed on the details page
300+
cy.findByText(vpcUnassignLinodeRebootNotice).click();
301+
cy.findByText('Linodes to be Unassigned from Subnet (2)').should(
302+
'be.visible'
303+
);
304+
cy.findByText(mockSecondLinode.label).should('be.visible');
305+
306+
mockDeleteLinodeConfigInterface(
307+
mockLinode.id,
308+
mockLinodeConfig.id,
309+
mockLinodeConfig.interfaces[0].id
310+
).as('deleteLinodeConfigInterface1');
311+
mockDeleteLinodeConfigInterface(
312+
mockSecondLinode.id,
313+
mockLinodeConfig.id,
314+
mockLinodeConfig.interfaces[0].id
315+
).as('deleteLinodeConfigInterface2');
316+
ui.button
317+
.findByTitle('Unassign Linodes')
318+
.should('be.visible')
319+
.should('be.enabled')
320+
.click();
321+
322+
// Confirm that click on 'Unassign Linodes' button will send request to update the subnet details on the VPC page.
323+
cy.wait('@deleteLinodeConfigInterface1')
324+
.its('response.statusCode')
325+
.should('eq', 200);
326+
cy.wait('@deleteLinodeConfigInterface2')
327+
.its('response.statusCode')
328+
.should('eq', 200);
329+
});
330+
});
331+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** Notice shown to users trying to assign a linode to a VPC. */
2+
export const vpcAssignLinodeRebootNotice =
3+
'Assigning a Linode to a subnet requires you to reboot the Linode to update its configuration.';
4+
5+
/** Notice shown to users trying to unassign a linode from a VPC. */
6+
export const vpcUnassignLinodeRebootNotice =
7+
'Unassigning Linodes from a subnet requires you to reboot the Linodes to update its configuration.';

0 commit comments

Comments
 (0)