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-8393] - Cypress test for Account Maintenance CSV downloads #11168

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
107b657
Updated test confirm maintenance details in the tables - to validate …
subsingh-akamai Oct 28, 2024
f5a15e1
Merge branch 'linode:develop' into M3-8393-Cypress-test-for-Account-M…
subsingh-akamai Oct 28, 2024
6740ebd
Added changeset: Cypress test for Account Maintenance CSV downloads
subsingh-akamai Oct 28, 2024
d64f88c
Merge branch 'M3-8393-Cypress-test-for-Account-Maintenance-CSV-downlo…
subsingh-akamai Oct 28, 2024
f31498a
Removed console.log
subsingh-akamai Nov 8, 2024
d14241e
Removed library - papaparse to parse csv;Implemented script to parse …
subsingh-akamai Nov 11, 2024
b1220ef
Removed it.only from test
subsingh-akamai Nov 11, 2024
3f4394b
Merge branch 'linode:develop' into M3-8393-Cypress-test-for-Account-M…
subsingh-akamai Nov 14, 2024
b649c44
Merge branch 'linode:develop' into M3-8393-Cypress-test-for-Account-M…
subsingh-akamai Nov 18, 2024
2c538c2
Using specific locator for Download CSV and added code to delete down…
subsingh-akamai Nov 18, 2024
25dccb2
Merge branch 'M3-8393-Cypress-test-for-Account-Maintenance-CSV-downlo…
subsingh-akamai Nov 18, 2024
00ff32c
Refeactoring as per review comment for delete files
subsingh-akamai Nov 21, 2024
58fbdd7
Merge branch 'linode:develop' into M3-8393-Cypress-test-for-Account-M…
subsingh-akamai Nov 21, 2024
97004e4
Merge branch 'M3-8393-Cypress-test-for-Account-Maintenance-CSV-downlo…
subsingh-akamai Nov 21, 2024
764fed3
Added assertion in readFile to ensure file content is not empty
subsingh-akamai Nov 21, 2024
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
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-11168-tests-1730108478631.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tests
---

Cypress test for Account Maintenance CSV downloads ([#11168](https://github.com/linode/manager/pull/11168))
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { mockGetMaintenance } from 'support/intercepts/account';
import { accountMaintenanceFactory } from 'src/factories';
import { parseCsv } from 'support/util/csvUtils';

describe('Maintenance', () => {
/*
Expand Down Expand Up @@ -118,12 +119,140 @@ describe('Maintenance', () => {
});
});

// Confirm download buttons work
cy.get('button')
.filter(':contains("Download CSV")')
.should('be.visible')
.should('be.enabled')
.click({ multiple: true });
// TODO Need to add assertions to confirm CSV contains the expected contents on first trial (M3-8393)
// Validate content of the downloaded CSV for pending maintenance
cy.get('a[download*="pending-maintenance"]')
.invoke('attr', 'download')
.then((fileName) => {
const downloadsFolder = Cypress.config('downloadsFolder');

// Delete the file before the test
cy.exec(`rm -f ${downloadsFolder}/${fileName}`).then((result) => {
if (result.code === 0) {
cy.log(`Deleted file: ${fileName}`);
} else {
cy.log(`Failed to delete file: ${fileName}`);
}
});
subsingh-akamai marked this conversation as resolved.
Show resolved Hide resolved

// Locate the <a> element for pending-maintenance and then find its sibling <button> element
cy.get('a[download*="pending-maintenance"]')
.siblings('button')
.should('be.visible')
.and('contain', 'Download CSV')
.click();

// Map the expected CSV content to match the structure of the downloaded CSV
const expectedPendingMigrationContent = accountpendingMaintenance.map(
(maintenance) => ({
entity_label: maintenance.entity.label,
entity_type: maintenance.entity.type,
type: maintenance.type,
status: maintenance.status,
reason: maintenance.reason,
})
);

// Read the downloaded CSV and compare its content to the expected CSV content
cy.readFile(`${downloadsFolder}/${fileName}`)
.then((csvContent) => {
const parsedCsvPendingMigration = parseCsv(csvContent);
subsingh-akamai marked this conversation as resolved.
Show resolved Hide resolved
expect(parsedCsvPendingMigration.length).to.equal(
expectedPendingMigrationContent.length
);
// Map the parsedCsv to match the structure of expectedCsvContent
const actualPendingMigrationCsvContent = parsedCsvPendingMigration.map(
(entry: any) => ({
entity_label: entry['Entity Label'],
entity_type: entry['Entity Type'],
type: entry['Type'],
status: entry['Status'],
reason: entry['Reason'],
})
);

expect(actualPendingMigrationCsvContent).to.deep.equal(
expectedPendingMigrationContent
);
})
.then(() => {
// Delete the file after the assertions
cy.exec(`rm -f ${downloadsFolder}/${fileName}`).then((result) => {
if (result.code === 0) {
cy.log(`Deleted file: ${fileName}`);
} else {
cy.log(`Failed to delete file: ${fileName}`);
}
});
});
subsingh-akamai marked this conversation as resolved.
Show resolved Hide resolved
});

// Validate content of the downloaded CSV for completed maintenance
cy.get('a[download*="completed-maintenance"]')
.invoke('attr', 'download')
.then((fileName) => {
const downloadsFolder = Cypress.config('downloadsFolder');

// Delete the file before the test
cy.exec(`rm -f ${downloadsFolder}/${fileName}`).then((result) => {
if (result.code === 0) {
cy.log(`Deleted file: ${fileName}`);
} else {
cy.log(`Failed to delete file: ${fileName}`);
}
});

// Locate the <a> element for completed-maintenance and then find its sibling <button> element
cy.get('a[download*="completed-maintenance"]')
.siblings('button')
.should('be.visible')
.and('contain', 'Download CSV')
.click();

// Map the expected CSV content to match the structure of the downloaded CSV
const expectedCompletedMigrationContent = accountcompletedMaintenance.map(
(maintenance) => ({
entity_label: maintenance.entity.label,
entity_type: maintenance.entity.type,
type: maintenance.type,
status: maintenance.status,
reason: maintenance.reason,
})
);

// Read the downloaded CSV and compare its content to the expected CSV content
cy.readFile(`${downloadsFolder}/${fileName}`)
.then((csvContent) => {
const parsedCsvCompletedMigration = parseCsv(csvContent);

expect(parsedCsvCompletedMigration.length).to.equal(
expectedCompletedMigrationContent.length
);

// Map the parsedCsv to match the structure of expectedCsvContent
const actualCompletedMigrationCsvContent = parsedCsvCompletedMigration.map(
(entry: any) => ({
entity_label: entry['Entity Label'],
entity_type: entry['Entity Type'],
type: entry['Type'],
status: entry['Status'],
reason: entry['Reason'],
})
);

expect(actualCompletedMigrationCsvContent).to.deep.equal(
expectedCompletedMigrationContent
);
})
.then(() => {
// Delete the file after the assertions
cy.exec(`rm -f ${downloadsFolder}/${fileName}`).then((result) => {
if (result.code === 0) {
cy.log(`Deleted file: ${fileName}`);
} else {
cy.log(`Failed to delete file: ${fileName}`);
}
});
});
});
});
});
48 changes: 48 additions & 0 deletions packages/manager/cypress/support/util/csvUtils.ts
subsingh-akamai marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @file Utilities for handling csv files.
*/

/**
* Parses a CSV string and returns an array of objects representing the data.
*
* @param {string} csvContent - The CSV content as a string.
* @returns {any[]} - An array of objects where each object represents a row in the CSV.
* The keys of the objects are the headers from the CSV.
*/

export function parseCsv(csvContent: string): any[] {
// Split the CSV content into lines and filter out any empty lines
const lines = csvContent.split('\n').filter((line) => line.trim() !== '');

// Extract the headers from the first line and remove any quotes
const headers = lines[0]
.split(',')
.map((header) => header.trim().replace(/^"|"$/g, ''));

// Map the remaining lines to objects using the headers
const data = lines.slice(1).map((line) => {
// Split each line into values, handling quoted values with commas and embedded quotes
// The regular expression matches:
// - Values enclosed in double quotes, which may contain commas and escaped double quotes (e.g., "value, with, commas" or "value with ""embedded"" quotes")
// - Values not enclosed in double quotes, which are separated by commas
// The map function then:
// - Trims any leading or trailing whitespace from each value
// - Removes the enclosing double quotes from quoted values
// - Replaces any escaped double quotes within quoted values with a single double quote
const values = line
.match(/("([^"]|"")*"|[^",\s]+)(?=\s*,|\s*$)/g)
?.map((value) => value.trim().replace(/^"|"$/g, '').replace(/""/g, '"'));

// Create an object to represent the row
const entry: any = {};
headers.forEach((header, index) => {
entry[header] = values ? values[index] : '';
});

// Return the object representing the row
return entry;
});

// Return the array of objects representing the CSV data
return data;
}