Skip to content

Commit

Permalink
test: [M3-7955] - Cypress integration test to add SSH key via Linode …
Browse files Browse the repository at this point in the history
…Create (#10448)

* M3-7955 Cypress integration test to add SSH key via Linode Create

* Added changeset: Cypress integration test to add SSH key via Linode Create

* Fixed merge issue
  • Loading branch information
cliu-akamai authored Jul 10, 2024
1 parent 79136c5 commit 14dcd37
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 2 deletions.
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10448-tests-1718906550137.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tests
---

Cypress integration test to add SSH key via Linode Create ([#10448](https://github.com/linode/manager/pull/10448))
211 changes: 209 additions & 2 deletions packages/manager/cypress/e2e/core/linodes/create-linode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { ui } from 'support/ui';
import { chooseRegion } from 'support/util/regions';
import { randomLabel, randomString } from 'support/util/random';
import { randomLabel, randomString, randomNumber } from 'support/util/random';
import { LINODE_CREATE_TIMEOUT } from 'support/constants/linodes';
import { cleanUp } from 'support/util/cleanup';
import { linodeCreatePage } from 'support/ui/pages';
Expand All @@ -16,13 +16,47 @@ import {
import { interceptCreateLinode } from 'support/intercepts/linodes';
import { makeFeatureFlagData } from 'support/util/feature-flags';
import { interceptGetProfile } from 'support/intercepts/profile';

import { Region, VLAN, Config, Disk } from '@linode/api-v4';
import { getRegionById } from 'support/util/regions';
import {
linodeFactory,
linodeConfigFactory,
VLANFactory,
vpcFactory,
subnetFactory,
regionFactory,
LinodeConfigInterfaceFactory,
LinodeConfigInterfaceFactoryWithVPC,
} from 'src/factories';
import { dcPricingMockLinodeTypes } from 'support/constants/dc-specific-pricing';
import {
mockGetLinodeType,
mockGetLinodeTypes,
} from 'support/intercepts/linodes';
import { mockGetRegions } from 'support/intercepts/regions';
import { mockGetVLANs } from 'support/intercepts/vlans';
import { mockGetVPC, mockGetVPCs } from 'support/intercepts/vpc';
import {
mockCreateLinode,
mockGetLinodeDisks,
mockGetLinodeVolumes,
} from 'support/intercepts/linodes';
import { mockGetLinodeConfigs } from 'support/intercepts/configs';
import {
fbtClick,
fbtVisible,
getClick,
getVisible,
containsVisible,
} from 'support/helpers';
import {} from 'support/helpers';
let username: string;

authenticate();
describe('Create Linode', () => {
before(() => {
cleanUp('linodes');
cleanUp('ssh-keys');
});

// Enable the `linodeCreateRefactor` feature flag.
Expand Down Expand Up @@ -143,4 +177,177 @@ describe('Create Linode', () => {
});
});
});

it('adds an SSH key to the linode during create flow', () => {
const rootpass = randomString(32);
const sshPublicKeyLabel = randomLabel();
const randomKey = randomString(400, {
uppercase: true,
lowercase: true,
numbers: true,
spaces: false,
symbols: false,
});
const sshPublicKey = `ssh-rsa e2etestkey${randomKey} e2etest@linode`;
const linodeLabel = randomLabel();
const region: Region = getRegionById('us-southeast');
const diskLabel: string = 'Debian 10 Disk';
const mockLinode = linodeFactory.build({
label: linodeLabel,
region: region.id,
type: dcPricingMockLinodeTypes[0].id,
});
const mockVLANs: VLAN[] = VLANFactory.buildList(2);
const mockSubnet = subnetFactory.build({
id: randomNumber(2),
label: randomLabel(),
});
const mockVPC = vpcFactory.build({
id: randomNumber(),
region: 'us-southeast',
subnets: [mockSubnet],
});
const mockVPCRegion = regionFactory.build({
id: region.id,
label: region.label,
capabilities: ['Linodes', 'VPCs', 'Vlans'],
});
const mockPublicConfigInterface = LinodeConfigInterfaceFactory.build({
ipam_address: null,
purpose: 'public',
});
const mockVlanConfigInterface = LinodeConfigInterfaceFactory.build();
const mockVpcConfigInterface = LinodeConfigInterfaceFactoryWithVPC.build({
vpc_id: mockVPC.id,
purpose: 'vpc',
active: true,
});
const mockConfig: Config = linodeConfigFactory.build({
id: randomNumber(),
interfaces: [
// The order of this array is significant. Index 0 (eth0) should be public.
mockPublicConfigInterface,
mockVlanConfigInterface,
mockVpcConfigInterface,
],
});
const mockDisks: Disk[] = [
{
id: 44311273,
status: 'ready',
label: diskLabel,
created: '2020-08-21T17:26:14',
updated: '2020-08-21T17:26:30',
filesystem: 'ext4',
size: 81408,
},
{
id: 44311274,
status: 'ready',
label: '512 MB Swap Image',
created: '2020-08-21T17:26:14',
updated: '2020-08-21T17:26:31',
filesystem: 'swap',
size: 512,
},
];

// Mock requests to get individual types.
mockGetLinodeType(dcPricingMockLinodeTypes[0]);
mockGetLinodeType(dcPricingMockLinodeTypes[1]);
mockGetLinodeTypes(dcPricingMockLinodeTypes).as('getLinodeTypes');

mockAppendFeatureFlags({
vpc: makeFeatureFlagData(true),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientStream');

mockGetRegions([mockVPCRegion]).as('getRegions');

mockGetVLANs(mockVLANs);
mockGetVPC(mockVPC).as('getVPC');
mockGetVPCs([mockVPC]).as('getVPCs');
mockCreateLinode(mockLinode).as('linodeCreated');
mockGetLinodeConfigs(mockLinode.id, [mockConfig]).as('getLinodeConfigs');
mockGetLinodeDisks(mockLinode.id, mockDisks).as('getDisks');
mockGetLinodeVolumes(mockLinode.id, []).as('getVolumes');

// intercept request
cy.visitWithLogin('/linodes/create');
cy.wait([
'@getLinodeTypes',
'@getClientStream',
'@getFeatureFlags',
'@getVPCs',
]);

cy.get('[data-qa-header="Create"]').should('have.text', 'Create');

// Check the 'Backups' add on
cy.get('[data-testid="backups"]').should('be.visible').click();
ui.regionSelect.find().click().type(`${region.label} {enter}`);
fbtClick('Shared CPU');
getClick(`[id="${dcPricingMockLinodeTypes[0].id}"]`);

// the "VPC" section is present, and the VPC in the same region of
// the linode can be selected.
getVisible('[data-testid="vpc-panel"]').within(() => {
containsVisible('Assign this Linode to an existing VPC.');
// select VPC
cy.get('[data-qa-enhanced-select="None"]')
.should('be.visible')
.click()
.type(`${mockVPC.label}{enter}`);
// select subnet
cy.findByText('Select Subnet')
.should('be.visible')
.click()
.type(`${mockSubnet.label}{enter}`);
});

// The drawer opens when clicking "Add an SSH Key" button
ui.button
.findByTitle('Add an SSH Key')
.should('be.visible')
.should('be.enabled')
.click();
ui.drawer
.findByTitle('Add SSH Key')
.should('be.visible')
.within(() => {
cy.get('[id="label"]').clear().type(sshPublicKeyLabel);

// An alert displays when the format of SSH key is incorrect
cy.get('[id="ssh-public-key"]').clear().type('WrongFormatSshKey');
ui.button
.findByTitle('Add Key')
.should('be.visible')
.should('be.enabled')
.click();
cy.findAllByText(
'SSH Key key-type must be ssh-dss, ssh-rsa, ecdsa-sha2-nistp, ssh-ed25519, or sk-ecdsa-sha2-nistp256.'
).should('be.visible');

// Create a new ssh key
cy.get('[id="ssh-public-key"]').clear().type(sshPublicKey);
ui.button
.findByTitle('Add Key')
.should('be.visible')
.should('be.enabled')
.click();
});

// When a user creates an SSH key, a toast notification appears that says "Successfully created SSH key."
ui.toast.assertMessage('Successfully created SSH key.');

// When a user creates an SSH key, the list of SSH keys for each user updates to show the new key for the signed in user
cy.findAllByText(sshPublicKeyLabel).should('be.visible');

getClick('#linode-label').clear().type(linodeLabel);
cy.get('#root-password').type(rootpass);
getClick('[data-qa-deploy-linode]');
cy.wait('@linodeCreated').its('response.statusCode').should('eq', 200);
fbtVisible(linodeLabel);
cy.contains('RUNNING', { timeout: 300000 }).should('be.visible');
});
});
18 changes: 18 additions & 0 deletions packages/manager/cypress/support/api/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import {
OAuthClient,
deleteOAuthClient,
getOAuthClients,
SSHKey,
deleteSSHKey,
getSSHKeys,
} from '@linode/api-v4';
import { isTestLabel } from 'support/api/common';
import { pageSize } from 'support/constants/api';
Expand All @@ -26,3 +29,18 @@ export const deleteAllTestOAuthApps = async (): Promise<void> => {

await Promise.all(deletionPromises);
};

export const deleteAllTestSSHKeys = async (): Promise<void> => {
const sshKeys = await depaginate<SSHKey>((page: number) => {
return getSSHKeys({ page, page_size: pageSize });
});

const deletionPromises = sshKeys
.filter((sshKey: SSHKey) => isTestLabel(sshKey.label))
.map(async (sshKey: SSHKey) => {
console.log(`deleting ${sshKey.label}`);
await deleteSSHKey(sshKey.id);
});

await Promise.all(deletionPromises);
};
3 changes: 3 additions & 0 deletions packages/manager/cypress/support/util/cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { deleteAllTestStackScripts } from 'support/api/stackscripts';
import { deleteAllTestTags } from 'support/api/tags';
import { deleteAllTestVolumes } from 'support/api/volumes';
import { deleteAllTestSSHKeys } from 'support/api/profile';

/** Types of resources that can be cleaned up. */
export type CleanUpResource =
Expand All @@ -27,6 +28,7 @@ export type CleanUpResource =
| 'obj-buckets'
| 'service-transfers'
| 'stackscripts'
| 'ssh-keys'
| 'tags'
| 'volumes';

Expand All @@ -48,6 +50,7 @@ const cleanUpMap: CleanUpMap = {
'obj-buckets': () => deleteAllTestBuckets(),
'service-transfers': () => cancelAllTestEntityTransfers(),
stackscripts: () => deleteAllTestStackScripts(),
'ssh-keys': () => deleteAllTestSSHKeys(),
tags: () => deleteAllTestTags(),
volumes: () => deleteAllTestVolumes(),
};
Expand Down

0 comments on commit 14dcd37

Please sign in to comment.