Skip to content

Commit

Permalink
Cypress e2e Test - Verify links in the Manifest Directory (#3570)
Browse files Browse the repository at this point in the history
* Initial WIP version of resource creation test

* Experimental changes to poll the UI for updates

* Working version if resource is present

* increase card timeout and delete active wait

* Added changes to find namespace from variables

* Final changes to read variables, cleaned up utils

* Small change to a comment

* Dummy change to trigger mocks

* Save changes on cypress-RHOAIENG-12649

* Changed file directories and names as requested on a PR comment

* Saving changes to current branch

* Additional directory/file name changes

* Additional changes to save

* Resolving timeout issue breaking mock tests, also resolved latest PR comments

* Further changes for this test

* Changes to revert the exist method appended to getCardView.

* Fixed linting

* Linting fixes

* Final comments added

* Fixed merge conflict

* Small change to page object name

* dummy commit

* Removed RHOAI bug workaround

* Removed comments

* Last comment change

* initial working commit, manifest dir is read but error in validating

* additional debugging stuff

* Comitting working version of the test which contains exclusions. Note, these wil be removed in the next commit

* Draft commit

* Committing working test - second version. Expanded the grep to find http and also nested elements

* Removed commented URLs that were excluded. Raised bugs and linked to https://issues.redhat.com/browse/RHOAIENG-9235

* Fixed hardcoded local path

* Making changes requested to pull excluded URLs from fixtures and extract a function to utils

* Dummy change to kick off mocks

* Dummy change to kick off Mock tests

---------

Co-authored-by: Fede Alonso <[email protected]>
  • Loading branch information
antowaddle and FedeAlonso authored Dec 13, 2024
1 parent 5331d69 commit 4510b81
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 2 deletions.
4 changes: 4 additions & 0 deletions frontend/src/__tests__/cypress/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { mergeFiles } from 'junit-report-merger';
import { interceptSnapshotFile } from '~/__tests__/cypress/cypress/utils/snapshotUtils';
import { setup as setupWebsockets } from '~/__tests__/cypress/cypress/support/websockets';
import { env, cypressEnv, BASE_URL } from '~/__tests__/cypress/cypress/utils/testConfig';
import { extractHttpsUrls } from '~/__tests__/cypress/cypress/utils/urlExtractor';

const resultsDir = `${env.CY_RESULTS_DIR || 'results'}/${env.CY_MOCK ? 'mocked' : 'e2e'}`;

Expand Down Expand Up @@ -78,6 +79,9 @@ export default defineConfig({

return Promise.resolve({});
},
extractHttpsUrls(directory: string) {
return extractHttpsUrls(directory);
},
log(message) {
// eslint-disable-next-line no-console
console.log(message);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# testManifestLinks.cy.ts Test Data #
excludedSubstrings:
- my-project-s2i-python-service
- clusterip/
- ClusterIP
- s2i-python-service
- user-dev-rhoam-quarkus
- project-simple
- example.apps
- localhost
- console-openshift-console.apps.test-cluster.example.com/
- console-openshift-console.apps.test-cluster.example.com
- repo.anaconda.cloud/repo/t/$
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { getOcResourceNames } from '~/__tests__/cypress/cypress/utils/oc_command

const applicationNamespace = Cypress.env('TEST_NAMESPACE');

describe('Verify RHODS Explore Section Contains Only Configured ISVs', () => {
describe('Verify RHODS Explore Section Contains Only Expected ISVs', () => {
let expectedISVs: string[];

before(() => {
// Setup: Retrieve the names of OdhApplication resources in the specified namespace.
getOcResourceNames(applicationNamespace, 'OdhApplication').then((metadataNames) => {
expectedISVs = metadataNames.filter((isv) => isv);
cy.log(
`Configured ISVs which should display as Cards in Explore Section: ${expectedISVs.join(
`Expected ISVs which should display as Cards in Explore Section: ${expectedISVs.join(
', ',
)}`,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as yaml from 'js-yaml';
import { isUrlExcluded } from '~/__tests__/cypress/cypress/utils/urlExtractor';

describe('Verify that all the URLs referenced in the Manifest directory are operational', () => {
let excludedSubstrings: string[];

// Setup: Load test data
before(() => {
cy.fixture('e2e/dashboardNavigation/testManifestLinks.yaml', 'utf8').then((yamlString) => {
const yamlData = yaml.load(yamlString) as { excludedSubstrings: string[] };
excludedSubstrings = yamlData.excludedSubstrings;
});
});

it('Reads the manifest directory, filters out test/sample URLs and validates the remaining URLs', () => {
const manifestsDir = '../../../../manifests';
cy.log(`Resolved manifests directory: ${manifestsDir}`);

// Extract URLs from the manifests directory using the registered task
cy.task<string[]>('extractHttpsUrls', manifestsDir).then((urls) => {
// Filter out Sample/Test URLs
const filteredUrls = urls.filter((url) => !isUrlExcluded(url, excludedSubstrings));

// Log filtered URLs for debugging
filteredUrls.forEach((url) => {
cy.log(url);
});

// Verify that each remaining URL is accessible and returns a 200 status code
cy.step(
'Verify that each filtered URL is accessible and that a 200 is returned - currently failing due to issues linked RHOAIENG-9235',
);
filteredUrls.forEach((url) => {
cy.request(url).then((response) => {
const { status } = response;
const logMessage =
status === 200 ? `✅ ${url} - Status: ${status}` : `❌ ${url} - Status: ${status}`;
cy.log(logMessage);
expect(status).to.eq(200); // Assert that the response status is 200
});
});
});
});
});
94 changes: 94 additions & 0 deletions frontend/src/__tests__/cypress/cypress/utils/urlExtractor.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as yaml from 'js-yaml';

/**
* Extracts URLs from all anchor elements on the current page.
* This function finds all 'a' elements, extracts their 'href' attributes,
Expand All @@ -17,3 +21,93 @@ export function extractLauncherUrls(): Cypress.Chainable<string[]> {
return extractedUrls;
});
}
/**
* Type definitions for YAML values.
*/
type YamlValue = string | number | boolean | null | YamlObject | YamlArray;
interface YamlObject {
[key: string]: YamlValue;
}
type YamlArray = YamlValue[];

/**
* Extracts URLs from a given value and adds them to the provided set.
*
* @param {YamlValue} value - The value to extract URLs from.
* @param {Set<string>} urlSet - A set to store unique URLs.
*/
function extractUrlsFromValue(value: YamlValue, urlSet: Set<string>): void {
if (typeof value === 'string') {
const urlRegex = /https?:\/\/[^\s\](),"'}*]*(?=[\s\](),"'}*]|$)/g; // Matches both HTTP and HTTPS
const matches = value.match(urlRegex);
if (matches) {
matches.forEach((url) => {
const cleanUrl = url.replace(/\*+$/, ''); // Remove trailing asterisks if any
urlSet.add(cleanUrl); // Add URL to the set for uniqueness
});
}
} else if (typeof value === 'object' && value !== null) {
if (Array.isArray(value)) {
value.forEach((item) => extractUrlsFromValue(item, urlSet)); // Process array items
} else {
Object.values(value).forEach((val) => extractUrlsFromValue(val, urlSet)); // Process object values
}
}
}

/**
* Extracts HTTPS URLs from YAML files in specified directories.
*
* @param {string} directory - The directory path to search for YAML files.
* @returns {string[]} An array of HTTPS URLs extracted from YAML files.
*/
export function extractHttpsUrls(directory: string): string[] {
const httpsUrlSet = new Set<string>();

function walkDir(dir: string): void {
const files = fs.readdirSync(dir);
files.forEach((file) => {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
walkDir(filePath); // Recursively walk into subdirectories
} else if (file.endsWith('.yaml') || file.endsWith('.yml')) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const yamlDocuments = yaml.loadAll(content); // Load multiple YAML documents

// Process each document
yamlDocuments.forEach((yamlContent) => {
// Check if yamlContent is an object or array and extract URLs
if (typeof yamlContent === 'object' && yamlContent !== null) {
// Check for 'spec' key and extract URL if it exists
if (
'spec' in yamlContent &&
typeof yamlContent.spec === 'object' &&
yamlContent.spec !== null
) {
if ('url' in yamlContent.spec) {
// Use type assertion to specify that spec.url is a string or undefined
extractUrlsFromValue(yamlContent.spec.url as string, httpsUrlSet);
}
}

// Process all other values to find additional URLs
Object.values(yamlContent).forEach((value) =>
extractUrlsFromValue(value, httpsUrlSet),
);
}
});
} catch (error) {
// Catches file reading errors
}
}
});
}

walkDir(directory); // Start walking the directory
return Array.from(httpsUrlSet); // Return the unique set of URLs as an array
}
export const isUrlExcluded = (url: string, excludedSubstrings: string[]): boolean => {
return excludedSubstrings.some((substring) => url.includes(substring));
};

0 comments on commit 4510b81

Please sign in to comment.