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

test: [M3-7955] - Cypress integration test to add SSH key via Linode Create #10448

Merged
merged 6 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
174 changes: 174 additions & 0 deletions packages/manager/cypress/e2e/core/linodes/create-linode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ authenticate();
describe('create linode', () => {
before(() => {
cleanUp('linodes');
cleanUp('ssh-keys');
Copy link
Contributor

@jdamore-linode jdamore-linode May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cleanUp('ssh-keys');

Very minor nit: This call to cleanUp('ssh-keys') isn't really necessary since your test mocks the SSH key (no real SSH key is being created, so nothing will need to be cleaned up). However, I really appreciate you setting up the deleteAllTestSSHKeys util and hooking it up to the cleanUp function since this will be needed for M3-7956!

Oops, never mind! Should have looked closer, realizing now that this test does actually create an SSH key! The ticket technically called for an integration test but I'm thinking leaving this as-is might be fine

});

/*
Expand Down Expand Up @@ -511,4 +512,177 @@ describe('create linode', () => {
containsVisible(`eth2 – VPC: ${mockVPC.label}`);
});
});

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';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a lot of this mock data is held over from other tests, and we can get rid of them for this test just to make things a little cleaner! Locally, I was able to get rid of all the mock data and setup for disks, VLANs, VPCs & subnets, configs, and the mocks for the Linode DC-specific pricing types and your test still ran really nicely and passed!

Not a super big deal since your test is still testing all of the important things we need to cover for SSH keys 👍

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
Loading