diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a37aaf3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.env* +.git* +.nvmrc +README.md diff --git a/.env.example b/.env.example index 057fe67..f0962cb 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,6 @@ CLUSTER_DOMAIN= PASSWORD= USERNAME= -RETRY_DELAY= \ No newline at end of file +RETRY_DELAY=10000 +CLOUD_PROVIDER=aws +MAX_TIME_TO_WAIT=5m diff --git a/cypress.config.ts b/cypress.config.ts index 8cb1966..51cecbc 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,7 +1,5 @@ import { defineConfig } from "cypress"; -import * as dotenv from "dotenv"; - -dotenv.config(); +import "dotenv/config"; export default defineConfig({ e2e: { @@ -10,6 +8,8 @@ export default defineConfig({ USERNAME: process.env.USERNAME, PASSWORD: process.env.PASSWORD, RETRY_DELAY: process.env.RETRY_DELAY || 10000, + CLOUD_PROVIDER: process.env.CLOUD_PROVIDER, + MAX_TIME_TO_WAIT: process.env.MAX_TIME_TO_WAIT || "1h", }, viewportWidth: 2000, viewportHeight: 900, diff --git a/cypress/e2e/physical-cluster.cy.ts b/cypress/e2e/physical-cluster.cy.ts index a5052be..036f6ee 100644 --- a/cypress/e2e/physical-cluster.cy.ts +++ b/cypress/e2e/physical-cluster.cy.ts @@ -1,11 +1,17 @@ +import ms from "ms"; + import { Account } from "../../types/accouts"; +const CLUSTER_NAME = "test-cluster"; +const isAWS = Cypress.env("CLOUD_PROVIDER") === "aws"; +const MAX_TIME_TO_WAIT = Cypress.env("MAX_TIME_TO_WAIT"); + // Utility function to fill out the form to create a physical cluster const fillOutForm = () => { cy.get("form").as("form"); cy.get("@form").should("exist"); - cy.findByRole("textbox").type("test-cluster", { + cy.findByRole("textbox").type(CLUSTER_NAME, { delay: 10, }); @@ -44,42 +50,84 @@ describe("Test to validate physical cluster creation", () => { cy.login(username, password); }); - it("should create a physical cluster", () => { - cy.visit("/"); - cy.findByRole("button", { name: /add workload cluster/i }).as("button"); + if (isAWS) { + it("should create a physical cluster", () => { + cy.visit("/"); + cy.findByRole("button", { name: /add workload cluster/i }).as("button"); + + cy.request("GET", "/api/proxy?url=%2Fcloud-account").then( + ({ status, body }) => { + expect(status).to.eq(200); + + const { cloud_accounts: cloudAccounts } = body; + cy.wrap(cloudAccounts as Account[]).as("accounts"); + } + ); + + cy.get("@accounts").then((accounts) => { + const cloudAccounts = accounts as unknown as Account[]; + expect(cloudAccounts).to.be.an("array"); + expect(cloudAccounts).to.have.length.greaterThan(0); + + const defaultAccount = cloudAccounts.find( + (account) => account.name === "default" + ); + + expect(defaultAccount).to.exist; + }); + + cy.get("@button").click(); - cy.request("GET", "/api/proxy?url=%2Fcloud-account").then( - ({ status, body }) => { - expect(status).to.eq(200); + fillOutForm(); - const { cloud_accounts: cloudAccounts } = body; - cy.wrap(cloudAccounts as Account[]).as("accounts"); - } - ); + cy.findByRole("button", { + name: /create cluster/i, + }).click(); - cy.get("@accounts").then((accounts) => { - const cloudAccounts = accounts as unknown as Account[]; - expect(cloudAccounts).to.be.an("array"); - expect(cloudAccounts).to.have.length.greaterThan(0); + cy.wait(2000); - const defaultAccount = cloudAccounts.find( - (account) => account.name === "default" + cy.findByRole("heading", { name: new RegExp(CLUSTER_NAME, "i") }).should( + "exist" ); + cy.contains("Provisioning").should("exist"); + }); + + it("should validate the cluster is provisioning", () => { + cy.visit("/"); + + cy.goClusterManagement(); + + cy.findAllByRole("button", { name: new RegExp(CLUSTER_NAME, "i") }) + .eq(0) + .as("clusterProvisioningButton"); - expect(defaultAccount).to.exist; + cy.get("@clusterProvisioningButton").should("exist"); + + cy.get("@clusterProvisioningButton").within(() => { + cy.findByText(/provisioning/i).should("exist"); + }); }); - cy.get("@button").click(); + it("should validate the cluster is provisioned", { retries: 3 }, () => { + cy.visit("/"); - fillOutForm(); + cy.goClusterManagement(); - cy.findByRole("button", { - name: /create cluster/i, - }).click(); + cy.findAllByRole("button", { name: new RegExp(CLUSTER_NAME, "i") }) + .eq(0) + .as("clusterProvisionedButton"); - cy.wait(2000); + cy.get("@clusterProvisionedButton").should("exist"); - cy.findByRole("heading", { name: /test-cluster/i }).should("exist"); - cy.contains("Provisioning").should("exist"); - }); + cy.get("@clusterProvisionedButton").within(() => { + cy.findByText(/provisioned/i, { + timeout: Number(ms(MAX_TIME_TO_WAIT)), + }).should("exist"); + }); + }); + } else { + it("Test not run because the cloud provider is not AWS", () => { + cy.log("Skipping test because the cloud provider is not AWS"); + }); + } }); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index f72e8f7..7c3b83f 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -4,6 +4,7 @@ declare global { interface Chainable { login(username: string, password: string): Chainable; goApplications(): Chainable; + goClusterManagement(): Chainable; } } } @@ -36,3 +37,8 @@ Cypress.Commands.add("goApplications", () => { cy.visit("/"); cy.findByRole("link", { name: /applications/i }).click(); }); + +Cypress.Commands.add("goClusterManagement", () => { + cy.visit("/"); + cy.findByRole("link", { name: /cluster management/i }).click(); +}); diff --git a/cypress/support/hooks.ts b/cypress/support/hooks.ts index 60d5ea5..351175a 100644 --- a/cypress/support/hooks.ts +++ b/cypress/support/hooks.ts @@ -1,6 +1,6 @@ afterEach(function () { - // if (this.currentTest.isFailed()) { - // const retryDelay = Cypress.env("RETRY_DELAY"); - // cy.wait(+retryDelay); - // } + if (this.currentTest.state === "failed") { + const retryDelay = Cypress.env("RETRY_DELAY"); + cy.wait(+retryDelay); + } }); diff --git a/git-clone.sh b/git-clone.sh deleted file mode 100755 index 337be00..0000000 --- a/git-clone.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash - -# Check if the correct number of arguments is provided -if [ "$#" -ne 3 ]; then - echo "{\"code\": 1, \"status\": 400, \"error\": \"Usage: $0 \"}" - exit 1 -fi - -# Assign arguments to variables -git_owner=$1 -gitops_token=$2 -git_username=$3 - -# Install GitHub CLI (make sure this is necessary and handle it carefully) -# apk update -# apk add github-cli -# brew install gh - -# Save the token to a file (for authentication) -echo "$gitops_token" > token.txt - -# Authenticate with GitHub -gh auth login --git-protocol https --with-token < token.txt -if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 401, \"error\": \"Authentication failed.\"}" - exit 1 -fi - -# Clone the repository -url="https://$gitops_token@github.com/$git_username/gitops.git" -git clone "$url" -if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to clone repository.\"}" - exit 1 -fi - -git config --global user.name "$git_owner" -git config --global user.email "1@gmail.com" - -# Change directory to the cloned repository -cd gitops || { echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to change directory to gitops\"}"; exit 1; } - -# Create and checkout a new branch -git checkout -b cypress-patch -if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to checkout branch.\"}" - exit 1 -fi - -# Append whitespace to the specified files -for file in terraform/vault/main.tf terraform/users/main.tf; do - echo " " >> "$file" -done - -# Add, commit, and push changes -git add . -git commit -m "whitespace changes to trigger atlantis" -# Check if the branch exists on the remote -if git ls-remote --exit-code --heads origin cypress-patch; then - echo "Deleting remote branch cypress-patch..." - git push origin --delete cypress-patch - - # Check if the delete command succeeded - if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to delete remote branch.\"}" - exit 1 - fi -fi - -# Attempt to push changes to the branch -git push origin cypress-patch - -# Check if the push command succeeded -if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to push changes.\"}" - exit 1 -fi - -# Create a pull request -gh pr create --title "Trigger Atlantis" --body "Trigger Atlantis with whitespace changes" -if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to create pull request.\"}" - exit 1 -fi - -# Comment on the pull request (ensure you have the PR number or provide it dynamically) -gh pr comment --body "atlantis plan" -if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to comment on pull request.\"}" - exit 1 -fi - -gh pr comment --body "atlantis apply" -if [ $? -ne 0 ]; then - echo "{\"code\": 1, \"status\": 500, \"error\": \"Failed to comment on pull request.\"}" - exit 1 -fi - -# Return success response -echo "{\"code\": 0, \"status\": 200, \"error\": \"\"}" diff --git a/package-lock.json b/package-lock.json index 95ecd03..cb47461 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,11 @@ "@testing-library/cypress": "^10.0.2", "cypress": "^13.16.1", "dotenv": "^16.4.7", + "ms": "^2.1.3", "typescript": "^5.7.2" + }, + "devDependencies": { + "@types/ms": "^0.7.34" } }, "node_modules/@babel/code-frame": { @@ -221,6 +225,12 @@ "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "license": "MIT" }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "node_modules/@types/node": { "version": "22.1.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", @@ -785,6 +795,11 @@ } } }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -1529,10 +1544,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/npm-run-path": { "version": "4.0.1", diff --git a/package.json b/package.json index 097ebb6..d208904 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,17 @@ { + "scripts": { + "deps:update": "npx npm-check-updates --interactive --format group", + "cy:open": "cypress open", + "cy:run": "cypress run" + }, "dependencies": { "@testing-library/cypress": "^10.0.2", "cypress": "^13.16.1", "dotenv": "^16.4.7", + "ms": "^2.1.3", "typescript": "^5.7.2" }, - "scripts": { - "deps:update": "npx npm-check-updates --interactive --format group", - "cy:open": "cypress open", - "cy:run": "cypress run" + "devDependencies": { + "@types/ms": "^0.7.34" } -} +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 74fc54d..63eccb0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,9 @@ "compilerOptions": { "target": "es5", "lib": ["es5", "dom"], - "types": ["cypress", "node","@testing-library/cypress"] + "types": ["cypress", "node", "@testing-library/cypress"], + "esModuleInterop": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "custom.d.ts", ".next/types/**/*.ts", "checkStatus.ts"], - "exclude": ["node_modules", "dist", "server.js", "tests", "**/*.test.*", "**/*.spec.ts", ".next"] + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] }