Skip to content

Commit

Permalink
test: [M3-6550] - Add Domain delete, import a zone, and download zone…
Browse files Browse the repository at this point in the history
… file Cypress tests (#9111)

* M3-6550: Add new Domain tests

* Fix comments
  • Loading branch information
cliu-akamai authored May 16, 2023
1 parent 0e7448c commit 1e52cb0
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 2 deletions.
86 changes: 86 additions & 0 deletions packages/manager/cypress/e2e/domains/smoke-delete-domain.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Domain } from '@linode/api-v4/types';
import { domainFactory } from '@src/factories';
import { containsClick } from 'support/helpers';
import { authenticate } from 'support/api/authentication';
import { randomDomainName } from 'support/util/random';
import { createDomain } from '@linode/api-v4/lib/domains';
import { ui } from 'support/ui';

authenticate();
describe('Delete a Domain', () => {
/*
* - Clicks "Delete" action menu item for domain but cancels operation.
* - Clicks "Delete" action menu item for domain and confirms operation.
* - Confirms that domain is still in landing page list after canceled operation.
* - Confirms that domain is removed from landing page list after confirmed operation.
*/
it('deletes a domain', () => {
const domainRequest = domainFactory.build({
domain: randomDomainName(),
group: 'test-group',
});

cy.defer(createDomain(domainRequest)).then((domain: Domain) => {
cy.visitWithLogin('/domains');

// Confirm that domain is listed and initiate deletion.
cy.findByText(domain.domain)
.should('be.visible')
.closest('tr')
.within(() => {
ui.actionMenu
.findByTitle(`Action menu for Domain ${domain}`)
.should('be.visible')
.click();
});
ui.actionMenuItem.findByTitle('Delete').should('be.visible').click();

// Cancel deletion when prompted to confirm.
ui.dialog
.findByTitle(`Delete Domain ${domain.domain}?`)
.should('be.visible')
.within(() => {
ui.buttonGroup
.findButtonByTitle('Cancel')
.should('be.visible')
.should('be.enabled')
.click();
});

// Confirm that domain is still listed and initiate deletion again.
cy.findByText(domain.domain)
.should('be.visible')
.closest('tr')
.within(() => {
ui.actionMenu
.findByTitle(`Action menu for Domain ${domain}`)
.should('be.visible')
.click();
});
ui.actionMenuItem.findByTitle('Delete').should('be.visible').click();

// Confirm deletion.
ui.dialog
.findByTitle(`Delete Domain ${domain.domain}?`)
.should('be.visible')
.within(() => {
// The button should be disabled before confirming the correct domain
ui.buttonGroup
.findButtonByTitle('Delete Domain')
.should('be.visible')
.should('be.disabled');

containsClick('Domain Name').type(domain.domain);
ui.buttonGroup
.findButtonByTitle('Delete Domain')
.should('be.visible')
.should('be.enabled')
.click();
});

// Confirm that domain is deleted.
cy.visitWithLogin('/domains');
cy.findByText(domain.domain).should('not.exist');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
domainFactory,
domainRecordFactory,
domainZoneFileFactory,
} from '@src/factories';
import { authenticate } from 'support/api/authentication';
import { fbtClick, fbtVisible } from 'support/helpers';
import {
mockGetDomains,
mockGetDomain,
mockGetDomainRecords,
mockGetDomainZoneFile,
} from 'support/intercepts/domains';
import { randomDomainName } from 'support/util/random';
import { readDownload } from 'support/util/downloads';
import { ui } from 'support/ui';

authenticate();
describe('Download a Zone file', () => {
/*
* - Clicks "Import A Zone" button and confirms operation.
* - Confirms that Domain won't be imported when the domain is empty or invalid.
* - Confirms that Domain won't be imported when the name server is empty or invalid.
* - Confirms that Domain exists after imported operation.
*/
it('downloads a zone in the domain page', () => {
const mockDomain = domainFactory.build({
id: 123,
domain: randomDomainName(),
group: 'test-group',
});
const mockDomainRecords = domainRecordFactory.build();
const mockDomainZoneFile = domainZoneFileFactory.build();
const mockZoneFileContents = mockDomainZoneFile.zone_file.join('\n');

cy.visitWithLogin('/domains');
ui.button
.findByTitle('Import a Zone')
.should('be.visible')
.should('be.enabled')
.click();

mockGetDomains(mockDomain).as('getDomains');
cy.visitWithLogin('/domains');
cy.wait('@getDomains');

mockGetDomain(mockDomain.id, mockDomain).as('getDomain');
mockGetDomainRecords(mockDomainRecords).as('getDomainRecords');
fbtVisible(mockDomain.domain);
fbtClick(mockDomain.domain);
cy.wait('@getDomain');
cy.wait('@getDomainRecords');

mockGetDomainZoneFile(mockDomain.id, mockDomainZoneFile).as(
'getDomainZoneFile'
);
ui.button
.findByTitle('Download DNS Zone File')
.should('be.visible')
.click();
cy.wait('@getDomainZoneFile');

readDownload(`${mockDomain.domain}.txt`).should('eq', mockZoneFileContents);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { ImportZonePayload } from '@linode/api-v4/types';
import { domainFactory } from '@src/factories';
import { authenticate } from 'support/api/authentication';
import { fbltClick } from 'support/helpers';
import { randomDomainName, randomIp } from 'support/util/random';
import { mockGetDomains, mockImportDomain } from 'support/intercepts/domains';
import { ui } from 'support/ui';

authenticate();
describe('Import a Zone', () => {
/*
* - Clicks "Import A Zone" button and confirms operation.
* - Confirms that Domain won't be imported when the domain is empty or invalid.
* - Confirms that Domain won't be imported when the name server is empty or invalid.
* - Confirms that Domain exists after imported operation.
*/
it('imports a zone in the domain page', () => {
const zone: ImportZonePayload = {
domain: randomDomainName(),
remote_nameserver: randomIp(),
};

const mockDomain = domainFactory.build({
domain: zone.domain,
group: 'test-group',
});

mockGetDomains(mockDomain).as('getDomains');
cy.visitWithLogin('/domains');
cy.wait('@getDomains');

ui.button
.findByTitle('Import a Zone')
.should('be.visible')
.should('be.enabled')
.click();

ui.drawer
.findByTitle('Import a Zone')
.should('be.visible')
.within(() => {
// The button should be disabled before providing any values
ui.buttonGroup
.findButtonByTitle('Import')
.should('be.visible')
.should('be.disabled');

// Verify only filling out Domain cannot import
fbltClick('Domain').clear().type(zone.domain);
ui.buttonGroup
.findButtonByTitle('Import')
.should('be.visible')
.should('be.enabled')
.click();
cy.findByText('Remote nameserver is required.');

// Verify invalid domain cannot import
fbltClick('Domain').clear().type('1');
fbltClick('Remote Nameserver').clear().type(zone.remote_nameserver);
ui.buttonGroup
.findButtonByTitle('Import')
.should('be.visible')
.should('be.enabled')
.click();
cy.findByText('Domain is not valid.');

// Verify only filling out RemoteNameserver cannot import
fbltClick('Domain').clear();
fbltClick('Remote Nameserver').clear().type(zone.remote_nameserver);
ui.buttonGroup
.findButtonByTitle('Import')
.should('be.visible')
.should('be.enabled')
.click();
cy.findByText('Domain is required.');

// Verify invalid remote nameserver cannot import
fbltClick('Domain').clear().type(zone.domain);
fbltClick('Remote Nameserver').clear().type('1');
ui.buttonGroup
.findButtonByTitle('Import')
.should('be.visible')
.should('be.enabled')
.click();
cy.findByText(`The nameserver '1' is not valid.`);

// Fill out and import the zone.
mockImportDomain(mockDomain).as('importDomain');
mockGetDomains(mockDomain).as('getDomains');
fbltClick('Domain').clear().type(zone.domain);
fbltClick('Remote Nameserver').clear().type(zone.remote_nameserver);
ui.buttonGroup
.findButtonByTitle('Import')
.should('be.visible')
.should('be.enabled')
.click();
});

// Confirm that zone is imported.
cy.wait('@importDomain');
cy.visitWithLogin('/domains');
cy.wait('@getDomains');
cy.findByText(zone.domain).should('be.visible');
});
});
8 changes: 8 additions & 0 deletions packages/manager/cypress/support/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ export const fbtVisible = (text) => {
export const fbtClick = (text) => {
return cy.findByText(text).click();
};

export const fbltVisible = (text) => {
return cy.findByLabelText(text).should(visible);
};

export const fbltClick = (text) => {
return cy.findByLabelText(text).click();
};
64 changes: 63 additions & 1 deletion packages/manager/cypress/support/intercepts/domains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @file Cypress intercepts and mocks for Domain API requests.
*/

import type { Domain } from '@linode/api-v4/types';
import type { Domain, DomainRecord, ZoneFile } from '@linode/api-v4/types';
import { apiMatcher } from 'support/util/intercepts';
import { paginateResponse } from 'support/util/paginate';

Expand Down Expand Up @@ -34,3 +34,65 @@ export const mockGetDomains = (domains: Domain[]): Cypress.Chainable<null> => {
export const interceptCreateDomainRecord = (): Cypress.Chainable<null> => {
return cy.intercept('POST', apiMatcher('domains/*/record*'));
};

/**
* Intercepts GET request to get Domain records.
*
* @param records - an array of mock domain record objects
*
* @returns Cypress chainable.
*/
export const mockGetDomainRecords = (
records: DomainRecord[]
): Cypress.Chainable<null> => {
return cy.intercept(
'GET',
apiMatcher('domains/*/record*'),
paginateResponse(records)
);
};

/**
* Intercepts POST request to import a Domain Zone.
*
* @param domain - a mock domain object
*
* @returns Cypress chainable.
*/
export const mockImportDomain = (domain: Domain): Cypress.Chainable<null> => {
return cy.intercept('POST', apiMatcher('domains/import'), domain);
};

/**
* Intercepts GET request to get a Domain detail.
*
* @param domainId - a mock domain ID
* @param domain - a mock domain
*
* @returns Cypress chainable.
*/
export const mockGetDomain = (
domainId: string,
domain: Domain
): Cypress.Chainable<null> => {
return cy.intercept('GET', apiMatcher(`domains/${domainId}`), domain);
};

/**
* Intercepts GET request to get a Domain detail.
*
* @param domainId - a mock domain ID
* @param zoneFile - a mock ZoneFile object
*
* @returns Cypress chainable.
*/
export const mockGetDomainZoneFile = (
domainId: string,
zoneFile: ZoneFile
): Cypress.Chainable<null> => {
return cy.intercept(
'GET',
apiMatcher(`domains/${domainId}/zone-file`),
zoneFile
);
};
10 changes: 9 additions & 1 deletion packages/manager/src/factories/domain.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import * as Factory from 'factory.ts';
import { Domain, DomainRecord } from '@linode/api-v4/lib/domains/types';
import {
Domain,
DomainRecord,
ZoneFile,
} from '@linode/api-v4/lib/domains/types';

export const domainFactory = Factory.Sync.makeFactory<Domain>({
domain: Factory.each((id) => `domain-${id}`),
Expand Down Expand Up @@ -34,3 +38,7 @@ export const domainRecordFactory = Factory.Sync.makeFactory<DomainRecord>({
created: '2020-05-19T19:07:36',
updated: '2020-05-19T19:07:36',
});

export const domainZoneFileFactory = Factory.Sync.makeFactory<ZoneFile>({
zone_file: ['test line 1', 'test line 2'],
});

0 comments on commit 1e52cb0

Please sign in to comment.